494 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			494 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|  | /*=========================================================================*\
 | ||
|  | * TCP object | ||
|  | * LuaSocket toolkit | ||
|  | \*=========================================================================*/ | ||
|  | #include <string.h>
 | ||
|  | 
 | ||
|  | #include "lua.h"
 | ||
|  | #include "lauxlib.h"
 | ||
|  | 
 | ||
|  | #include "auxiliar.h"
 | ||
|  | #include "socket.h"
 | ||
|  | #include "inet.h"
 | ||
|  | #include "options.h"
 | ||
|  | #include "tcp.h"
 | ||
|  | 
 | ||
|  | namespace NS_SLUA {     | ||
|  | 
 | ||
|  | /*=========================================================================*\
 | ||
|  | * Internal function prototypes | ||
|  | \*=========================================================================*/ | ||
|  | static int tcp_global_create(lua_State *L); | ||
|  | static int tcp_global_create6(lua_State *L); | ||
|  | static int tcp_global_connect(lua_State *L); | ||
|  | static int tcp_meth_connect(lua_State *L); | ||
|  | static int tcp_meth_listen(lua_State *L); | ||
|  | static int tcp_meth_getfamily(lua_State *L); | ||
|  | static int tcp_meth_bind(lua_State *L); | ||
|  | static int tcp_meth_send(lua_State *L); | ||
|  | static int tcp_meth_getstats(lua_State *L); | ||
|  | static int tcp_meth_setstats(lua_State *L); | ||
|  | static int tcp_meth_getsockname(lua_State *L); | ||
|  | static int tcp_meth_getpeername(lua_State *L); | ||
|  | static int tcp_meth_shutdown(lua_State *L); | ||
|  | static int tcp_meth_receive(lua_State *L); | ||
|  | static int tcp_meth_accept(lua_State *L); | ||
|  | static int tcp_meth_close(lua_State *L); | ||
|  | static int tcp_meth_getoption(lua_State *L); | ||
|  | static int tcp_meth_setoption(lua_State *L); | ||
|  | static int tcp_meth_settimeout(lua_State *L); | ||
|  | static int tcp_meth_getfd(lua_State *L); | ||
|  | static int tcp_meth_setfd(lua_State *L); | ||
|  | static int tcp_meth_dirty(lua_State *L); | ||
|  | 
 | ||
|  | /* tcp object methods */ | ||
|  | static luaL_Reg tcp_methods[] = { | ||
|  |     {"__gc",        tcp_meth_close}, | ||
|  |     {"__tostring",  auxiliar_tostring}, | ||
|  |     {"accept",      tcp_meth_accept}, | ||
|  |     {"bind",        tcp_meth_bind}, | ||
|  |     {"close",       tcp_meth_close}, | ||
|  |     {"connect",     tcp_meth_connect}, | ||
|  |     {"dirty",       tcp_meth_dirty}, | ||
|  |     {"getfamily",   tcp_meth_getfamily}, | ||
|  |     {"getfd",       tcp_meth_getfd}, | ||
|  |     {"getoption",   tcp_meth_getoption}, | ||
|  |     {"getpeername", tcp_meth_getpeername}, | ||
|  |     {"getsockname", tcp_meth_getsockname}, | ||
|  |     {"getstats",    tcp_meth_getstats}, | ||
|  |     {"setstats",    tcp_meth_setstats}, | ||
|  |     {"listen",      tcp_meth_listen}, | ||
|  |     {"receive",     tcp_meth_receive}, | ||
|  |     {"send",        tcp_meth_send}, | ||
|  |     {"setfd",       tcp_meth_setfd}, | ||
|  |     {"setoption",   tcp_meth_setoption}, | ||
|  |     {"setpeername", tcp_meth_connect}, | ||
|  |     {"setsockname", tcp_meth_bind}, | ||
|  |     {"settimeout",  tcp_meth_settimeout}, | ||
|  |     {"shutdown",    tcp_meth_shutdown}, | ||
|  |     {NULL,          NULL} | ||
|  | }; | ||
|  | 
 | ||
|  | /* socket option handlers */ | ||
|  | static t_opt tcp_optget[] = { | ||
|  |     {"keepalive",   opt_get_keepalive}, | ||
|  |     {"reuseaddr",   opt_get_reuseaddr}, | ||
|  |     {"tcp-nodelay", opt_get_tcp_nodelay}, | ||
|  |     {"linger",      opt_get_linger}, | ||
|  |     {"error",       opt_get_error}, | ||
|  |     {NULL,          NULL} | ||
|  | }; | ||
|  | 
 | ||
|  | static t_opt tcp_optset[] = { | ||
|  |     {"keepalive",   opt_set_keepalive}, | ||
|  |     {"reuseaddr",   opt_set_reuseaddr}, | ||
|  |     {"tcp-nodelay", opt_set_tcp_nodelay}, | ||
|  |     {"ipv6-v6only", opt_set_ip6_v6only}, | ||
|  |     {"linger",      opt_set_linger}, | ||
|  |     {NULL,          NULL} | ||
|  | }; | ||
|  | 
 | ||
|  | /* functions in library namespace */ | ||
|  | static luaL_Reg tcp_func[] = { | ||
|  |     {"tcp", tcp_global_create}, | ||
|  |     {"tcp6", tcp_global_create6}, | ||
|  |     {"connect", tcp_global_connect}, | ||
|  |     {NULL, NULL} | ||
|  | }; | ||
|  | 
 | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Initializes module | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | int tcp_open(lua_State *L) | ||
|  | { | ||
|  |     /* create classes */ | ||
|  |     auxiliar_newclass(L, "tcp{master}", tcp_methods); | ||
|  |     auxiliar_newclass(L, "tcp{client}", tcp_methods); | ||
|  |     auxiliar_newclass(L, "tcp{server}", tcp_methods); | ||
|  |     /* create class groups */ | ||
|  |     auxiliar_add2group(L, "tcp{master}", "tcp{any}"); | ||
|  |     auxiliar_add2group(L, "tcp{client}", "tcp{any}"); | ||
|  |     auxiliar_add2group(L, "tcp{server}", "tcp{any}"); | ||
|  |     /* define library functions */ | ||
|  | #if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE)
 | ||
|  |     luaL_setfuncs(L, tcp_func, 0); | ||
|  | #else
 | ||
|  |     luaL_openlib(L, NULL, tcp_func, 0); | ||
|  | #endif
 | ||
|  |     return 0; | ||
|  | } | ||
|  | 
 | ||
|  | /*=========================================================================*\
 | ||
|  | * Lua methods | ||
|  | \*=========================================================================*/ | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Just call buffered IO methods | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | static int tcp_meth_send(lua_State *L) { | ||
|  |     p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1); | ||
|  |     return buffer_meth_send(L, &tcp->buf); | ||
|  | } | ||
|  | 
 | ||
