Skip to content

Commit b81cb1c

Browse files
author
alonbg
committed
tcp bind
1 parent aafd50b commit b81cb1c

File tree

5 files changed

+287
-4
lines changed

5 files changed

+287
-4
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ src/config.[ch]
4141
src/worker.[ch]
4242
src/misc.[ch]
4343
src/initby.[ch]
44-
src/shdict.[ch]
4544
src/initworkerby.[ch]
4645
src/variable.[ch]
4746
src/args.[ch]
@@ -56,3 +55,4 @@ html_out/
5655
ebooks.sh
5756
*.pdf
5857
*.patch
58+
*.project

src/ngx_stream_lua_socket_tcp.c

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919

2020
static int ngx_stream_lua_socket_tcp(lua_State *L);
21+
static int ngx_stream_lua_socket_tcp_bind(lua_State *L);
2122
static int ngx_stream_lua_socket_tcp_connect(lua_State *L);
2223
#if (NGX_STREAM_SSL)
2324
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);
147148
enum {
148149
SOCKET_CTX_INDEX = 1,
149150
SOCKET_TIMEOUT_INDEX = 2,
150-
SOCKET_KEY_INDEX = 3
151+
SOCKET_KEY_INDEX = 3,
152+
SOCKET_BIND_INDEX = 4 /* only in upstream cosocket */
151153
};
152154

153155

@@ -277,7 +279,10 @@ ngx_stream_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L)
277279

278280
/* {{{tcp object metatable */
279281
lua_pushlightuserdata(L, &ngx_stream_lua_tcp_socket_metatable_key);
280-
lua_createtable(L, 0 /* narr */, 11 /* nrec */);
282+
lua_createtable(L, 0 /* narr */, 12 /* nrec */);
283+
284+
lua_pushcfunction(L, ngx_stream_lua_socket_tcp_bind);
285+
lua_setfield(L, -2, "bind");
281286

282287
lua_pushcfunction(L, ngx_stream_lua_socket_tcp_connect);
283288
lua_setfield(L, -2, "connect");
@@ -396,7 +401,7 @@ ngx_stream_lua_socket_tcp(lua_State *L)
396401
ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT
397402
| NGX_STREAM_LUA_CONTEXT_TIMER);
398403

399-
lua_createtable(L, 3 /* narr */, 1 /* nrec */);
404+
lua_createtable(L, 4 /* narr */, 1 /* nrec */);
400405
lua_pushlightuserdata(L, &ngx_stream_lua_tcp_socket_metatable_key);
401406
lua_rawget(L, LUA_REGISTRYINDEX);
402407
lua_setmetatable(L, -2);
@@ -407,6 +412,54 @@ ngx_stream_lua_socket_tcp(lua_State *L)
407412
}
408413

409414

