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.
This commit is contained in:
Sam Roberts 2012-05-10 14:14:22 -07:00
parent 3d3e69c6e4
commit 156669c28b
7 changed files with 69 additions and 48 deletions

View File

@ -145,6 +145,8 @@ Support, Manual">
<blockquote> <blockquote>
<a href="socket.html#bind">bind</a>, <a href="socket.html#bind">bind</a>,
<a href="socket.html#connect">connect</a>, <a href="socket.html#connect">connect</a>,
<a href="socket.html#connect">connect4</a>,
<a href="socket.html#connect">connect6</a>,
<a href="socket.html#debug">_DEBUG</a>, <a href="socket.html#debug">_DEBUG</a>,
<a href="dns.html#dns">dns</a>, <a href="dns.html#dns">dns</a>,
<a href="socket.html#gettime">gettime</a>, <a href="socket.html#gettime">gettime</a>,

View File

@ -73,14 +73,19 @@ set to <tt><b>true</b></tt>.
<!-- connect ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- connect ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
<p class=name id=connect> <p class=name id=connect>
socket.<b>connect(</b>address, port [, locaddr, locport]<b>)</b> socket.<b>connect[46](</b>address, port [, locaddr] [, locport] [, family]<b>)</b>
</p> </p>
<p class=description> <p class=description>
This function is a shortcut that creates and returns a TCP client object This function is a shortcut that creates and returns a TCP client object
connected to a remote <tt>host</tt> at a given <tt>port</tt>. Optionally, connected to a remote <tt>address</tt> at a given <tt>port</tt>. Optionally,
the user can also specify the local address and port to bind the user can also specify the local address and port to bind
(<tt>locaddr</tt> and <tt>locport</tt>). (<tt>locaddr</tt> and <tt>locport</tt>), or restrict the socket family
to "<tt>inet</tt>" or "<tt>inet6</tt>".
Without specifying <tt>family</tt> to <tt>connect</tt>, whether a tcp or tcp6
connection is created depends on your system configuration. Two variations
of connect are defined as simple helper functions that restrict the
<tt>family</tt>, <tt>socket.connect4</tt> and <tt>socket.connect6</tt>.
</p> </p>
<!-- debug ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> <!-- debug ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->

View File