|  | static int tcp_meth_receive(lua_State *L) { | ||
|  |     p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1); | ||
|  |     return buffer_meth_receive(L, &tcp->buf); | ||
|  | } | ||
|  | 
 | ||
|  | static int tcp_meth_getstats(lua_State *L) { | ||
|  |     p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1); | ||
|  |     return buffer_meth_getstats(L, &tcp->buf); | ||
|  | } | ||
|  | 
 | ||
|  | static int tcp_meth_setstats(lua_State *L) { | ||
|  |     p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1); | ||
|  |     return buffer_meth_setstats(L, &tcp->buf); | ||
|  | } | ||
|  | 
 | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Just call option handler | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | static int tcp_meth_getoption(lua_State *L) | ||
|  | { | ||
|  |     p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); | ||
|  |     return opt_meth_getoption(L, tcp_optget, &tcp->sock); | ||
|  | } | ||
|  | 
 | ||
|  | static int tcp_meth_setoption(lua_State *L) | ||
|  | { | ||
|  |     p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); | ||
|  |     return opt_meth_setoption(L, tcp_optset, &tcp->sock); | ||
|  | } | ||
|  | 
 | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Select support methods | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | static int tcp_meth_getfd(lua_State *L) | ||
|  | { | ||
|  |     p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); | ||
|  |     lua_pushnumber(L, (int) tcp->sock); | ||
|  |     return 1; | ||
|  | } | ||
|  | 
 | ||
|  | /* this is very dangerous, but can be handy for those that are brave enough */ | ||
|  | static int tcp_meth_setfd(lua_State *L) | ||
|  | { | ||
|  |     p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); | ||
|  |     tcp->sock = (t_socket) luaL_checknumber(L, 2); | ||
|  |     return 0; | ||
|  | } | ||
|  | 
 | ||
|  | static int tcp_meth_dirty(lua_State *L) | ||
|  | { | ||
|  |     p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); | ||
|  |     lua_pushboolean(L, !buffer_isempty(&tcp->buf)); | ||
|  |     return 1; | ||
|  | } | ||
|  | 
 | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Waits for and returns a client object attempting connection to the | ||
