diff options
author | Sam Roberts <vieuxtech@gmail.com> | 2012-05-10 14:14:22 -0700 |
---|---|---|
committer | Sam Roberts <vieuxtech@gmail.com> | 2012-05-10 14:14:22 -0700 |
commit | 156669c28bc62bfddbd5625c4bb4c1f8da94802b (patch) | |
tree | 074ea57dd044a9bed306464af8c9f141bdf0ca19 | |
parent | 3d3e69c6e43f6431969e072e78df43b0ab73c48d (diff) | |
download | luasocket-156669c28bc62bfddbd5625c4bb4c1f8da94802b.tar.gz luasocket-156669c28bc62bfddbd5625c4bb4c1f8da94802b.tar.bz2 luasocket-156669c28bc62bfddbd5625c4bb4c1f8da94802b.zip |
socket.connect now implemented in the C core
This avoid socket.lua duplicating the iteration over the results
of getaddrinfo(). Some problems with the C implementation not
initializing sockets or the luasocket family have also been fixed,
and error reporting made more robust.
-rw-r--r-- | doc/reference.html | 2 | ||||
-rw-r--r-- | doc/socket.html | 11 | ||||
-rw-r--r-- | src/inet.c | 54 | ||||
-rw-r--r-- | src/inet.h | 3 | ||||
-rw-r--r-- | src/socket.lua | 34 | ||||
-rw-r--r-- | src/tcp.c | 11 | ||||
-rw-r--r-- | src/usocket.c | 2 |
7 files changed, 69 insertions, 48 deletions
diff --git a/doc/reference.html b/doc/reference.html index f069d47..e9bb5eb 100644 --- a/doc/reference.html +++ b/doc/reference.html | |||
@@ -145,6 +145,8 @@ Support, Manual"> | |||
145 | <blockquote> | 145 | <blockquote> |
146 | <a href="socket.html#bind">bind</a>, | 146 | <a href="socket.html#bind">bind</a>, |
147 | <a href="socket.html#connect">connect</a>, | 147 | <a href="socket.html#connect">connect</a>, |
148 | <a href="socket.html#connect">connect4</a>, | ||
149 | <a href="socket.html#connect">connect6</a>, | ||
148 | <a href="socket.html#debug">_DEBUG</a>, | 150 | <a href="socket.html#debug">_DEBUG</a>, |
149 | <a href="dns.html#dns">dns</a>, | 151 | <a href="dns.html#dns">dns</a>, |
150 | <a href="socket.html#gettime">gettime</a>, | 152 | <a href="socket.html#gettime">gettime</a>, |
diff --git a/doc/socket.html b/doc/socket.html index dcf8b61..b9303cb 100644 --- a/doc/socket.html +++ b/doc/socket.html | |||
@@ -73,14 +73,19 @@ set to <tt><b>true</b></tt>. | |||
73 | <!-- connect ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | 73 | <!-- connect ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> |
74 | 74 | ||
75 | <p class=name id=connect> | 75 | <p class=name id=connect> |
76 | socket.<b>connect(</b>address, port [, locaddr, locport]<b>)</b> | 76 | socket.<b>connect[46](</b>address, port [, locaddr] [, locport] [, family]<b>)</b> |
77 | </p> | 77 | </p> |
78 | 78 | ||
79 | <p class=description> | 79 | <p class=description> |
80 | This function is a shortcut that creates and returns a TCP client object | 80 | This function is a shortcut that creates and returns a TCP client object |
81 | connected to a remote <tt>host</tt> at a given <tt>port</tt>. Optionally, | 81 | connected to a remote <tt>address</tt> at a given <tt>port</tt>. Optionally, |
82 | the user can also specify the local address and port to bind | 82 | the user can also specify the local address and port to bind |
83 | (<tt>locaddr</tt> and <tt>locport</tt>). | 83 | (<tt>locaddr</tt> and <tt>locport</tt>), or restrict the socket family |
84 | to "<tt>inet</tt>" or "<tt>inet6</tt>". | ||
85 | Without specifying <tt>family</tt> to <tt>connect</tt>, whether a tcp or tcp6 | ||
86 | connection is created depends on your system configuration. Two variations | ||
87 | of connect are defined as simple helper functions that restrict the | ||
88 | <tt>family</tt>, <tt>socket.connect4</tt> and <tt>socket.connect6</tt>. | ||
84 | </p> | 89 | </p> |
85 | 90 | ||
86 | <!-- debug ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> | 91 | <!-- debug ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> |
@@ -143,6 +143,22 @@ static int inet_global_toip(lua_State *L) | |||
143 | return 2; | 143 | return 2; |
144 | } | 144 | } |
145 | 145 | ||
146 | int inet_optfamily(lua_State* L, int narg, const char* def) | ||
147 | { | ||
148 | static const char* optname[] = { "unspec", "inet", "inet6", NULL }; | ||
149 | static int optvalue[] = { PF_UNSPEC, PF_INET, PF_INET6, 0 }; | ||
150 | |||
151 | return optvalue[luaL_checkoption(L, narg, def, optname)]; | ||
152 | } | ||
153 | |||
154 | int inet_optsocktype(lua_State* L, int narg, const char* def) | ||
155 | { | ||
156 | static const char* optname[] = { "stream", "dgram", NULL }; | ||
157 | static int optvalue[] = { SOCK_STREAM, SOCK_DGRAM, 0 }; | ||
158 | |||
159 | return optvalue[luaL_checkoption(L, narg, def, optname)]; | ||
160 | } | ||
161 | |||
146 | static int inet_global_getaddrinfo(lua_State *L) | 162 | static int inet_global_getaddrinfo(lua_State *L) |
147 | { | 163 | { |
148 | const char *hostname = luaL_checkstring(L, 1); | 164 | const char *hostname = luaL_checkstring(L, 1); |
@@ -197,7 +213,7 @@ static int inet_global_gethostname(lua_State *L) | |||
197 | name[256] = '\0'; | 213 | name[256] = '\0'; |
198 | if (gethostname(name, 256) < 0) { | 214 | if (gethostname(name, 256) < 0) { |
199 | lua_pushnil(L); | 215 | lua_pushnil(L); |
200 | lua_pushstring(L, "gethostname failed"); | 216 | lua_pushstring(L, socket_strerror(errno)); |
201 | return 2; | 217 | return 2; |
202 | } else { | 218 | } else { |
203 | lua_pushstring(L, name); | 219 | lua_pushstring(L, name); |
@@ -222,7 +238,7 @@ int inet_meth_getpeername(lua_State *L, p_socket ps, int family) | |||
222 | char name[INET_ADDRSTRLEN]; | 238 | char name[INET_ADDRSTRLEN]; |
223 | if (getpeername(*ps, (SA *) &peer, &peer_len) < 0) { | 239 | if (getpeername(*ps, (SA *) &peer, &peer_len) < 0) { |
224 | lua_pushnil(L); | 240 | lua_pushnil(L); |
225 | lua_pushstring(L, "getpeername failed"); | 241 | lua_pushstring(L, socket_strerror(errno)); |
226 | return 2; | 242 | return 2; |
227 | } else { | 243 | } else { |
228 | inet_ntop(family, &peer.sin_addr, name, sizeof(name)); | 244 | inet_ntop(family, &peer.sin_addr, name, sizeof(name)); |
@@ -238,7 +254,7 @@ int inet_meth_getpeername(lua_State *L, p_socket ps, int family) | |||
238 | char name[INET6_ADDRSTRLEN]; | 254 | char name[INET6_ADDRSTRLEN]; |
239 | if (getpeername(*ps, (SA *) &peer, &peer_len) < 0) { | 255 | if (getpeername(*ps, (SA *) &peer, &peer_len) < 0) { |
240 | lua_pushnil(L); | 256 | lua_pushnil(L); |
241 | lua_pushstring(L, "getpeername failed"); | 257 | lua_pushstring(L, socket_strerror(errno)); |
242 | return 2; | 258 | return 2; |
243 | } else { | 259 | } else { |
244 | inet_ntop(family, &peer.sin6_addr, name, sizeof(name)); | 260 | inet_ntop(family, &peer.sin6_addr, name, sizeof(name)); |
@@ -251,7 +267,7 @@ int inet_meth_getpeername(lua_State *L, p_socket ps, int family) | |||
251 | } | 267 | } |
252 | default: | 268 | default: |
253 | lua_pushnil(L); | 269 | lua_pushnil(L); |
254 | lua_pushstring(L, "unknown family"); | 270 | lua_pushfstring(L, "unknown family %d", family); |
255 | return 2; | 271 | return 2; |
256 | } | 272 | } |
257 | } | 273 | } |
@@ -268,7 +284,7 @@ int inet_meth_getsockname(lua_State *L, p_socket ps, int family) | |||
268 | char name[INET_ADDRSTRLEN]; | 284 | char name[INET_ADDRSTRLEN]; |
269 | if (getsockname(*ps, (SA *) &local, &local_len) < 0) { | 285 | if (getsockname(*ps, (SA *) &local, &local_len) < 0) { |
270 | lua_pushnil(L); | 286 | lua_pushnil(L); |
271 | lua_pushstring(L, "getsockname failed"); | 287 | lua_pushstring(L, socket_strerror(errno)); |
272 | return 2; | 288 | return 2; |
273 | } else { | 289 | } else { |
274 | inet_ntop(family, &local.sin_addr, name, sizeof(name)); | 290 | inet_ntop(family, &local.sin_addr, name, sizeof(name)); |
@@ -284,7 +300,7 @@ int inet_meth_getsockname(lua_State *L, p_socket ps, int family) | |||
284 | char name[INET6_ADDRSTRLEN]; | 300 | char name[INET6_ADDRSTRLEN]; |
285 | if (getsockname(*ps, (SA *) &local, &local_len) < 0) { | 301 | if (getsockname(*ps, (SA *) &local, &local_len) < 0) { |
286 | lua_pushnil(L); | 302 | lua_pushnil(L); |
287 | lua_pushstring(L, "getsockname failed"); | 303 | lua_pushstring(L, socket_strerror(errno)); |
288 | return 2; | 304 | return 2; |
289 | } else { | 305 | } else { |
290 | inet_ntop(family, &local.sin6_addr, name, sizeof(name)); | 306 | inet_ntop(family, &local.sin6_addr, name, sizeof(name)); |
@@ -296,7 +312,7 @@ int inet_meth_getsockname(lua_State *L, p_socket ps, int family) | |||
296 | } | 312 | } |
297 | default: | 313 | default: |
298 | lua_pushnil(L); | 314 | lua_pushnil(L); |
299 | lua_pushstring(L, "unknown family"); | 315 | lua_pushfstring(L, "unknown family %d", family); |
300 | return 2; | 316 | return 2; |
301 | } | 317 | } |
302 | } | 318 | } |
@@ -390,6 +406,7 @@ const char *inet_trybind(p_socket ps, const char *address, const char *serv, | |||
390 | { | 406 | { |
391 | struct addrinfo *iterator = NULL, *resolved = NULL; | 407 | struct addrinfo *iterator = NULL, *resolved = NULL; |
392 | const char *err = NULL; | 408 | const char *err = NULL; |
409 | t_socket sock = *ps; | ||
393 | /* translate luasocket special values to C */ | 410 | /* translate luasocket special values to C */ |
394 | if (strcmp(address, "*") == 0) address = NULL; | 411 | if (strcmp(address, "*") == 0) address = NULL; |
395 | if (!serv) serv = "0"; | 412 | if (!serv) serv = "0"; |
@@ -402,17 +419,30 @@ const char *inet_trybind(p_socket ps, const char *address, const char *serv, | |||
402 | } | 419 | } |
403 | /* iterate over resolved addresses until one is good */ | 420 | /* iterate over resolved addresses until one is good */ |
404 | for (iterator = resolved; iterator; iterator = iterator->ai_next) { | 421 | for (iterator = resolved; iterator; iterator = iterator->ai_next) { |
422 | if(sock == SOCKET_INVALID) { | ||
423 | err = socket_strerror( socket_create(&sock, iterator->ai_family, | ||
424 | iterator->ai_socktype, iterator->ai_protocol)); | ||
425 | if(err) | ||
426 | continue; | ||
427 | } | ||
405 | /* try binding to local address */ | 428 | /* try binding to local address */ |
406 | err = socket_strerror(socket_bind(ps, | 429 | err = socket_strerror(socket_bind(&sock, |
407 | (SA *) iterator->ai_addr, | 430 | (SA *) iterator->ai_addr, |
408 | iterator->ai_addrlen)); | 431 | iterator->ai_addrlen)); |
409 | /* if faiiled, we try the next one */ | 432 | |
410 | if (err != NULL) socket_destroy(ps); | 433 | /* keep trying unless bind succeeded */ |
411 | /* if success, we abort loop */ | 434 | if (err) { |
412 | else break; | 435 | if(sock != *ps) |
436 | socket_destroy(&sock); | ||
437 | } else { | ||
438 | /* remember what we connected to, particularly the family */ | ||
439 | *bindhints = *iterator; | ||
440 | break; | ||
441 | } | ||
413 | } | 442 | } |
414 | /* cleanup and return error */ | 443 | /* cleanup and return error */ |
415 | freeaddrinfo(resolved); | 444 | freeaddrinfo(resolved); |
445 | *ps = sock; | ||
416 | return err; | 446 | return err; |
417 | } | 447 | } |
418 | 448 | ||
@@ -33,6 +33,9 @@ const char *inet_trybind(p_socket ps, const char *address, const char *serv, | |||
33 | int inet_meth_getpeername(lua_State *L, p_socket ps, int family); | 33 | 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); | 34 | int inet_meth_getsockname(lua_State *L, p_socket ps, int family); |
35 | 35 | ||
36 | int inet_optfamily(lua_State* L, int narg, const char* def); | ||
37 | int inet_optsocktype(lua_State* L, int narg, const char* def); | ||
38 | |||
36 | #ifdef INET_ATON | 39 | #ifdef INET_ATON |
37 | int inet_aton(const char *cp, struct in_addr *inp); | 40 | int inet_aton(const char *cp, struct in_addr *inp); |
38 | #endif | 41 | #endif |
diff --git a/src/socket.lua b/src/socket.lua index 8c5f231..e8def75 100644 --- a/src/socket.lua +++ b/src/socket.lua | |||
@@ -15,34 +15,12 @@ module("socket") | |||
15 | ----------------------------------------------------------------------------- | 15 | ----------------------------------------------------------------------------- |
16 | -- Exported auxiliar functions | 16 | -- Exported auxiliar functions |
17 | ----------------------------------------------------------------------------- | 17 | ----------------------------------------------------------------------------- |
18 | function connect(address, port, laddress, lport) | 18 | function connect4(address, port, laddress, lport) |
19 | if address == "*" then address = "0.0.0.0" end | 19 | return socket.connect(address, port, laddress, lport, "inet") |
20 | local addrinfo, err = socket.dns.getaddrinfo(address); | 20 | end |
21 | if not addrinfo then return nil, err end | 21 | |
22 | local sock, res | 22 | function connect6(address, port, laddress, lport) |
23 | err = "no info on address" | 23 | return socket.connect(address, port, laddress, lport, "inet6") |
24 | for i, alt in base.ipairs(addrinfo) do | ||
25 | if alt.family == "inet" then | ||
26 | sock, err = socket.tcp() | ||
27 | else | ||
28 | sock, err = socket.tcp6() | ||
29 | end | ||
30 | if not sock then return nil, err end | ||
31 | if laddress then | ||
32 | res, err = sock:bind(laddress, lport) | ||
33 | if not res then | ||
34 | sock:close() | ||
35 | return nil, err | ||
36 | end | ||
37 | end | ||
38 | res, err = sock:connect(alt.addr, port) | ||
39 | if not res then | ||
40 | sock:close() | ||
41 | else | ||
42 | return sock | ||
43 | end | ||
44 | end | ||
45 | return nil, err | ||
46 | end | 24 | end |
47 | 25 | ||
48 | function bind(host, port, backlog) | 26 | function bind(host, port, backlog) |
@@ -18,7 +18,7 @@ | |||
18 | \*=========================================================================*/ | 18 | \*=========================================================================*/ |
19 | static int global_create(lua_State *L); | 19 | static int global_create(lua_State *L); |
20 | static int global_create6(lua_State *L); | 20 | static int global_create6(lua_State *L); |
21 | static int global_connect6(lua_State *L); | 21 | static int global_connect(lua_State *L); |
22 | static int meth_connect(lua_State *L); | 22 | static int meth_connect(lua_State *L); |
23 | static int meth_listen(lua_State *L); | 23 | static int meth_listen(lua_State *L); |
24 | static int meth_getfamily(lua_State *L); | 24 | static int meth_getfamily(lua_State *L); |
@@ -89,7 +89,7 @@ static t_opt optset[] = { | |||
89 | static luaL_Reg func[] = { | 89 | static luaL_Reg func[] = { |
90 | {"tcp", global_create}, | 90 | {"tcp", global_create}, |
91 | {"tcp6", global_create6}, | 91 | {"tcp6", global_create6}, |
92 | {"connect6", global_connect6}, | 92 | {"connect", global_connect}, |
93 | {NULL, NULL} | 93 | {NULL, NULL} |
94 | }; | 94 | }; |
95 | 95 | ||
@@ -408,6 +408,7 @@ static const char *tryconnect6(const char *remoteaddr, const char *remoteserv, | |||
408 | freeaddrinfo(resolved); | 408 | freeaddrinfo(resolved); |
409 | return err; | 409 | return err; |
410 | } | 410 | } |
411 | tcp->family = iterator->ai_family; | ||
411 | /* all sockets initially non-blocking */ | 412 | /* all sockets initially non-blocking */ |
412 | socket_setnonblocking(&tcp->sock); | 413 | socket_setnonblocking(&tcp->sock); |
413 | } | 414 | } |
@@ -424,11 +425,12 @@ static const char *tryconnect6(const char *remoteaddr, const char *remoteserv, | |||
424 | return err; | 425 | return err; |
425 | } | 426 | } |
426 | 427 | ||
427 | static int global_connect6(lua_State *L) { | 428 | static int global_connect(lua_State *L) { |
428 | const char *remoteaddr = luaL_checkstring(L, 1); | 429 | const char *remoteaddr = luaL_checkstring(L, 1); |
429 | const char *remoteserv = luaL_checkstring(L, 2); | 430 | const char *remoteserv = luaL_checkstring(L, 2); |
430 | const char *localaddr = luaL_optstring(L, 3, NULL); | 431 | const char *localaddr = luaL_optstring(L, 3, NULL); |
431 | const char *localserv = luaL_optstring(L, 4, "0"); | 432 | const char *localserv = luaL_optstring(L, 4, "0"); |
433 | int family = inet_optfamily(L, 5, "unspec"); | ||
432 | p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); | 434 | p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); |
433 | struct addrinfo bindhints, connecthints; | 435 | struct addrinfo bindhints, connecthints; |
434 | const char *err = NULL; | 436 | const char *err = NULL; |
@@ -441,7 +443,7 @@ static int global_connect6(lua_State *L) { | |||
441 | /* allow user to pick local address and port */ | 443 | /* allow user to pick local address and port */ |
442 | memset(&bindhints, 0, sizeof(bindhints)); | 444 | memset(&bindhints, 0, sizeof(bindhints)); |
443 | bindhints.ai_socktype = SOCK_STREAM; | 445 | bindhints.ai_socktype = SOCK_STREAM; |
444 | bindhints.ai_family = PF_UNSPEC; | 446 | bindhints.ai_family = family; |
445 | bindhints.ai_flags = AI_PASSIVE; | 447 | bindhints.ai_flags = AI_PASSIVE; |
446 | if (localaddr) { | 448 | if (localaddr) { |
447 | err = inet_trybind(&tcp->sock, localaddr, localserv, &bindhints); | 449 | err = inet_trybind(&tcp->sock, localaddr, localserv, &bindhints); |
@@ -450,6 +452,7 @@ static int global_connect6(lua_State *L) { | |||
450 | lua_pushstring(L, err); | 452 | lua_pushstring(L, err); |
451 | return 2; | 453 | return 2; |
452 | } | 454 | } |
455 | tcp->family = bindhints.ai_family; | ||
453 | } | 456 | } |
454 | /* try to connect to remote address and port */ | 457 | /* try to connect to remote address and port */ |
455 | memset(&connecthints, 0, sizeof(connecthints)); | 458 | memset(&connecthints, 0, sizeof(connecthints)); |
diff --git a/src/usocket.c b/src/usocket.c index a168bf6..7150996 100644 --- a/src/usocket.c +++ b/src/usocket.c | |||
@@ -447,7 +447,7 @@ const char *socket_gaistrerror(int err) { | |||
447 | case EAI_SERVICE: return "service not supported for socket type"; | 447 | case EAI_SERVICE: return "service not supported for socket type"; |
448 | case EAI_SOCKTYPE: return "ai_socktype not supported"; | 448 | case EAI_SOCKTYPE: return "ai_socktype not supported"; |
449 | case EAI_SYSTEM: return strerror(errno); | 449 | case EAI_SYSTEM: return strerror(errno); |
450 | default: return "unknown error"; | 450 | default: return gai_strerror(err); |
451 | } | 451 | } |
452 | } | 452 | } |
453 | 453 | ||