415+
static int
416+
ngx_stream_lua_socket_tcp_bind(lua_State *L)
417+
{
418+
ngx_stream_session_t *s;
419+
ngx_stream_lua_ctx_t *ctx;
420+
int n;
421+
u_char *text;
422+
size_t len;
423+
ngx_addr_t *local;
424+
425+
n = lua_gettop(L);
426+
if (n != 2) {
427+
return luaL_error(L, "expecting 2 arguments, but got %d",
428+
lua_gettop(L));
429+
}
430+
431+
s = ngx_stream_lua_get_session(L);
432+
if (s == NULL) {
433+
return luaL_error(L, "no request found");
434+
}
435+
436+
ctx = ngx_stream_get_module_ctx(s, ngx_stream_lua_module);
437+
if (ctx == NULL) {
438+
return luaL_error(L, "no ctx found");
439+
}
440+
441+
ngx_stream_lua_check_context(L, ctx, NGX_STREAM_LUA_CONTEXT_CONTENT
442+
| NGX_STREAM_LUA_CONTEXT_TIMER);
443+
444+
luaL_checktype(L, 1, LUA_TTABLE);
445+
446+
text = (u_char *) luaL_checklstring(L, 2, &len);
447+
local = ngx_stream_lua_parse_addr(L, text, len);
448+
if (local == NULL) {
449+
lua_pushnil(L);
450+
lua_pushfstring(L, "bad address");
451+
return 2;
452+
}
453+
454+
/* TODO: we may reuse the userdata here */
455+
lua_rawseti(L, 1, SOCKET_BIND_INDEX);
456+
ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
457+
"lua tcp socket bind ip: %V", &local->name);
458+
lua_pushboolean(L, 1);
459+
return 1;
460+
}
461+
462+
410463
static int
411464
ngx_stream_lua_socket_tcp_connect(lua_State *L)
412465
{
@@ -423,6 +476,7 @@ ngx_stream_lua_socket_tcp_connect(lua_State *L)
423476
ngx_int_t rc;
424477
ngx_stream_lua_srv_conf_t *lscf;
425478
ngx_peer_connection_t *pc;
479+
ngx_addr_t *local;
426480
int timeout;
427481
unsigned custom_pool;
428482
int key_index;
@@ -579,6 +633,14 @@ ngx_stream_lua_socket_tcp_connect(lua_State *L)
579633

580634
dd("lua peer connection log: %p", pc->log);
581635

636+
lua_rawgeti(L, 1, SOCKET_BIND_INDEX);
637+
local = lua_touserdata(L, -1);
638+
lua_pop(L, 1);
639+
640+
if (local) {
641+
u->peer.local = local;
642+
}
643+
582644
lua_rawgeti(L, 1, SOCKET_TIMEOUT_INDEX);
583645
timeout = (ngx_int_t) lua_tointeger(L, -1);
584646
lua_pop(L, 1);

src/ngx_stream_lua_util.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3543,3 +3543,62 @@ ngx_stream_lua_set_multi_value_table(lua_State *L, int index)
35433543
}
35443544
}
35453545
}
3546+
3547+
3548+
ngx_addr_t *
3549+
ngx_stream_lua_parse_addr(lua_State *L, u_char *text, size_t len)
3550+
{
3551+
ngx_addr_t *addr;
3552+
size_t socklen;
3553+
in_addr_t inaddr;
3554+
ngx_uint_t family;
3555+
struct sockaddr_in *sin;
3556+
#if (NGX_HAVE_INET6)
3557+
struct in6_addr inaddr6;
3558+
struct sockaddr_in6 *sin6;
3559+
/*
3560+
* prevent MSVC8 warning:
3561+
* potentially uninitialized local variable 'inaddr6' used
3562+
*/
3563+
ngx_memzero(&inaddr6, (sizeof(struct in6_addr)));
3564+
#endif
3565+
3566+
inaddr = ngx_inet_addr(text, len);
3567+
if (inaddr != INADDR_NONE) {
3568+
family = AF_INET;
3569+
socklen = sizeof(struct sockaddr_in);
3570+
#if (NGX_HAVE_INET6)
3571+
} else if (ngx_inet6_addr(text, len, inaddr6.s6_addr) == NGX_OK) {
3572+
family = AF_INET6;
3573+
socklen = sizeof(struct sockaddr_in6);
3574+
#endif
3575+
} else {
3576+
return NULL;
3577+
}
3578+
addr = lua_newuserdata(L, sizeof(ngx_addr_t) + socklen + len);
3579+
if (addr == NULL) {
3580+
luaL_error(L, "no memory");
3581+
return NULL;
3582+
}
3583+
addr->sockaddr = (struct sockaddr *) ((u_char *) addr + sizeof(ngx_addr_t));
3584+
ngx_memzero(addr->sockaddr, socklen);
3585+
addr->sockaddr->sa_family = (u_char) family;
3586+
addr->socklen = socklen;
3587+
switch (family) {
3588+
#if (NGX_HAVE_INET6)
3589+
case AF_INET6:
3590+
sin6 = (struct sockaddr_in6 *) addr->sockaddr;
3591+
ngx_memcpy(sin6->sin6_addr.s6_addr, inaddr6.s6_addr, 16);
3592+
break;
3593+
#endif
3594+
default: /* AF_INET */
3595+
sin = (struct sockaddr_in *) addr->sockaddr;
3596+
sin->sin_addr.s_addr = inaddr;
3597+
break;
3598+
}
3599+
addr->name.data = (u_char *) addr->sockaddr + socklen;
3600+
addr->name.len = len;
3601+
ngx_memcpy(addr->name.data, text, len);
3602+
return addr;
3603+
}
3604+

src/ngx_stream_lua_util.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ void ngx_stream_lua_free_session(ngx_stream_session_t *s);
7373
void ngx_stream_lua_process_args_option(ngx_stream_session_t *s, lua_State *L,
7474
int table, ngx_str_t *args);
7575
void ngx_stream_lua_set_multi_value_table(lua_State *L, int index);
76+
ngx_addr_t *ngx_stream_lua_parse_addr(lua_State *L, u_char *text, size_t len);
7677

7778

7879
#ifndef NGX_UNESCAPE_URI_COMPONENT

