Skip to content
Browse files

add sources

  • Loading branch information...
0 parents commit e23f2db9a913b9e84da5185ed63973ee51a57832 @yaoweibin committed Mar 1, 2010
Showing with 4,856 additions and 0 deletions.
  1. +109 −0 README
  2. +24 −0 config
  3. +648 −0 modules/ngx_tcp_proxy_module.c
  4. +228 −0 modules/ngx_tcp_upstream_ip_hash_module.c
  5. +513 −0 ngx_tcp.c
  6. +185 −0 ngx_tcp.h
  7. +492 −0 ngx_tcp_core_module.c
  8. +386 −0 ngx_tcp_session.c
  9. +104 −0 ngx_tcp_session.h
  10. +979 −0 ngx_tcp_upstream.c
  11. +325 −0 ngx_tcp_upstream.h
  12. +751 −0 ngx_tcp_upstream_round_robin.c
  13. +80 −0 ngx_tcp_upstream_round_robin.h
  14. +32 −0 tcp.patch
109 README
@@ -0,0 +1,109 @@
+Name
+ nginx_tcp_proxy_module
+
+Status
+ This module is at its very early phase of development and
+ considered highly experimental. But you're encouraged to
+ test it out on your side and report any quirks that you
+ experience.
+
+ We need your help! If you find this module useful and/or
+ interesting, please consider joining the development!
+ Commit bit can be freely delivered at your request ;)
+
+Synopsis
+
+ tcp {
+ ...
+
+ upstream cluster {
+ # simple round-robin
+ server 127.0.0.1:3306;
+ server 127.0.0.1:1234;
+ }
+
+ server {
+ proxy_pass cluster;
+ }
+ }
+
+Description
+
+Installation
+ Grab the nginx source code from nginx.net (<http://nginx.net/>), for
+ example, the version 0.8.30 (see nginx compatibility), and then build
+ the source with this module:
+
+ $ wget 'http://sysoev.ru/nginx/nginx-0.8.30.tar.gz'
+ $ tar -xzvf nginx-0.8.30.tar.gz
+ $ cd nginx-0.8.30/
+
+ $ ./configure --add-module=/path/to/nginx_tcp_proxy_module
+
+ $ make
+ $ make install
+
+ Download the latest version of the release tarball of this module from
+
+Compatibility
+ The following versions of Nginx should work with this module:
+
+ * 0.8.x (last tested version is 0.8.32)
+
+ * 0.7.x >= 0.7.44 (last tested version is 0.7.64)
+
+ Earlier versions of Nginx like 0.6.x and 0.5.x will *not* work.
+
+ If you find that any particular version of Nginx above 0.7.44 does not
+ work with this module, please consider reporting a bug.
+
+TODO
+ * test
+
+Known Issues
+ * test
+
+See Also
+ * test
+
+Authors
+ Weibin Yao(姚伟斌) <yaoweibin at gmail dot com>,
+
+Copyright & License
+
+ I borrowed a lot of code from upstream and mail module in the nginx 0.7.*
+ core. This part of code is copyrighted by Igor Sysoev is .
+
+ This module is licenced under the BSD license.
+
+ Copyright (C) 2010 by Weibin Yao <yaoweibin@gmail.com>.
+
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the Taobao Inc. nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
24 config
@@ -0,0 +1,24 @@
+ngx_feature="nginx_tcp_module"
+ngx_feature_name=
+ngx_feature_run=no
+ngx_feature_incs=
+ngx_feature_path="$ngx_addon_dir/modules $ngx_addon_dir"
+ngx_feature_deps="$ngx_addon_dir/ngx_tcp.h $ngx_addon_dir/ngx_tcp_session.h $ngx_addon_dir/ngx_tcp_upstream.h $ngx_addon_dir/ngx_tcp_upstream_round_robin.h"
+ngx_tcp_src="$ngx_addon_dir/ngx_tcp.c $ngx_addon_dir/ngx_tcp_core_module.c $ngx_addon_dir/ngx_tcp_session.c $ngx_addon_dir/ngx_tcp_upstream.c $ngx_addon_dir/ngx_tcp_upstream_round_robin.c $ngx_addon_dir/modules/ngx_tcp_proxy_module.c $ngx_addon_dir/modules/ngx_tcp_upstream_ip_hash_module.c "
+ngx_feature_test="int a;"
+. auto/feature
+
+if [ $ngx_found = yes ]; then
+ CORE_INCS="$CORE_INCS $ngx_feature_path"
+ ngx_addon_name=ngx_tcp_module
+ TCP_CORE_MODULES="ngx_tcp_module ngx_tcp_core_module ngx_tcp_upstream_module"
+ TCP_MODULES="ngx_tcp_proxy_module ngx_tcp_upstream_ip_hash_module"
+ CORE_MODULES="$CORE_MODULES $TCP_CORE_MODULES $TCP_MODULES"
+ NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_feature_deps"
+ NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_tcp_src"
+else
+ cat << END
+ $0: error: the ngx_tcp_module addon error.
+END
+ exit 1
+fi
648 modules/ngx_tcp_proxy_module.c
@@ -0,0 +1,648 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_connect.h>
+#include <ngx_tcp.h>
+
+
+typedef struct ngx_tcp_proxy_conf_s {
+ ngx_tcp_upstream_conf_t upstream;
+
+ ngx_str_t url;
+ size_t buffer_size;
+ ngx_msec_t timeout;
+
+ ngx_array_t *proxy_lengths;
+ ngx_array_t *proxy_values;
+} ngx_tcp_proxy_conf_t;
+
+
+static void ngx_tcp_proxy_init(ngx_connection_t *c, ngx_tcp_session_t *s);
+static char *ngx_tcp_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static void ngx_tcp_proxy_dummy_read_handler(ngx_event_t *ev);
+static void ngx_tcp_proxy_dummy_write_handler(ngx_event_t *ev);
+static void ngx_tcp_proxy_handler(ngx_event_t *ev);
+/*static void ngx_tcp_proxy_upstream_error(ngx_tcp_session_t *s);*/
+/*static void ngx_tcp_proxy_internal_server_error(ngx_tcp_session_t *s);*/
+static void *ngx_tcp_proxy_create_conf(ngx_conf_t *cf);
+static char *ngx_tcp_proxy_merge_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+
+static ngx_command_t ngx_tcp_proxy_commands[] = {
+
+ { ngx_string("proxy_pass"),
+ NGX_TCP_MAIN_CONF|NGX_TCP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_tcp_proxy_pass,
+ NGX_TCP_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("proxy_buffer"),
+ NGX_TCP_MAIN_CONF|NGX_TCP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_TCP_SRV_CONF_OFFSET,
+ offsetof(ngx_tcp_proxy_conf_t, buffer_size),
+ NULL },
+
+ { ngx_string("proxy_timeout"),
+ NGX_TCP_MAIN_CONF|NGX_TCP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_TCP_SRV_CONF_OFFSET,
+ offsetof(ngx_tcp_proxy_conf_t, timeout),
+ NULL },
+
+ { ngx_string("proxy_connect_timeout"),
+ NGX_TCP_MAIN_CONF|NGX_TCP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_TCP_SRV_CONF_OFFSET,
+ offsetof(ngx_tcp_proxy_conf_t, upstream.connect_timeout),
+ NULL },
+
+ { ngx_string("proxy_read_timeout"),
+ NGX_TCP_MAIN_CONF|NGX_TCP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_TCP_SRV_CONF_OFFSET,
+ offsetof(ngx_tcp_proxy_conf_t, upstream.read_timeout),
+ NULL },
+
+ { ngx_string("proxy_send_timeout"),
+ NGX_TCP_MAIN_CONF|NGX_TCP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_TCP_SRV_CONF_OFFSET,
+ offsetof(ngx_tcp_proxy_conf_t, upstream.send_timeout),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_tcp_module_t ngx_tcp_proxy_module_ctx = {
+ NULL, /* protocol */
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_tcp_proxy_create_conf, /* create server configuration */
+ ngx_tcp_proxy_merge_conf /* merge server configuration */
+};
+
+
+ngx_module_t ngx_tcp_proxy_module = {
+ NGX_MODULE_V1,
+ &ngx_tcp_proxy_module_ctx, /* module context */
+ ngx_tcp_proxy_commands, /* module directives */
+ NGX_TCP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+void
+ngx_tcp_upstream_proxy_generic_handler(ngx_tcp_session_t *s, ngx_tcp_upstream_t *u) {
+
+ ngx_tcp_proxy_conf_t *pcf;
+ ngx_tcp_proxy_ctx_t *pctx;
+ ngx_connection_t *c;
+
+ c = s->connection;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_TCP, s->connection->log, 0, "tcp proxy upstream init proxy");
+
+ pcf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_proxy_module);
+
+ pctx = ngx_tcp_get_module_ctx(s, ngx_tcp_proxy_module);
+
+ if (pcf == NULL || pctx == NULL) {
+ ngx_tcp_finalize_session(s);
+ return;
+ }
+
+ s->connection->read->handler = ngx_tcp_proxy_handler;
+ s->connection->write->handler = ngx_tcp_proxy_handler;
+
+ pctx->upstream->connection->read->handler = ngx_tcp_proxy_handler;
+ pctx->upstream->connection->write->handler = ngx_tcp_proxy_handler;
+
+ ngx_add_timer(s->connection->read, pcf->timeout);
+ ngx_add_timer(pctx->upstream->connection->read, pcf->upstream.read_timeout);
+ ngx_add_timer(pctx->upstream->connection->write, pcf->upstream.send_timeout);
+
+ c->log->action = "ngx_tcp_proxy_handler";
+
+ ngx_tcp_proxy_handler(s->connection->read);
+
+ return;
+}
+
+static void
+ngx_tcp_proxy_generic_handler(ngx_event_t *rev) {
+
+ ngx_connection_t *c;
+ ngx_tcp_session_t *s;
+ ngx_tcp_proxy_conf_t *pcf;
+ ngx_tcp_proxy_ctx_t *pctx;
+
+ c = rev->data;
+ s = c->data;
+
+ if (rev->timedout) {
+ c->timedout = 1;
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "upstream timed out");
+ ngx_tcp_finalize_session(s);
+ return;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_TCP, rev->log, 0, "tcp proxy init proxy");
+
+ pcf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_proxy_module);
+
+ pctx = ngx_tcp_get_module_ctx(s, ngx_tcp_proxy_module);
+
+ if (pcf == NULL || pctx == NULL) {
+ ngx_tcp_finalize_session(s);
+ return;
+ }
+
+ s->connection->read->handler = ngx_tcp_proxy_handler;
+ s->connection->write->handler = ngx_tcp_proxy_handler;
+
+ pctx->upstream->connection->read->handler = ngx_tcp_proxy_handler;
+ pctx->upstream->connection->write->handler = ngx_tcp_proxy_handler;
+
+ ngx_add_timer(s->connection->read, pcf->timeout);
+ ngx_add_timer(pctx->upstream->connection->read, pcf->upstream.read_timeout);
+ ngx_add_timer(pctx->upstream->connection->write, pcf->upstream.send_timeout);
+
+ c->log->action = "ngx_tcp_proxy_handler";
+
+ ngx_tcp_proxy_handler(s->connection->read);
+
+ return;
+}
+
+void
+ngx_tcp_proxy_init_session(ngx_connection_t *c, ngx_tcp_session_t *s) {
+
+ c->read->handler = ngx_tcp_proxy_dummy_write_handler;
+ c->read->handler = ngx_tcp_proxy_dummy_read_handler;
+
+ if (ngx_tcp_upstream_create(s) != NGX_OK) {
+ ngx_tcp_finalize_session(s);
+ return;
+ }
+
+ /*do something about the proxy related part in the session struct*/
+
+ ngx_tcp_proxy_init(c, s);
+
+ return;
+}
+
+static void
+ngx_tcp_proxy_init(ngx_connection_t *c, ngx_tcp_session_t *s) {
+
+ int keepalive;
+ ngx_tcp_proxy_ctx_t *p;
+ ngx_tcp_proxy_conf_t *pcf;
+ ngx_tcp_core_srv_conf_t *cscf;
+ ngx_tcp_upstream_t *u;
+
+ s->connection->log->action = "connecting to upstream";
+
+ pcf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_proxy_module);
+
+ cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module);
+
+ if (cscf->so_keepalive) {
+ keepalive = 1;
+
+ if (setsockopt(s->connection->fd, SOL_SOCKET, SO_KEEPALIVE,
+ (const void *) &keepalive, sizeof(int)) == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, s->connection->log, ngx_socket_errno,
+ "setsockopt(SO_KEEPALIVE) failed");
+ }
+ }
+
+ p = ngx_pcalloc(s->connection->pool, sizeof(ngx_tcp_proxy_ctx_t));
+ if (p == NULL) {
+ ngx_tcp_finalize_session(s);
+ return;
+ }
+
+ ngx_tcp_set_ctx(s, p, ngx_tcp_proxy_module);
+
+ u = s->upstream;
+
+ u->conf = &pcf->upstream;
+
+ u->write_event_handler = ngx_tcp_upstream_proxy_generic_handler;
+ u->read_event_handler = ngx_tcp_upstream_proxy_generic_handler;
+
+ ngx_tcp_upstream_init(s);
+
+ /*upstream init fail*/
+ if (u->peer.connection->fd <= 0) {
+ return;
+ }
+
+ /*upstream init ok*/
+ p->upstream = &u->peer;
+
+ s->connection->read->handler = ngx_tcp_proxy_generic_handler;
+
+ p->buffer = ngx_create_temp_buf(s->connection->pool, pcf->buffer_size);
+ if (p->buffer == NULL) {
+ ngx_tcp_finalize_session(s);
+ return;
+ }
+
+ s->out.len = 0;
+
+ if (ngx_handle_read_event(s->connection->read, 0) != NGX_OK) {
+ ngx_tcp_finalize_session(s);
+ return;
+ }
+
+ return;
+}
+
+static ngx_int_t
+ngx_tcp_proxy_test_connect(ngx_connection_t *c) {
+
+ int err;
+ socklen_t len;
+
+#if (NGX_HAVE_KQUEUE)
+
+ if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+ if (c->write->pending_eof) {
+ c->log->action = "connecting to upstream";
+ (void) ngx_connection_error(c, c->write->kq_errno,
+ "kevent() reported that connect() failed");
+ return NGX_ERROR;
+ }
+
+ } else
+#endif
+ {
+ err = 0;
+ len = sizeof(int);
+
+ /**/
+ /** BSDs and Linux return 0 and set a pending error in err*/
+ /** Solaris returns -1 and sets errno*/
+
+
+ if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len)
+ == -1)
+ {
+ err = ngx_errno;
+ }
+
+ if (err) {
+ c->log->action = "connecting to upstream";
+ (void) ngx_connection_error(c, err, "connect() failed");
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
+
+static void
+ngx_tcp_proxy_dummy_write_handler(ngx_event_t *wev) {
+
+ ngx_connection_t *c;
+ ngx_tcp_session_t *s;
+
+ c = wev->data;
+ s = c->data;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_TCP, wev->log, 0, "tcp proxy dummy write handler: %d", c->fd);
+
+ if (ngx_tcp_proxy_test_connect(c) != NGX_OK
+ || ngx_handle_write_event(wev, 0) != NGX_OK) {
+ ngx_tcp_finalize_session(s);
+ }
+}
+
+static void
+ngx_tcp_proxy_dummy_read_handler(ngx_event_t *rev) {
+
+ ngx_connection_t *c;
+ ngx_tcp_session_t *s;
+
+ c = rev->data;
+ s = c->data;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_TCP, rev->log, 0, "tcp proxy dummy read handler: %d", c->fd);
+
+ if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+ ngx_tcp_finalize_session(s);
+ }
+}
+
+static void
+ngx_tcp_proxy_handler(ngx_event_t *ev) {
+
+ char *action, *recv_action, *send_action;
+ size_t size;
+ ssize_t n;
+ ngx_buf_t *b;
+ ngx_uint_t do_write;
+ ngx_connection_t *c, *src, *dst;
+ ngx_tcp_session_t *s;
+ ngx_tcp_proxy_conf_t *pcf;
+ ngx_tcp_proxy_ctx_t *pctx;
+
+ c = ev->data;
+ s = c->data;
+
+ if (ev->timedout) {
+ c->log->action = "proxying";
+
+ if (c == s->connection) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "client timed out");
+ c->timedout = 1;
+
+ } else {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "upstream timed out");
+ }
+
+ ngx_tcp_finalize_session(s);
+ return;
+ }
+
+ pctx = ngx_tcp_get_module_ctx(s, ngx_tcp_proxy_module);
+
+ if (pctx == NULL) {
+ ngx_tcp_finalize_session(s);
+ return;
+ }
+
+ if (c == s->connection) {
+ if (ev->write) {
+ recv_action = "proxying and reading from upstream";
+ send_action = "proxying and sending to client";
+ src = pctx->upstream->connection;
+ dst = c;
+ b = pctx->buffer;
+
+ } else {
+ recv_action = "proxying and reading from client";
+ send_action = "proxying and sending to upstream";
+ src = c;
+ dst = pctx->upstream->connection;
+ b = s->buffer;
+ }
+
+ } else {
+ if (ev->write) {
+ recv_action = "proxying and reading from client";
+ send_action = "proxying and sending to upstream";
+ src = s->connection;
+ dst = c;
+ b = s->buffer;
+
+ } else {
+ recv_action = "proxying and reading from upstream";
+ send_action = "proxying and sending to client";
+ src = c;
+ dst = s->connection;
+ b = pctx->buffer;
+ }
+ }
+
+ do_write = ev->write ? 1 : 0;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_TCP, ev->log, 0,
+ "tcp proxy handler: %d, #%d > #%d",
+ do_write, src->fd, dst->fd);
+
+ for ( ;; ) {
+
+ if (do_write) {
+
+ size = b->last - b->pos;
+
+ if (size && dst->write->ready) {
+ c->log->action = send_action;
+
+ n = dst->send(dst, b->pos, size);
+ ngx_log_debug1(NGX_LOG_DEBUG_TCP, ev->log, 0, "tcp proxy handler send:%d", n);
+
+ if (n == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, "send error: proxied session done");
+ ngx_tcp_finalize_session(s);
+ return;
+ }
+
+ if (n > 0) {
+ b->pos += n;
+
+ if (b->pos == b->last) {
+ b->pos = b->start;
+ b->last = b->start;
+ }
+ }
+ }
+ }
+
+ size = b->end - b->last;
+
+ if (size && src->read->ready) {
+ c->log->action = recv_action;
+
+ n = src->recv(src, b->last, size);
+ ngx_log_debug1(NGX_LOG_DEBUG_TCP, ev->log, 0, "tcp proxy handler recv:%d", n);
+
+ if (n == NGX_AGAIN || n == 0) {
+ break;
+ }
+
+ if (n > 0) {
+ do_write = 1;
+ b->last += n;
+
+ continue;
+ }
+
+ if (n == NGX_ERROR) {
+ src->read->eof = 1;
+ }
+ }
+
+ break;
+ }
+
+ c->log->action = "nginx tcp proxying";
+
+ if ((s->connection->read->eof && s->buffer->pos == s->buffer->last)
+ || (pctx->upstream->connection->read->eof
+ && pctx->buffer->pos == pctx->buffer->last)
+ || (s->connection->read->eof
+ && pctx->upstream->connection->read->eof))
+ {
+ action = c->log->action;
+ c->log->action = NULL;
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, "proxied session done");
+ c->log->action = action;
+
+ ngx_tcp_finalize_session(s);
+ return;
+ }
+
+ if (ngx_handle_write_event(dst->write, 0) != NGX_OK) {
+ ngx_tcp_finalize_session(s);
+ return;
+ }
+
+ if (ngx_handle_read_event(dst->read, 0) != NGX_OK) {
+ ngx_tcp_finalize_session(s);
+ return;
+ }
+
+ if (ngx_handle_write_event(src->write, 0) != NGX_OK) {
+ ngx_tcp_finalize_session(s);
+ return;
+ }
+
+ if (ngx_handle_read_event(src->read, 0) != NGX_OK) {
+ ngx_tcp_finalize_session(s);
+ return;
+ }
+
+ pcf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_proxy_module);
+
+ if (c == s->connection) {
+ ngx_add_timer(c->read, pcf->timeout);
+ }
+
+ if (c == pctx->upstream->connection) {
+ if (ev->write) {
+ ngx_add_timer(c->read, pcf->upstream.read_timeout);
+ } else {
+ ngx_add_timer(c->write, pcf->upstream.send_timeout);
+ }
+ }
+}
+
+
+/*static void*/
+/*ngx_tcp_proxy_upstream_error(ngx_tcp_session_t *s)*/
+/*{*/
+/*if (s->proxy->upstream->connection) {*/
+/*ngx_log_debug1(NGX_LOG_DEBUG_TCP, s->connection->log, 0,*/
+/*"close tcp proxy connection: %d",*/
+/*s->proxy->upstream->connection->fd);*/
+
+/*ngx_close_connection(s->proxy->upstream->connection);*/
+/*}*/
+
+/*if (s->out.len == 0) {*/
+/*ngx_tcp_finalize_session(s);*/
+/*return;*/
+/*}*/
+
+/*s->quit = 1;*/
+/*ngx_tcp_send(s->connection->write);*/
+/*}*/
+
+
+/*static void*/
+/*ngx_tcp_proxy_internal_server_error(ngx_tcp_session_t *s)*/
+/*{*/
+/*if (s->proxy->upstream->connection) {*/
+/*ngx_log_debug1(NGX_LOG_DEBUG_TCP, s->connection->log, 0,*/
+/*"close tcp proxy connection: %d",*/
+/*s->proxy->upstream->connection->fd);*/
+
+/*ngx_close_connection(s->proxy->upstream->connection);*/
+/*}*/
+
+/*ngx_tcp_finalize_session(s);*/
+/*}*/
+
+
+static char *
+ngx_tcp_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
+ ngx_tcp_proxy_conf_t *pcf = conf;
+
+ u_short port = 80;
+ ngx_str_t *value, *url = &pcf->url;
+ ngx_url_t u;
+
+ if (pcf->upstream.upstream) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ url = &value[1];
+
+ ngx_memzero(&u, sizeof(u));
+
+ u.url.len = url->len;
+ u.url.data = url->data;
+ u.default_port = port;
+ u.uri_part = 1;
+ u.no_resolve = 1;
+
+ pcf->upstream.upstream = ngx_tcp_upstream_add(cf, &u, 0);
+ if (pcf->upstream.upstream == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+static void *
+ngx_tcp_proxy_create_conf(ngx_conf_t *cf) {
+ ngx_tcp_proxy_conf_t *pcf;
+
+ pcf = ngx_pcalloc(cf->pool, sizeof(ngx_tcp_proxy_conf_t));
+ if (pcf == NULL) {
+ return NULL;
+ }
+
+ pcf->buffer_size = NGX_CONF_UNSET_SIZE;
+
+ pcf->timeout = NGX_CONF_UNSET_MSEC;
+
+ pcf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
+ pcf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
+ pcf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
+
+ return pcf;
+}
+
+static char *
+ngx_tcp_proxy_merge_conf(ngx_conf_t *cf, void *parent, void *child) {
+ ngx_tcp_proxy_conf_t *prev = parent;
+ ngx_tcp_proxy_conf_t *conf = child;
+
+ ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, (size_t) ngx_pagesize);
+
+ ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);
+
+ ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
+ prev->upstream.connect_timeout, 60000);
+
+ ngx_conf_merge_msec_value(conf->upstream.send_timeout,
+ prev->upstream.send_timeout, 60000);
+
+ ngx_conf_merge_msec_value(conf->upstream.read_timeout,
+ prev->upstream.read_timeout, 60000);
+
+ return NGX_CONF_OK;
+}
228 modules/ngx_tcp_upstream_ip_hash_module.c
@@ -0,0 +1,228 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_tcp.h>
+
+
+typedef struct {
+ /* the round robin data must be first */
+ ngx_tcp_upstream_rr_peer_data_t rrp;
+
+ ngx_uint_t hash;
+
+ u_char addr[3];
+
+ u_char tries;
+
+ ngx_event_get_peer_pt get_rr_peer;
+} ngx_tcp_upstream_ip_hash_peer_data_t;
+
+
+static ngx_int_t ngx_tcp_upstream_init_ip_hash_peer(ngx_tcp_session_t *s,
+ ngx_tcp_upstream_srv_conf_t *us);
+static ngx_int_t ngx_tcp_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc,
+ void *data);
+static char *ngx_tcp_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_command_t ngx_tcp_upstream_ip_hash_commands[] = {
+
+ { ngx_string("ip_hash"),
+ NGX_TCP_UPS_CONF|NGX_CONF_NOARGS,
+ ngx_tcp_upstream_ip_hash,
+ 0,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_tcp_module_t ngx_tcp_upstream_ip_hash_module_ctx = {
+ NULL,
+
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ NULL, /* create server configuration */
+ NULL, /* merge server configuration */
+};
+
+
+ngx_module_t ngx_tcp_upstream_ip_hash_module = {
+ NGX_MODULE_V1,
+ &ngx_tcp_upstream_ip_hash_module_ctx, /* module context */
+ ngx_tcp_upstream_ip_hash_commands, /* module directives */
+ NGX_TCP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+ngx_int_t
+ngx_tcp_upstream_init_ip_hash(ngx_conf_t *cf, ngx_tcp_upstream_srv_conf_t *us)
+{
+ if (ngx_tcp_upstream_init_round_robin(cf, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ us->peer.init = ngx_tcp_upstream_init_ip_hash_peer;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_tcp_upstream_init_ip_hash_peer(ngx_tcp_session_t *s,
+ ngx_tcp_upstream_srv_conf_t *us)
+{
+ u_char *p;
+ struct sockaddr_in *sin;
+ ngx_tcp_upstream_ip_hash_peer_data_t *iphp;
+
+ iphp = ngx_palloc(s->pool, sizeof(ngx_tcp_upstream_ip_hash_peer_data_t));
+ if (iphp == NULL) {
+ return NGX_ERROR;
+ }
+
+ s->upstream->peer.data = &iphp->rrp;
+
+ if (ngx_tcp_upstream_init_round_robin_peer(s, us) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ s->upstream->peer.get = ngx_tcp_upstream_get_ip_hash_peer;
+
+ /* AF_INET only */
+
+ if (s->connection->sockaddr->sa_family == AF_INET) {
+
+ sin = (struct sockaddr_in *) s->connection->sockaddr;
+ p = (u_char *) &sin->sin_addr.s_addr;
+ iphp->addr[0] = p[0];
+ iphp->addr[1] = p[1];
+ iphp->addr[2] = p[2];
+
+ } else {
+ iphp->addr[0] = 0;
+ iphp->addr[1] = 0;
+ iphp->addr[2] = 0;
+ }
+
+ iphp->hash = 89;
+ iphp->tries = 0;
+ iphp->get_rr_peer = ngx_tcp_upstream_get_round_robin_peer;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_tcp_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)
+{
+ ngx_tcp_upstream_ip_hash_peer_data_t *iphp = data;
+
+ time_t now;
+ uintptr_t m;
+ ngx_uint_t i, n, p, hash;
+ ngx_tcp_upstream_rr_peer_t *peer;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_TCP, pc->log, 0,
+ "get ip hash peer, try: %ui", pc->tries);
+
+ /* TODO: cached */
+
+ if (iphp->tries > 20 || iphp->rrp.peers->single) {
+ return iphp->get_rr_peer(pc, &iphp->rrp);
+ }
+
+ now = ngx_time();
+
+ pc->cached = 0;
+ pc->connection = NULL;
+
+ hash = iphp->hash;
+
+ for ( ;; ) {
+
+ for (i = 0; i < 3; i++) {
+ hash = (hash * 113 + iphp->addr[i]) % 6271;
+ }
+
+ p = hash % iphp->rrp.peers->number;
+
+ n = p / (8 * sizeof(uintptr_t));
+ m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));
+
+ if (!(iphp->rrp.tried[n] & m)) {
+
+ ngx_log_debug4(NGX_LOG_DEBUG_TCP, pc->log, 0,
+ "get ip hash peer, hash: %d, %ui, %04XA, num: %d",
+ hash, p, m, iphp->rrp.peers->number);
+
+ peer = &iphp->rrp.peers->peer[p];
+
+ /* ngx_lock_mutex(iphp->rrp.peers->mutex); */
+
+ if (!peer->down) {
+
+ if (peer->max_fails == 0 || peer->fails < peer->max_fails) {
+ break;
+ }
+
+ if (now - peer->accessed > peer->fail_timeout) {
+ peer->fails = 0;
+ break;
+ }
+ }
+
+ iphp->rrp.tried[n] |= m;
+
+ /* ngx_unlock_mutex(iphp->rrp.peers->mutex); */
+
+ pc->tries--;
+ }
+
+ if (++iphp->tries >= 20) {
+ return iphp->get_rr_peer(pc, &iphp->rrp);
+ }
+ }
+
+ iphp->rrp.current = p;
+
+ pc->sockaddr = peer->sockaddr;
+ pc->socklen = peer->socklen;
+ pc->name = &peer->name;
+
+ /* ngx_unlock_mutex(iphp->rrp.peers->mutex); */
+
+ iphp->rrp.tried[n] |= m;
+ iphp->hash = hash;
+
+ return NGX_OK;
+}
+
+
+static char *
+ngx_tcp_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_tcp_upstream_srv_conf_t *uscf;
+
+ uscf = ngx_tcp_conf_get_module_srv_conf(cf, ngx_tcp_upstream_module);
+
+ uscf->peer.init_upstream = ngx_tcp_upstream_init_ip_hash;
+
+ uscf->flags = NGX_TCP_UPSTREAM_CREATE
+ |NGX_TCP_UPSTREAM_MAX_FAILS
+ |NGX_TCP_UPSTREAM_FAIL_TIMEOUT
+ |NGX_TCP_UPSTREAM_DOWN;
+
+ return NGX_CONF_OK;
+}
513 ngx_tcp.c
@@ -0,0 +1,513 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_tcp.h>
+
+
+static char *ngx_tcp_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static ngx_int_t ngx_tcp_add_ports(ngx_conf_t *cf, ngx_array_t *ports,
+ ngx_tcp_listen_t *listen);
+static char *ngx_tcp_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports);
+static ngx_int_t ngx_tcp_add_addrs(ngx_conf_t *cf, ngx_tcp_port_t *mport,
+ ngx_tcp_conf_addr_t *addr);
+#if (NGX_HAVE_INET6)
+static ngx_int_t ngx_tcp_add_addrs6(ngx_conf_t *cf, ngx_tcp_port_t *mport,
+ ngx_tcp_conf_addr_t *addr);
+#endif
+static ngx_int_t ngx_tcp_cmp_conf_addrs(const void *one, const void *two);
+
+
+ngx_uint_t ngx_tcp_max_module;
+
+
+static ngx_command_t ngx_tcp_commands[] = {
+
+ { ngx_string("tcp"),
+ NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+ ngx_tcp_block,
+ 0,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_core_module_t ngx_tcp_module_ctx = {
+ ngx_string("tcp"),
+ NULL,
+ NULL
+};
+
+
+ngx_module_t ngx_tcp_module = {
+ NGX_MODULE_V1,
+ &ngx_tcp_module_ctx, /* module context */
+ ngx_tcp_commands, /* module directives */
+ NGX_CORE_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static char *
+ngx_tcp_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *rv;
+ ngx_uint_t i, m, mi, s;
+ ngx_conf_t pcf;
+ ngx_array_t ports;
+ ngx_tcp_listen_t *listen;
+ ngx_tcp_module_t *module;
+ ngx_tcp_conf_ctx_t *ctx;
+ ngx_tcp_core_srv_conf_t **cscfp;
+ ngx_tcp_core_main_conf_t *cmcf;
+
+
+ /* the main tcp context */
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_tcp_conf_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *(ngx_tcp_conf_ctx_t **) conf = ctx;
+
+ /* count the number of the tcp modules and set up their indices */
+
+ ngx_tcp_max_module = 0;
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_TCP_MODULE) {
+ continue;
+ }
+
+ ngx_modules[m]->ctx_index = ngx_tcp_max_module++;
+ }
+
+
+ /* the tcp main_conf context, it is the same in the all tcp contexts */
+
+ ctx->main_conf = ngx_pcalloc(cf->pool,
+ sizeof(void *) * ngx_tcp_max_module);
+ if (ctx->main_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+
+ /*
+ * the tcp null srv_conf context, it is used to merge
+ * the server{}s' srv_conf's
+ */
+
+ ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_tcp_max_module);
+ if (ctx->srv_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+
+ /*
+ * create the main_conf's, the null srv_conf's, and the null loc_conf's
+ * of the all tcp modules
+ */
+
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_TCP_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+ mi = ngx_modules[m]->ctx_index;
+
+ if (module->create_main_conf) {
+ ctx->main_conf[mi] = module->create_main_conf(cf);
+ if (ctx->main_conf[mi] == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ if (module->create_srv_conf) {
+ ctx->srv_conf[mi] = module->create_srv_conf(cf);
+ if (ctx->srv_conf[mi] == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+ }
+
+
+ /* parse inside the tcp{} block */
+
+ pcf = *cf;
+ cf->ctx = ctx;
+
+ cf->module_type = NGX_TCP_MODULE;
+ cf->cmd_type = NGX_TCP_MAIN_CONF;
+ rv = ngx_conf_parse(cf, NULL);
+
+ if (rv != NGX_CONF_OK) {
+ *cf = pcf;
+ return rv;
+ }
+
+
+ /* init tcp{} main_conf's, merge the server{}s' srv_conf's */
+
+ cmcf = ctx->main_conf[ngx_tcp_core_module.ctx_index];
+ cscfp = cmcf->servers.elts;
+
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_TCP_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+ mi = ngx_modules[m]->ctx_index;
+
+ /* init tcp{} main_conf's */
+
+ cf->ctx = ctx;
+
+ if (module->init_main_conf) {
+ rv = module->init_main_conf(cf, ctx->main_conf[mi]);
+ if (rv != NGX_CONF_OK) {
+ *cf = pcf;
+ return rv;
+ }
+ }
+
+ for (s = 0; s < cmcf->servers.nelts; s++) {
+
+ /* merge the server{}s' srv_conf's */
+
+ cf->ctx = cscfp[s]->ctx;
+
+ if (module->merge_srv_conf) {
+ rv = module->merge_srv_conf(cf, ctx->srv_conf[mi],
+ cscfp[s]->ctx->srv_conf[mi]);
+ if (rv != NGX_CONF_OK) {
+ *cf = pcf;
+ return rv;
+ }
+ }
+ }
+ }
+
+ *cf = pcf;
+
+ if (ngx_array_init(&ports, cf->temp_pool, 4, sizeof(ngx_tcp_conf_port_t))
+ != NGX_OK)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ listen = cmcf->listen.elts;
+
+ for (i = 0; i < cmcf->listen.nelts; i++) {
+ if (ngx_tcp_add_ports(cf, &ports, &listen[i]) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ return ngx_tcp_optimize_servers(cf, &ports);
+}
+
+
+static ngx_int_t
+ngx_tcp_add_ports(ngx_conf_t *cf, ngx_array_t *ports,
+ ngx_tcp_listen_t *listen)
+{
+ in_port_t p;
+ ngx_uint_t i;
+ struct sockaddr *sa;
+ struct sockaddr_in *sin;
+ ngx_tcp_conf_port_t *port;
+ ngx_tcp_conf_addr_t *addr;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+
+ sa = (struct sockaddr *) &listen->sockaddr;
+
+ switch (sa->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) sa;
+ p = sin6->sin6_port;
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) sa;
+ p = sin->sin_port;
+ break;
+ }
+
+ port = ports->elts;
+ for (i = 0; i < ports->nelts; i++) {
+ if (p == port[i].port && sa->sa_family == port[i].family) {
+
+ /* a port is already in the port list */
+
+ port = &port[i];
+ goto found;
+ }
+ }
+
+ /* add a port to the port list */
+
+ port = ngx_array_push(ports);
+ if (port == NULL) {
+ return NGX_ERROR;
+ }
+
+ port->family = sa->sa_family;
+ port->port = p;
+
+ if (ngx_array_init(&port->addrs, cf->temp_pool, 2,
+ sizeof(ngx_tcp_conf_addr_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+found:
+
+ addr = ngx_array_push(&port->addrs);
+ if (addr == NULL) {
+ return NGX_ERROR;
+ }
+
+ addr->sockaddr = (struct sockaddr *) &listen->sockaddr;
+ addr->socklen = listen->socklen;
+ addr->ctx = listen->ctx;
+ addr->bind = listen->bind;
+ addr->wildcard = listen->wildcard;
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ addr->ipv6only = listen->ipv6only;
+#endif
+
+ return NGX_OK;
+}
+
+
+static char *
+ngx_tcp_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports)
+{
+ ngx_uint_t i, p, last, bind_wildcard;
+ ngx_listening_t *ls;
+ ngx_tcp_port_t *mport;
+ ngx_tcp_conf_port_t *port;
+ ngx_tcp_conf_addr_t *addr;
+
+ port = ports->elts;
+ for (p = 0; p < ports->nelts; p++) {
+
+ ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts,
+ sizeof(ngx_tcp_conf_addr_t), ngx_tcp_cmp_conf_addrs);
+
+ addr = port[p].addrs.elts;
+ last = port[p].addrs.nelts;
+
+ /*
+ * if there is the binding to the "*:port" then we need to bind()
+ * to the "*:port" only and ignore the other bindings
+ */
+
+ if (addr[last - 1].wildcard) {
+ addr[last - 1].bind = 1;
+ bind_wildcard = 1;
+
+ } else {
+ bind_wildcard = 0;
+ }
+
+ i = 0;
+
+ while (i < last) {
+
+ if (bind_wildcard && !addr[i].bind) {
+ i++;
+ continue;
+ }
+
+ ls = ngx_create_listening(cf, addr[i].sockaddr, addr[i].socklen);
+ if (ls == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ls->addr_ntop = 1;
+ ls->handler = ngx_tcp_init_connection;
+ ls->pool_size = 256;
+
+ /* TODO: error_log directive */
+ ls->logp = &cf->cycle->new_log;
+ ls->log.data = &ls->addr_text;
+ ls->log.handler = ngx_accept_log_error;
+
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ ls->ipv6only = addr[i].ipv6only;
+#endif
+
+ mport = ngx_palloc(cf->pool, sizeof(ngx_tcp_port_t));
+ if (mport == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ls->servers = mport;
+
+ if (i == last - 1) {
+ mport->naddrs = last;
+
+ } else {
+ mport->naddrs = 1;
+ i = 0;
+ }
+
+ switch (ls->sockaddr->sa_family) {
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ if (ngx_tcp_add_addrs6(cf, mport, addr) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ break;
+#endif
+ default: /* AF_INET */
+ if (ngx_tcp_add_addrs(cf, mport, addr) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+ break;
+ }
+
+ addr++;
+ last--;
+ }
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_tcp_add_addrs(ngx_conf_t *cf, ngx_tcp_port_t *mport,
+ ngx_tcp_conf_addr_t *addr)
+{
+ u_char *p;
+ size_t len;
+ ngx_uint_t i;
+ ngx_tcp_in_addr_t *addrs;
+ struct sockaddr_in *sin;
+ u_char buf[NGX_SOCKADDR_STRLEN];
+
+ mport->addrs = ngx_pcalloc(cf->pool,
+ mport->naddrs * sizeof(ngx_tcp_in_addr_t));
+ if (mport->addrs == NULL) {
+ return NGX_ERROR;
+ }
+
+ addrs = mport->addrs;
+
+ for (i = 0; i < mport->naddrs; i++) {
+
+ sin = (struct sockaddr_in *) addr[i].sockaddr;
+ addrs[i].addr = sin->sin_addr.s_addr;
+
+ addrs[i].conf.ctx = addr[i].ctx;
+
+ len = ngx_sock_ntop(addr[i].sockaddr, buf, NGX_SOCKADDR_STRLEN, 1);
+
+ p = ngx_pnalloc(cf->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(p, buf, len);
+
+ addrs[i].conf.addr_text.len = len;
+ addrs[i].conf.addr_text.data = p;
+ }
+
+ return NGX_OK;
+}
+
+
+#if (NGX_HAVE_INET6)
+
+static ngx_int_t
+ngx_tcp_add_addrs6(ngx_conf_t *cf, ngx_tcp_port_t *mport,
+ ngx_tcp_conf_addr_t *addr)
+{
+ u_char *p;
+ size_t len;
+ ngx_uint_t i;
+ ngx_tcp_in6_addr_t *addrs6;
+ struct sockaddr_in6 *sin6;
+ u_char buf[NGX_SOCKADDR_STRLEN];
+
+ mport->addrs = ngx_pcalloc(cf->pool,
+ mport->naddrs * sizeof(ngx_tcp_in6_addr_t));
+ if (mport->addrs == NULL) {
+ return NGX_ERROR;
+ }
+
+ addrs6 = mport->addrs;
+
+ for (i = 0; i < mport->naddrs; i++) {
+
+ sin6 = (struct sockaddr_in6 *) addr[i].sockaddr;
+ addrs6[i].addr6 = sin6->sin6_addr;
+
+ addrs6[i].conf.ctx = addr[i].ctx;
+
+ len = ngx_sock_ntop(addr[i].sockaddr, buf, NGX_SOCKADDR_STRLEN, 1);
+
+ p = ngx_pnalloc(cf->pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(p, buf, len);
+
+ addrs6[i].conf.addr_text.len = len;
+ addrs6[i].conf.addr_text.data = p;
+ }
+
+ return NGX_OK;
+}
+
+#endif
+
+
+static ngx_int_t
+ngx_tcp_cmp_conf_addrs(const void *one, const void *two)
+{
+ ngx_tcp_conf_addr_t *first, *second;
+
+ first = (ngx_tcp_conf_addr_t *) one;
+ second = (ngx_tcp_conf_addr_t *) two;
+
+ if (first->wildcard) {
+ /* a wildcard must be the last resort, shift it to the end */
+ return 1;
+ }
+
+ if (first->bind && !second->bind) {
+ /* shift explicit bind()ed addresses to the start */
+ return -1;
+ }
+
+ if (!first->bind && second->bind) {
+ /* shift explicit bind()ed addresses to the start */
+ return 1;
+ }
+
+ /* do not sort by default */
+
+ return 0;
+}
185 ngx_tcp.h
@@ -0,0 +1,185 @@
+
+#ifndef _NGX_TCP_H_INCLUDED_
+#define _NGX_TCP_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_connect.h>
+
+
+typedef struct ngx_tcp_protocol_s ngx_tcp_protocol_t;
+typedef struct ngx_tcp_upstream_s ngx_tcp_upstream_t;
+typedef struct ngx_tcp_cleanup_s ngx_tcp_cleanup_t;
+
+
+#include <ngx_tcp_session.h>
+#include <ngx_tcp_upstream.h>
+#include <ngx_tcp_upstream_round_robin.h>
+
+
+typedef struct {
+ void **main_conf;
+ void **srv_conf;
+} ngx_tcp_conf_ctx_t;
+
+
+typedef struct {
+ u_char sockaddr[NGX_SOCKADDRLEN];
+ socklen_t socklen;
+
+ /* server ctx */
+ ngx_tcp_conf_ctx_t *ctx;
+
+ unsigned bind:1;
+ unsigned wildcard:1;
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ unsigned ipv6only:2;
+#endif
+} ngx_tcp_listen_t;
+
+
+typedef struct {
+ ngx_tcp_conf_ctx_t *ctx;
+ ngx_str_t addr_text;
+} ngx_tcp_addr_conf_t;
+
+typedef struct {
+ in_addr_t addr;
+ ngx_tcp_addr_conf_t conf;
+} ngx_tcp_in_addr_t;
+
+
+#if (NGX_HAVE_INET6)
+
+typedef struct {
+ struct in6_addr addr6;
+ ngx_tcp_addr_conf_t conf;
+} ngx_tcp_in6_addr_t;
+
+#endif
+
+
+typedef struct {
+ /* ngx_tcp_in_addr_t or ngx_tcp_in6_addr_t */
+ void *addrs;
+ ngx_uint_t naddrs;
+} ngx_tcp_port_t;
+
+
+typedef struct {
+ int family;
+ in_port_t port;
+ ngx_array_t addrs; /* array of ngx_tcp_conf_addr_t */
+} ngx_tcp_conf_port_t;
+
+
+typedef struct {
+ struct sockaddr *sockaddr;
+ socklen_t socklen;
+
+ ngx_tcp_conf_ctx_t *ctx;
+
+ unsigned bind:1;
+ unsigned wildcard:1;
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ unsigned ipv6only:2;
+#endif
+} ngx_tcp_conf_addr_t;
+
+
+typedef struct {
+ ngx_array_t servers; /* ngx_tcp_core_srv_conf_t */
+ ngx_array_t listen; /* ngx_tcp_listen_t */
+} ngx_tcp_core_main_conf_t;
+
+
+typedef struct {
+ ngx_tcp_protocol_t *protocol;
+
+ ngx_msec_t timeout;
+ ngx_msec_t resolver_timeout;
+
+ ngx_flag_t so_keepalive;
+
+ ngx_str_t server_name;
+
+ u_char *file_name;
+ ngx_int_t line;
+
+ ngx_resolver_t *resolver;
+
+ /* server ctx */
+ ngx_tcp_conf_ctx_t *ctx;
+} ngx_tcp_core_srv_conf_t;
+
+typedef struct {
+ ngx_str_t *client;
+ ngx_tcp_session_t *session;
+} ngx_tcp_log_ctx_t;
+
+
+typedef void (*ngx_tcp_init_session_pt)(ngx_tcp_session_t *s,
+ ngx_connection_t *c);
+typedef void (*ngx_tcp_init_protocol_pt)(ngx_event_t *rev);
+typedef void (*ngx_tcp_auth_state_pt)(ngx_event_t *rev);
+typedef ngx_int_t (*ngx_tcp_parse_command_pt)(ngx_tcp_session_t *s);
+
+
+struct ngx_tcp_protocol_s {
+ ngx_str_t name;
+ in_port_t port[4];
+ ngx_uint_t type;
+
+ ngx_tcp_init_session_pt init_session;
+ ngx_tcp_init_protocol_pt init_protocol;
+ ngx_tcp_parse_command_pt parse_command;
+ ngx_tcp_auth_state_pt auth_state;
+
+ ngx_str_t internal_server_error;
+};
+
+
+typedef struct {
+ ngx_tcp_protocol_t *protocol;
+
+ void *(*create_main_conf)(ngx_conf_t *cf);
+ char *(*init_main_conf)(ngx_conf_t *cf, void *conf);
+
+ void *(*create_srv_conf)(ngx_conf_t *cf);
+ char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev,
+ void *conf);
+} ngx_tcp_module_t;
+
+
+#define NGX_TCP_MODULE 0x00504354 /* "TCP" */
+
+#define NGX_TCP_MAIN_CONF 0x10000000
+#define NGX_TCP_SRV_CONF 0x20000000
+#define NGX_TCP_UPS_CONF 0x40000000
+
+
+#define NGX_TCP_MAIN_CONF_OFFSET offsetof(ngx_tcp_conf_ctx_t, main_conf)
+#define NGX_TCP_SRV_CONF_OFFSET offsetof(ngx_tcp_conf_ctx_t, srv_conf)
+
+
+#define ngx_tcp_get_module_ctx(s, module) (s)->ctx[module.ctx_index]
+#define ngx_tcp_set_ctx(s, c, module) s->ctx[module.ctx_index] = c;
+#define ngx_tcp_delete_ctx(s, module) s->ctx[module.ctx_index] = NULL;
+
+
+#define ngx_tcp_get_module_main_conf(s, module) \
+ (s)->main_conf[module.ctx_index]
+#define ngx_tcp_get_module_srv_conf(s, module) (s)->srv_conf[module.ctx_index]
+
+#define ngx_tcp_conf_get_module_main_conf(cf, module) \
+ ((ngx_tcp_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index]
+#define ngx_tcp_conf_get_module_srv_conf(cf, module) \
+ ((ngx_tcp_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index]
+
+
+extern ngx_uint_t ngx_tcp_max_module;
+extern ngx_module_t ngx_tcp_core_module;
+
+#endif /* _NGX_TCP_H_INCLUDED_ */
492 ngx_tcp_core_module.c
@@ -0,0 +1,492 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_tcp.h>
+
+
+static void *ngx_tcp_core_create_main_conf(ngx_conf_t *cf);
+static void *ngx_tcp_core_create_srv_conf(ngx_conf_t *cf);
+static char *ngx_tcp_core_merge_srv_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static char *ngx_tcp_core_server(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_tcp_core_listen(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_tcp_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_command_t ngx_tcp_core_commands[] = {
+
+ { ngx_string("server"),
+ NGX_TCP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_MULTI|NGX_CONF_NOARGS,
+ ngx_tcp_core_server,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("listen"),
+ NGX_TCP_SRV_CONF|NGX_CONF_TAKE12,
+ ngx_tcp_core_listen,
+ NGX_TCP_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("so_keepalive"),
+ NGX_TCP_MAIN_CONF|NGX_TCP_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_TCP_SRV_CONF_OFFSET,
+ offsetof(ngx_tcp_core_srv_conf_t, so_keepalive),
+ NULL },
+
+ { ngx_string("timeout"),
+ NGX_TCP_MAIN_CONF|NGX_TCP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_TCP_SRV_CONF_OFFSET,
+ offsetof(ngx_tcp_core_srv_conf_t, timeout),
+ NULL },
+
+ { ngx_string("server_name"),
+ NGX_TCP_MAIN_CONF|NGX_TCP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ NGX_TCP_SRV_CONF_OFFSET,
+ offsetof(ngx_tcp_core_srv_conf_t, server_name),
+ NULL },
+
+ { ngx_string("resolver"),
+ NGX_TCP_MAIN_CONF|NGX_TCP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_tcp_core_resolver,
+ NGX_TCP_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("resolver_timeout"),
+ NGX_TCP_MAIN_CONF|NGX_TCP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_TCP_SRV_CONF_OFFSET,
+ offsetof(ngx_tcp_core_srv_conf_t, resolver_timeout),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_tcp_module_t ngx_tcp_core_module_ctx = {
+ NULL, /* protocol */
+
+ ngx_tcp_core_create_main_conf, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_tcp_core_create_srv_conf, /* create server configuration */
+ ngx_tcp_core_merge_srv_conf /* merge server configuration */
+};
+
+
+ngx_module_t ngx_tcp_core_module = {
+ NGX_MODULE_V1,
+ &ngx_tcp_core_module_ctx, /* module context */
+ ngx_tcp_core_commands, /* module directives */
+ NGX_TCP_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+ static void *
+ngx_tcp_core_create_main_conf(ngx_conf_t *cf)
+{
+ ngx_tcp_core_main_conf_t *cmcf;
+
+ cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_tcp_core_main_conf_t));
+ if (cmcf == NULL) {
+ return NULL;
+ }
+
+ if (ngx_array_init(&cmcf->servers, cf->pool, 4,
+ sizeof(ngx_tcp_core_srv_conf_t *))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ if (ngx_array_init(&cmcf->listen, cf->pool, 4, sizeof(ngx_tcp_listen_t))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ return cmcf;
+}
+
+
+ static void *
+ngx_tcp_core_create_srv_conf(ngx_conf_t *cf)
+{
+ ngx_tcp_core_srv_conf_t *cscf;
+
+ cscf = ngx_pcalloc(cf->pool, sizeof(ngx_tcp_core_srv_conf_t));
+ if (cscf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc():
+ *
+ * cscf->protocol = NULL;
+ */
+
+ cscf->timeout = NGX_CONF_UNSET_MSEC;
+ cscf->resolver_timeout = NGX_CONF_UNSET_MSEC;
+ cscf->so_keepalive = NGX_CONF_UNSET;
+
+ cscf->resolver = NGX_CONF_UNSET_PTR;
+
+ cscf->file_name = cf->conf_file->file.name.data;
+ cscf->line = cf->conf_file->line;
+
+ return cscf;
+}
+
+
+ static char *
+ngx_tcp_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_tcp_core_srv_conf_t *prev = parent;
+ ngx_tcp_core_srv_conf_t *conf = child;
+
+ ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);
+ ngx_conf_merge_msec_value(conf->resolver_timeout, prev->resolver_timeout,
+ 30000);
+
+ ngx_conf_merge_value(conf->so_keepalive, prev->so_keepalive, 0);
+
+
+ ngx_conf_merge_str_value(conf->server_name, prev->server_name, "");
+
+ if (conf->server_name.len == 0) {
+ conf->server_name = cf->cycle->hostname;
+ }
+
+ /*if (conf->protocol == NULL) {*/
+ /*ngx_log_error(NGX_LOG_EMERG, cf->log, 0,*/
+ /*"unknown tcp protocol for server in %s:%ui",*/
+ /*conf->file_name, conf->line);*/
+ /*return NGX_CONF_ERROR;*/
+ /*}*/
+
+ ngx_conf_merge_ptr_value(conf->resolver, prev->resolver, NULL);
+
+ return NGX_CONF_OK;
+}
+
+
+ static char *
+ngx_tcp_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *rv;
+ void *mconf;
+ ngx_uint_t m;
+ ngx_conf_t pcf;
+ ngx_tcp_module_t *module;
+ ngx_tcp_conf_ctx_t *ctx, *tcp_ctx;
+ ngx_tcp_core_srv_conf_t *cscf, **cscfp;
+ ngx_tcp_core_main_conf_t *cmcf;
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_tcp_conf_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ tcp_ctx = cf->ctx;
+ ctx->main_conf = tcp_ctx->main_conf;
+
+ /* the server{}'s srv_conf */
+
+ ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_tcp_max_module);
+ if (ctx->srv_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_TCP_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+
+ if (module->create_srv_conf) {
+ mconf = module->create_srv_conf(cf);
+ if (mconf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->srv_conf[ngx_modules[m]->ctx_index] = mconf;
+ }
+ }
+
+ /* the server configuration context */
+
+ cscf = ctx->srv_conf[ngx_tcp_core_module.ctx_index];
+ cscf->ctx = ctx;
+
+ cmcf = ctx->main_conf[ngx_tcp_core_module.ctx_index];
+
+ cscfp = ngx_array_push(&cmcf->servers);
+ if (cscfp == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *cscfp = cscf;
+
+ /* parse inside server{} */
+
+ pcf = *cf;
+ cf->ctx = ctx;
+ cf->cmd_type = NGX_TCP_SRV_CONF;
+
+ rv = ngx_conf_parse(cf, NULL);
+
+ *cf = pcf;
+
+ return rv;
+}
+
+
+ static char *
+ngx_tcp_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ /*ngx_tcp_core_srv_conf_t *cscf = conf;*/
+
+ size_t len, off;
+ in_port_t port;
+ ngx_str_t *value;
+ ngx_url_t u;
+ ngx_uint_t i, m;
+ struct sockaddr *sa;
+ ngx_tcp_listen_t *ls;
+ ngx_tcp_module_t *module;
+ struct sockaddr_in *sin;
+ ngx_tcp_core_main_conf_t *cmcf;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+
+ value = cf->args->elts;
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.url = value[1];
+ u.listen = 1;
+
+ if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
+ if (u.err) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "%s in \"%V\" of the \"listen\" directive",
+ u.err, &u.url);
+ }
+
+ return NGX_CONF_ERROR;
+ }
+
+ cmcf = ngx_tcp_conf_get_module_main_conf(cf, ngx_tcp_core_module);
+
+ ls = cmcf->listen.elts;
+
+ for (i = 0; i < cmcf->listen.nelts; i++) {
+
+ sa = (struct sockaddr *) ls[i].sockaddr;
+
+ if (sa->sa_family != u.family) {
+ continue;
+ }
+
+ switch (sa->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ off = offsetof(struct sockaddr_in6, sin6_addr);
+ len = 16;
+ sin6 = (struct sockaddr_in6 *) sa;
+ port = sin6->sin6_port;
+ break;
+#endif
+
+ default: /* AF_INET */
+ off = offsetof(struct sockaddr_in, sin_addr);
+ len = 4;
+ sin = (struct sockaddr_in *) sa;
+ port = sin->sin_port;
+ break;
+ }
+
+ if (ngx_memcmp(ls[i].sockaddr + off, u.sockaddr + off, len) != 0) {
+ continue;
+ }
+
+ if (port != u.port) {
+ continue;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "duplicate \"%V\" address and port pair", &u.url);
+ return NGX_CONF_ERROR;
+ }
+
+ ls = ngx_array_push(&cmcf->listen);
+ if (ls == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(ls, sizeof(ngx_tcp_listen_t));
+
+ ngx_memcpy(ls->sockaddr, u.sockaddr, u.socklen);
+
+ ls->socklen = u.socklen;
+ ls->wildcard = u.wildcard;
+ ls->ctx = cf->ctx;
+
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_TCP_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+
+ if (module->protocol == NULL) {
+ continue;
+ }
+
+ /*for (i = 0; module->protocol->port[i]; i++) {*/
+ /*if (module->protocol->port[i] == u.port) {*/
+ /*cscf->protocol = module->protocol;*/
+ /*break;*/
+ /*}*/
+ /*}*/
+ }
+
+ for (i = 2; i < cf->args->nelts; i++) {
+
+ if (ngx_strcmp(value[i].data, "bind") == 0) {
+ ls->bind = 1;
+ continue;
+ }
+
+ if (ngx_strncmp(value[i].data, "ipv6only=o", 10) == 0) {
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ struct sockaddr *sa;
+ u_char buf[NGX_SOCKADDR_STRLEN];
+
+ sa = (struct sockaddr *) ls->sockaddr;
+
+ if (sa->sa_family == AF_INET6) {
+
+ if (ngx_strcmp(&value[i].data[10], "n") == 0) {
+ ls->ipv6only = 1;
+
+ } else if (ngx_strcmp(&value[i].data[10], "ff") == 0) {
+ ls->ipv6only = 2;
+
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid ipv6only flags \"%s\"",
+ &value[i].data[9]);
+ return NGX_CONF_ERROR;
+ }
+
+ ls->bind = 1;
+
+ } else {
+ len = ngx_sock_ntop(sa, buf, NGX_SOCKADDR_STRLEN, 1);
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "ipv6only is not supported "
+ "on addr \"%*s\", ignored", len, buf);
+ }
+
+ continue;
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "bind ipv6only is not supported "
+ "on this platform");
+ return NGX_CONF_ERROR;
+#endif
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the invalid \"%V\" parameter", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+ static char *
+ngx_tcp_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_tcp_core_srv_conf_t *cscf = conf;
+
+ ngx_url_t u;
+ ngx_str_t *value;
+
+ value = cf->args->elts;
+
+ if (cscf->resolver != NGX_CONF_UNSET_PTR) {
+ return "is duplicate";
+ }
+
+ if (ngx_strcmp(value[1].data, "off") == 0) {
+ cscf->resolver = NULL;
+ return NGX_CONF_OK;
+ }
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.host = value[1];
+ u.port = 53;
+
+ if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V: %s", &u.host, u.err);
+ return NGX_CONF_ERROR;
+ }
+
+ cscf->resolver = ngx_resolver_create(cf, &u.addrs[0]);
+ if (cscf->resolver == NULL) {
+ return NGX_CONF_OK;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+ char *
+ngx_tcp_capabilities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+
+ ngx_str_t *c, *value;
+ ngx_uint_t i;
+ ngx_array_t *a;
+
+ a = (ngx_array_t *) (p + cmd->offset);
+
+ value = cf->args->elts;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+ c = ngx_array_push(a);
+ if (c == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *c = value[i];
+ }
+
+ return NGX_CONF_OK;
+}
386 ngx_tcp_session.c
@@ -0,0 +1,386 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_tcp.h>
+
+
+static void ngx_tcp_init_session(ngx_connection_t *c);
+
+void
+ngx_tcp_init_connection(ngx_connection_t *c)
+{
+ ngx_uint_t i;
+ ngx_tcp_port_t *port;
+ struct sockaddr *sa;
+ struct sockaddr_in *sin;
+ ngx_tcp_log_ctx_t *ctx;
+ ngx_tcp_in_addr_t *addr;
+ ngx_tcp_session_t *s;
+ ngx_tcp_addr_conf_t *addr_conf;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+ ngx_tcp_in6_addr_t *addr6;
+#endif
+
+
+ /* find the server configuration for the address:port */
+
+ /* AF_INET only */
+
+ port = c->listening->servers;
+
+ if (port->naddrs > 1) {
+
+ /*
+ * There are several addresses on this port and one of them
+ * is the "*:port" wildcard so getsockname() is needed to determine
+ * the server address.
+ *
+ * AcceptEx() already gave this address.
+ */
+
+ if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
+ ngx_tcp_close_connection(c);
+ return;
+ }
+
+ sa = c->local_sockaddr;
+
+ switch (sa->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) sa;
+
+ addr6 = port->addrs;
+
+ /* the last address is "*" */
+
+ for (i = 0; i < port->naddrs - 1; i++) {
+ if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {
+ break;
+ }
+ }
+
+ addr_conf = &addr6[i].conf;
+
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) sa;
+
+ addr = port->addrs;
+
+ /* the last address is "*" */
+
+ for (i = 0; i < port->naddrs - 1; i++) {
+ if (addr[i].addr == sin->sin_addr.s_addr) {
+ break;
+ }
+ }
+
+ addr_conf = &addr[i].conf;
+
+ break;
+ }
+
+ } else {
+ switch (c->local_sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ addr6 = port->addrs;
+ addr_conf = &addr6[0].conf;
+ break;
+#endif
+
+ default: /* AF_INET */
+ addr = port->addrs;
+ addr_conf = &addr[0].conf;
+ break;
+ }
+ }
+
+ s = ngx_pcalloc(c->pool, sizeof(ngx_tcp_session_t));
+ if (s == NULL) {
+ ngx_tcp_close_connection(c);
+ return;
+ }
+
+ s->main_conf = addr_conf->ctx->main_conf;
+ s->srv_conf = addr_conf->ctx->srv_conf;
+
+ s->addr_text = &addr_conf->addr_text;
+
+ c->data = s;
+ s->connection = c;
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%ui client %V connected to %V",
+ c->number, &c->addr_text, s->addr_text);
+
+ ctx = ngx_palloc(c->pool, sizeof(ngx_tcp_log_ctx_t));
+ if (ctx == NULL) {
+ ngx_tcp_close_connection(c);
+ return;
+ }
+
+ ctx->client = &c->addr_text;
+ ctx->session = s;
+
+ c->log->connection = c->number;
+ c->log->handler = ngx_tcp_log_error;
+ c->log->data = ctx;
+ c->log->action = "nginx tcp module init connection";
+
+ c->log_error = NGX_ERROR_INFO;
+
+ ngx_tcp_init_session(c);
+}
+
+
+static void
+ngx_tcp_init_session(ngx_connection_t *c)
+{
+ ngx_tcp_session_t *s;
+ ngx_tcp_core_srv_conf_t *cscf;
+
+ s = c->data;
+
+ s->signature = NGX_TCP_MODULE;
+ s->pool = c->pool;
+
+ cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module);
+ if (cscf == NULL) {
+ ngx_tcp_finalize_session(s);
+ return;
+ }
+
+ /*s->protocol = cscf->protocol->type;*/
+
+ s->ctx = ngx_pcalloc(s->pool, sizeof(void *) * ngx_tcp_max_module);
+ if (s->ctx == NULL) {
+ ngx_tcp_finalize_session(s);
+ return;
+ }
+
+ if (s->buffer == NULL) {
+ /*if (ngx_array_init(&s->args, s->pool, 2, sizeof(ngx_str_t))*/
+ /*== NGX_ERROR)*/
+ /*{*/
+ /*ngx_tcp_finalize_session(s);*/
+ /*return;*/
+ /*}*/
+
+ s->buffer = ngx_create_temp_buf(s->pool, 4096);
+ if (s->buffer == NULL) {
+ ngx_tcp_finalize_session(s);
+ return;
+ }
+ }
+
+ c->write->handler = ngx_tcp_send;
+
+ ngx_tcp_proxy_init_session(c, s);
+
+ /*cscf->protocol->init_session(s, c);*/
+}
+
+void
+ngx_tcp_send(ngx_event_t *wev)
+{
+ ngx_int_t n;
+ ngx_connection_t *c;
+ ngx_tcp_session_t *s;
+ ngx_tcp_core_srv_conf_t *cscf;
+
+ c = wev->data;
+ s = c->data;
+
+ if (wev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ c->timedout = 1;
+ ngx_tcp_close_connection(c);
+ return;
+ }
+
+ if (s->out.len == 0) {
+ if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+ ngx_tcp_close_connection(c);
+ }
+
+ return;
+ }
+
+ n = c->send(c, s->out.data, s->out.len);
+ ngx_log_debug1(NGX_LOG_DEBUG_TCP, wev->log, 0, "nginx tcp send:%d", n);
+
+ if (n > 0) {
+ s->out.len -= n;
+
+ if (wev->timer_set) {
+ ngx_del_timer(wev);
+ }
+
+ if (s->quit) {
+ ngx_tcp_close_connection(c);
+ return;
+ }
+
+ /*if (s->blocked) {*/
+ /*c->read->handler(c->read);*/
+ /*}*/
+
+ return;
+ }
+
+ if (n == NGX_ERROR) {
+ ngx_tcp_close_connection(c);
+ return;
+ }
+
+ /* n == NGX_AGAIN */
+
+ cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module);
+
+ ngx_add_timer(c->write, cscf->timeout);
+
+ if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+ ngx_tcp_close_connection(c);
+ return;
+ }
+}
+
+void
+ngx_tcp_session_internal_server_error(ngx_tcp_session_t *s)
+{
+ ngx_tcp_core_srv_conf_t *cscf;
+
+ cscf = ngx_tcp_get_module_srv_conf(s, ngx_tcp_core_module);
+
+ s->out = cscf->protocol->internal_server_error;
+ s->quit = 1;
+
+ ngx_tcp_send(s->connection->write);
+}
+
+void
+ngx_tcp_finalize_session(ngx_tcp_session_t *s)
+{
+ ngx_connection_t *c;
+ ngx_tcp_cleanup_t *cln;
+
+ c = s->connection;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_TCP, c->log, 0,
+ "close tcp session: %d", c->fd);
+
+ for (cln = s->cleanup; cln; cln = cln->next) {
+ if (cln->handler) {
+ cln->handler(cln->data);
+ cln->handler = NULL;
+ }
+ }
+
+ ngx_tcp_close_connection(c);
+
+ return;
+}
+
+void
+ngx_tcp_close_connection(ngx_connection_t *c)
+{
+ ngx_pool_t *pool;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_TCP, c->log, 0,
+ "close tcp connection: %d", c->fd);
+
+#if (NGX_STAT_STUB)
+ (void) ngx_atomic_fetch_add(ngx_stat_active, -1);
+#endif
+
+ c->destroyed = 1;
+
+ pool = c->pool;
+
+ ngx_close_connection(c);
+
+ ngx_destroy_pool(pool);
+}
+
+
+u_char *
+ngx_tcp_log_error(ngx_log_t *log, u_char *buf, size_t len)
+{
+ u_char *p;
+ ngx_tcp_session_t *s;
+ ngx_tcp_log_ctx_t *ctx;
+ ngx_tcp_proxy_ctx_t *pctx;
+
+
+ if (log->action) {
+ p = ngx_snprintf(buf, len, " while %s", log->action);
+ len -= p - buf;
+ buf = p;
+ }
+
+ ctx = log->data;
+
+ p = ngx_snprintf(buf, len, ", client: %V", ctx->client);
+ len -= p - buf;
+ buf = p;
+
+ s = ctx->session;
+
+ if (s == NULL) {
+ return p;
+ }
+
+ p = ngx_snprintf(buf, len, ", server: %V",
+ s->addr_text);
+ len -= p - buf;
+ buf = p;
+
+ pctx = ngx_tcp_get_module_ctx(s, ngx_tcp_proxy_module);
+
+ if (pctx == NULL) {
+ return p;
+ }
+
+ p = ngx_snprintf(buf, len, ", upstream: %V", pctx->upstream->name);
+
+ return p;
+}
+
+
+ngx_tcp_cleanup_t *
+ngx_tcp_cleanup_add(ngx_tcp_session_t *s, size_t size)
+{
+ ngx_tcp_cleanup_t *cln;
+
+ cln = ngx_palloc(s->pool, sizeof(ngx_tcp_cleanup_t));
+ if (cln == NULL) {
+ return NULL;
+ }
+
+ if (size) {
+ cln->data = ngx_palloc(s->pool, size);
+ if (cln->data == NULL) {
+ return NULL;
+ }
+
+ } else {
+ cln->data = NULL;
+ }
+
+ cln->handler = NULL;
+ cln->next = s->cleanup;
+
+ s->cleanup = cln;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_TCP, s->connection->log, 0,
+ "tcp cleanup add: %p", cln);
+
+ return cln;
+}
104 ngx_tcp_session.h
@@ -0,0 +1,104 @@
+
+#ifndef _NGX_TCP_SESSION_H_INCLUDED_
+#define _NGX_TCP_SESSION_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_connect.h>
+#include <ngx_tcp.h>
+
+
+
+typedef struct ngx_tcp_proxy_s {
+ ngx_peer_connection_t *upstream;
+ ngx_buf_t *buffer;
+} ngx_tcp_proxy_ctx_t;
+
+typedef struct ngx_tcp_session_s {
+ uint32_t signature; /* "TCP" */
+
+ ngx_pool_t *pool;
+
+ ngx_connection_t *connection;
+ ngx_tcp_upstream_t *upstream;
+
+ ngx_str_t out;
+ ngx_buf_t *buffer;
+
+ void **ctx;
+ void **main_conf;
+ void **srv_conf;
+
+ ngx_resolver_ctx_t *resolver_ctx;
+
+ ngx_tcp_cleanup_t *cleanup;
+
+
+ /*ngx_tcp_proxy_ctx_t *proxy;*/
+
+ /*ngx_uint_t tcp_state;*/
+
+ /*unsigned protocol:3;*/
+ /*unsigned blocked:1;*/
+ unsigned quit:1;
+ /*unsigned quoted:1;*/
+ /*unsigned backslash:1;*/
+ /*unsigned no_sync_literal:1;*/
+ /*unsigned starttls:1;*/
+ /*unsigned esmtp:1;*/
+ /*unsigned auth_method:3;*/
+ /*unsigned auth_wait:1;*/
+
+ /*ngx_str_t login;*/
+ /*ngx_str_t passwd;*/