|  | * server object | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | static int tcp_meth_accept(lua_State *L) | ||
|  | { | ||
|  |     p_tcp server = (p_tcp) auxiliar_checkclass(L, "tcp{server}", 1); | ||
|  |     p_timeout tm = timeout_markstart(&server->tm); | ||
|  |     t_socket sock; | ||
|  |     const char *err = inet_tryaccept(&server->sock, server->family, &sock, tm); | ||
|  |     /* if successful, push client socket */ | ||
|  |     if (err == NULL) { | ||
|  |         p_tcp clnt = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); | ||
|  |         auxiliar_setclass(L, "tcp{client}", -1); | ||
|  |         /* initialize structure fields */ | ||
|  |         memset(clnt, 0, sizeof(t_tcp)); | ||
|  |         socket_setnonblocking(&sock); | ||
|  |         clnt->sock = sock; | ||
|  |         io_init(&clnt->io, (p_send) socket_send, (p_recv) socket_recv, | ||
|  |                 (p_error) socket_ioerror, &clnt->sock); | ||
|  |         timeout_init(&clnt->tm, -1, -1); | ||
|  |         buffer_init(&clnt->buf, &clnt->io, &clnt->tm); | ||
|  |         clnt->family = server->family; | ||
|  |         return 1; | ||
|  |     } else { | ||
|  |         lua_pushnil(L); | ||
|  |         lua_pushstring(L, err); | ||
|  |         return 2; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Binds an object to an address | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | static int tcp_meth_bind(lua_State *L) | ||
|  | { | ||
|  |     p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{master}", 1); | ||
|  |     const char *address =  luaL_checkstring(L, 2); | ||
|  |     const char *port = luaL_checkstring(L, 3); | ||
|  |     const char *err; | ||
|  |     struct addrinfo bindhints; | ||
|  |     memset(&bindhints, 0, sizeof(bindhints)); | ||
|  |     bindhints.ai_socktype = SOCK_STREAM; | ||
|  |     bindhints.ai_family = tcp->family; | ||
|  |     bindhints.ai_flags = AI_PASSIVE; | ||
|  |     err = inet_trybind(&tcp->sock, address, port, &bindhints); | ||
|  |     if (err) { | ||
|  |         lua_pushnil(L); | ||
|  |         lua_pushstring(L, err); | ||
|  |         return 2; | ||
|  |     } | ||
|  |     lua_pushnumber(L, 1); | ||
|  |     return 1; | ||
|  | } | ||
|  | 
 | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Turns a master tcp object into a client object. | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | static int tcp_meth_connect(lua_State *L) | ||
|  | { | ||
|  |     p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); | ||
|  |     const char *address =  luaL_checkstring(L, 2); | ||
|  |     const char *port = luaL_checkstring(L, 3); | ||
|  |     struct addrinfo connecthints; | ||
|  |     const char *err; | ||
|  |     memset(&connecthints, 0, sizeof(connecthints)); | ||
|  |     connecthints.ai_socktype = SOCK_STREAM; | ||
|  |     /* make sure we try to connect only to the same family */ | ||
|  |     connecthints.ai_family = tcp->family; | ||
|  |     timeout_markstart(&tcp->tm); | ||
|  |     err = inet_tryconnect(&tcp->sock, &tcp->family, address, port,  | ||
|  |         &tcp->tm, &connecthints); | ||
|  |     /* have to set the class even if it failed due to non-blocking connects */ | ||
|  |     auxiliar_setclass(L, "tcp{client}", 1); | ||
|  |     if (err) { | ||
|  |         lua_pushnil(L); | ||
|  |         lua_pushstring(L, err); | ||
|  |         return 2; | ||
|  |     } | ||
|  |     lua_pushnumber(L, 1); | ||
|  |     return 1; | ||
|  | } | ||
|  | 
 | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Closes socket used by object | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | static int tcp_meth_close(lua_State *L) | ||
|  | { | ||
|  |     p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); | ||
|  |     socket_destroy(&tcp->sock); | ||
|  |     lua_pushnumber(L, 1); | ||
|  |     return 1; | ||
|  | } | ||
|  | 
 | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Returns family as string | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | static int tcp_meth_getfamily(lua_State *L) | ||
