diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | doc/tcp.html | 4 | ||||
-rw-r--r-- | src/inet.c | 45 | ||||
-rw-r--r-- | src/inet.h | 1 | ||||
-rw-r--r-- | src/tcp.c | 4 | ||||
-rw-r--r-- | src/udp.c | 35 | ||||
-rw-r--r-- | test/udpconnectclnt.lua | 19 | ||||
-rw-r--r-- | test/udpconnectsrvr.lua | 16 |
9 files changed, 96 insertions, 31 deletions
@@ -1,3 +1,4 @@ | |||
1 | *.o | 1 | *.o |
2 | *.so | 2 | *.so |
3 | *.so.* | 3 | *.so.* |
4 | mac | ||
@@ -1,3 +1,5 @@ | |||
1 | - bizarre default values for getnameinfo should throw error instead! | ||
2 | |||
1 | - document the new bind and connect behavior. | 3 | - document the new bind and connect behavior. |
2 | - shouldn't we instead make the code compatible to Lua 5.2 | 4 | - shouldn't we instead make the code compatible to Lua 5.2 |
3 | without any compat stuff, and use a compatibility layer to | 5 | without any compat stuff, and use a compatibility layer to |
diff --git a/doc/tcp.html b/doc/tcp.html index 5f39d0e..151a4c9 100644 --- a/doc/tcp.html +++ b/doc/tcp.html | |||
@@ -125,7 +125,9 @@ local host. | |||
125 | <tt>Port</tt> must be an integer number in the range [0..64K). | 125 | <tt>Port</tt> must be an integer number in the range [0..64K). |
126 | If <tt>address</tt> | 126 | If <tt>address</tt> |
127 | is '<tt>*</tt>', the system binds to all local interfaces | 127 | is '<tt>*</tt>', the system binds to all local interfaces |
128 | using the <tt>INADDR_ANY</tt> constant. If <tt>port</tt> is 0, the system automatically | 128 | using the <tt>INADDR_ANY</tt> constant or |
129 | <tt>IN6ADDR_ANY_INIT</tt>, according to the family. | ||
130 | If <tt>port</tt> is 0, the system automatically | ||
129 | chooses an ephemeral port. | 131 | chooses an ephemeral port. |
130 | </p> | 132 | </p> |
131 | 133 | ||
@@ -177,8 +177,8 @@ static int inet_global_getaddrinfo(lua_State *L) | |||
177 | lua_newtable(L); | 177 | lua_newtable(L); |
178 | for (iterator = resolved; iterator; iterator = iterator->ai_next) { | 178 | for (iterator = resolved; iterator; iterator = iterator->ai_next) { |
179 | char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; | 179 | char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; |
180 | getnameinfo(iterator->ai_addr, iterator->ai_addrlen, hbuf, sizeof(hbuf), | 180 | getnameinfo(iterator->ai_addr, iterator->ai_addrlen, hbuf, |
181 | sbuf, 0, NI_NUMERICHOST); | 181 | sizeof(hbuf), sbuf, 0, NI_NUMERICHOST); |
182 | lua_pushnumber(L, i); | 182 | lua_pushnumber(L, i); |
183 | lua_newtable(L); | 183 | lua_newtable(L); |
184 | switch (iterator->ai_family) { | 184 | switch (iterator->ai_family) { |
@@ -368,6 +368,34 @@ const char *inet_trycreate(p_socket ps, int family, int type) { | |||
368 | } | 368 | } |
369 | 369 | ||
370 | /*-------------------------------------------------------------------------*\ | 370 | /*-------------------------------------------------------------------------*\ |
371 | * "Disconnects" a DGRAM socket | ||
372 | \*-------------------------------------------------------------------------*/ | ||
373 | const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm) | ||
374 | { | ||
375 | switch (family) { | ||
376 | case PF_INET: { | ||
377 | struct sockaddr_in sin; | ||
378 | memset((char *) &sin, 0, sizeof(sin)); | ||
379 | sin.sin_family = AF_UNSPEC; | ||
380 | sin.sin_addr.s_addr = INADDR_ANY; | ||
381 | return socket_strerror(socket_connect(ps, (SA *) &sin, | ||
382 | sizeof(sin), tm)); | ||
383 | } | ||
384 | case PF_INET6: { | ||
385 | struct sockaddr_in6 sin6; | ||
386 | struct in6_addr addrany = IN6ADDR_ANY_INIT; | ||
387 | memset((char *) &sin6, 0, sizeof(sin6)); | ||
388 | sin6.sin6_family = AF_UNSPEC; | ||
389 | fprintf(stderr, "disconnecting\n"); | ||
390 | sin6.sin6_addr = addrany; | ||
391 | return socket_strerror(socket_connect(ps, (SA *) &sin6, | ||
392 | sizeof(sin6), tm)); | ||
393 | } | ||
394 | } | ||
395 | return NULL; | ||
396 | } | ||
397 | |||
398 | /*-------------------------------------------------------------------------*\ | ||
371 | * Tries to connect to remote address (address, port) | 399 | * Tries to connect to remote address (address, port) |
372 | \*-------------------------------------------------------------------------*/ | 400 | \*-------------------------------------------------------------------------*/ |
373 | const char *inet_tryconnect(p_socket ps, const char *address, | 401 | const char *inet_tryconnect(p_socket ps, const char *address, |
@@ -382,17 +410,14 @@ const char *inet_tryconnect(p_socket ps, const char *address, | |||
382 | if (resolved) freeaddrinfo(resolved); | 410 | if (resolved) freeaddrinfo(resolved); |
383 | return err; | 411 | return err; |
384 | } | 412 | } |
385 | /* iterate over all returned addresses trying to connect */ | ||
386 | for (iterator = resolved; iterator; iterator = iterator->ai_next) { | 413 | for (iterator = resolved; iterator; iterator = iterator->ai_next) { |
387 | timeout_markstart(tm); | 414 | timeout_markstart(tm); |
388 | /* try connecting to remote address */ | 415 | /* try connecting to remote address */ |
389 | err = socket_strerror(socket_connect(ps, | 416 | err = socket_strerror(socket_connect(ps, (SA *) iterator->ai_addr, |
390 | (SA *) iterator->ai_addr, | ||
391 | iterator->ai_addrlen, tm)); | 417 | iterator->ai_addrlen, tm)); |
392 | /* if success, break out of loop */ | 418 | /* if success, break out of loop */ |
393 | if (err == NULL) break; | 419 | if (err == NULL) break; |
394 | } | 420 | } |
395 | |||
396 | freeaddrinfo(resolved); | 421 | freeaddrinfo(resolved); |
397 | /* here, if err is set, we failed */ | 422 | /* here, if err is set, we failed */ |
398 | return err; | 423 | return err; |
@@ -407,12 +432,8 @@ const char *inet_trybind(p_socket ps, const char *address, const char *serv, | |||
407 | struct addrinfo *iterator = NULL, *resolved = NULL; | 432 | struct addrinfo *iterator = NULL, *resolved = NULL; |
408 | const char *err = NULL; | 433 | const char *err = NULL; |
409 | t_socket sock = *ps; | 434 | t_socket sock = *ps; |
410 | /* translate luasocket special values to C */ | ||
411 | if (strcmp(address, "*") == 0) address = NULL; | ||
412 | if (!serv) serv = "0"; | ||
413 | /* try resolving */ | 435 | /* try resolving */ |
414 | err = socket_gaistrerror(getaddrinfo(address, serv, | 436 | err = socket_gaistrerror(getaddrinfo(address, serv, bindhints, &resolved)); |
415 | bindhints, &resolved)); | ||
416 | if (err) { | 437 | if (err) { |
417 | if (resolved) freeaddrinfo(resolved); | 438 | if (resolved) freeaddrinfo(resolved); |
418 | return err; | 439 | return err; |
@@ -420,7 +441,7 @@ const char *inet_trybind(p_socket ps, const char *address, const char *serv, | |||
420 | /* iterate over resolved addresses until one is good */ | 441 | /* iterate over resolved addresses until one is good */ |
421 | for (iterator = resolved; iterator; iterator = iterator->ai_next) { | 442 | for (iterator = resolved; iterator; iterator = iterator->ai_next) { |
422 | if(sock == SOCKET_INVALID) { | 443 | if(sock == SOCKET_INVALID) { |
423 | err = socket_strerror( socket_create(&sock, iterator->ai_family, | 444 | err = socket_strerror(socket_create(&sock, iterator->ai_family, |
424 | iterator->ai_socktype, iterator->ai_protocol)); | 445 | iterator->ai_socktype, iterator->ai_protocol)); |
425 | if(err) | 446 | if(err) |
426 | continue; | 447 | continue; |
@@ -29,6 +29,7 @@ const char *inet_tryconnect(p_socket ps, const char *address, | |||
29 | const char *serv, p_timeout tm, struct addrinfo *connecthints); | 29 | const char *serv, p_timeout tm, struct addrinfo *connecthints); |
30 | const char *inet_trybind(p_socket ps, const char *address, const char *serv, | 30 | const char *inet_trybind(p_socket ps, const char *address, const char *serv, |
31 | struct addrinfo *bindhints); | 31 | struct addrinfo *bindhints); |
32 | const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm); | ||
32 | 33 | ||
33 | int inet_meth_getpeername(lua_State *L, p_socket ps, int family); | 34 | int inet_meth_getpeername(lua_State *L, p_socket ps, int family); |
34 | int inet_meth_getsockname(lua_State *L, p_socket ps, int family); | 35 | int inet_meth_getsockname(lua_State *L, p_socket ps, int family); |
@@ -222,6 +222,7 @@ static int meth_bind(lua_State *L) | |||
222 | bindhints.ai_socktype = SOCK_STREAM; | 222 | bindhints.ai_socktype = SOCK_STREAM; |
223 | bindhints.ai_family = tcp->family; | 223 | bindhints.ai_family = tcp->family; |
224 | bindhints.ai_flags = AI_PASSIVE; | 224 | bindhints.ai_flags = AI_PASSIVE; |
225 | address = strcmp(address, "*")? address: NULL; | ||
225 | err = inet_trybind(&tcp->sock, address, port, &bindhints); | 226 | err = inet_trybind(&tcp->sock, address, port, &bindhints); |
226 | if (err) { | 227 | if (err) { |
227 | lua_pushnil(L); | 228 | lua_pushnil(L); |
@@ -247,8 +248,7 @@ static int meth_connect(lua_State *L) | |||
247 | /* make sure we try to connect only to the same family */ | 248 | /* make sure we try to connect only to the same family */ |
248 | connecthints.ai_family = tcp->family; | 249 | connecthints.ai_family = tcp->family; |
249 | timeout_markstart(&tcp->tm); | 250 | timeout_markstart(&tcp->tm); |
250 | err = inet_tryconnect(&tcp->sock, address, port, | 251 | err = inet_tryconnect(&tcp->sock, address, port, &tcp->tm, &connecthints); |
251 | &tcp->tm, &connecthints); | ||
252 | /* have to set the class even if it failed due to non-blocking connects */ | 252 | /* have to set the class even if it failed due to non-blocking connects */ |
253 | auxiliar_setclass(L, "tcp{client}", 1); | 253 | auxiliar_setclass(L, "tcp{client}", 1); |
254 | if (err) { | 254 | if (err) { |
@@ -275,10 +275,10 @@ static int meth_receivefrom(lua_State *L) { | |||
275 | } | 275 | } |
276 | break; | 276 | break; |
277 | } | 277 | } |
278 | default: | 278 | default: |
279 | lua_pushnil(L); | 279 | lua_pushnil(L); |
280 | lua_pushfstring(L, "unknown family %d", udp->family); | 280 | lua_pushfstring(L, "unknown family %d", udp->family); |
281 | return 2; | 281 | return 2; |
282 | } | 282 | } |
283 | lua_pushnil(L); | 283 | lua_pushnil(L); |
284 | lua_pushstring(L, udp_strerror(err)); | 284 | lua_pushstring(L, udp_strerror(err)); |
@@ -366,27 +366,30 @@ static int meth_settimeout(lua_State *L) { | |||
366 | static int meth_setpeername(lua_State *L) { | 366 | static int meth_setpeername(lua_State *L) { |
367 | p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); | 367 | p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); |
368 | p_timeout tm = &udp->tm; | 368 | p_timeout tm = &udp->tm; |
369 | const char *address = luaL_checkstring(L, 2); | 369 | const char *address = luaL_checkstring(L, 2); |
370 | int connecting = strcmp(address, "*"); | 370 | int connecting = strcmp(address, "*"); |
371 | const char *port = connecting ? | 371 | const char *port = connecting? luaL_checkstring(L, 3): "0"; |
372 | luaL_checkstring(L, 3) : | ||
373 | luaL_optstring(L, 3, "0"); | ||
374 | struct addrinfo connecthints; | 372 | struct addrinfo connecthints; |
375 | const char *err; | 373 | const char *err; |
376 | memset(&connecthints, 0, sizeof(connecthints)); | 374 | memset(&connecthints, 0, sizeof(connecthints)); |
377 | connecthints.ai_socktype = SOCK_DGRAM; | 375 | connecthints.ai_socktype = SOCK_DGRAM; |
378 | /* make sure we try to connect only to the same family */ | 376 | /* make sure we try to connect only to the same family */ |
379 | connecthints.ai_family = udp->family; | 377 | connecthints.ai_family = udp->family; |
380 | err = inet_tryconnect(&udp->sock, address, port, | 378 | if (connecting) { |
381 | tm, &connecthints); | 379 | err = inet_tryconnect(&udp->sock, address, port, tm, &connecthints); |
382 | if (err) { | 380 | if (err) { |
383 | lua_pushnil(L); | 381 | lua_pushnil(L); |
384 | lua_pushstring(L, err); | 382 | lua_pushstring(L, err); |
385 | return 2; | 383 | return 2; |
384 | } | ||
385 | auxiliar_setclass(L, "udp{connected}", 1); | ||
386 | } else { | ||
387 | /* we ignore possible errors because Mac OS X always | ||
388 | * returns EAFNOSUPPORT */ | ||
389 | inet_trydisconnect(&udp->sock, udp->family, tm); | ||
390 | auxiliar_setclass(L, "udp{unconnected}", 1); | ||
386 | } | 391 | } |
387 | /* change class to connected or unconnected depending on address */ | 392 | /* change class to connected or unconnected depending on address */ |
388 | if (connecting) auxiliar_setclass(L, "udp{connected}", 1); | ||
389 | else auxiliar_setclass(L, "udp{unconnected}", 1); | ||
390 | lua_pushnumber(L, 1); | 393 | lua_pushnumber(L, 1); |
391 | return 1; | 394 | return 1; |
392 | } | 395 | } |
diff --git a/test/udpconnectclnt.lua b/test/udpconnectclnt.lua new file mode 100644 index 0000000..effe13a --- /dev/null +++ b/test/udpconnectclnt.lua | |||
@@ -0,0 +1,19 @@ | |||
1 | local socket = require"socket" | ||
2 | local udp = socket.udp | ||
3 | local localhost = "127.0.0.1" | ||
4 | local port = arg[1] | ||
5 | |||
6 | se = udp(); se:setoption("reuseaddr", true) | ||
7 | se:setsockname(localhost, 5062) | ||
8 | print("se", se:getsockname()) | ||
9 | sc = udp(); sc:setoption("reuseaddr", true) | ||
10 | sc:setsockname(localhost, 5061) | ||
11 | print("sc", sc:getsockname()) | ||
12 | |||
13 | se:sendto("this is a test from se", localhost, port) | ||
14 | socket.sleep(1) | ||
15 | sc:sendto("this is a test from sc", localhost, port) | ||
16 | socket.sleep(1) | ||
17 | se:sendto("this is a test from se", localhost, port) | ||
18 | socket.sleep(1) | ||
19 | sc:sendto("this is a test from sc", localhost, port) | ||
diff --git a/test/udpconnectsrvr.lua b/test/udpconnectsrvr.lua new file mode 100644 index 0000000..5a9772e --- /dev/null +++ b/test/udpconnectsrvr.lua | |||
@@ -0,0 +1,16 @@ | |||
1 | local socket = require"socket" | ||
2 | local udp = socket.udp | ||
3 | local localhost = "127.0.0.1" | ||
4 | local s = assert(udp()) | ||
5 | assert(tostring(s):find("udp{unconnected}")) | ||
6 | print("setpeername", s:setpeername(localhost, 5061)) | ||
7 | print("getsockname", s:getsockname()) | ||
8 | assert(tostring(s):find("udp{connected}")) | ||
9 | print(s:receive()) | ||
10 | print("setpeername", s:setpeername("*")) | ||
11 | print("getsockname", s:getsockname()) | ||
12 | s:sendto("a", localhost, 12345) | ||
13 | print("getsockname", s:getsockname()) | ||
14 | assert(tostring(s):find("udp{unconnected}")) | ||
15 | print(s:receivefrom()) | ||
16 | s:close() | ||