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 | ||