|  | { | ||
|  |     p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); | ||
|  |     if (tcp->family == PF_INET6) { | ||
|  |         lua_pushliteral(L, "inet6"); | ||
|  |         return 1; | ||
|  |     } else { | ||
|  |         lua_pushliteral(L, "inet4"); | ||
|  |         return 1; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Puts the sockt in listen mode | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | static int tcp_meth_listen(lua_State *L) | ||
|  | { | ||
|  |     p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{master}", 1); | ||
|  |     int backlog = (int) luaL_optnumber(L, 2, 32); | ||
|  |     int err = socket_listen(&tcp->sock, backlog); | ||
|  |     if (err != IO_DONE) { | ||
|  |         lua_pushnil(L); | ||
|  |         lua_pushstring(L, socket_strerror(err)); | ||
|  |         return 2; | ||
|  |     } | ||
|  |     /* turn master object into a server object */ | ||
|  |     auxiliar_setclass(L, "tcp{server}", 1); | ||
|  |     lua_pushnumber(L, 1); | ||
|  |     return 1; | ||
|  | } | ||
|  | 
 | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Shuts the connection down partially | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | static int tcp_meth_shutdown(lua_State *L) | ||
|  | { | ||
|  |     /* SHUT_RD,  SHUT_WR,  SHUT_RDWR  have  the value 0, 1, 2, so we can use method index directly */ | ||
|  |     static const char* methods[] = { "receive", "send", "both", NULL }; | ||
|  |     p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{client}", 1); | ||
|  |     int how = luaL_checkoption(L, 2, "both", methods); | ||
|  |     socket_shutdown(&tcp->sock, how); | ||
|  |     lua_pushnumber(L, 1); | ||
|  |     return 1; | ||
|  | } | ||
|  | 
 | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Just call inet methods | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | static int tcp_meth_getpeername(lua_State *L) | ||
|  | { | ||
|  |     p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); | ||
|  |     return inet_meth_getpeername(L, &tcp->sock, tcp->family); | ||
|  | } | ||
|  | 
 | ||
|  | static int tcp_meth_getsockname(lua_State *L) | ||
|  | { | ||
|  |     p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); | ||
|  |     return inet_meth_getsockname(L, &tcp->sock, tcp->family); | ||
|  | } | ||
|  | 
 | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Just call tm methods | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | static int tcp_meth_settimeout(lua_State *L) | ||
|  | { | ||
|  |     p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); | ||
|  |     return timeout_meth_settimeout(L, &tcp->tm); | ||
|  | } | ||
|  | 
 | ||
