diff --git a/.gitignore b/.gitignore index 6d82f25b..635ecf10 100644 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,4 @@ src/ringbuf.c src/ringbuf.h src/ssl.c src/ssl.h +*.project diff --git a/src/ngx_stream_lua_socket_tcp.c b/src/ngx_stream_lua_socket_tcp.c index c5b33df5..2cddfd9c 100644 --- a/src/ngx_stream_lua_socket_tcp.c +++ b/src/ngx_stream_lua_socket_tcp.c @@ -28,6 +28,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); @@ -36,9 +37,9 @@ static int ngx_stream_lua_socket_tcp_receive(lua_State *L); static int ngx_stream_lua_socket_tcp_receiveany(lua_State *L); static int ngx_stream_lua_socket_tcp_send(lua_State *L); static int ngx_stream_lua_socket_tcp_close(lua_State *L); -static int ngx_stream_lua_socket_tcp_setoption(lua_State *L); static int ngx_stream_lua_socket_tcp_settimeout(lua_State *L); static int ngx_stream_lua_socket_tcp_settimeouts(lua_State *L); +static int ngx_stream_lua_socket_tcp_settransparent(lua_State *L); static void ngx_stream_lua_socket_tcp_handler(ngx_event_t *ev); static ngx_int_t ngx_stream_lua_socket_tcp_get_peer(ngx_peer_connection_t *pc, void *data); @@ -185,7 +186,8 @@ static int ngx_stream_lua_ssl_free_session(lua_State *L); static void ngx_stream_lua_socket_tcp_close_connection(ngx_connection_t *c); static int ngx_stream_lua_socket_tcp_peek(lua_State *L); -static ngx_int_t ngx_stream_lua_socket_tcp_peek_resume(ngx_stream_lua_request_t *r); +static ngx_int_t ngx_stream_lua_socket_tcp_peek_resume( + ngx_stream_lua_request_t *r); static int ngx_stream_lua_socket_tcp_shutdown(lua_State *L); @@ -195,6 +197,10 @@ enum { SOCKET_CONNECT_TIMEOUT_INDEX = 2, SOCKET_SEND_TIMEOUT_INDEX = 4, SOCKET_READ_TIMEOUT_INDEX = 5, + SOCKET_CLIENT_CERT_INDEX = 6, + SOCKET_CLIENT_PKEY_INDEX = 7, + SOCKET_BIND_INDEX = 8, /* only in upstream cosocket */ + SOCKET_IP_TRANSPARENT_INDEX = 9 }; @@ -205,6 +211,14 @@ enum { SOCKET_OP_RESUME_CONN }; +enum { + NGX_STREAM_LUA_SOCKOPT_KEEPALIVE = 1, + NGX_STREAM_LUA_SOCKOPT_REUSEADDR, + NGX_STREAM_LUA_SOCKOPT_TCP_NODELAY, + NGX_STREAM_LUA_SOCKOPT_SNDBUF, + NGX_STREAM_LUA_SOCKOPT_RCVBUF, +}; + #define ngx_stream_lua_socket_check_busy_connecting(r, u, L) \ if ((u)->conn_waiting) { \ @@ -248,6 +262,8 @@ static char ngx_stream_lua_pattern_udata_metatable_key; static char ngx_stream_lua_ssl_session_metatable_key; #endif +#define ngx_stream_lua_tcp_socket_metatable_literal_key "__tcp_cosocket_mt" + void ngx_stream_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) @@ -319,7 +335,10 @@ ngx_stream_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) /* {{{tcp object metatable */ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( tcp_socket_metatable_key)); - lua_createtable(L, 0 /* narr */, 14 /* nrec */); + lua_createtable(L, 0 /* narr */, 17 /* 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"); @@ -346,12 +365,12 @@ ngx_stream_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) lua_pushcfunction(L, ngx_stream_lua_socket_tcp_close); lua_setfield(L, -2, "close"); - lua_pushcfunction(L, ngx_stream_lua_socket_tcp_setoption); - lua_setfield(L, -2, "setoption"); - lua_pushcfunction(L, ngx_stream_lua_socket_tcp_settimeout); lua_setfield(L, -2, "settimeout"); /* ngx socket mt */ + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_settransparent); + lua_setfield(L, -2, "settransparent"); /* ngx socket mt */ + lua_pushcfunction(L, ngx_stream_lua_socket_tcp_settimeouts); lua_setfield(L, -2, "settimeouts"); /* ngx socket mt */ @@ -367,6 +386,12 @@ ngx_stream_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); lua_rawset(L, LUA_REGISTRYINDEX); + + lua_pushliteral(L, ngx_stream_lua_tcp_socket_metatable_literal_key); + lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( + tcp_socket_metatable_key)); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_rawset(L, LUA_REGISTRYINDEX); /* }}} */ /* {{{upstream userdata metatable */ @@ -457,7 +482,8 @@ ngx_stream_lua_socket_tcp(lua_State *L) static void ngx_stream_lua_socket_tcp_create_socket_pool(lua_State *L, - ngx_stream_lua_request_t *r, ngx_str_t key, ngx_int_t pool_size, ngx_int_t backlog, + ngx_stream_lua_request_t *r, ngx_str_t key, ngx_int_t pool_size, + ngx_int_t backlog, ngx_stream_lua_socket_pool_t **spool) { u_char *p; @@ -854,6 +880,54 @@ ngx_stream_lua_socket_tcp_connect_helper(lua_State *L, } +static int +ngx_stream_lua_socket_tcp_bind(lua_State *L) +{ + ngx_stream_lua_ctx_t *ctx; + int n; + u_char *text; + size_t len; + ngx_addr_t *local; + + ngx_stream_lua_request_t *r; + + n = lua_gettop(L); + if (n != 2) { + return luaL_error(L, "expecting 2 arguments, but got %d", + lua_gettop(L)); + } + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, 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_YIELDABLE); + + 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, r->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) { @@ -864,6 +938,7 @@ ngx_stream_lua_socket_tcp_connect(lua_State *L) u_char *p; size_t len; ngx_peer_connection_t *pc; + ngx_addr_t *local; int connect_timeout, send_timeout, read_timeout; unsigned custom_pool; int key_index; @@ -1078,6 +1153,26 @@ ngx_stream_lua_socket_tcp_connect(lua_State *L) lua_pop(L, 3); + lua_rawgeti(L, 1, SOCKET_BIND_INDEX); + local = lua_touserdata(L, -1); + lua_pop(L, 1); + + if (local) { + u->peer.local = local; + } + +#if (NGX_HAVE_TRANSPARENT_PROXY) + lua_rawgeti(L, 1, SOCKET_IP_TRANSPARENT_INDEX); + + if (lua_toboolean(L, -1)) { + pc->transparent = 1; + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, + "stream lua set TCP upstream with IP_TRANSPARENT"); + } + + lua_pop(L, 1); +#endif + if (connect_timeout > 0) { u->connect_timeout = (ngx_msec_t) connect_timeout; @@ -1205,7 +1300,8 @@ ngx_stream_lua_socket_resolve_handler(ngx_resolver_ctx_t *ctx) addr.data = text; for (i = 0; i < ctx->naddrs; i++) { - addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen, + addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, + ur->addrs[i].socklen, text, NGX_SOCKADDR_STRLEN, 0); ngx_log_debug1(NGX_LOG_DEBUG_STREAM, r->connection->log, 0, @@ -3256,11 +3352,123 @@ ngx_stream_lua_socket_tcp_shutdown(lua_State *L) } -static int -ngx_stream_lua_socket_tcp_setoption(lua_State *L) +int +ngx_stream_lua_ffi_socket_tcp_getoption(ngx_stream_lua_socket_tcp_upstream_t *u, + int option, int *val, u_char *err, size_t *errlen) { - /* TODO */ - return 0; + socklen_t len; + int fd, rc; + + if (u == NULL || u->peer.connection == NULL) { + *errlen = ngx_snprintf(err, *errlen, "closed") - err; + return NGX_ERROR; + } + + fd = u->peer.connection->fd; + + if (fd == (int) -1) { + *errlen = ngx_snprintf(err, *errlen, "invalid socket fd") - err; + return NGX_ERROR; + } + + len = sizeof(int); + + switch (option) { + case NGX_STREAM_LUA_SOCKOPT_KEEPALIVE: + rc = getsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *) val, &len); + break; + + case NGX_STREAM_LUA_SOCKOPT_REUSEADDR: + rc = getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *) val, &len); + break; + + case NGX_STREAM_LUA_SOCKOPT_TCP_NODELAY: + rc = getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *) val, &len); + break; + + case NGX_STREAM_LUA_SOCKOPT_SNDBUF: + rc = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *) val, &len); + break; + + case NGX_STREAM_LUA_SOCKOPT_RCVBUF: + rc = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void *) val, &len); + break; + + default: + *errlen = ngx_snprintf(err, *errlen, "unsupported option %d", option) + - err; + return NGX_ERROR; + } + + if (rc == -1) { + *errlen = ngx_strerror(ngx_errno, err, NGX_MAX_ERROR_STR) - err; + return NGX_ERROR; + } + + return NGX_OK; +} + + +int +ngx_stream_lua_ffi_socket_tcp_setoption( + ngx_stream_lua_socket_tcp_upstream_t *u, + int option, int val, u_char *err, size_t *errlen) +{ + socklen_t len; + int fd, rc; + + if (u == NULL || u->peer.connection == NULL) { + *errlen = ngx_snprintf(err, *errlen, "closed") - err; + return NGX_ERROR; + } + + fd = u->peer.connection->fd; + + if (fd == (int) -1) { + *errlen = ngx_snprintf(err, *errlen, "invalid socket fd") - err; + return NGX_ERROR; + } + + len = sizeof(int); + + switch (option) { + case NGX_STREAM_LUA_SOCKOPT_KEEPALIVE: + rc = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, + (const void *) &val, len); + break; + + case NGX_STREAM_LUA_SOCKOPT_REUSEADDR: + rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + (const void *) &val, len); + break; + + case NGX_STREAM_LUA_SOCKOPT_TCP_NODELAY: + rc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, + (const void *) &val, len); + break; + + case NGX_STREAM_LUA_SOCKOPT_SNDBUF: + rc = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, + (const void *) &val, len); + break; + + case NGX_STREAM_LUA_SOCKOPT_RCVBUF: + rc = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, + (const void *) &val, len); + break; + + default: + *errlen = ngx_snprintf(err, *errlen, "unsupported option: %d", option) + - err; + return NGX_ERROR; + } + + if (rc == -1) { + *errlen = ngx_strerror(ngx_errno, err, NGX_MAX_ERROR_STR) - err; + return NGX_ERROR; + } + + return NGX_OK; } @@ -3375,6 +3583,43 @@ ngx_stream_lua_socket_tcp_settimeouts(lua_State *L) } +static int +ngx_stream_lua_socket_tcp_settransparent(lua_State *L) +{ + ngx_stream_lua_ctx_t *ctx; + int n; + + ngx_stream_lua_request_t *r; + + n = lua_gettop(L); + + if (n != 1) { + return luaL_error(L, "ngx.socket settransparent: expecting 1" + "argument (including the object) but seen %d", + lua_gettop(L)); + } + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, 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_YIELDABLE); + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawseti(L, 1, SOCKET_IP_TRANSPARENT_INDEX); + lua_pushboolean(L, 1); + + return 1; +} + + static void ngx_stream_lua_socket_tcp_handler(ngx_event_t *ev) { @@ -4252,7 +4497,8 @@ ngx_stream_lua_socket_tcp_conn_op_resume_handler(ngx_event_t *ev) static int -ngx_stream_lua_socket_tcp_conn_op_resume_retval_handler(ngx_stream_lua_request_t *r, +ngx_stream_lua_socket_tcp_conn_op_resume_retval_handler( + ngx_stream_lua_request_t *r, ngx_stream_lua_socket_tcp_upstream_t *u, lua_State *L) { int nret; @@ -5048,7 +5294,9 @@ ngx_stream_lua_req_socket_tcp(lua_State *L) raw = 1; r = ngx_stream_lua_get_req(L); - + if (r == NULL) { + return luaL_error(L, "no request found"); + } ctx = ngx_stream_lua_get_module_ctx(r, ngx_stream_lua_module); if (ctx == NULL) { @@ -5068,8 +5316,6 @@ ngx_stream_lua_req_socket_tcp(lua_State *L) return 2; } - - dd("ctx acquired raw req socket: %d", ctx->acquired_raw_req_socket); if (ctx->acquired_raw_req_socket) { @@ -5192,6 +5438,7 @@ ngx_stream_lua_req_socket_rev_handler(ngx_stream_lua_request_t *r) u->read_event_handler(r, u); } + static int ngx_stream_lua_socket_tcp_getreusedtimes(lua_State *L) { diff --git a/src/ngx_stream_lua_socket_tcp.h b/src/ngx_stream_lua_socket_tcp.h index 3473250a..76586592 100644 --- a/src/ngx_stream_lua_socket_tcp.h +++ b/src/ngx_stream_lua_socket_tcp.h @@ -29,6 +29,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 a951d009..f451e02a 100644 --- a/src/ngx_stream_lua_socket_udp.c +++ b/src/ngx_stream_lua_socket_udp.c @@ -36,6 +36,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); @@ -66,7 +67,7 @@ static void ngx_stream_lua_socket_udp_read_handler(ngx_stream_lua_request_t *r, static void ngx_stream_lua_socket_udp_handle_success( ngx_stream_lua_request_t *r, 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 ngx_int_t ngx_stream_lua_socket_udp_resume(ngx_stream_lua_request_t *r); static void ngx_stream_lua_udp_resolve_cleanup(void *data); @@ -75,11 +76,18 @@ static void ngx_stream_lua_udp_socket_cleanup(void *data); static ssize_t ngx_stream_lua_udp_sendmsg(ngx_connection_t *c, ngx_iovec_t *vec); #endif +#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 +static int ngx_stream_lua_socket_udp_setoption(lua_State *L); enum { SOCKET_CTX_INDEX = 1, - SOCKET_TIMEOUT_INDEX = 2 + SOCKET_TIMEOUT_INDEX = 2, + SOCKET_BIND_INDEX = 3, /* only in upstream cosocket */ + SOCKET_IP_TRANSPARENT_INDEX = 4 }; @@ -101,7 +109,10 @@ ngx_stream_lua_inject_socket_udp_api(ngx_log_t *log, lua_State *L) /* udp upstream socket object metatable */ lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( 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 */ @@ -118,6 +129,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); @@ -188,7 +202,7 @@ ngx_stream_lua_socket_udp(lua_State *L) ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_YIELDABLE); - lua_createtable(L, 3 /* narr */, 1 /* nrec */); + lua_createtable(L, 4 /* narr */, 1 /* nrec */); lua_pushlightuserdata(L, ngx_stream_lua_lightudata_mask( socket_udp_metatable_key)); lua_rawget(L, LUA_REGISTRYINDEX); @@ -200,6 +214,54 @@ ngx_stream_lua_socket_udp(lua_State *L) } +static int +ngx_stream_lua_socket_udp_bind(lua_State *L) +{ + ngx_stream_lua_ctx_t *ctx; + int n; + u_char *text; + size_t len; + ngx_addr_t *local; + + ngx_stream_lua_request_t *r; + + n = lua_gettop(L); + if (n != 2) { + return luaL_error(L, "expecting 2 arguments, but got %d", + lua_gettop(L)); + } + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, 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_YIELDABLE); + + 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, r->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) { @@ -216,6 +278,7 @@ ngx_stream_lua_socket_udp_setpeername(lua_State *L) ngx_url_t url; ngx_int_t rc; int timeout; + ngx_addr_t *local; ngx_stream_lua_loc_conf_t *llcf; ngx_stream_lua_co_ctx_t *coctx; @@ -327,6 +390,24 @@ 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; + } + +#if (NGX_HAVE_TRANSPARENT_PROXY) + lua_rawgeti(L, 1, SOCKET_IP_TRANSPARENT_INDEX); + if (lua_toboolean(L, -1)) { + uc->transparent = 1; + ngx_log_debug0(NGX_LOG_DEBUG_STREAM, r->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); @@ -668,7 +749,7 @@ ngx_stream_lua_socket_resolve_retval_handler(ngx_stream_lua_request_t *r, 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; @@ -1416,16 +1497,108 @@ ngx_stream_lua_socket_udp_handle_success(ngx_stream_lua_request_t *r, } } +#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); @@ -1490,9 +1663,80 @@ ngx_stream_lua_udp_connect(ngx_stream_lua_udp_connection_t *uc) return NGX_ERROR; } + + goto connect; } #endif + local = uc->local; + + if (local) { + +#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) + { + int 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); @@ -1578,6 +1822,59 @@ ngx_stream_lua_socket_udp_close(lua_State *L) } +static int +ngx_stream_lua_socket_udp_setoption(lua_State *L) +{ + ngx_stream_lua_ctx_t *ctx; + int n; + int ip_transparent; + size_t len; + u_char *option; + + ngx_stream_lua_request_t *r; + + n = lua_gettop(L); + + if (n < 2) { + return luaL_error(L, "ngx.socket setoption: expecting 2" + "arguments (including the object) but seen %d", + lua_gettop(L)); + } + + r = ngx_stream_lua_get_req(L); + if (r == NULL) { + return luaL_error(L, "no request found"); + } + + ctx = ngx_stream_lua_get_module_ctx(r, 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_YIELDABLE); + + luaL_checktype(L, 1, LUA_TTABLE); + option = (u_char *) luaL_checklstring(L, 2, &len); + + if (len == sizeof("ip-transparent") - 1 + && memcmp(option, "ip-transparent", len) == 0) + { +#if (NGX_HAVE_TRANSPARENT_PROXY) + ip_transparent = lua_toboolean(L, 3); + lua_rawseti(L, 1, SOCKET_IP_TRANSPARENT_INDEX); + lua_pushboolean(L, ip_transparent); +#endif + lua_pushnumber(L, 0); + return 1; + } + + lua_pushnil(L); + lua_pushfstring(L, "unknown option %s", option); + + return 2; +} + + static ngx_int_t ngx_stream_lua_socket_udp_resume(ngx_stream_lua_request_t *r) { diff --git a/src/ngx_stream_lua_socket_udp.h b/src/ngx_stream_lua_socket_udp.h index abcc76d3..8f5b9e95 100644 --- a/src/ngx_stream_lua_socket_udp.h +++ b/src/ngx_stream_lua_socket_udp.h @@ -34,8 +34,12 @@ 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; +#if (NGX_HAVE_TRANSPARENT_PROXY) + unsigned transparent:1; +#endif ngx_str_t server; ngx_log_t log; } ngx_stream_lua_udp_connection_t; diff --git a/src/ngx_stream_lua_util.c b/src/ngx_stream_lua_util.c index e79f18e8..2bf96f8e 100644 --- a/src/ngx_stream_lua_util.c +++ b/src/ngx_stream_lua_util.c @@ -408,8 +408,6 @@ ngx_stream_lua_rebase_path(ngx_pool_t *pool, u_char *src, size_t len) } - - ngx_int_t ngx_stream_lua_send_chain_link(ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx, ngx_chain_t *in) @@ -424,8 +422,6 @@ ngx_stream_lua_send_chain_link(ngx_stream_lua_request_t *r, } - - static ngx_int_t ngx_stream_lua_output_filter(ngx_stream_lua_request_t *r, ngx_chain_t *in) { @@ -448,8 +444,6 @@ ngx_stream_lua_output_filter(ngx_stream_lua_request_t *r, ngx_chain_t *in) } - - static void ngx_stream_lua_init_registry(lua_State *L, ngx_log_t *log) { @@ -693,8 +687,6 @@ ngx_stream_lua_reset_ctx(ngx_stream_lua_request_t *r, lua_State *L, } - - void ngx_stream_lua_request_cleanup_handler(void *data) { @@ -1773,6 +1765,7 @@ ngx_stream_lua_escape_uri(u_char *dst, u_char *src, size_t size, if (escape[*src >> 5] & (1 << (*src & 0x1f))) { n++; } + src++; size--; } @@ -1790,6 +1783,7 @@ ngx_stream_lua_escape_uri(u_char *dst, u_char *src, size_t size, } else { *dst++ = *src++; } + size--; } @@ -1982,8 +1976,6 @@ ngx_stream_lua_inject_req_api(ngx_log_t *log, lua_State *L) } - - static ngx_int_t ngx_stream_lua_handle_exit(lua_State *L, ngx_stream_lua_request_t *r, ngx_stream_lua_ctx_t *ctx) @@ -1999,7 +1991,6 @@ ngx_stream_lua_handle_exit(lua_State *L, ngx_stream_lua_request_t *r, ctx->cur_co_ctx->co_status = NGX_STREAM_LUA_CO_DEAD; - ngx_stream_lua_request_cleanup(ctx, 0); if (r->connection->fd == (ngx_socket_t) -1) { /* fake request */ @@ -2280,7 +2271,6 @@ ngx_stream_lua_process_args_option(ngx_stream_lua_request_t *r, lua_State *L, } - /* XXX ngx_open_and_stat_file is static in the core. sigh. */ ngx_int_t ngx_stream_lua_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, @@ -2585,8 +2575,6 @@ ngx_stream_lua_traceback(lua_State *L) } - - ngx_stream_lua_co_ctx_t * ngx_stream_lua_get_co_ctx(lua_State *L, ngx_stream_lua_ctx_t *ctx) { @@ -3113,8 +3101,6 @@ ngx_stream_lua_on_abort_resume(ngx_stream_lua_request_t *r) } - - void ngx_stream_lua_finalize_request(ngx_stream_lua_request_t *r, ngx_int_t rc) { @@ -3325,6 +3311,7 @@ ngx_stream_lua_init_vm(lua_State **new_vm, lua_State *parent_vm, if (state == NULL) { return NGX_ERROR; } + state->vm = L; state->count = 1; @@ -3607,8 +3594,6 @@ ngx_stream_lua_get_raw_phase_context(lua_State *L) } - - void ngx_stream_lua_cleanup_free(ngx_stream_lua_request_t *r, ngx_stream_lua_cleanup_pt *cleanup) @@ -3622,7 +3607,6 @@ ngx_stream_lua_cleanup_free(ngx_stream_lua_request_t *r, return; } - cln = (ngx_stream_lua_cleanup_t *) ((u_char *) cleanup - offsetof(ngx_stream_lua_cleanup_t, handler)); @@ -3677,4 +3661,67 @@ ngx_stream_lua_set_sa_restart(ngx_log_t *log) #endif +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; +} + + /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_stream_lua_util.h b/src/ngx_stream_lua_util.h index 3ea872d0..e6f11d2b 100644 --- a/src/ngx_stream_lua_util.h +++ b/src/ngx_stream_lua_util.h @@ -27,6 +27,8 @@ #include "ngx_stream_lua_api.h" + + #ifndef NGX_UNESCAPE_URI_COMPONENT #define NGX_UNESCAPE_URI_COMPONENT 0 #endif @@ -191,6 +193,8 @@ ngx_int_t ngx_stream_lua_open_and_stat_file(u_char *name, ngx_chain_t *ngx_stream_lua_chain_get_free_buf(ngx_log_t *log, ngx_pool_t *p, ngx_chain_t **free, size_t len); +ngx_addr_t *ngx_stream_lua_parse_addr(lua_State *L, u_char *text, size_t len); + static ngx_inline void ngx_stream_lua_attach_co_ctx_to_L(lua_State *L, ngx_stream_lua_co_ctx_t *coctx) diff --git a/t/062-count.t b/t/062-count.t index e3eb4ad2..fb59dd42 100644 --- a/t/062-count.t +++ b/t/062-count.t @@ -251,7 +251,7 @@ worker: 5 ngx.say("n = ", n) } --- stream_response -n = 14 +n = 17 --- no_error_log [error] @@ -268,7 +268,7 @@ n = 14 ngx.say("n = ", n) } --- stream_response -n = 6 +n = 8 --- no_error_log [error] @@ -298,3 +298,20 @@ n = 6 n = 9 --- no_error_log [error] + + + +=== TEST 16: 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 = 17 +--- no_error_log +[error] diff --git a/t/087-udp-socket.t b/t/087-udp-socket.t index e768ad57..bdd5fa91 100644 --- a/t/087-udp-socket.t +++ b/t/087-udp-socket.t @@ -287,7 +287,6 @@ qr/content_by_lua\(nginx\.conf:\d+\):7: bad request/ === 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\(nginx\.conf:\d+\):7: bad request/ 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 @@ -540,6 +539,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 new file mode 100644 index 00000000..c4119e36 --- /dev/null +++ b/t/141-tcp-socket-bind.t @@ -0,0 +1,226 @@ + +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); +#repeat_each(1); + +plan tests => repeat_each() * (blocks() * 3 + 2); + +my $default_route_dev = `ip route | grep default | awk '{printf "%s", \$5}' | head -n1`; +my $local_ip = `ip route | grep $default_route_dev | grep -oE 'src [^ ]+' | awk '{print \$2}' | head -n1`; +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'; +no_shuffle(); + +run_tests(); + +__DATA__ + +=== TEST 1: upstream sockets bind 127.0.0.10 +--- stream_config +server { + listen 127.0.1.2:2986; + content_by_lua_block { + ngx.say(ngx.var.remote_addr) + } +} +--- stream_server_config + content_by_lua_block { + local ip = "127.0.0.10" + 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.1.2", 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.10 +--- no_error_log +[error] + + + +=== TEST 2: upstream sockets bind non loopback ip +--- stream_config +server { + listen 127.0.1.2: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.1.2", 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, "line: ", line, ", err: ", err, ", part: ", part) + end + } + +--- stream_response +ip matched +--- no_error_log +[error] + + + +=== TEST 3: upstream sockets bind not exist ip +--- stream_config +server { + listen 127.0.1.2: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.INFO, err) + end + + local ok, err = sock:connect("127.0.1.2", port) + if not ok then + ngx.say(err) + end +} + +--- stream_response +cannot assign requested address +--- error_log eval +["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 +--- stream_config +server { + listen 127.0.1.2: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(err) + end + + local ok, err = sock:connect("127.0.1.2", 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 +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 new file mode 100644 index 00000000..e5dce000 --- /dev/null +++ b/t/142-udp-socket-bind.t @@ -0,0 +1,274 @@ + +use Test::Nginx::Socket::Lua::Stream; + +repeat_each(2); +#repeat_each(1); + +plan tests => repeat_each() * (blocks() * 3 + 4); + +my $default_route_dev = `ip route | grep default | awk '{printf "%s", \$5}' | head -n1`; +my $local_ip = `ip route | grep $default_route_dev | grep -oE 'src [^ ]+' | awk '{print \$2}' | head -n1`; +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'; +no_shuffle(); + +run_tests(); + +__DATA__ + +=== TEST 1: upstream sockets bind 127.0.0.1 +--- stream_config +server { + listen 127.0.1.2: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.1.2", 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, 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.1.2: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.1.2", 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}, 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.1.2: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.1.2", 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.1.2: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.1.2", 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, 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 +--- 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: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] diff --git a/t/143-ip-transparent.t b/t/143-ip-transparent.t new file mode 100644 index 00000000..040916ac --- /dev/null +++ b/t/143-ip-transparent.t @@ -0,0 +1,97 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +our $SkipReason; + +BEGIN { + if ($^O ne 'linux') { + $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("ip-transparent", true) + 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("ip-transparent", true) + 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"]