From b81cb1ce9bfa7a7c2ed406849fe06bc9478063c0 Mon Sep 17 00:00:00 2001 From: alonbg Date: Mon, 12 Sep 2016 13:59:06 +0300 Subject: [PATCH 1/7] tcp bind --- .gitignore | 2 +- src/ngx_stream_lua_socket_tcp.c | 68 +++++++++++++- src/ngx_stream_lua_util.c | 59 ++++++++++++ src/ngx_stream_lua_util.h | 1 + t/141-tcp-socket-bind.t | 161 ++++++++++++++++++++++++++++++++ 5 files changed, 287 insertions(+), 4 deletions(-) create mode 100644 t/141-tcp-socket-bind.t diff --git a/.gitignore b/.gitignore index 662d32df..23efe51c 100644 --- a/.gitignore +++ b/.gitignore @@ -41,7 +41,6 @@ src/config.[ch] src/worker.[ch] src/misc.[ch] src/initby.[ch] -src/shdict.[ch] src/initworkerby.[ch] src/variable.[ch] src/args.[ch] @@ -56,3 +55,4 @@ html_out/ ebooks.sh *.pdf *.patch +*.project diff --git a/src/ngx_stream_lua_socket_tcp.c b/src/ngx_stream_lua_socket_tcp.c index 68750aa6..c4f9c277 100644 --- a/src/ngx_stream_lua_socket_tcp.c +++ b/src/ngx_stream_lua_socket_tcp.c @@ -18,6 +18,7 @@ static int ngx_stream_lua_socket_tcp(lua_State *L); +static int ngx_stream_lua_socket_tcp_bind(lua_State *L); static int ngx_stream_lua_socket_tcp_connect(lua_State *L); #if (NGX_STREAM_SSL) static int ngx_stream_lua_socket_tcp_sslhandshake(lua_State *L); @@ -147,7 +148,8 @@ static void ngx_stream_lua_socket_tcp_close_connection(ngx_connection_t *c); enum { SOCKET_CTX_INDEX = 1, SOCKET_TIMEOUT_INDEX = 2, - SOCKET_KEY_INDEX = 3 + SOCKET_KEY_INDEX = 3, + SOCKET_BIND_INDEX = 4 /* only in upstream cosocket */ }; @@ -277,7 +279,10 @@ ngx_stream_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) /* {{{tcp object metatable */ lua_pushlightuserdata(L, &ngx_stream_lua_tcp_socket_metatable_key); - lua_createtable(L, 0 /* narr */, 11 /* nrec */); + lua_createtable(L, 0 /* narr */, 12 /* nrec */); + + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_bind); + lua_setfield(L, -2, "bind"); lua_pushcfunction(L, ngx_stream_lua_socket_tcp_connect); lua_setfield(L, -2, "connect"); @@ -396,7 +401,7 @@ ngx_stream_lua_socket_tcp(lua_State *L) ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT | NGX_STREAM_LUA_CONTEXT_TIMER); - lua_createtable(L, 3 /* narr */, 1 /* nrec */); + lua_createtable(L, 4 /* narr */, 1 /* nrec */); lua_pushlightuserdata(L, &ngx_stream_lua_tcp_socket_metatable_key); lua_rawget(L, LUA_REGISTRYINDEX); lua_setmetatable(L, -2); @@ -407,6 +412,54 @@ ngx_stream_lua_socket_tcp(lua_State *L) } +static int +ngx_stream_lua_socket_tcp_bind(lua_State *L) +{ + ngx_stream_session_t *s; + ngx_stream_lua_ctx_t *ctx; + int n; + u_char *text; + size_t len; + ngx_addr_t *local; + + n = lua_gettop(L); + if (n != 2) { + return luaL_error(L, "expecting 2 arguments, but got %d", + lua_gettop(L)); + } + + s = ngx_stream_lua_get_session(L); + if (s == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT + | NGX_STREAM_LUA_CONTEXT_TIMER); + + luaL_checktype(L, 1, LUA_TTABLE); + + text = (u_char *) luaL_checklstring(L, 2, &len); + local = ngx_stream_lua_parse_addr(L, text, len); + if (local == NULL) { + lua_pushnil(L); + lua_pushfstring(L, "bad address"); + return 2; + } + + /* TODO: we may reuse the userdata here */ + lua_rawseti(L, 1, SOCKET_BIND_INDEX); + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "lua tcp socket bind ip: %V", &local->name); + lua_pushboolean(L, 1); + return 1; +} + + static int ngx_stream_lua_socket_tcp_connect(lua_State *L) { @@ -423,6 +476,7 @@ ngx_stream_lua_socket_tcp_connect(lua_State *L) ngx_int_t rc; ngx_stream_lua_srv_conf_t *lscf; ngx_peer_connection_t *pc; + ngx_addr_t *local; int timeout; unsigned custom_pool; int key_index; @@ -579,6 +633,14 @@ ngx_stream_lua_socket_tcp_connect(lua_State *L) dd("lua peer connection log: %p", pc->log); + lua_rawgeti(L, 1, SOCKET_BIND_INDEX); + local = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (local) { + u->peer.local = local; + } + lua_rawgeti(L, 1, SOCKET_TIMEOUT_INDEX); timeout = (ngx_int_t) lua_tointeger(L, -1); lua_pop(L, 1); diff --git a/src/ngx_stream_lua_util.c b/src/ngx_stream_lua_util.c index bc161035..c3c03985 100644 --- a/src/ngx_stream_lua_util.c +++ b/src/ngx_stream_lua_util.c @@ -3543,3 +3543,62 @@ ngx_stream_lua_set_multi_value_table(lua_State *L, int index) } } } + + +ngx_addr_t * +ngx_stream_lua_parse_addr(lua_State *L, u_char *text, size_t len) +{ + ngx_addr_t *addr; + size_t socklen; + in_addr_t inaddr; + ngx_uint_t family; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct in6_addr inaddr6; + struct sockaddr_in6 *sin6; + /* + * prevent MSVC8 warning: + * potentially uninitialized local variable 'inaddr6' used + */ + ngx_memzero(&inaddr6, (sizeof(struct in6_addr))); +#endif + + inaddr = ngx_inet_addr(text, len); + if (inaddr != INADDR_NONE) { + family = AF_INET; + socklen = sizeof(struct sockaddr_in); +#if (NGX_HAVE_INET6) + } else if (ngx_inet6_addr(text, len, inaddr6.s6_addr) == NGX_OK) { + family = AF_INET6; + socklen = sizeof(struct sockaddr_in6); +#endif + } else { + return NULL; + } + addr = lua_newuserdata(L, sizeof(ngx_addr_t) + socklen + len); + if (addr == NULL) { + luaL_error(L, "no memory"); + return NULL; + } + addr->sockaddr = (struct sockaddr *) ((u_char *) addr + sizeof(ngx_addr_t)); + ngx_memzero(addr->sockaddr, socklen); + addr->sockaddr->sa_family = (u_char) family; + addr->socklen = socklen; + switch (family) { +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) addr->sockaddr; + ngx_memcpy(sin6->sin6_addr.s6_addr, inaddr6.s6_addr, 16); + break; +#endif + default: /* AF_INET */ + sin = (struct sockaddr_in *) addr->sockaddr; + sin->sin_addr.s_addr = inaddr; + break; + } + addr->name.data = (u_char *) addr->sockaddr + socklen; + addr->name.len = len; + ngx_memcpy(addr->name.data, text, len); + return addr; +} + diff --git a/src/ngx_stream_lua_util.h b/src/ngx_stream_lua_util.h index 8b646648..01cbc5f0 100644 --- a/src/ngx_stream_lua_util.h +++ b/src/ngx_stream_lua_util.h @@ -73,6 +73,7 @@ void ngx_stream_lua_free_session(ngx_stream_session_t *s); void ngx_stream_lua_process_args_option(ngx_stream_session_t *s, lua_State *L, int table, ngx_str_t *args); void ngx_stream_lua_set_multi_value_table(lua_State *L, int index); +ngx_addr_t *ngx_stream_lua_parse_addr(lua_State *L, u_char *text, size_t len); #ifndef NGX_UNESCAPE_URI_COMPONENT diff --git a/t/141-tcp-socket-bind.t b/t/141-tcp-socket-bind.t new file mode 100644 index 00000000..4ab9dc76 --- /dev/null +++ b/t/141-tcp-socket-bind.t @@ -0,0 +1,161 @@ + +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); +#repeat_each(1); + +plan tests => repeat_each() * (blocks() * 3 + 1); + +my $local_ip = `ifconfig | grep -oE '([0-9]{1,3}\\.?){4}' | grep '\\.' | grep -v '127.0.0.1' | head -n 1`; +chomp $local_ip; + +$ENV{TEST_NGINX_SERVER_IP} ||= $local_ip; +$ENV{TEST_NGINX_NOT_EXIST_IP} ||= '8.8.8.8'; +$ENV{TEST_NGINX_INVALID_IP} ||= '127.0.0.1:8899'; + +no_long_string(); +#no_diff(); +#log_level 'warn'; +no_shuffle(); + +run_tests(); + +__DATA__ + +=== TEST 1: upstream sockets bind 127.0.0.1 +--- stream_config +server { + listen 2986; + content_by_lua_block { + ngx.say(ngx.var.remote_addr) + } +} +--- stream_server_config + content_by_lua_block { + local ip = "127.0.0.1" + local port = 2986 + local sock = ngx.socket.tcp() + local ok, err = sock:bind(ip) + if not ok then + ngx.log(ngx.ERR, err) + return + end + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.log(ngx.ERR, err) + return + end + + + local line, err, part = sock:receive() + if line then + ngx.say(line) + else + ngx.log(ngx.ERR, err) + end + } + +--- stream_response +127.0.0.1 +--- no_error_log +[error] + + +=== TEST 2: upstream sockets bind non loopback ip +--- stream_config +server { + listen 2986; + content_by_lua_block { + ngx.say(ngx.var.remote_addr) + } +} +--- stream_server_config + content_by_lua_block { + local ip = "$TEST_NGINX_SERVER_IP" + local port = 2986 + local sock = ngx.socket.tcp() + local ok, err = sock:bind(ip) + if not ok then + ngx.log(ngx.ERR, err) + return + end + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.log(ngx.ERR, err) + return + end + + local line, err, part = sock:receive() + if line == ip then + ngx.say("ip matched") + else + ngx.log(ngx.ERR, err) + end + } + +--- stream_response +ip matched +--- no_error_log +[error] + + +=== TEST 3: upstream sockets bind not exist ip +--- stream_config +server { + listen 2986; + content_by_lua_block { + ngx.say(ngx.var.remote_addr) + } +} +--- stream_server_config + content_by_lua_block { + local ip = "$TEST_NGINX_NOT_EXIST_IP" + local port = 2986 + local sock = ngx.socket.tcp() + local ok, err = sock:bind(ip) + if not ok then + ngx.log(ngx.ERR, err) + end + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.say(err) + end +} + +--- stream_response +cannot assign requested address +--- error_log eval +["bind(8.8.8.8) failed", +"lua tcp socket bind ip: 8.8.8.8"] + + +=== TEST 4: upstream sockets bind invalid ip +--- stream_config +server { + listen 2986; + content_by_lua_block { + ngx.say(ngx.var.remote_addr) + } +} +--- stream_server_config + content_by_lua_block { + local ip = "$TEST_NGINX_INVALID_IP" + local port = 2986 + local sock = ngx.socket.tcp() + local ok, err = sock:bind(ip) + if not ok then + ngx.say("failed to bind: ", err) + end + + local ok, err = sock:connect("127.0.0.1", port) + if not ok then + ngx.log(ngx.ERR, err) + end +} + +--- stream_response +failed to bind: bad address +--- error_log eval +--- no_error_log +[error] From 82defd6dd91210c6a42cdff260387e022f6ed892 Mon Sep 17 00:00:00 2001 From: alonbg Date: Mon, 12 Sep 2016 18:42:36 +0300 Subject: [PATCH 2/7] udp bind --- src/ngx_stream_lua_socket_udp.c | 80 ++++++++++++-- t/062-count.t | 18 +++- t/141-tcp-socket-bind.t | 30 ++++-- t/142-udp-socket-bind.t | 178 ++++++++++++++++++++++++++++++++ 4 files changed, 291 insertions(+), 15 deletions(-) create mode 100644 t/142-udp-socket-bind.t diff --git a/src/ngx_stream_lua_socket_udp.c b/src/ngx_stream_lua_socket_udp.c index 82329815..66469bdc 100644 --- a/src/ngx_stream_lua_socket_udp.c +++ b/src/ngx_stream_lua_socket_udp.c @@ -27,6 +27,7 @@ static int ngx_stream_lua_socket_udp(lua_State *L); +static int ngx_stream_lua_socket_udp_bind(lua_State *L); static int ngx_stream_lua_socket_udp_setpeername(lua_State *L); static int ngx_stream_lua_socket_udp_send(lua_State *L); static int ngx_stream_lua_socket_udp_receive(lua_State *L); @@ -54,7 +55,7 @@ static void ngx_stream_lua_socket_udp_read_handler(ngx_stream_session_t *s, ngx_stream_lua_socket_udp_upstream_t *u); static void ngx_stream_lua_socket_udp_handle_success(ngx_stream_session_t *s, ngx_stream_lua_socket_udp_upstream_t *u); -static ngx_int_t ngx_stream_lua_udp_connect( +static ngx_int_t ngx_stream_lua_udp_connect(lua_State *L, ngx_stream_lua_udp_connection_t *uc); static int ngx_stream_lua_socket_udp_close(lua_State *L); static ngx_int_t ngx_stream_lua_socket_udp_resume(ngx_stream_session_t *s, @@ -65,7 +66,8 @@ static void ngx_stream_lua_udp_socket_cleanup(ngx_stream_lua_co_ctx_t *coctx); enum { SOCKET_CTX_INDEX = 1, - SOCKET_TIMEOUT_INDEX = 2 + SOCKET_TIMEOUT_INDEX = 2, + SOCKET_BIND_INDEX = 3 /* only in upstream cosocket */ }; @@ -84,7 +86,10 @@ ngx_stream_lua_inject_socket_udp_api(ngx_log_t *log, lua_State *L) /* udp socket object metatable */ lua_pushlightuserdata(L, &ngx_stream_lua_socket_udp_metatable_key); - lua_createtable(L, 0 /* narr */, 6 /* nrec */); + lua_createtable(L, 0 /* narr */, 7 /* nrec */); + + lua_pushcfunction(L, ngx_stream_lua_socket_udp_bind); + lua_setfield(L, -2, "bind"); /* ngx socket mt */ lua_pushcfunction(L, ngx_stream_lua_socket_udp_setpeername); lua_setfield(L, -2, "setpeername"); /* ngx socket mt */ @@ -142,7 +147,7 @@ ngx_stream_lua_socket_udp(lua_State *L) ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT | NGX_STREAM_LUA_CONTEXT_TIMER); - lua_createtable(L, 3 /* narr */, 1 /* nrec */); + lua_createtable(L, 4 /* narr */, 1 /* nrec */); lua_pushlightuserdata(L, &ngx_stream_lua_socket_udp_metatable_key); lua_rawget(L, LUA_REGISTRYINDEX); lua_setmetatable(L, -2); @@ -153,6 +158,54 @@ ngx_stream_lua_socket_udp(lua_State *L) } +static int +ngx_stream_lua_socket_udp_bind(lua_State *L) +{ + ngx_stream_session_t *s; + ngx_stream_lua_ctx_t *ctx; + int n; + u_char *text; + size_t len; + ngx_addr_t *local; + + n = lua_gettop(L); + if (n != 2) { + return luaL_error(L, "expecting 2 arguments, but got %d", + lua_gettop(L)); + } + + s = ngx_stream_lua_get_session(L); + if (s == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT + | NGX_STREAM_LUA_CONTEXT_TIMER); + + luaL_checktype(L, 1, LUA_TTABLE); + + text = (u_char *) luaL_checklstring(L, 2, &len); + local = ngx_stream_lua_parse_addr(L, text, len); + if (local == NULL) { + lua_pushnil(L); + lua_pushfstring(L, "bad address"); + return 2; + } + + /* TODO: we may reuse the userdata here */ + lua_rawseti(L, 1, SOCKET_BIND_INDEX); + ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "lua udp socket bind ip: %V", &local->name); + lua_pushboolean(L, 1); + return 1; +} + + static int ngx_stream_lua_socket_udp_setpeername(lua_State *L) { @@ -652,7 +705,7 @@ ngx_stream_lua_socket_resolve_retval_handler(ngx_stream_session_t *s, return 2; } - rc = ngx_stream_lua_udp_connect(uc); + rc = ngx_stream_lua_udp_connect(L, uc); if (rc != NGX_OK) { u->socket_errno = ngx_socket_errno; @@ -1318,11 +1371,12 @@ ngx_stream_lua_socket_udp_handle_success(ngx_stream_session_t *s, static ngx_int_t -ngx_stream_lua_udp_connect(ngx_stream_lua_udp_connection_t *uc) +ngx_stream_lua_udp_connect(lua_State *L, ngx_stream_lua_udp_connection_t *uc) { int rc; ngx_int_t event; ngx_event_t *rev, *wev; + ngx_addr_t *local; ngx_socket_t s; ngx_connection_t *c; @@ -1393,6 +1447,20 @@ ngx_stream_lua_udp_connect(ngx_stream_lua_udp_connection_t *uc) } #endif + lua_rawgeti(L, 1, SOCKET_BIND_INDEX); + local = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (local && (uc->sockaddr->sa_family == AF_INET + || uc->sockaddr->sa_family == AF_INET6)) { + if (bind(uc->connection->fd, + local->sockaddr, local->socklen) != 0) { + ngx_log_error(NGX_LOG_CRIT, &uc->log, ngx_socket_errno, + "bind(%V) failed", &local->name); + return NGX_ERROR; + } + } + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, &uc->log, 0, "connect to %V, fd:%d #%d", &uc->server, s, c->number); diff --git a/t/062-count.t b/t/062-count.t index bdb531c6..310d8407 100644 --- a/t/062-count.t +++ b/t/062-count.t @@ -249,7 +249,7 @@ worker: 4 ngx.say("n = ", n) } --- stream_response -n = 6 +n = 7 --- no_error_log [error] @@ -279,3 +279,19 @@ n = 6 n = 5 --- no_error_log [error] + + +=== TEST 15: entries under the metatable of tcp sockets +--- stream_server_config + content_by_lua_block { + local n = 0 + local sock = ngx.socket.tcp() + for k, v in pairs(getmetatable(sock)) do + n = n + 1 + end + ngx.say("n = ", n) + } +--- stream_response +n = 12 +--- no_error_log +[error] diff --git a/t/141-tcp-socket-bind.t b/t/141-tcp-socket-bind.t index 4ab9dc76..56b714be 100644 --- a/t/141-tcp-socket-bind.t +++ b/t/141-tcp-socket-bind.t @@ -4,7 +4,7 @@ use Test::Nginx::Socket::Lua::Stream; repeat_each(2); #repeat_each(1); -plan tests => repeat_each() * (blocks() * 3 + 1); +plan tests => repeat_each() * (blocks() * 3 + 2); my $local_ip = `ifconfig | grep -oE '([0-9]{1,3}\\.?){4}' | grep '\\.' | grep -v '127.0.0.1' | head -n 1`; chomp $local_ip; @@ -35,18 +35,19 @@ server { local ip = "127.0.0.1" local port = 2986 local sock = ngx.socket.tcp() + local ok, err = sock:bind(ip) if not ok then ngx.log(ngx.ERR, err) return end + local ok, err = sock:connect("127.0.0.1", port) if not ok then ngx.log(ngx.ERR, err) return end - local line, err, part = sock:receive() if line then ngx.say(line) @@ -74,11 +75,13 @@ server { local ip = "$TEST_NGINX_SERVER_IP" local port = 2986 local sock = ngx.socket.tcp() + local ok, err = sock:bind(ip) if not ok then ngx.log(ngx.ERR, err) return end + local ok, err = sock:connect("127.0.0.1", port) if not ok then ngx.log(ngx.ERR, err) @@ -112,9 +115,10 @@ server { local ip = "$TEST_NGINX_NOT_EXIST_IP" local port = 2986 local sock = ngx.socket.tcp() + local ok, err = sock:bind(ip) if not ok then - ngx.log(ngx.ERR, err) + ngx.log(ngx.INFO, err) end local ok, err = sock:connect("127.0.0.1", port) @@ -126,8 +130,10 @@ server { --- stream_response cannot assign requested address --- error_log eval -["bind(8.8.8.8) failed", -"lua tcp socket bind ip: 8.8.8.8"] +["bind($ENV{TEST_NGINX_NOT_EXIST_IP}) failed", +"lua tcp socket bind ip: $ENV{TEST_NGINX_NOT_EXIST_IP}"] +--- no_error_log +[error] === TEST 4: upstream sockets bind invalid ip @@ -143,19 +149,27 @@ server { local ip = "$TEST_NGINX_INVALID_IP" local port = 2986 local sock = ngx.socket.tcp() + local ok, err = sock:bind(ip) if not ok then - ngx.say("failed to bind: ", err) + ngx.say(err) end local ok, err = sock:connect("127.0.0.1", port) if not ok then ngx.log(ngx.ERR, err) end + + local line, err, part = sock:receive() + if line then + ngx.say(line) + else + ngx.log(ngx.ERR, err) + end } --- stream_response -failed to bind: bad address ---- error_log eval +bad address +127.0.0.1 --- no_error_log [error] diff --git a/t/142-udp-socket-bind.t b/t/142-udp-socket-bind.t new file mode 100644 index 00000000..46f8f165 --- /dev/null +++ b/t/142-udp-socket-bind.t @@ -0,0 +1,178 @@ + +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); +#repeat_each(1); + +plan tests => repeat_each() * (blocks() * 4); + +my $local_ip = `ifconfig | grep -oE '([0-9]{1,3}\\.?){4}' | grep '\\.' | grep -v '127.0.0.1' | head -n 1`; +chomp $local_ip; + +$ENV{TEST_NGINX_SERVER_IP} ||= $local_ip; +$ENV{TEST_NGINX_NOT_EXIST_IP} ||= '8.8.8.8'; +$ENV{TEST_NGINX_INVALID_IP} ||= '127.0.0.1:8899'; + +no_long_string(); +#no_diff(); +#log_level 'warn'; +no_shuffle(); + +run_tests(); + +__DATA__ + +=== TEST 1: upstream sockets bind 127.0.0.1 +--- stream_config +server { + listen 127.0.0.1:2986 udp; + content_by_lua_block { + ngx.log(ngx.INFO, "udp bind address: " .. ngx.var.remote_addr) + } +} +--- stream_server_config + content_by_lua_block { + local ip = "127.0.0.1" + local port = 2986 + local sock = ngx.socket.udp() + + local ok, err = sock:bind(ip) + if not ok then + ngx.log(ngx.ERR, err) + return + end + + local ok, err = sock:setpeername("127.0.0.1", port) + if not ok then + ngx.log(ngx.ERR, err) + return + end + + local ok, err = sock:send("trigger") + if not ok then + ngx.log(ngx.ERR, err) + end + } + +--- no_error_log +[error] +--- error_log eval +["lua udp socket bind ip: 127.0.0.1", +"udp bind address: 127.0.0.1 while handling client connection, udp client: 127.0.0.1, server: 127.0.0.1:2986"] + + +=== TEST 2: upstream sockets bind non loopback ip +--- stream_config +server { + listen 127.0.0.1:2986 udp; + content_by_lua_block { + ngx.log(ngx.INFO, "udp bind address: " .. ngx.var.remote_addr) + } +} +--- stream_server_config + content_by_lua_block { + local ip = "$TEST_NGINX_SERVER_IP" + local port = 2986 + local sock = ngx.socket.udp() + + local ok, err = sock:bind(ip) + if not ok then + ngx.log(ngx.ERR, err) + return + end + + local ok, err = sock:setpeername("127.0.0.1", port) + if not ok then + ngx.log(ngx.ERR, err) + return + end + + local ok, err = sock:send("trigger") + if not ok then + ngx.log(ngx.ERR, err) + end + } + +--- no_error_log +[error] +--- error_log eval +["lua udp socket bind ip: $ENV{TEST_NGINX_SERVER_IP}", +"udp bind address: $ENV{TEST_NGINX_SERVER_IP} while handling client connection, udp client: $ENV{TEST_NGINX_SERVER_IP}, server: 127.0.0.1:2986"] + + +=== TEST 3: upstream sockets bind not exist ip +--- stream_config +server { + listen 127.0.0.1:2986 udp; + content_by_lua_block { + ngx.log(ngx.INFO, "udp bind address: " .. ngx.var.remote_addr) + } +} +--- stream_server_config + content_by_lua_block { + local ip = "$TEST_NGINX_NOT_EXIST_IP" + local port = 2986 + local sock = ngx.socket.udp() + + local ok, err = sock:bind(ip) + if not ok then + ngx.log(ngx.ERR, err) + return + end + + local ok, err = sock:setpeername("127.0.0.1", port) + if not ok then + ngx.log(ngx.INFO, err) + return + end + + local ok, err = sock:send("trigger") + if not ok then + ngx.log(ngx.ERR, err) + end +} + +--- error_log eval +["lua udp socket bind ip: $ENV{TEST_NGINX_NOT_EXIST_IP}", +"bind($ENV{TEST_NGINX_NOT_EXIST_IP}) failed"] +--- no_error_log +[error] + + +=== TEST 4: upstream sockets bind invalid ip +--- stream_config +server { + listen 127.0.0.1:2986 udp; + content_by_lua_block { + ngx.log(ngx.INFO, "udp bind address from remote: " .. ngx.var.remote_addr) + return + } +} +--- stream_server_config + content_by_lua_block { + local ip = "$TEST_NGINX_INVALID_IP" + local port = 2986 + local sock = ngx.socket.udp() + + local ok, err = sock:bind(ip) + if not ok then + ngx.log(ngx.INFO, err) + end + + local ok, err = sock:setpeername("127.0.0.1", port) + if not ok then + ngx.log(ngx.ERR, err) + return + end + + local ok, err = sock:send("trigger") + if not ok then + ngx.log(ngx.ERR, err) + end +} + +--- no_error_log +[error] +--- error_log eval +["bad address while handling client connection, client: 127.0.0.1", +"udp bind address from remote: 127.0.0.1"] From f14ebf18d6cf553f1fcc658a114db2475a45db19 Mon Sep 17 00:00:00 2001 From: alonbg Date: Wed, 14 Sep 2016 15:51:32 +0300 Subject: [PATCH 3/7] fix udp bind and update tests --- src/ngx_stream_lua_socket_udp.c | 37 ++++++----- src/ngx_stream_lua_socket_udp.h | 1 + t/087-udp-socket.t | 6 +- t/141-tcp-socket-bind.t | 62 +++++++++++++++--- t/142-udp-socket-bind.t | 111 ++++++++++++++++++++++++++++---- 5 files changed, 181 insertions(+), 36 deletions(-) diff --git a/src/ngx_stream_lua_socket_udp.c b/src/ngx_stream_lua_socket_udp.c index 66469bdc..afb9c351 100644 --- a/src/ngx_stream_lua_socket_udp.c +++ b/src/ngx_stream_lua_socket_udp.c @@ -55,7 +55,7 @@ static void ngx_stream_lua_socket_udp_read_handler(ngx_stream_session_t *s, ngx_stream_lua_socket_udp_upstream_t *u); static void ngx_stream_lua_socket_udp_handle_success(ngx_stream_session_t *s, ngx_stream_lua_socket_udp_upstream_t *u); -static ngx_int_t ngx_stream_lua_udp_connect(lua_State *L, +static ngx_int_t ngx_stream_lua_udp_connect( ngx_stream_lua_udp_connection_t *uc); static int ngx_stream_lua_socket_udp_close(lua_State *L); static ngx_int_t ngx_stream_lua_socket_udp_resume(ngx_stream_session_t *s, @@ -224,6 +224,7 @@ ngx_stream_lua_socket_udp_setpeername(lua_State *L) ngx_stream_lua_udp_connection_t *uc; int timeout; ngx_stream_lua_co_ctx_t *coctx; + ngx_addr_t *local; ngx_stream_lua_socket_udp_upstream_t *u; @@ -333,6 +334,14 @@ ngx_stream_lua_socket_udp_setpeername(lua_State *L) dd("lua peer connection log: %p", &uc->log); + lua_rawgeti(L, 1, SOCKET_BIND_INDEX); + local = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (local) { + uc->local = local; + } + lua_rawgeti(L, 1, SOCKET_TIMEOUT_INDEX); timeout = (ngx_int_t) lua_tointeger(L, -1); lua_pop(L, 1); @@ -705,7 +714,7 @@ ngx_stream_lua_socket_resolve_retval_handler(ngx_stream_session_t *s, return 2; } - rc = ngx_stream_lua_udp_connect(L, uc); + rc = ngx_stream_lua_udp_connect(uc); if (rc != NGX_OK) { u->socket_errno = ngx_socket_errno; @@ -1371,7 +1380,7 @@ ngx_stream_lua_socket_udp_handle_success(ngx_stream_session_t *s, static ngx_int_t -ngx_stream_lua_udp_connect(lua_State *L, ngx_stream_lua_udp_connection_t *uc) +ngx_stream_lua_udp_connect(ngx_stream_lua_udp_connection_t *uc) { int rc; ngx_int_t event; @@ -1447,19 +1456,17 @@ ngx_stream_lua_udp_connect(lua_State *L, ngx_stream_lua_udp_connection_t *uc) } #endif - lua_rawgeti(L, 1, SOCKET_BIND_INDEX); - local = lua_touserdata(L, -1); - lua_pop(L, 1); + local = uc->local; + if (local) { + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, &uc->log, 0, "udp socket bind"); - if (local && (uc->sockaddr->sa_family == AF_INET - || uc->sockaddr->sa_family == AF_INET6)) { - if (bind(uc->connection->fd, - local->sockaddr, local->socklen) != 0) { - ngx_log_error(NGX_LOG_CRIT, &uc->log, ngx_socket_errno, - "bind(%V) failed", &local->name); - return NGX_ERROR; - } - } + if (bind(c->fd, local->sockaddr, local->socklen) != 0) { + ngx_log_error(NGX_LOG_CRIT, &uc->log, ngx_socket_errno, + "bind(%V) failed", &local->name); + + return NGX_ERROR; + } + } ngx_log_debug3(NGX_LOG_DEBUG_EVENT, &uc->log, 0, "connect to %V, fd:%d #%d", &uc->server, s, c->number); diff --git a/src/ngx_stream_lua_socket_udp.h b/src/ngx_stream_lua_socket_udp.h index 7055c4f6..bce0f322 100644 --- a/src/ngx_stream_lua_socket_udp.h +++ b/src/ngx_stream_lua_socket_udp.h @@ -26,6 +26,7 @@ typedef void (*ngx_stream_lua_socket_udp_upstream_handler_pt) typedef struct { ngx_connection_t *connection; + ngx_addr_t *local; struct sockaddr *sockaddr; socklen_t socklen; ngx_str_t server; diff --git a/t/087-udp-socket.t b/t/087-udp-socket.t index 1b0b6a91..88e319e3 100644 --- a/t/087-udp-socket.t +++ b/t/087-udp-socket.t @@ -287,7 +287,6 @@ qr/content_by_lua_block\(nginx\.conf:\d+\):7: bad session/ === TEST 6: connect again immediately --- stream_server_config - content_by_lua_block { local sock = ngx.socket.udp() local port = $TEST_NGINX_MEMCACHED_PORT @@ -301,7 +300,7 @@ qr/content_by_lua_block\(nginx\.conf:\d+\):7: bad session/ ngx.say("connected: ", ok) ok, err = sock:setpeername("127.0.0.1", port) - if not ok then + if not ok then ngx.say("failed to connect: ", err) return end @@ -520,6 +519,8 @@ lua udp socket receive buffer size: 8192 === TEST 11: access the google DNS server (using domain names) --- stream_server_config lua_resolver $TEST_NGINX_RESOLVER ipv6=off; + #lua_resolver agentzh.org ipv6=off; + #lua_resolver 8.8.8.8 ipv6=off; content_by_lua_block { -- avoid flushing google in "check leak" testing mode: local counter = package.loaded.counter @@ -540,6 +541,7 @@ lua udp socket receive buffer size: 8192 udp:settimeout(2000) -- 2 sec local ok, err = udp:setpeername("google-public-dns-a.google.com", 53) + --local ok, err = udp:setpeername("127.0.1.1", 53) if not ok then ngx.say("failed to connect: ", err) return diff --git a/t/141-tcp-socket-bind.t b/t/141-tcp-socket-bind.t index 56b714be..1dd8b0a3 100644 --- a/t/141-tcp-socket-bind.t +++ b/t/141-tcp-socket-bind.t @@ -9,10 +9,14 @@ plan tests => repeat_each() * (blocks() * 3 + 2); my $local_ip = `ifconfig | grep -oE '([0-9]{1,3}\\.?){4}' | grep '\\.' | grep -v '127.0.0.1' | head -n 1`; chomp $local_ip; +my $local_domain_server = `dig something | grep -oE ' ([0-9]{1,3}+\\.){3}[0-9]{1,3}'`; +chomp $local_domain_server; +$ENV{TEST_NGINX_LOCAL_DOMAIN_SERVER} ||= $local_domain_server; $ENV{TEST_NGINX_SERVER_IP} ||= $local_ip; $ENV{TEST_NGINX_NOT_EXIST_IP} ||= '8.8.8.8'; $ENV{TEST_NGINX_INVALID_IP} ||= '127.0.0.1:8899'; + no_long_string(); #no_diff(); #log_level 'warn'; @@ -25,7 +29,7 @@ __DATA__ === TEST 1: upstream sockets bind 127.0.0.1 --- stream_config server { - listen 2986; + listen 127.0.1.2:2986; content_by_lua_block { ngx.say(ngx.var.remote_addr) } @@ -42,7 +46,7 @@ server { return end - local ok, err = sock:connect("127.0.0.1", port) + local ok, err = sock:connect("127.0.1.2", port) if not ok then ngx.log(ngx.ERR, err) return @@ -65,7 +69,7 @@ server { === TEST 2: upstream sockets bind non loopback ip --- stream_config server { - listen 2986; + listen 127.0.1.2:2986; content_by_lua_block { ngx.say(ngx.var.remote_addr) } @@ -82,7 +86,7 @@ server { return end - local ok, err = sock:connect("127.0.0.1", port) + local ok, err = sock:connect("127.0.1.2", port) if not ok then ngx.log(ngx.ERR, err) return @@ -105,7 +109,7 @@ ip matched === TEST 3: upstream sockets bind not exist ip --- stream_config server { - listen 2986; + listen 127.0.1.2:2986; content_by_lua_block { ngx.say(ngx.var.remote_addr) } @@ -121,7 +125,7 @@ server { ngx.log(ngx.INFO, err) end - local ok, err = sock:connect("127.0.0.1", port) + local ok, err = sock:connect("127.0.1.2", port) if not ok then ngx.say(err) end @@ -139,7 +143,7 @@ cannot assign requested address === TEST 4: upstream sockets bind invalid ip --- stream_config server { - listen 2986; + listen 127.0.1.2:2986; content_by_lua_block { ngx.say(ngx.var.remote_addr) } @@ -155,7 +159,7 @@ server { ngx.say(err) end - local ok, err = sock:connect("127.0.0.1", port) + local ok, err = sock:connect("127.0.1.2", port) if not ok then ngx.log(ngx.ERR, err) end @@ -173,3 +177,45 @@ bad address 127.0.0.1 --- no_error_log [error] + + +=== TEST 5: upstream sockets bind 127.0.0.1 and resolve peername +--- SKIP +--- stream_config +lua_resolver $TEST_NGINX_LOCAL_DOMAIN_SERVER ipv6=off; +server { + listen localhost:2986; + content_by_lua_block { + ngx.say(ngx.var.remote_addr) + } +} +--- stream_server_config + content_by_lua_block { + local ip = "127.0.0.1" + local port = 2986 + local sock = ngx.socket.tcp() + + local ok, err = sock:bind(ip) + if not ok then + ngx.log(ngx.ERR, err) + return + end + + local ok, err = sock:connect("localhost", port) + if not ok then + ngx.log(ngx.ERR, err) + return + end + + local line, err, part = sock:receive() + if line then + ngx.say(line) + else + ngx.log(ngx.ERR, err) + end + } + +--- stream_response +127.0.0.1 +--- no_error_log +[error] diff --git a/t/142-udp-socket-bind.t b/t/142-udp-socket-bind.t index 46f8f165..c4b8a407 100644 --- a/t/142-udp-socket-bind.t +++ b/t/142-udp-socket-bind.t @@ -4,11 +4,14 @@ use Test::Nginx::Socket::Lua::Stream; repeat_each(2); #repeat_each(1); -plan tests => repeat_each() * (blocks() * 4); +plan tests => repeat_each() * (blocks() * 3 + 4); my $local_ip = `ifconfig | grep -oE '([0-9]{1,3}\\.?){4}' | grep '\\.' | grep -v '127.0.0.1' | head -n 1`; chomp $local_ip; +my $local_domain_server = `dig something | grep -oE ' ([0-9]{1,3}+\\.){3}[0-9]{1,3}'`; +chomp $local_domain_server; +$ENV{TEST_NGINX_LOCAL_DOMAIN_SERVER} ||= $local_domain_server; $ENV{TEST_NGINX_SERVER_IP} ||= $local_ip; $ENV{TEST_NGINX_NOT_EXIST_IP} ||= '8.8.8.8'; $ENV{TEST_NGINX_INVALID_IP} ||= '127.0.0.1:8899'; @@ -25,7 +28,7 @@ __DATA__ === TEST 1: upstream sockets bind 127.0.0.1 --- stream_config server { - listen 127.0.0.1:2986 udp; + listen 127.0.1.2:2986 udp; content_by_lua_block { ngx.log(ngx.INFO, "udp bind address: " .. ngx.var.remote_addr) } @@ -42,7 +45,7 @@ server { return end - local ok, err = sock:setpeername("127.0.0.1", port) + local ok, err = sock:setpeername("127.0.1.2", port) if not ok then ngx.log(ngx.ERR, err) return @@ -58,13 +61,13 @@ server { [error] --- error_log eval ["lua udp socket bind ip: 127.0.0.1", -"udp bind address: 127.0.0.1 while handling client connection, udp client: 127.0.0.1, server: 127.0.0.1:2986"] +"udp bind address: 127.0.0.1 while handling client connection, udp client: 127.0.0.1, server: 127.0.1.2:2986"] === TEST 2: upstream sockets bind non loopback ip --- stream_config server { - listen 127.0.0.1:2986 udp; + listen 127.0.1.2:2986 udp; content_by_lua_block { ngx.log(ngx.INFO, "udp bind address: " .. ngx.var.remote_addr) } @@ -81,7 +84,7 @@ server { return end - local ok, err = sock:setpeername("127.0.0.1", port) + local ok, err = sock:setpeername("127.0.1.2", port) if not ok then ngx.log(ngx.ERR, err) return @@ -97,13 +100,13 @@ server { [error] --- error_log eval ["lua udp socket bind ip: $ENV{TEST_NGINX_SERVER_IP}", -"udp bind address: $ENV{TEST_NGINX_SERVER_IP} while handling client connection, udp client: $ENV{TEST_NGINX_SERVER_IP}, server: 127.0.0.1:2986"] +"udp bind address: $ENV{TEST_NGINX_SERVER_IP} while handling client connection, udp client: $ENV{TEST_NGINX_SERVER_IP}, server: 127.0.1.2:2986"] === TEST 3: upstream sockets bind not exist ip --- stream_config server { - listen 127.0.0.1:2986 udp; + listen 127.0.1.2:2986 udp; content_by_lua_block { ngx.log(ngx.INFO, "udp bind address: " .. ngx.var.remote_addr) } @@ -120,7 +123,7 @@ server { return end - local ok, err = sock:setpeername("127.0.0.1", port) + local ok, err = sock:setpeername("127.0.1.2", port) if not ok then ngx.log(ngx.INFO, err) return @@ -142,7 +145,7 @@ server { === TEST 4: upstream sockets bind invalid ip --- stream_config server { - listen 127.0.0.1:2986 udp; + listen 127.0.1.2:2986 udp; content_by_lua_block { ngx.log(ngx.INFO, "udp bind address from remote: " .. ngx.var.remote_addr) return @@ -159,7 +162,7 @@ server { ngx.log(ngx.INFO, err) end - local ok, err = sock:setpeername("127.0.0.1", port) + local ok, err = sock:setpeername("127.0.1.2", port) if not ok then ngx.log(ngx.ERR, err) return @@ -176,3 +179,89 @@ server { --- error_log eval ["bad address while handling client connection, client: 127.0.0.1", "udp bind address from remote: 127.0.0.1"] + + +=== TEST 5: upstream sockets bind 127.0.0.1 and resolve peername +--- stream_config +lua_resolver 127.0.1.1 ipv6=off; +server { + listen localhost:2986 udp; + content_by_lua_block { + ngx.log(ngx.INFO, "udp bind address: " .. ngx.var.remote_addr) + } +} +--- stream_server_config + content_by_lua_block { + local ip = "127.0.0.1" + local port = 2986 + local sock = ngx.socket.udp() + + local ok, err = sock:bind(ip) + if not ok then + ngx.log(ngx.ERR, err) + return + end + + local ok, err = sock:setpeername("localhost", port) + if not ok then + ngx.log(ngx.ERR, err) + return + end + + local ok, err = sock:send("trigger") + if not ok then + ngx.log(ngx.ERR, err) + end + +} + +--- no_error_log +[error] +--- error_log eval +["lua udp socket bind ip: 127.0.0.1", +"udp bind address: 127.0.0.1 while handling client connection, udp client: 127.0.0.1, server: 127.0.0.1:2986"] + + +=== TEST 6: upstream sockets double bind 127.0.0.1 and resolve peername +--- SKIP +--- stream_config +lua_resolver $TEST_NGINX_LOCAL_DOMAIN_SERVER ipv6=off; +server { + listen localhost:2986 udp; + content_by_lua_block { + ngx.log(ngx.INFO, "udp bind address: " .. ngx.var.remote_addr) + } +} +--- stream_server_config + content_by_lua_block { + local ip = "127.0.0.1" + local port = 2986 + local sock = ngx.socket.udp() + + local ok, err = sock:bind(ip) + if not ok then + ngx.log(ngx.ERR, err) + return + end + + local ok, err = sock:setpeername("localhost", port) + if not ok then + ngx.log(ngx.ERR, err) + return + end + + local ok, err = sock:bind("$TEST_NGINX_SERVER_IP") + if not ok then + ngx.log(ngx.ERR, err) + return + end + + local ok, err = sock:setpeername("localhost", port) + if not ok then + ngx.log(ngx.ERR, err) + return + end +} + +--- no_error_log +[error] From c01163ca12296b0082d6819c7fab124a73b3d8f5 Mon Sep 17 00:00:00 2001 From: alonbg Date: Wed, 14 Sep 2016 15:51:32 +0300 Subject: [PATCH 4/7] fix udp bind and update tests --- t/141-tcp-socket-bind.t | 2 +- t/142-udp-socket-bind.t | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/t/141-tcp-socket-bind.t b/t/141-tcp-socket-bind.t index 1dd8b0a3..ce4abedb 100644 --- a/t/141-tcp-socket-bind.t +++ b/t/141-tcp-socket-bind.t @@ -6,7 +6,7 @@ repeat_each(2); plan tests => repeat_each() * (blocks() * 3 + 2); -my $local_ip = `ifconfig | grep -oE '([0-9]{1,3}\\.?){4}' | grep '\\.' | grep -v '127.0.0.1' | head -n 1`; +my $local_ip = `ifconfig | grep -oE 'addr:([0-9]{1,3}+\\.){3}[0-9]{1,3}' | sed -e 's/addr://' | grep -v '127.0.0.1' | head -n 1`; chomp $local_ip; my $local_domain_server = `dig something | grep -oE ' ([0-9]{1,3}+\\.){3}[0-9]{1,3}'`; diff --git a/t/142-udp-socket-bind.t b/t/142-udp-socket-bind.t index c4b8a407..7127ee3f 100644 --- a/t/142-udp-socket-bind.t +++ b/t/142-udp-socket-bind.t @@ -6,7 +6,7 @@ repeat_each(2); plan tests => repeat_each() * (blocks() * 3 + 4); -my $local_ip = `ifconfig | grep -oE '([0-9]{1,3}\\.?){4}' | grep '\\.' | grep -v '127.0.0.1' | head -n 1`; +my $local_ip = `ifconfig | grep -oE 'addr:([0-9]{1,3}+\\.){3}[0-9]{1,3}' | sed -e 's/addr://' | grep -v '127.0.0.1' | head -n 1`; chomp $local_ip; my $local_domain_server = `dig something | grep -oE ' ([0-9]{1,3}+\\.){3}[0-9]{1,3}'`; @@ -182,8 +182,9 @@ server { === TEST 5: upstream sockets bind 127.0.0.1 and resolve peername +--- SKIP --- stream_config -lua_resolver 127.0.1.1 ipv6=off; +lua_resolver $TEST_NGINX_LOCAL_DOMAIN_SERVER ipv6=off; server { listen localhost:2986 udp; content_by_lua_block { From b66a9708ebe2c81726b86a6cc366e843ccc0a248 Mon Sep 17 00:00:00 2001 From: alonbg Date: Thu, 15 Sep 2016 17:08:01 +0300 Subject: [PATCH 5/7] ip_transparent --- src/ngx_stream_lua_socket_tcp.c | 76 ++++++++++- src/ngx_stream_lua_socket_tcp.h | 3 + src/ngx_stream_lua_socket_udp.c | 234 ++++++++++++++++++++++++++++++-- src/ngx_stream_lua_socket_udp.h | 6 +- t/062-count.t | 2 +- t/143-ip_transparent.t | 96 +++++++++++++ 6 files changed, 402 insertions(+), 15 deletions(-) create mode 100644 t/143-ip_transparent.t diff --git a/src/ngx_stream_lua_socket_tcp.c b/src/ngx_stream_lua_socket_tcp.c index c4f9c277..d401d845 100644 --- a/src/ngx_stream_lua_socket_tcp.c +++ b/src/ngx_stream_lua_socket_tcp.c @@ -143,13 +143,16 @@ static void ngx_stream_lua_ssl_handshake_handler(ngx_connection_t *c); static int ngx_stream_lua_ssl_free_session(lua_State *L); #endif static void ngx_stream_lua_socket_tcp_close_connection(ngx_connection_t *c); - +#if (NGX_HAVE_TRANSPARENT_PROXY) +static void ngx_stream_lua_inject_socket_option_consts(lua_State *L); +#endif enum { SOCKET_CTX_INDEX = 1, SOCKET_TIMEOUT_INDEX = 2, SOCKET_KEY_INDEX = 3, - SOCKET_BIND_INDEX = 4 /* only in upstream cosocket */ + SOCKET_BIND_INDEX = 4, /* only in upstream cosocket */ + SOCKET_IP_TRANSPARENT_INDEX = 5 }; @@ -205,11 +208,24 @@ static char ngx_stream_lua_ssl_session_metatable_key; #endif +static void +ngx_stream_lua_inject_socket_option_consts(lua_State *L) +{ + /* {{{ socket option constants */ +#if (NGX_HAVE_TRANSPARENT_PROXY) + lua_pushinteger(L, NGX_STREAM_LUA_SOCKET_OPTION_TRANSPARENT); + lua_setfield(L, -2, "IP_TRANSPARENT"); +#endif +} + + void ngx_stream_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) { ngx_int_t rc; + ngx_stream_lua_inject_socket_option_consts(L); + lua_createtable(L, 0, 3 /* nrec */); /* ngx.socket */ lua_pushcfunction(L, ngx_stream_lua_socket_tcp); @@ -641,6 +657,17 @@ ngx_stream_lua_socket_tcp_connect(lua_State *L) u->peer.local = local; } +#if (NGX_HAVE_TRANSPARENT_PROXY) + lua_rawgeti(L, 1, SOCKET_IP_TRANSPARENT_INDEX); + + if (lua_tointeger(L, -1) > 0) { + pc->transparent = 1; + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream lua set TCP upstream with IP_TRANSPARENT"); + } + lua_pop(L, 1); +#endif + lua_rawgeti(L, 1, SOCKET_TIMEOUT_INDEX); timeout = (ngx_int_t) lua_tointeger(L, -1); lua_pop(L, 1); @@ -2567,8 +2594,49 @@ ngx_stream_lua_socket_tcp_close(lua_State *L) static int ngx_stream_lua_socket_tcp_setoption(lua_State *L) { - /* TODO */ - return 0; + ngx_stream_session_t *s; + ngx_stream_lua_ctx_t *ctx; + int n; + int option; + + n = lua_gettop(L); + + if (n < 2) { + return luaL_error(L, "ngx.socket setoption: expecting 2 or 3 " + "arguments (including the object) but seen %d", + lua_gettop(L)); + } + + s = ngx_stream_lua_get_session(L); + if (s == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT + | NGX_STREAM_LUA_CONTEXT_TIMER); + + luaL_checktype(L, 1, LUA_TTABLE); + + option = luaL_checkint(L, 2); + + switch (option) { +#if (NGX_HAVE_TRANSPARENT_PROXY) + case NGX_STREAM_LUA_SOCKET_OPTION_TRANSPARENT: + lua_rawseti(L, 1, SOCKET_IP_TRANSPARENT_INDEX); + lua_pushboolean(L, 1); + break; +#endif + default: + return luaL_error(L, "invalid tcp socket option: %d", option); + + } + + return 1; } diff --git a/src/ngx_stream_lua_socket_tcp.h b/src/ngx_stream_lua_socket_tcp.h index 68bcfb0e..a56adb85 100644 --- a/src/ngx_stream_lua_socket_tcp.h +++ b/src/ngx_stream_lua_socket_tcp.h @@ -21,6 +21,9 @@ #define NGX_STREAM_LUA_SOCKET_FT_CLIENTABORT 0x0080 #define NGX_STREAM_LUA_SOCKET_FT_SSL 0x0100 +#if (NGX_HAVE_TRANSPARENT_PROXY) +#define NGX_STREAM_LUA_SOCKET_OPTION_TRANSPARENT 1 +#endif typedef struct ngx_stream_lua_socket_tcp_upstream_s ngx_stream_lua_socket_tcp_upstream_t; diff --git a/src/ngx_stream_lua_socket_udp.c b/src/ngx_stream_lua_socket_udp.c index afb9c351..514364fb 100644 --- a/src/ngx_stream_lua_socket_udp.c +++ b/src/ngx_stream_lua_socket_udp.c @@ -56,18 +56,23 @@ static void ngx_stream_lua_socket_udp_read_handler(ngx_stream_session_t *s, static void ngx_stream_lua_socket_udp_handle_success(ngx_stream_session_t *s, ngx_stream_lua_socket_udp_upstream_t *u); static ngx_int_t ngx_stream_lua_udp_connect( - ngx_stream_lua_udp_connection_t *uc); + ngx_stream_lua_socket_udp_upstream_t *u); static int ngx_stream_lua_socket_udp_close(lua_State *L); +static int ngx_stream_lua_socket_udp_setoption(lua_State *L); static ngx_int_t ngx_stream_lua_socket_udp_resume(ngx_stream_session_t *s, ngx_stream_lua_ctx_t *ctx); static void ngx_stream_lua_udp_resolve_cleanup(ngx_stream_lua_co_ctx_t *coctx); static void ngx_stream_lua_udp_socket_cleanup(ngx_stream_lua_co_ctx_t *coctx); - +#if (NGX_HAVE_TRANSPARENT_PROXY) +static ngx_int_t ngx_stream_lua_udp_connect_set_transparent( + ngx_stream_lua_udp_connection_t *uc, ngx_socket_t s); +#endif enum { SOCKET_CTX_INDEX = 1, SOCKET_TIMEOUT_INDEX = 2, - SOCKET_BIND_INDEX = 3 /* only in upstream cosocket */ + SOCKET_BIND_INDEX = 3, /* only in upstream cosocket */ + SOCKET_IP_TRANSPARENT_INDEX = 4 }; @@ -106,6 +111,9 @@ ngx_stream_lua_inject_socket_udp_api(ngx_log_t *log, lua_State *L) lua_pushcfunction(L, ngx_stream_lua_socket_udp_close); lua_setfield(L, -2, "close"); /* ngx socket mt */ + lua_pushcfunction(L, ngx_stream_lua_socket_udp_setoption); + lua_setfield(L, -2, "setoption"); + lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); lua_rawset(L, LUA_REGISTRYINDEX); @@ -342,6 +350,17 @@ ngx_stream_lua_socket_udp_setpeername(lua_State *L) uc->local = local; } +#if (NGX_HAVE_TRANSPARENT_PROXY) + lua_rawgeti(L, 1, SOCKET_IP_TRANSPARENT_INDEX); + + if (lua_tointeger(L, -1) > 0) { + uc->transparent = 1; + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, + "stream lua set UDP upstream with IP_TRANSPARENT"); + } + lua_pop(L, 1); +#endif + lua_rawgeti(L, 1, SOCKET_TIMEOUT_INDEX); timeout = (ngx_int_t) lua_tointeger(L, -1); lua_pop(L, 1); @@ -714,7 +733,7 @@ ngx_stream_lua_socket_resolve_retval_handler(ngx_stream_session_t *s, return 2; } - rc = ngx_stream_lua_udp_connect(uc); + rc = ngx_stream_lua_udp_connect(u); if (rc != NGX_OK) { u->socket_errno = ngx_socket_errno; @@ -1378,17 +1397,108 @@ ngx_stream_lua_socket_udp_handle_success(ngx_stream_session_t *s, } } +#if (NGX_HAVE_TRANSPARENT_PROXY) +static ngx_int_t +ngx_stream_lua_udp_connect_set_transparent(ngx_stream_lua_udp_connection_t *uc, + ngx_socket_t s) +{ + int value; + + value = 1; + +#if defined(SO_BINDANY) + + if (setsockopt(s, SOL_SOCKET, SO_BINDANY, + (const void *) &value, sizeof(int)) == -1) + { + ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno, + "setsockopt(SO_BINDANY) failed"); + return NGX_ERROR; + } + +#else + + switch (uc->sockaddr->sa_family) { + + case AF_INET: + +#if defined(IP_TRANSPARENT) + + if (setsockopt(s, IPPROTO_IP, IP_TRANSPARENT, + (const void *) &value, sizeof(int)) == -1) + { + ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno, + "setsockopt(IP_TRANSPARENT) failed"); + return NGX_ERROR; + } + +#elif defined(IP_BINDANY) + + if (setsockopt(s, IPPROTO_IP, IP_BINDANY, + (const void *) &value, sizeof(int)) == -1) + { + ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno, + "setsockopt(IP_BINDANY) failed"); + return NGX_ERROR; + } + +#endif + + break; + +#if (NGX_HAVE_INET6) + + case AF_INET6: + +#if defined(IPV6_TRANSPARENT) + + if (setsockopt(s, IPPROTO_IPV6, IPV6_TRANSPARENT, + (const void *) &value, sizeof(int)) == -1) + { + ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno, + "setsockopt(IPV6_TRANSPARENT) failed"); + return NGX_ERROR; + } + +#elif defined(IPV6_BINDANY) + + if (setsockopt(s, IPPROTO_IPV6, IPV6_BINDANY, + (const void *) &value, sizeof(int)) == -1) + { + ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno, + "setsockopt(IPV6_BINDANY) failed"); + return NGX_ERROR; + } + +#endif + break; + +#endif /* NGX_HAVE_INET6 */ + + } + +#endif /* SO_BINDANY */ + +return NGX_OK; +} +#endif + static ngx_int_t -ngx_stream_lua_udp_connect(ngx_stream_lua_udp_connection_t *uc) +ngx_stream_lua_udp_connect(ngx_stream_lua_socket_udp_upstream_t *u) { int rc; +#if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT || NGX_LINUX) + in_port_t port; +#endif ngx_int_t event; ngx_event_t *rev, *wev; ngx_addr_t *local; ngx_socket_t s; ngx_connection_t *c; + ngx_stream_lua_udp_connection_t *uc = &u->udp_connection; + s = ngx_socket(uc->sockaddr->sa_family, SOCK_DGRAM, 0); ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &uc->log, 0, "UDP socket %d", s); @@ -1453,20 +1563,79 @@ ngx_stream_lua_udp_connect(ngx_stream_lua_udp_connection_t *uc) return NGX_ERROR; } + + goto connect; } #endif local = uc->local; + if (local) { - ngx_log_debug0(NGX_LOG_DEBUG_EVENT, &uc->log, 0, "udp socket bind"); - if (bind(c->fd, local->sockaddr, local->socklen) != 0) { +#if (NGX_HAVE_TRANSPARENT_PROXY) + if (uc->transparent) { + if (ngx_stream_lua_udp_connect_set_transparent(uc, s) != NGX_OK) { + return NGX_ERROR; + } + } +#endif + +#if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT || NGX_LINUX) + port = u->resolved->port; +#endif + +#if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT) + + if (uc->sockaddr->sa_family != AF_UNIX && port == 0) { + static int bind_address_no_port = 1; + + if (bind_address_no_port) { + if (setsockopt(s, IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT, + (const void *) &bind_address_no_port, + sizeof(int)) == -1) + { + err = ngx_socket_errno; + + if (err != NGX_EOPNOTSUPP && err != NGX_ENOPROTOOPT) { + ngx_log_error(NGX_LOG_ALERT, &uc->log, err, + "setsockopt(IP_BIND_ADDRESS_NO_PORT) " + "failed, ignored"); + + } else { + bind_address_no_port = 0; + } + } + } + } + +#endif + +#if (NGX_LINUX) + + if (port != 0) { + int reuse_addr = 1; + + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, + (const void *) &reuse_addr, sizeof(int)) + == -1) + { + ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno, + "setsockopt(SO_REUSEADDR) failed"); + return NGX_ERROR; + } + } + +#endif + + if (bind(s, local->sockaddr, local->socklen) == -1) { ngx_log_error(NGX_LOG_CRIT, &uc->log, ngx_socket_errno, "bind(%V) failed", &local->name); return NGX_ERROR; } - } + } + +connect: ngx_log_debug3(NGX_LOG_DEBUG_EVENT, &uc->log, 0, "connect to %V, fd:%d #%d", &uc->server, s, c->number); @@ -1553,6 +1722,55 @@ ngx_stream_lua_socket_udp_close(lua_State *L) } +static int +ngx_stream_lua_socket_udp_setoption(lua_State *L) +{ + ngx_stream_session_t *s; + ngx_stream_lua_ctx_t *ctx; + int n; + int option; + + n = lua_gettop(L); + + if (n < 2) { + return luaL_error(L, "ngx.socket setoption: expecting 2 or 3 " + "arguments (including the object) but seen %d", + lua_gettop(L)); + } + + s = ngx_stream_lua_get_session(L); + if (s == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module); + if (ctx == NULL) { + return luaL_error(L, "no ctx found"); + } + + ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT + | NGX_STREAM_LUA_CONTEXT_TIMER); + + luaL_checktype(L, 1, LUA_TTABLE); + + option = luaL_checkint(L, 2); + + switch (option) { +#if (NGX_HAVE_TRANSPARENT_PROXY) + case NGX_STREAM_LUA_SOCKET_OPTION_TRANSPARENT: + lua_rawseti(L, 1, SOCKET_IP_TRANSPARENT_INDEX); + lua_pushboolean(L, 1); + break; +#endif + default: + return luaL_error(L, "invalid udp socket option: %d", option); + + } + + return 1; +} + + static ngx_int_t ngx_stream_lua_socket_udp_resume(ngx_stream_session_t *s, ngx_stream_lua_ctx_t *ctx) diff --git a/src/ngx_stream_lua_socket_udp.h b/src/ngx_stream_lua_socket_udp.h index bce0f322..6887055a 100644 --- a/src/ngx_stream_lua_socket_udp.h +++ b/src/ngx_stream_lua_socket_udp.h @@ -29,6 +29,9 @@ typedef struct { ngx_addr_t *local; struct sockaddr *sockaddr; socklen_t socklen; +#if (NGX_HAVE_TRANSPARENT_PROXY) + unsigned transparent:1; +#endif ngx_str_t server; ngx_log_t log; } ngx_stream_lua_udp_connection_t; @@ -52,8 +55,7 @@ struct ngx_stream_lua_socket_udp_upstream_s { size_t received; /* for receive */ size_t recv_buf_size; - ngx_stream_lua_co_ctx_t *co_ctx; - + ngx_stream_lua_co_ctx_t *co_ctx; unsigned waiting; /* :1 */ }; diff --git a/t/062-count.t b/t/062-count.t index 310d8407..a02674b7 100644 --- a/t/062-count.t +++ b/t/062-count.t @@ -30,7 +30,7 @@ __DATA__ ngx.say("ngx: ", n) } --- stream_response -ngx: 56 +ngx: 57 --- no_error_log [error] diff --git a/t/143-ip_transparent.t b/t/143-ip_transparent.t new file mode 100644 index 00000000..e90eeb25 --- /dev/null +++ b/t/143-ip_transparent.t @@ -0,0 +1,96 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +our $SkipReason; + +BEGIN { + if ( !$ENV{NGX_HAVE_TRANSPARENT_PROXY}) { + $SkipReason = "Not a transparent proxy build"; + } +} + +use Test::Nginx::Socket::Lua::Stream $SkipReason ? (skip_all => $SkipReason) : (); + +repeat_each(1); + +plan tests => blocks() * (repeat_each() * 3 + 1); + +run_tests(); + +__DATA__ + +=== TEST 1: tcp ip_transparent sanity +--- stream_config +server { + listen 127.0.0.1:2986; + content_by_lua_block { + ngx.say(ngx.var.remote_addr) + } +} +--- stream_server_config + content_by_lua_block { + local ip = "127.0.0.1" + local port = 2986 + local sock = ngx.socket.tcp() + + local ok, err = sock:setoption(ngx.IP_TRANSPARENT) + if not ok then + ngx.log(ngx.ERR, err) + end + + local ok, err = sock:connect(ip, port) + if not ok then + ngx.log(ngx.ERR, err) + return + end + + local line, err, part = sock:receive() + if line then + ngx.say(line) + else + ngx.log(ngx.ERR, err) + end + } + +--- stream_response +127.0.0.1 +--- no_error_log +[error] +--- error_log eval +["stream lua set TCP upstream with IP_TRANSPARENT"] + + +=== TEST 2: udp ip_transparent sanity +--- stream_config +server { + listen 127.0.0.1:2986 udp; + content_by_lua_block { + ngx.log(ngx.INFO, "remote udp address: " .. ngx.var.remote_addr) + } +} +--- stream_server_config + content_by_lua_block { + local ip = "127.0.0.1" + local port = 2986 + local sock = ngx.socket.udp() + + local ok, err = sock:setoption(ngx.IP_TRANSPARENT) + if not ok then + ngx.log(ngx.ERR, err) + end + + local ok, err = sock:setpeername(ip, port) + if not ok then + ngx.log(ngx.ERR, err) + return + end + + local ok, err = sock:send("trigger") + if not ok then + ngx.log(ngx.ERR, err) + end + } + +--- no_error_log +[error] +--- error_log eval +["stream lua set UDP upstream with IP_TRANSPARENT", "remote udp address: 127.0.0.1"] From a4f14f90eaacf7c6483b11e2e05025c004ce9f6f Mon Sep 17 00:00:00 2001 From: alonbg Date: Thu, 15 Sep 2016 18:13:11 +0300 Subject: [PATCH 6/7] minor --- src/ngx_stream_lua_socket_tcp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ngx_stream_lua_socket_tcp.c b/src/ngx_stream_lua_socket_tcp.c index d401d845..2743f4c5 100644 --- a/src/ngx_stream_lua_socket_tcp.c +++ b/src/ngx_stream_lua_socket_tcp.c @@ -143,9 +143,8 @@ static void ngx_stream_lua_ssl_handshake_handler(ngx_connection_t *c); static int ngx_stream_lua_ssl_free_session(lua_State *L); #endif static void ngx_stream_lua_socket_tcp_close_connection(ngx_connection_t *c); -#if (NGX_HAVE_TRANSPARENT_PROXY) static void ngx_stream_lua_inject_socket_option_consts(lua_State *L); -#endif + enum { SOCKET_CTX_INDEX = 1, From 0ce2374884281fc1e08475cb6c988f4aeb6db360 Mon Sep 17 00:00:00 2001 From: alonbg Date: Sun, 18 Sep 2016 11:53:39 +0300 Subject: [PATCH 7/7] test fix --- t/062-count.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/062-count.t b/t/062-count.t index a02674b7..ed7a0488 100644 --- a/t/062-count.t +++ b/t/062-count.t @@ -249,7 +249,7 @@ worker: 4 ngx.say("n = ", n) } --- stream_response -n = 7 +n = 8 --- no_error_log [error]