@ -143,6 +143,22 @@ static int inet_global_toip(lua_State *L)
return 2; return 2;
} }
int inet_optfamily(lua_State* L, int narg, const char* def)
{
static const char* optname[] = { "unspec", "inet", "inet6", NULL };
static int optvalue[] = { PF_UNSPEC, PF_INET, PF_INET6, 0 };
return optvalue[luaL_checkoption(L, narg, def, optname)];
}
int inet_optsocktype(lua_State* L, int narg, const char* def)
{
static const char* optname[] = { "stream", "dgram", NULL };
static int optvalue[] = { SOCK_STREAM, SOCK_DGRAM, 0 };
return optvalue[luaL_checkoption(L, narg, def, optname)];
}
static int inet_global_getaddrinfo(lua_State *L) static int inet_global_getaddrinfo(lua_State *L)
{ {
const char *hostname = luaL_checkstring(L, 1); const char *hostname = luaL_checkstring(L, 1);
@ -197,7 +213,7 @@ static int inet_global_gethostname(lua_State *L)
name[256] = '\0'; name[256] = '\0';
if (gethostname(name, 256) < 0) { if (gethostname(name, 256) < 0) {
lua_pushnil(L); lua_pushnil(L);
lua_pushstring(L, "gethostname failed"); lua_pushstring(L, socket_strerror(errno));
return 2; return 2;
} else { } else {
lua_pushstring(L, name); lua_pushstring(L, name);
@ -222,7 +238,7 @@ int inet_meth_getpeername(lua_State *L, p_socket ps, int family)
char name[INET_ADDRSTRLEN]; char name[INET_ADDRSTRLEN];
if (getpeername(*ps, (SA *) &peer, &peer_len) < 0) { if (getpeername(*ps, (SA *) &peer, &peer_len) < 0) {
lua_pushnil(L); lua_pushnil(L);
lua_pushstring(L, "getpeername failed"); lua_pushstring(L, socket_strerror(errno));
return 2; return 2;
} else { } else {
inet_ntop(family, &peer.sin_addr, name, sizeof(name)); 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)
char name[INET6_ADDRSTRLEN]; char name[INET6_ADDRSTRLEN];
if (getpeername(*ps, (SA *) &peer, &peer_len) < 0) { if (getpeername(*ps, (SA *) &peer, &peer_len) < 0) {
lua_pushnil(L); lua_pushnil(L);
lua_pushstring(L, "getpeername failed"); lua_pushstring(L, socket_strerror(errno));
return 2; return 2;
} else { } else {
inet_ntop(family, &peer.sin6_addr, name, sizeof(name)); 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)
} }
default: default:
lua_pushnil(L); lua_pushnil(L);
lua_pushstring(L, "unknown family"); lua_pushfstring(L, "unknown family %d", family);
return 2; return 2;
} }
} }
@ -268,7 +284,7 @@ int inet_meth_getsockname(lua_State *L, p_socket ps, int family)
char name[INET_ADDRSTRLEN]; char name[INET_ADDRSTRLEN];
if (getsockname(*ps, (SA *) &local, &local_len) < 0) { if (getsockname(*ps, (SA *) &local, &local_len) < 0) {
lua_pushnil(L); lua_pushnil(L);
lua_pushstring(L, "getsockname failed"); lua_pushstring(L, socket_strerror(errno));
return 2; return 2;
} else { } else {
inet_ntop(family, &local.sin_addr, name, sizeof(name)); 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)
char name[INET6_ADDRSTRLEN]; char name[INET6_ADDRSTRLEN];
if (getsockname(*ps, (SA *) &local, &local_len) < 0) { if (getsockname(*ps, (SA *) &local, &local_len) < 0) {
lua_pushnil(L); lua_pushnil(L);
lua_pushstring(L, "getsockname failed"); lua_pushstring(L, socket_strerror(errno));
return 2; return 2;
} else { } else {
inet_ntop(family, &local.sin6_addr, name, sizeof(name)); 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)
} }
default: default:
lua_pushnil(L); lua_pushnil(L);
lua_pushstring(L, "unknown family"); lua_pushfstring(L, "unknown family %d", family);
return 2; return 2;
} }
} }
@ -390,6 +406,7 @@ const char *inet_trybind(p_socket ps, const char *address, const char *serv,
{ {
struct addrinfo *iterator = NULL, *resolved = NULL; struct addrinfo *iterator = NULL, *resolved = NULL;
const char *err = NULL; const char *err = NULL;
t_socket sock = *ps;
/* translate luasocket special values to C */ /* translate luasocket special values to C */
if (strcmp(address, "*") == 0) address = NULL; if (strcmp(address, "*") == 0) address = NULL;
if (!serv) serv = "0"; if (!serv) serv = "0";
@ -402,17 +419,30 @@ const char *inet_trybind(p_socket ps, const char *address, const char *serv,
} }
/* iterate over resolved addresses until one is good */ /* iterate over resolved addresses until one is good */
for (iterator = resolved; iterator; iterator = iterator->ai_next) { for (iterator = resolved; iterator; iterator = iterator->ai_next) {
if(sock == SOCKET_INVALID) {
err = socket_strerror( socket_create(&sock, iterator->ai_family,
iterator->ai_socktype, iterator->ai_protocol));
if(err)
continue;
}
/* try binding to local address */ /* try binding to local address */
err = socket_strerror(socket_bind(ps, err = socket_strerror(socket_bind(&sock,
(SA *) iterator->ai_addr, (SA *) iterator->ai_addr,
iterator->ai_addrlen)); iterator->ai_addrlen));
/* if faiiled, we try the next one */
if (err != NULL) socket_destroy(ps); /* keep trying unless bind succeeded */
/* if success, we abort loop */ if (err) {
else break; if(sock != *ps)
socket_destroy(&sock);
} else {
/* remember what we connected to, particularly the family */
*bindhints = *iterator;
break;
}
} }
/* cleanup and return error */ /* cleanup and return error */
freeaddrinfo(resolved); freeaddrinfo(resolved);
*ps = sock;
return err; return err;
} }

View File