t/141-tcp-socket-bind.t

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
2+
use Test::Nginx::Socket::Lua::Stream;
3+
4+
repeat_each(2);
5+
#repeat_each(1);
6+
7+
plan tests => repeat_each() * (blocks() * 3 + 1);
8+
9+
my $local_ip = `ifconfig | grep -oE '([0-9]{1,3}\\.?){4}' | grep '\\.' | grep -v '127.0.0.1' | head -n 1`;
10+
chomp $local_ip;
11+
12+
$ENV{TEST_NGINX_SERVER_IP} ||= $local_ip;
13+
$ENV{TEST_NGINX_NOT_EXIST_IP} ||= '8.8.8.8';
14+
$ENV{TEST_NGINX_INVALID_IP} ||= '127.0.0.1:8899';
15+
16+
no_long_string();
17+
#no_diff();
18+
#log_level 'warn';
19+
no_shuffle();
20+
21+
run_tests();
22+
23+
__DATA__
24+
25+
=== TEST 1: upstream sockets bind 127.0.0.1
26+
--- stream_config
27+
server {
28+
listen 2986;
29+
content_by_lua_block {
30+
ngx.say(ngx.var.remote_addr)
31+
}
32+
}
33+
--- stream_server_config
34+
content_by_lua_block {
35+
local ip = "127.0.0.1"
36+
local port = 2986
37+
local sock = ngx.socket.tcp()
38+
local ok, err = sock:bind(ip)
39+
if not ok then
40+
ngx.log(ngx.ERR, err)
41+
return
42+
end
43+
local ok, err = sock:connect("127.0.0.1", port)
44+
if not ok then
45+
ngx.log(ngx.ERR, err)
46+
return
47+
end
48+
49+
50+
local line, err, part = sock:receive()
51+
if line then
52+
ngx.say(line)
53+
else
54+
ngx.log(ngx.ERR, err)
55+
end
56+
}
57+
58+
--- stream_response
59+
127.0.0.1
60+
--- no_error_log
61+
[error]
62+
63+
64+
=== TEST 2: upstream sockets bind non loopback ip
65+
--- stream_config
66+
server {
67+
listen 2986;
68+
content_by_lua_block {
69+
ngx.say(ngx.var.remote_addr)
70+
}
71+
}
72+
--- stream_server_config
73+
content_by_lua_block {
74+
local ip = "$TEST_NGINX_SERVER_IP"
75+
local port = 2986
76+
local sock = ngx.socket.tcp()
77+
local ok, err = sock:bind(ip)
78+
if not ok then
79+
ngx.log(ngx.ERR, err)
80+
return
81+
end
82+
local ok, err = sock:connect("127.0.0.1", port)
83+
if not ok then
84+
ngx.log(ngx.ERR, err)
85+
return
86+
end
87+
88+
local line, err, part = sock:receive()
89+
if line == ip then
90+
ngx.say("ip matched")
91+
else
92+
ngx.log(ngx.ERR, err)
93+
end
94+
}
95+
96+
--- stream_response
97+
ip matched
98+
--- no_error_log
99+
[error]
100+
101+
102+
=== TEST 3: upstream sockets bind not exist ip
103+
--- stream_config
104+
server {
105+
listen 2986;
106+
content_by_lua_block {
107+
ngx.say(ngx.var.remote_addr)
108+
}
109+
}
110+
--- stream_server_config
111+
content_by_lua_block {
112+
local ip = "$TEST_NGINX_NOT_EXIST_IP"
113+
local port = 2986
114+
local sock = ngx.socket.tcp()
115+
local ok, err = sock:bind(ip)
116+
if not ok then
117+
ngx.log(ngx.ERR, err)
118+
end
119+
120+
local ok, err = sock:connect("127.0.0.1", port)
121+
if not ok then
122+
ngx.say(err)
123+
end
124+
}
125+
126+
--- stream_response
127+
cannot assign requested address
128+
--- error_log eval
129+
["bind(8.8.8.8) failed",
130+
"lua tcp socket bind ip: 8.8.8.8"]
131+
132+
133+
=== TEST 4: upstream sockets bind invalid ip
134+
--- stream_config
135+
server {
136+
listen 2986;
137+
content_by_lua_block {
138+
ngx.say(ngx.var.remote_addr)
139+
}
140+
}
141+
--- stream_server_config
142+
content_by_lua_block {
143+
local ip = "$TEST_NGINX_INVALID_IP"
144+
local port = 2986
145+
local sock = ngx.socket.tcp()
146+
local ok, err = sock:bind(ip)
147+
if not ok then
148+
ngx.say("failed to bind: ", err)
149+
end
150+
151+
local ok, err = sock:connect("127.0.0.1", port)
152+
if not ok then
153+
ngx.log(ngx.ERR, err)
154+
end
155+
}
156+
157+
--- stream_response
158+
failed to bind: bad address
159+
--- error_log eval
160+
--- no_error_log
161+
[error]

0 commit comments

Comments
 (0)