|  | /*=========================================================================*\
 | ||
|  | * Library functions | ||
|  | \*=========================================================================*/ | ||
|  | /*-------------------------------------------------------------------------*\
 | ||
|  | * Creates a master tcp object | ||
|  | \*-------------------------------------------------------------------------*/ | ||
|  | static int tcp_create(lua_State *L, int family) { | ||
|  |     t_socket sock; | ||
|  |     const char *err = inet_trycreate(&sock, family, SOCK_STREAM); | ||
|  |     /* try to allocate a system socket */ | ||
|  |     if (!err) { | ||
|  |         /* allocate tcp object */ | ||
|  |         p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); | ||
|  |         memset(tcp, 0, sizeof(t_tcp)); | ||
|  |         /* set its type as master object */ | ||
|  |         auxiliar_setclass(L, "tcp{master}", -1); | ||
|  |         /* initialize remaining structure fields */ | ||
|  |         socket_setnonblocking(&sock); | ||
|  |         if (family == PF_INET6) { | ||
|  |             int yes = 1; | ||
|  |             setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, | ||
|  |                 reinterpret_cast<const char *>(&yes), sizeof(yes)); | ||
|  |         } | ||
|  |         tcp->sock = sock; | ||
|  |         io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv, | ||
|  |                 (p_error) socket_ioerror, &tcp->sock); | ||
|  |         timeout_init(&tcp->tm, -1, -1); | ||
|  |         buffer_init(&tcp->buf, &tcp->io, &tcp->tm); | ||
|  |         tcp->family = family; | ||
|  |         return 1; | ||
|  |     } else { | ||
|  |         lua_pushnil(L); | ||
|  |         lua_pushstring(L, err); | ||
|  |         return 2; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | static int tcp_global_create(lua_State *L) { | ||
|  |     return tcp_create(L, AF_INET); | ||
|  | } | ||
|  | 
 | ||
|  | static int tcp_global_create6(lua_State *L) { | ||
|  |     return tcp_create(L, AF_INET6); | ||
|  | } | ||
|  | 
 | ||
|  | #if 0
 | ||
|  | static const char *tryconnect6(const char *remoteaddr, const char *remoteserv, | ||
|  |     struct addrinfo *connecthints, p_tcp tcp) { | ||
|  |     struct addrinfo *iterator = NULL, *resolved = NULL; | ||
|  |     const char *err = NULL; | ||
|  |     /* try resolving */ | ||
|  |     err = socket_gaistrerror(getaddrinfo(remoteaddr, remoteserv, | ||
|  |                 connecthints, &resolved)); | ||
|  |     if (err != NULL) { | ||
|  |         if (resolved) freeaddrinfo(resolved); | ||
|  |         return err; | ||
|  |     } | ||
|  |     /* iterate over all returned addresses trying to connect */ | ||
|  |     for (iterator = resolved; iterator; iterator = iterator->ai_next) { | ||
|  |         p_timeout tm = timeout_markstart(&tcp->tm); | ||
|  |         /* create new socket if necessary. if there was no
 | ||
|  |          * bind, we need to create one for every new family | ||
|  |          * that shows up while iterating. if there was a | ||
|  |          * bind, all families will be the same and we will | ||
|  |          * not enter this branch. */ | ||
|  |         if (tcp->family != iterator->ai_family) { | ||
|  |             socket_destroy(&tcp->sock); | ||
|  |             err = socket_strerror(socket_create(&tcp->sock, | ||
|  |                 iterator->ai_family, iterator->ai_socktype, | ||
|  |                 iterator->ai_protocol)); | ||
|  |             if (err != NULL) { | ||
|  |                 freeaddrinfo(resolved); | ||
|  |                 return err; | ||
|  |             } | ||
|  |             tcp->family = iterator->ai_family; | ||
|  |             /* all sockets initially non-blocking */ | ||
|  |             socket_setnonblocking(&tcp->sock); | ||
|  |         } | ||
|  |         /* finally try connecting to remote address */ | ||
|  |         err = socket_strerror(socket_connect(&tcp->sock, | ||
|  |             (SA *) iterator->ai_addr, | ||
|  |             (socklen_t) iterator->ai_addrlen, tm)); | ||
|  |         /* if success, break out of loop */ | ||
|  |         if (err == NULL) break; | ||
|  |     } | ||
|  | 
 | ||
|  |     freeaddrinfo(resolved); | ||
|  |     /* here, if err is set, we failed */ | ||
|  |     return err; | ||
|  | } | ||
|  | #endif
 | ||
|  | 
 | ||
|  | static int tcp_global_connect(lua_State *L) { | ||
|  |     const char *remoteaddr = luaL_checkstring(L, 1); | ||
|  |     const char *remoteserv = luaL_checkstring(L, 2); | ||
|  |     const char *localaddr  = luaL_optstring(L, 3, NULL); | ||
|  |     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)); | ||
|  |     struct addrinfo bindhints, connecthints; | ||
|  |     const char *err = NULL; | ||
|  |     /* initialize tcp structure */ | ||
|  |     memset(tcp, 0, sizeof(t_tcp)); | ||
|  |     io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv, | ||
|  |             (p_error) socket_ioerror, &tcp->sock); | ||
|  |     timeout_init(&tcp->tm, -1, -1); | ||
|  |     buffer_init(&tcp->buf, &tcp->io, &tcp->tm); | ||
|  |     tcp->sock = SOCKET_INVALID; | ||
|  |     tcp->family = PF_UNSPEC; | ||
|  |     /* allow user to pick local address and port */ | ||
|  |     memset(&bindhints, 0, sizeof(bindhints)); | ||
|  |     bindhints.ai_socktype = SOCK_STREAM; | ||
|  |     bindhints.ai_family = family; | ||
|  |     bindhints.ai_flags = AI_PASSIVE; | ||
|  |     if (localaddr) { | ||
|  |         err = inet_trybind(&tcp->sock, localaddr, localserv, &bindhints); | ||
|  |         if (err) { | ||
|  |             lua_pushnil(L); | ||
|  |             lua_pushstring(L, err); | ||
|  |             return 2; | ||
|  |         } | ||
|  |         tcp->family = bindhints.ai_family; | ||
|  |     } | ||
|  |     /* try to connect to remote address and port */ | ||
|  |     memset(&connecthints, 0, sizeof(connecthints)); | ||
|  |     connecthints.ai_socktype = SOCK_STREAM; | ||
|  |     /* make sure we try to connect only to the same family */ | ||
|  |     connecthints.ai_family = bindhints.ai_family; | ||
|  |     err = inet_tryconnect(&tcp->sock, &tcp->family, remoteaddr, remoteserv, | ||
|  |          &tcp->tm, &connecthints); | ||
|  |     if (err) { | ||
|  |         socket_destroy(&tcp->sock); | ||
|  |         lua_pushnil(L); | ||
|  |         lua_pushstring(L, err); | ||
|  |         return 2; | ||
|  |     } | ||
|  |     auxiliar_setclass(L, "tcp{client}", -1); | ||
|  |     return 1; | ||
|  | } | ||
|  | 
 | ||
|  | } // end NS_SLUA
 |