@ -33,6 +33,9 @@ const char *inet_trybind(p_socket ps, const char *address, const char *serv,
int inet_meth_getpeername(lua_State *L, p_socket ps, int family); int inet_meth_getpeername(lua_State *L, p_socket ps, int family);
int inet_meth_getsockname(lua_State *L, p_socket ps, int family); int inet_meth_getsockname(lua_State *L, p_socket ps, int family);
int inet_optfamily(lua_State* L, int narg, const char* def);
int inet_optsocktype(lua_State* L, int narg, const char* def);
#ifdef INET_ATON #ifdef INET_ATON
int inet_aton(const char *cp, struct in_addr *inp); int inet_aton(const char *cp, struct in_addr *inp);
#endif #endif

View File

@ -15,34 +15,12 @@ module("socket")
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Exported auxiliar functions -- Exported auxiliar functions
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
function connect(address, port, laddress, lport) function connect4(address, port, laddress, lport)
if address == "*" then address = "0.0.0.0" end return socket.connect(address, port, laddress, lport, "inet")
local addrinfo, err = socket.dns.getaddrinfo(address); end
if not addrinfo then return nil, err end
local sock, res function connect6(address, port, laddress, lport)
err = "no info on address" return socket.connect(address, port, laddress, lport, "inet6")
for i, alt in base.ipairs(addrinfo) do
if alt.family == "inet" then
sock, err = socket.tcp()
else
sock, err = socket.tcp6()
end
if not sock then return nil, err end
if laddress then
res, err = sock:bind(laddress, lport)
if not res then
sock:close()
return nil, err
end
end
res, err = sock:connect(alt.addr, port)
if not res then
sock:close()
else
return sock
end
end
return nil, err
end end
function bind(host, port, backlog) function bind(host, port, backlog)

View File

@ -18,7 +18,7 @@
\*=========================================================================*/ \*=========================================================================*/
static int global_create(lua_State *L); static int global_create(lua_State *L);
static int global_create6(lua_State *L); static int global_create6(lua_State *L);
static int global_connect6(lua_State *L); static int global_connect(lua_State *L);
static int meth_connect(lua_State *L); static int meth_connect(lua_State *L);
static int meth_listen(lua_State *L); static int meth_listen(lua_State *L);
static int meth_getfamily(lua_State *L); static int meth_getfamily(lua_State *L);
@ -89,7 +89,7 @@ static t_opt optset[] = {
static luaL_Reg func[] = { static luaL_Reg func[] = {
{"tcp", global_create}, {"tcp", global_create},
{"tcp6", global_create6}, {"tcp6", global_create6},
{"connect6", global_connect6}, {"connect", global_connect},
{NULL, NULL} {NULL, NULL}
}; };
@ -408,6 +408,7 @@ static const char *tryconnect6(const char *remoteaddr, const char *remoteserv,
freeaddrinfo(resolved); freeaddrinfo(resolved);
return err; return err;
} }
tcp->family = iterator->ai_family;
/* all sockets initially non-blocking */ /* all sockets initially non-blocking */
socket_setnonblocking(&tcp->sock); socket_setnonblocking(&tcp->sock);
} }
@ -424,11 +425,12 @@ static const char *tryconnect6(const char *remoteaddr, const char *remoteserv,
return err; return err;
} }
static int global_connect6(lua_State *L) { static int global_connect(lua_State *L) {
const char *remoteaddr = luaL_checkstring(L, 1); const char *remoteaddr = luaL_checkstring(L, 1);
const char *remoteserv = luaL_checkstring(L, 2); const char *remoteserv = luaL_checkstring(L, 2);
const char *localaddr = luaL_optstring(L, 3, NULL); const char *localaddr = luaL_optstring(L, 3, NULL);
const char *localserv = luaL_optstring(L, 4, "0"); const char *localserv = luaL_optstring(L, 4, "0");
int family = inet_optfamily(L, 5, "unspec");
p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp));
struct addrinfo bindhints, connecthints; struct addrinfo bindhints, connecthints;
const char *err = NULL; const char *err = NULL;
@ -441,7 +443,7 @@ static int global_connect6(lua_State *L) {
/* allow user to pick local address and port */ /* allow user to pick local address and port */
memset(&bindhints, 0, sizeof(bindhints)); memset(&bindhints, 0, sizeof(bindhints));
bindhints.ai_socktype = SOCK_STREAM; bindhints.ai_socktype = SOCK_STREAM;
bindhints.ai_family = PF_UNSPEC; bindhints.ai_family = family;
bindhints.ai_flags = AI_PASSIVE; bindhints.ai_flags = AI_PASSIVE;
if (localaddr) { if (localaddr) {
err = inet_trybind(&tcp->sock, localaddr, localserv, &bindhints); err = inet_trybind(&tcp->sock, localaddr, localserv, &bindhints);
@ -450,6 +452,7 @@ static int global_connect6(lua_State *L) {
lua_pushstring(L, err); lua_pushstring(L, err);
return 2; return 2;
} }
tcp->family = bindhints.ai_family;
} }
/* try to connect to remote address and port */ /* try to connect to remote address and port */
memset(&connecthints, 0, sizeof(connecthints)); memset(&connecthints, 0, sizeof(connecthints));

View File

@ -447,7 +447,7 @@ const char *socket_gaistrerror(int err) {
case EAI_SERVICE: return "service not supported for socket type"; case EAI_SERVICE: return "service not supported for socket type";
case EAI_SOCKTYPE: return "ai_socktype not supported"; case EAI_SOCKTYPE: return "ai_socktype not supported";
case EAI_SYSTEM: return strerror(errno); case EAI_SYSTEM: return strerror(errno);
default: return "unknown error"; default: return gai_strerror(err);
} }
} }