From 96965b179c7311f850f72a8629b9ba6d3a31d117 Mon Sep 17 00:00:00 2001 From: Diego Nehab Date: Sat, 22 Aug 2015 19:52:01 -0300 Subject: [PATCH 1/2] New agnostic IPv4 IPv6 functions. Also dealing with EPROTOTYPE Yosemite seems to be throwing at us for no reason. --- doc/reference.html | 2 + doc/tcp.html | 195 +++++++++++++++++++++++----------------- doc/udp.html | 159 +++++++++++++++++++------------- src/buffer.c | 24 ++--- src/inet.c | 132 ++++++++++++++------------- src/inet.h | 10 +-- src/io.h | 2 +- src/socket.lua | 10 +-- src/tcp.c | 85 ++++++------------ src/udp.c | 19 +++- src/usocket.c | 9 +- test/testclnt.lua | 100 ++++++++++----------- test/testsrvr.lua | 2 +- test/udpconnectclnt.lua | 2 +- 14 files changed, 399 insertions(+), 352 deletions(-) diff --git a/doc/reference.html b/doc/reference.html index e9bb5eb..6067ba6 100644 --- a/doc/reference.html +++ b/doc/reference.html @@ -160,9 +160,11 @@ Support, Manual"> _SETSIZE, source, tcp, +tcp4, tcp6, try, udp, +udp4, udp6, _VERSION. diff --git a/doc/tcp.html b/doc/tcp.html index 4226d78..6fc9900 100644 --- a/doc/tcp.html +++ b/doc/tcp.html @@ -1,10 +1,10 @@ - - + LuaSocket: TCP/IP support @@ -28,7 +28,7 @@ download · installation · introduction · -reference +reference


@@ -36,21 +36,48 @@ -

TCP

+

TCP

-

+

socket.tcp()

-

-Creates and returns an IPv4 TCP master object. A master object can -be transformed into a server object with the method +

+Creates and returns an TCP master object. A master object can +be transformed into a server object with the method listen (after a call to bind) or into a client object with -the method connect. The only other -method supported by a master object is the +href=#bind>bind) or into a client object with +the method connect. The only other +method supported by a master object is the +close method.

+ +

+In case of success, a new master object is returned. In case of error, +nil is returned, followed by an error message. +

+ +

+Note: The choice between IPv4 and IPv6 happens during a call to +bind or connect, depending on the address +family obtained from the resolver. +

+ + + +

+socket.tcp4() +

+ +

+Creates and returns an IPv4 TCP master object. A master object can +be transformed into a server object with the method +listen (after a call to bind) or into a client object with +the method connect. The only other +method supported by a master object is the close method.

@@ -60,17 +87,17 @@ In case of success, a new master object is returned. In case of error, -

+

socket.tcp6()

-

+

Creates and returns an IPv6 TCP master object. A master object can -be transformed into a server object with the method +be transformed into a server object with the method listen (after a call to bind) or into a client object with -the method connect. The only other -method supported by a master object is the +href=#bind>bind) or into a client object with +the method connect. The only other +method supported by a master object is the close method.

@@ -85,7 +112,7 @@ Note: The TCP object returned will have the option -

+

server:accept()

@@ -95,9 +122,9 @@ object and returns a client object representing that connection.

-If a connection is successfully initiated, a client object is returned. +If a connection is successfully initiated, a client object is returned. If a timeout condition is met, the method returns nil -followed by the error string 'timeout'. Other errors are +followed by the error string 'timeout'. Other errors are reported by nil followed by a message describing the error.

@@ -107,28 +134,28 @@ with a server object in the recvt parameter before a call to accept does not guarantee accept will return immediately. Use the settimeout method or accept -might block until another client shows up. +might block until another client shows up.

-

+

master:bind(address, port)

Binds a master object to address and port on the -local host. +local host.

-Address can be an IP address or a host name. -Port must be an integer number in the range [0..64K). +Address can be an IP address or a host name. +Port must be an integer number in the range [0..64K). If address is '*', the system binds to all local interfaces using the INADDR_ANY constant or -IN6ADDR_ANY_INIT, according to the family. +IN6ADDR_ANY_INIT, according to the family. If port is 0, the system automatically -chooses an ephemeral port. +chooses an ephemeral port.

@@ -137,13 +164,13 @@ method returns nil followed by an error message.

-Note: The function socket.bind +Note: The function socket.bind is available and is a shortcut for the creation of server sockets.

-

+

master:close()
client:close()
server:close() @@ -154,14 +181,14 @@ Closes a TCP object. The internal socket used by the object is closed and the local address to which the object was bound is made available to other applications. No further operations (except for further calls to the close method) are allowed on -a closed socket. +a closed socket.

Note: It is important to close all used sockets once they are not needed, since, in many systems, each socket uses a file descriptor, which are limited system resources. Garbage-collected objects are -automatically closed before destruction, though. +automatically closed before destruction, though.

@@ -172,19 +199,19 @@ master:connect(address, port)

Attempts to connect a master object to a remote host, transforming it into a -client object. -Client objects support methods +client object. +Client objects support methods send, -receive, -getsockname, +receive, +getsockname, getpeername, -settimeout, +settimeout, and close.

-Address can be an IP address or a host name. -Port must be an integer number in the range [1..64K). +Address can be an IP address or a host name. +Port must be an integer number in the range [1..64K).

@@ -193,14 +220,14 @@ describing the error. In case of success, the method returns 1.

-Note: The function socket.connect +Note: The function socket.connect is available and is a shortcut for the creation of client sockets.

-Note: Starting with LuaSocket 2.0, +Note: Starting with LuaSocket 2.0, the settimeout -method affects the behavior of connect, causing it to return +method affects the behavior of connect, causing it to return with an error in case of a timeout. If that happens, you can still call socket.select with the socket in the sendt table. The socket will be writable when the connection is @@ -227,10 +254,10 @@ Returns information about the remote side of a connected client object.

-Returns a string with the IP address of the peer, the -port number that peer is using for the connection, -and a string with the family ("inet" or "inet6"). -In case of error, the method returns nil. +Returns a string with the IP address of the peer, the +port number that peer is using for the connection, +and a string with the family ("inet" or "inet6"). +In case of error, the method returns nil.

@@ -246,13 +273,13 @@ server:getsockname()

-Returns the local address information associated to the object. +Returns the local address information associated to the object.

-The method returns a string with local IP address, a number with -the local port, -and a string with the family ("inet" or "inet6"). +The method returns a string with local IP address, a number with +the local port, +and a string with the family ("inet" or "inet6"). In case of error, the method returns nil.

@@ -266,32 +293,32 @@ server:getstats()

Returns accounting information on the socket, useful for throttling -of bandwidth. +of bandwidth.

The method returns the number of bytes received, the number of bytes sent, -and the age of the socket object in seconds. +and the age of the socket object in seconds.

-

+

master:listen(backlog)

Specifies the socket is willing to receive connections, transforming the -object into a server object. Server objects support the -accept, -getsockname, -setoption, -settimeout, -and close methods. +object into a server object. Server objects support the +accept, +getsockname, +setoption, +settimeout, +and close methods.

-The parameter backlog specifies the number of client +The parameter backlog specifies the number of client connections that can be queued waiting for service. If the queue is full and another client attempts connection, the connection is refused. @@ -310,11 +337,11 @@ client:receive([pattern [, prefix]])

Reads data from a client object, according to the specified read -pattern. Patterns follow the Lua file I/O format, and the difference in performance between all patterns is negligible. +pattern. Patterns follow the Lua file I/O format, and the difference in performance between all patterns is negligible.

-Pattern can be any of the following: +Pattern can be any of the following:

@@ -347,10 +374,10 @@ closed before the transmission was completed or the string

Important note: This function was changed severely. It used to support multiple patterns (but I have never seen this feature used) and -now it doesn't anymore. Partial results used to be returned in the same +now it doesn't anymore. Partial results used to be returned in the same way as successful results. This last feature violated the idea that all functions should return nil on error. Thus it was changed -too. +too.

@@ -366,7 +393,7 @@ Sends data through client object.

Data is the string to be sent. The optional arguments i and j work exactly like the standard -string.sub Lua function to allow the selection of a +string.sub Lua function to allow the selection of a substring to be sent.

@@ -385,10 +412,10 @@ there was a timeout during the operation.

-Note: Output is not buffered. For small strings, -it is always better to concatenate them in Lua -(with the '..' operator) and send the result in one call -instead of calling the method several times. +Note: Output is not buffered. For small strings, +it is always better to concatenate them in Lua +(with the '..' operator) and send the result in one call +instead of calling the method several times.

@@ -400,12 +427,12 @@ server:setoption(option [, value])

Sets options for the TCP object. Options are only needed by low-level or -time-critical applications. You should only modify an option if you -are sure you need it. +time-critical applications. You should only modify an option if you +are sure you need it.

-Option is a string with the option name, and value +Option is a string with the option name, and value depends on the option being set:

@@ -268,7 +301,7 @@ Sends a datagram to the UDP peer of a connected object.

-Datagram is a string with the datagram contents. +Datagram is a string with the datagram contents. The maximum datagram size for UDP is 64K minus IP layer overhead. However datagrams larger than the link layer packet size will be fragmented, which may deteriorate performance and/or reliability. @@ -298,11 +331,11 @@ Sends a datagram to the specified IP address and port number.

Datagram is a string with the -datagram contents. +datagram contents. The maximum datagram size for UDP is 64K minus IP layer overhead. However datagrams larger than the link layer packet size will be fragmented, which may deteriorate performance and/or reliability. -Ip is the IP address of the recipient. +Ip is the IP address of the recipient. Host names are not allowed for performance reasons. Port is the port number at the recipient. @@ -337,9 +370,9 @@ object or vice versa. For connected objects, outgoing datagrams will be sent to the specified peer, and datagrams received from other peers will be discarded by the OS. Connected UDP objects must -use the send and -receive methods instead of -sendto and +use the send and +receive methods instead of +sendto and receivefrom.

@@ -421,16 +454,16 @@ only modify an option if you are sure you need it.

name, and value depends on the option being set:

-

The method returns 1 in case of success, or @@ -482,14 +515,14 @@ unconnected:settimeout(value)

-Changes the timeout values for the object. By default, the -receive and -receivefrom +Changes the timeout values for the object. By default, the +receive and +receivefrom operations are blocking. That is, any call to the methods will block indefinitely, until data arrives. The settimeout function defines a limit on the amount of time the functions can block. When a timeout is set and the specified amount of time has elapsed, the affected methods -give up and fail with an error code. +give up and fail with an error code.

@@ -524,7 +557,7 @@ imperative nature obvious. download · installation · introduction · -reference +reference

diff --git a/src/buffer.c b/src/buffer.c index 8fc1166..fff1634 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -38,7 +38,7 @@ int buffer_open(lua_State *L) { } /*-------------------------------------------------------------------------*\ -* Initializes C structure +* Initializes C structure \*-------------------------------------------------------------------------*/ void buffer_init(p_buffer buf, p_io io, p_timeout tm) { buf->first = buf->last = 0; @@ -62,8 +62,8 @@ int buffer_meth_getstats(lua_State *L, p_buffer buf) { * object:setstats() interface \*-------------------------------------------------------------------------*/ int buffer_meth_setstats(lua_State *L, p_buffer buf) { - buf->received = (long) luaL_optnumber(L, 2, (lua_Number) buf->received); - buf->sent = (long) luaL_optnumber(L, 3, (lua_Number) buf->sent); + buf->received = (long) luaL_optnumber(L, 2, (lua_Number) buf->received); + buf->sent = (long) luaL_optnumber(L, 3, (lua_Number) buf->sent); if (lua_isnumber(L, 4)) buf->birthday = timeout_gettime() - lua_tonumber(L, 4); lua_pushnumber(L, 1); return 1; @@ -88,7 +88,7 @@ int buffer_meth_send(lua_State *L, p_buffer buf) { /* check if there was an error */ if (err != IO_DONE) { lua_pushnil(L); - lua_pushstring(L, buf->io->error(buf->io->ctx, err)); + lua_pushstring(L, buf->io->error(buf->io->ctx, err)); lua_pushnumber(L, (lua_Number) (sent+start-1)); } else { lua_pushnumber(L, (lua_Number) (sent+start-1)); @@ -111,7 +111,7 @@ int buffer_meth_receive(lua_State *L, p_buffer buf) { size_t size; const char *part = luaL_optlstring(L, 3, "", &size); timeout_markstart(buf->tm); - /* initialize buffer with optional extra prefix + /* initialize buffer with optional extra prefix * (useful for concatenating previous partial results) */ luaL_buffinit(L, &b); luaL_addlstring(&b, part, size); @@ -119,12 +119,12 @@ int buffer_meth_receive(lua_State *L, p_buffer buf) { if (!lua_isnumber(L, 2)) { const char *p= luaL_optstring(L, 2, "*l"); if (p[0] == '*' && p[1] == 'l') err = recvline(buf, &b); - else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b); + else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b); else luaL_argcheck(L, 0, 2, "invalid receive pattern"); - /* get a fixed number of bytes (minus what was already partially + /* get a fixed number of bytes (minus what was already partially * received) */ } else { - double n = lua_tonumber(L, 2); + double n = lua_tonumber(L, 2); size_t wanted = (size_t) n; luaL_argcheck(L, n >= 0, 2, "invalid receive pattern"); if (size == 0 || wanted > size) @@ -135,8 +135,8 @@ int buffer_meth_receive(lua_State *L, p_buffer buf) { /* we can't push anyting in the stack before pushing the * contents of the buffer. this is the reason for the complication */ luaL_pushresult(&b); - lua_pushstring(L, buf->io->error(buf->io->ctx, err)); - lua_pushvalue(L, -2); + lua_pushstring(L, buf->io->error(buf->io->ctx, err)); + lua_pushvalue(L, -2); lua_pushnil(L); lua_replace(L, -4); } else { @@ -219,7 +219,7 @@ static int recvall(p_buffer buf, luaL_Buffer *b) { } /*-------------------------------------------------------------------------*\ -* Reads a line terminated by a CR LF pair or just by a LF. The CR and LF +* Reads a line terminated by a CR LF pair or just by a LF. The CR and LF * are not returned by the function and are discarded from the buffer \*-------------------------------------------------------------------------*/ static int recvline(p_buffer buf, luaL_Buffer *b) { @@ -249,7 +249,7 @@ static int recvline(p_buffer buf, luaL_Buffer *b) { static void buffer_skip(p_buffer buf, size_t count) { buf->received += count; buf->first += count; - if (buffer_isempty(buf)) + if (buffer_isempty(buf)) buf->first = buf->last = 0; } diff --git a/src/inet.c b/src/inet.c index 68087db..8f0fac2 100644 --- a/src/inet.c +++ b/src/inet.c @@ -94,7 +94,7 @@ static int inet_global_getnameinfo(lua_State *L) { memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; - hints.ai_family = PF_UNSPEC; + hints.ai_family = AF_UNSPEC; ret = getaddrinfo(host, serv, &hints, &resolved); if (ret != 0) { @@ -105,8 +105,8 @@ static int inet_global_getnameinfo(lua_State *L) { lua_newtable(L); for (i = 1, iter = resolved; iter; i++, iter = iter->ai_next) { - getnameinfo(iter->ai_addr, (socklen_t) iter->ai_addrlen, - hbuf, host? (socklen_t) sizeof(hbuf): 0, + getnameinfo(iter->ai_addr, (socklen_t) iter->ai_addrlen, + hbuf, host? (socklen_t) sizeof(hbuf): 0, sbuf, serv? (socklen_t) sizeof(sbuf): 0, 0); if (host) { lua_pushnumber(L, i); @@ -146,7 +146,7 @@ static int inet_global_toip(lua_State *L) 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 }; + static int optvalue[] = { AF_UNSPEC, AF_INET, AF_INET6, 0 }; return optvalue[luaL_checkoption(L, narg, def, optname)]; } @@ -167,7 +167,7 @@ static int inet_global_getaddrinfo(lua_State *L) int i = 1, ret = 0; memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; - hints.ai_family = PF_UNSPEC; + hints.ai_family = AF_UNSPEC; ret = getaddrinfo(hostname, NULL, &hints, &resolved); if (ret != 0) { lua_pushnil(L); @@ -177,7 +177,7 @@ static int inet_global_getaddrinfo(lua_State *L) lua_newtable(L); for (iterator = resolved; iterator; iterator = iterator->ai_next) { char hbuf[NI_MAXHOST]; - ret = getnameinfo(iterator->ai_addr, (socklen_t) iterator->ai_addrlen, + ret = getnameinfo(iterator->ai_addr, (socklen_t) iterator->ai_addrlen, hbuf, (socklen_t) sizeof(hbuf), NULL, 0, NI_NUMERICHOST); if (ret){ freeaddrinfo(resolved); @@ -198,6 +198,16 @@ static int inet_global_getaddrinfo(lua_State *L) lua_pushliteral(L, "inet6"); lua_settable(L, -3); break; + case AF_UNSPEC: + lua_pushliteral(L, "family"); + lua_pushliteral(L, "unspec"); + lua_settable(L, -3); + break; + default: + lua_pushliteral(L, "family"); + lua_pushliteral(L, "unknown"); + lua_settable(L, -3); + break; } lua_pushliteral(L, "addr"); lua_pushstring(L, hbuf); @@ -254,12 +264,11 @@ int inet_meth_getpeername(lua_State *L, p_socket ps, int family) } lua_pushstring(L, name); lua_pushinteger(L, (int) strtol(port, (char **) NULL, 10)); - if (family == PF_INET) { - lua_pushliteral(L, "inet"); - } else if (family == PF_INET6) { - lua_pushliteral(L, "inet6"); - } else { - lua_pushliteral(L, "uknown family"); + switch (family) { + case AF_INET: lua_pushliteral(L, "inet"); break; + case AF_INET6: lua_pushliteral(L, "inet6"); break; + case AF_UNSPEC: lua_pushliteral(L, "unspec"); break; + default: lua_pushliteral(L, "unknown"); break; } return 3; } @@ -279,7 +288,7 @@ int inet_meth_getsockname(lua_State *L, p_socket ps, int family) lua_pushstring(L, socket_strerror(errno)); return 2; } - err=getnameinfo((struct sockaddr *)&peer, peer_len, + err=getnameinfo((struct sockaddr *)&peer, peer_len, name, INET6_ADDRSTRLEN, port, 6, NI_NUMERICHOST | NI_NUMERICSERV); if (err) { lua_pushnil(L); @@ -288,12 +297,11 @@ int inet_meth_getsockname(lua_State *L, p_socket ps, int family) } lua_pushstring(L, name); lua_pushstring(L, port); - if (family == PF_INET) { - lua_pushliteral(L, "inet"); - } else if (family == PF_INET6) { - lua_pushliteral(L, "inet6"); - } else { - lua_pushliteral(L, "uknown family"); + switch (family) { + case AF_INET: lua_pushliteral(L, "inet"); break; + case AF_INET6: lua_pushliteral(L, "inet6"); break; + case AF_UNSPEC: lua_pushliteral(L, "unspec"); break; + default: lua_pushliteral(L, "unknown"); break; } return 3; } @@ -354,21 +362,21 @@ const char *inet_trycreate(p_socket ps, int family, int type) { const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm) { switch (family) { - case PF_INET: { + case AF_INET: { struct sockaddr_in sin; memset((char *) &sin, 0, sizeof(sin)); sin.sin_family = AF_UNSPEC; sin.sin_addr.s_addr = INADDR_ANY; - return socket_strerror(socket_connect(ps, (SA *) &sin, + return socket_strerror(socket_connect(ps, (SA *) &sin, sizeof(sin), tm)); } - case PF_INET6: { + case AF_INET6: { struct sockaddr_in6 sin6; - struct in6_addr addrany = IN6ADDR_ANY_INIT; + struct in6_addr addrany = IN6ADDR_ANY_INIT; memset((char *) &sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_UNSPEC; sin6.sin6_addr = addrany; - return socket_strerror(socket_connect(ps, (SA *) &sin6, + return socket_strerror(socket_connect(ps, (SA *) &sin6, sizeof(sin6), tm)); } } @@ -383,6 +391,7 @@ const char *inet_tryconnect(p_socket ps, int *family, const char *address, { struct addrinfo *iterator = NULL, *resolved = NULL; const char *err = NULL; + int current_family = *family; /* try resolving */ err = socket_gaistrerror(getaddrinfo(address, serv, connecthints, &resolved)); @@ -397,23 +406,23 @@ const char *inet_tryconnect(p_socket ps, int *family, const char *address, * that shows up while iterating. if there was a * bind, all families will be the same and we will * not enter this branch. */ - if (*family != iterator->ai_family) { + if (current_family != iterator->ai_family || *ps == SOCKET_INVALID) { socket_destroy(ps); - err = socket_strerror(socket_create(ps, iterator->ai_family, + err = socket_strerror(socket_create(ps, iterator->ai_family, iterator->ai_socktype, iterator->ai_protocol)); - if (err != NULL) { - freeaddrinfo(resolved); - return err; - } - *family = iterator->ai_family; - /* all sockets initially non-blocking */ + if (err) continue; + current_family = iterator->ai_family; + /* set non-blocking before connect */ socket_setnonblocking(ps); } /* try connecting to remote address */ - err = socket_strerror(socket_connect(ps, (SA *) iterator->ai_addr, + err = socket_strerror(socket_connect(ps, (SA *) iterator->ai_addr, (socklen_t) iterator->ai_addrlen, tm)); /* if success, break out of loop */ - if (err == NULL) break; + if (err == NULL) { + *family = current_family; + break; + } } freeaddrinfo(resolved); /* here, if err is set, we failed */ @@ -423,29 +432,27 @@ const char *inet_tryconnect(p_socket ps, int *family, const char *address, /*-------------------------------------------------------------------------*\ * Tries to accept a socket \*-------------------------------------------------------------------------*/ -const char *inet_tryaccept(p_socket server, int family, p_socket client, - p_timeout tm) -{ +const char *inet_tryaccept(p_socket server, int family, p_socket client, + p_timeout tm) { socklen_t len; t_sockaddr_storage addr; - if (family == PF_INET6) { - len = sizeof(struct sockaddr_in6); - } else { - len = sizeof(struct sockaddr_in); - } - return socket_strerror(socket_accept(server, client, (SA *) &addr, + switch (family) { + case AF_INET6: len = sizeof(struct sockaddr_in6); break; + case AF_INET: len = sizeof(struct sockaddr_in); break; + default: len = sizeof(addr); break; + } + return socket_strerror(socket_accept(server, client, (SA *) &addr, &len, tm)); } /*-------------------------------------------------------------------------*\ * Tries to bind socket to (address, port) \*-------------------------------------------------------------------------*/ -const char *inet_trybind(p_socket ps, const char *address, const char *serv, - struct addrinfo *bindhints) -{ +const char *inet_trybind(p_socket ps, int *family, const char *address, + const char *serv, struct addrinfo *bindhints) { struct addrinfo *iterator = NULL, *resolved = NULL; const char *err = NULL; - t_socket sock = *ps; + int current_family = *family; /* translate luasocket special values to C */ if (strcmp(address, "*") == 0) address = NULL; if (!serv) serv = "0"; @@ -457,35 +464,32 @@ const char *inet_trybind(p_socket ps, const char *address, const char *serv, } /* iterate over resolved addresses until one is good */ for (iterator = resolved; iterator; iterator = iterator->ai_next) { - if(sock == SOCKET_INVALID) { - err = socket_strerror(socket_create(&sock, iterator->ai_family, + if (current_family != iterator->ai_family || *ps == SOCKET_INVALID) { + socket_destroy(ps); + err = socket_strerror(socket_create(ps, iterator->ai_family, iterator->ai_socktype, iterator->ai_protocol)); - if(err) - continue; + if (err) continue; + current_family = iterator->ai_family; } /* try binding to local address */ - err = socket_strerror(socket_bind(&sock, - (SA *) iterator->ai_addr, + err = socket_strerror(socket_bind(ps, (SA *) iterator->ai_addr, (socklen_t) iterator->ai_addrlen)); - /* keep trying unless bind succeeded */ - if (err) { - if(sock != *ps) - socket_destroy(&sock); - } else { - /* remember what we connected to, particularly the family */ - *bindhints = *iterator; + if (err == NULL) { + *family = current_family; + /* set to non-blocking after bind */ + socket_setnonblocking(ps); break; } } /* cleanup and return error */ freeaddrinfo(resolved); - *ps = sock; + /* here, if err is set, we failed */ return err; } /*-------------------------------------------------------------------------*\ -* Some systems do not provide these so that we provide our own. +* Some systems do not provide these so that we provide our own. \*-------------------------------------------------------------------------*/ #ifdef LUASOCKET_INET_ATON int inet_aton(const char *cp, struct in_addr *inp) @@ -510,7 +514,7 @@ int inet_aton(const char *cp, struct in_addr *inp) #endif #ifdef LUASOCKET_INET_PTON -int inet_pton(int af, const char *src, void *dst) +int inet_pton(int af, const char *src, void *dst) { struct addrinfo hints, *res; int ret = 1; @@ -527,7 +531,7 @@ int inet_pton(int af, const char *src, void *dst) } else { ret = -1; } - freeaddrinfo(res); + freeaddrinfo(res); return ret; } diff --git a/src/inet.h b/src/inet.h index 1f1a96a..b85c20e 100644 --- a/src/inet.h +++ b/src/inet.h @@ -1,12 +1,12 @@ -#ifndef INET_H -#define INET_H +#ifndef INET_H +#define INET_H /*=========================================================================*\ * Internet domain functions * LuaSocket toolkit * * This module implements the creation and connection of internet domain * sockets, on top of the socket.h interface, and the interface of with the -* resolver. +* resolver. * * The function inet_aton is provided for the platforms where it is not * available. The module also implements the interface of the internet @@ -27,8 +27,8 @@ int inet_open(lua_State *L); const char *inet_trycreate(p_socket ps, int family, int type); const char *inet_tryconnect(p_socket ps, int *family, const char *address, const char *serv, p_timeout tm, struct addrinfo *connecthints); -const char *inet_trybind(p_socket ps, const char *address, const char *serv, - struct addrinfo *bindhints); +const char *inet_trybind(p_socket ps, int *family, const char *address, + const char *serv, struct addrinfo *bindhints); const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm); const char *inet_tryaccept(p_socket server, int family, p_socket client, p_timeout tm); diff --git a/src/io.h b/src/io.h index 76a3e58..8cca08a 100644 --- a/src/io.h +++ b/src/io.h @@ -22,7 +22,7 @@ enum { IO_DONE = 0, /* operation completed successfully */ IO_TIMEOUT = -1, /* operation timed out */ IO_CLOSED = -2, /* the connection has been closed */ - IO_UNKNOWN = -3 + IO_UNKNOWN = -3 }; /* interface to error message function */ diff --git a/src/socket.lua b/src/socket.lua index 3913e6f..d1c0b16 100644 --- a/src/socket.lua +++ b/src/socket.lua @@ -32,23 +32,23 @@ function _M.bind(host, port, backlog) err = "no info on address" for i, alt in base.ipairs(addrinfo) do if alt.family == "inet" then - sock, err = socket.tcp() + sock, err = socket.tcp4() else sock, err = socket.tcp6() end if not sock then return nil, err end sock:setoption("reuseaddr", true) res, err = sock:bind(alt.addr, port) - if not res then + if not res then sock:close() - else + else res, err = sock:listen(backlog) - if not res then + if not res then sock:close() else return sock end - end + end end return nil, err end diff --git a/src/tcp.c b/src/tcp.c index dcac0c8..4d12f08 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -18,6 +18,7 @@ * Internal function prototypes \*=========================================================================*/ static int global_create(lua_State *L); +static int global_create4(lua_State *L); static int global_create6(lua_State *L); static int global_connect(lua_State *L); static int meth_connect(lua_State *L); @@ -90,6 +91,7 @@ static t_opt optset[] = { /* functions in library namespace */ static luaL_Reg func[] = { {"tcp", global_create}, + {"tcp4", global_create4}, {"tcp6", global_create6}, {"connect", global_connect}, {NULL, NULL} @@ -213,8 +215,7 @@ static int meth_accept(lua_State *L) /*-------------------------------------------------------------------------*\ * Binds an object to an address \*-------------------------------------------------------------------------*/ -static int meth_bind(lua_State *L) -{ +static int 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); @@ -224,7 +225,7 @@ static int meth_bind(lua_State *L) bindhints.ai_socktype = SOCK_STREAM; bindhints.ai_family = tcp->family; bindhints.ai_flags = AI_PASSIVE; - err = inet_trybind(&tcp->sock, address, port, &bindhints); + err = inet_trybind(&tcp->sock, &tcp->family, address, port, &bindhints); if (err) { lua_pushnil(L); lua_pushstring(L, err); @@ -237,8 +238,7 @@ static int meth_bind(lua_State *L) /*-------------------------------------------------------------------------*\ * Turns a master tcp object into a client object. \*-------------------------------------------------------------------------*/ -static int meth_connect(lua_State *L) -{ +static int 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); @@ -249,7 +249,7 @@ static int meth_connect(lua_State *L) /* 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, + 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); @@ -279,9 +279,12 @@ static int meth_close(lua_State *L) static int meth_getfamily(lua_State *L) { p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); - if (tcp->family == PF_INET6) { + if (tcp->family == AF_INET6) { lua_pushliteral(L, "inet6"); return 1; + } else if (tcp->family == AF_INET) { + lua_pushliteral(L, "inet4"); + return 1; } else { lua_pushliteral(L, "inet4"); return 1; @@ -353,7 +356,12 @@ static int meth_settimeout(lua_State *L) \*-------------------------------------------------------------------------*/ static int tcp_create(lua_State *L, int family) { t_socket sock; - const char *err = inet_trycreate(&sock, family, SOCK_STREAM); + /* if family is AF_UNSPEC, we create an AF_INET socket + * but store AF_UNSPEC into tcp-family. This will allow it + * later be replaced with an AF_INET6 socket if + * trybind or tryconnect prefer it instead. */ + const char *err = inet_trycreate(&sock, family == AF_UNSPEC? + AF_INET: family, SOCK_STREAM); /* try to allocate a system socket */ if (!err) { /* allocate tcp object */ @@ -363,7 +371,7 @@ static int tcp_create(lua_State *L, int family) { auxiliar_setclass(L, "tcp{master}", -1); /* initialize remaining structure fields */ socket_setnonblocking(&sock); - if (family == PF_INET6) { + if (family == AF_INET6) { int yes = 1; setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&yes, sizeof(yes)); @@ -383,6 +391,10 @@ static int tcp_create(lua_State *L, int family) { } static int global_create(lua_State *L) { + return tcp_create(L, AF_UNSPEC); +} + +static int global_create4(lua_State *L) { return tcp_create(L, AF_INET); } @@ -390,53 +402,6 @@ static int 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 global_connect(lua_State *L) { const char *remoteaddr = luaL_checkstring(L, 1); const char *remoteserv = luaL_checkstring(L, 2); @@ -453,26 +418,26 @@ static int global_connect(lua_State *L) { timeout_init(&tcp->tm, -1, -1); buffer_init(&tcp->buf, &tcp->io, &tcp->tm); tcp->sock = SOCKET_INVALID; - tcp->family = PF_UNSPEC; + tcp->family = AF_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); + err = inet_trybind(&tcp->sock, &tcp->family, 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; + connecthints.ai_family = tcp->family; err = inet_tryconnect(&tcp->sock, &tcp->family, remoteaddr, remoteserv, &tcp->tm, &connecthints); if (err) { diff --git a/src/udp.c b/src/udp.c index 7ff00f5..6600859 100644 --- a/src/udp.c +++ b/src/udp.c @@ -27,6 +27,7 @@ * Internal function prototypes \*=========================================================================*/ static int global_create(lua_State *L); +static int global_create4(lua_State *L); static int global_create6(lua_State *L); static int meth_send(lua_State *L); static int meth_sendto(lua_State *L); @@ -107,6 +108,7 @@ static t_opt optget[] = { /* functions in library namespace */ static luaL_Reg func[] = { {"udp", global_create}, + {"udp4", global_create4}, {"udp6", global_create6}, {NULL, NULL} }; @@ -264,7 +266,7 @@ static int meth_receivefrom(lua_State *L) static int meth_getfamily(lua_State *L) { p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); - if (udp->family == PF_INET6) { + if (udp->family == AF_INET6) { lua_pushliteral(L, "inet6"); return 1; } else { @@ -391,7 +393,7 @@ static int meth_setsockname(lua_State *L) { bindhints.ai_socktype = SOCK_DGRAM; bindhints.ai_family = udp->family; bindhints.ai_flags = AI_PASSIVE; - err = inet_trybind(&udp->sock, address, port, &bindhints); + err = inet_trybind(&udp->sock, &udp->family, address, port, &bindhints); if (err) { lua_pushnil(L); lua_pushstring(L, err); @@ -409,7 +411,12 @@ static int meth_setsockname(lua_State *L) { \*-------------------------------------------------------------------------*/ static int udp_create(lua_State *L, int family) { t_socket sock; - const char *err = inet_trycreate(&sock, family, SOCK_DGRAM); + /* if family is AF_UNSPEC, we create an AF_INET socket + * but store AF_UNSPEC into tcp-family. This will allow it + * later be replaced with an AF_INET6 socket if + * trybind or tryconnect prefer it instead. */ + const char *err = inet_trycreate(&sock, family == AF_UNSPEC? + AF_INET: family, SOCK_DGRAM); /* try to allocate a system socket */ if (!err) { /* allocate udp object */ @@ -417,7 +424,7 @@ static int udp_create(lua_State *L, int family) { auxiliar_setclass(L, "udp{unconnected}", -1); /* initialize remaining structure fields */ socket_setnonblocking(&sock); - if (family == PF_INET6) { + if (family == AF_INET6) { int yes = 1; setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&yes, sizeof(yes)); @@ -434,6 +441,10 @@ static int udp_create(lua_State *L, int family) { } static int global_create(lua_State *L) { + return udp_create(L, AF_UNSPEC); +} + +static int global_create4(lua_State *L) { return udp_create(L, AF_INET); } diff --git a/src/usocket.c b/src/usocket.c index 99e551b..8adc573 100644 --- a/src/usocket.c +++ b/src/usocket.c @@ -211,6 +211,8 @@ int socket_send(p_socket ps, const char *data, size_t count, err = errno; /* EPIPE means the connection was closed */ if (err == EPIPE) return IO_CLOSED; + /* EPROTOTYPE means the connection is being closed (on Yosemite!)*/ + if (err == EPROTOTYPE) continue; /* we call was interrupted, just try again */ if (err == EINTR) continue; /* if failed fatal reason, report error */ @@ -239,6 +241,7 @@ int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, } err = errno; if (err == EPIPE) return IO_CLOSED; + if (err == EPROTOTYPE) continue; if (err == EINTR) continue; if (err != EAGAIN) return err; if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; @@ -317,6 +320,8 @@ int socket_write(p_socket ps, const char *data, size_t count, err = errno; /* EPIPE means the connection was closed */ if (err == EPIPE) return IO_CLOSED; + /* EPROTOTYPE means the connection is being closed (on Yosemite!)*/ + if (err == EPROTOTYPE) continue; /* we call was interrupted, just try again */ if (err == EINTR) continue; /* if failed fatal reason, report error */ @@ -410,7 +415,9 @@ const char *socket_strerror(int err) { case ECONNABORTED: return PIE_CONNABORTED; case ECONNRESET: return PIE_CONNRESET; case ETIMEDOUT: return PIE_TIMEDOUT; - default: return strerror(err); + default: { + return strerror(err); + } } } diff --git a/test/testclnt.lua b/test/testclnt.lua index 0014781..abf9608 100644 --- a/test/testclnt.lua +++ b/test/testclnt.lua @@ -8,7 +8,7 @@ function printf(...) end function pass(...) - printf(...) + printf(...) io.stderr:write("\n") end @@ -45,30 +45,30 @@ function check_timeout(tm, sl, elapsed, err, opp, mode, alldone) if not err then warn("must be buffered") elseif err == "timeout" then pass("proper timeout") else fail("unexpected error '%s'", err) end - else - if err ~= "timeout" then fail("should have timed out") + else + if err ~= "timeout" then fail("should have timed out") else pass("proper timeout") end end else if mode == "total" then - if elapsed > tm then + if elapsed > tm then if err ~= "timeout" then fail("should have timed out") else pass("proper timeout") end elseif elapsed < tm then - if err then fail(err) + if err then fail(err) else pass("ok") end - else - if alldone then - if err then fail("unexpected error '%s'", err) + else + if alldone then + if err then fail("unexpected error '%s'", err) else pass("ok") end else - if err ~= "timeout" then fail(err) + if err ~= "timeout" then fail(err) else pass("proper timeoutk") end end end - else - if err then fail(err) - else pass("ok") end + else + if err then fail(err) + else pass("ok") end end end end @@ -104,8 +104,8 @@ control:setoption("tcp-nodelay", true) ------------------------------------------------------------------------ function test_methods(sock, methods) for _, v in pairs(methods) do - if type(sock[v]) ~= "function" then - fail(sock.class .. " method '" .. v .. "' not registered") + if type(sock[v]) ~= "function" then + fail(sock.class .. " method '" .. v .. "' not registered") end end pass(sock.class .. " methods are ok") @@ -121,7 +121,7 @@ function test_mixed(len) local p3 = "raw " .. string.rep("z", inter) .. "bytes" local p4 = "end" .. string.rep("w", inter) .. "bytes" local bp1, bp2, bp3, bp4 -remote (string.format("str = data:receive(%d)", +remote (string.format("str = data:receive(%d)", string.len(p1)+string.len(p2)+string.len(p3)+string.len(p4))) sent, err = data:send(p1..p2..p3..p4) if err then fail(err) end @@ -166,7 +166,7 @@ function test_rawline(len) io.stderr:write("length " .. len .. ": ") local str, str10, back, err str = string.rep(string.char(47), math.mod(len, 10)) - str10 = string.rep(string.char(120,21,77,4,5,0,7,36,44,100), + str10 = string.rep(string.char(120,21,77,4,5,0,7,36,44,100), math.floor(len/10)) str = str .. str10 remote "str = data:receive()" @@ -216,7 +216,7 @@ function test_totaltimeoutreceive(len, tm, sl) data:settimeout(tm, "total") local t = socket.gettime() str, err, partial, elapsed = data:receive(2*len) - check_timeout(tm, sl, elapsed, err, "receive", "total", + check_timeout(tm, sl, elapsed, err, "receive", "total", string.len(str or partial) == 2*len) end @@ -236,7 +236,7 @@ function test_totaltimeoutsend(len, tm, sl) data:settimeout(tm, "total") str = string.rep("a", 2*len) total, err, partial, elapsed = data:send(str) - check_timeout(tm, sl, elapsed, err, "send", "total", + check_timeout(tm, sl, elapsed, err, "send", "total", total == 2*len) end @@ -256,7 +256,7 @@ function test_blockingtimeoutreceive(len, tm, sl) ]], 2*tm, len, sl, sl)) data:settimeout(tm) str, err, partial, elapsed = data:receive(2*len) - check_timeout(tm, sl, elapsed, err, "receive", "blocking", + check_timeout(tm, sl, elapsed, err, "receive", "blocking", string.len(str or partial) == 2*len) end @@ -290,10 +290,10 @@ function empty_connect() data = server:accept() ]] data, err = socket.connect("", port) - if not data then + if not data then pass("ok") data = socket.connect(host, port) - else + else pass("gethostbyname returns localhost on empty string...") end end @@ -327,7 +327,7 @@ function test_closed() data:close() data = nil ]], str)) - -- try to get a line + -- try to get a line back, err, partial = data:receive() if not err then fail("should have gotten 'closed'.") elseif err ~= "closed" then fail("got '"..err.."' instead of 'closed'.") @@ -340,25 +340,25 @@ function test_closed() data = nil ]] total, err, partial = data:send(string.rep("ugauga", 100000)) - if not err then + if not err then pass("failed: output buffer is at least %d bytes long!", total) - elseif err ~= "closed" then + elseif err ~= "closed" then fail("got '"..err.."' instead of 'closed'.") - else - pass("graceful 'closed' received after %d bytes were sent", partial) + else + pass("graceful 'closed' received after %d bytes were sent", partial) end end ------------------------------------------------------------------------ function test_selectbugs() local r, s, e = socket.select(nil, nil, 0.1) - assert(type(r) == "table" and type(s) == "table" and + assert(type(r) == "table" and type(s) == "table" and (e == "timeout" or e == "error")) pass("both nil: ok") local udp = socket.udp() udp:close() r, s, e = socket.select({ udp }, { udp }, 0.1) - assert(type(r) == "table" and type(s) == "table" and + assert(type(r) == "table" and type(s) == "table" and (e == "timeout" or e == "error")) pass("closed sockets: ok") e = pcall(socket.select, "wrong", 1, 0.1) @@ -389,7 +389,7 @@ function accept_timeout() local t = socket.gettime() s:settimeout(1) local c, e = s:accept() - assert(not c, "should not accept") + assert(not c, "should not accept") assert(e == "timeout", string.format("wrong error message (%s)", e)) t = socket.gettime() - t assert(t < 2, string.format("took to long to give up (%gs)", t)) @@ -407,9 +407,9 @@ function connect_timeout() local t = socket.gettime() local r, e = c:connect("10.0.0.1", 81) assert(not r, "should not connect") - assert(socket.gettime() - t < 2, "took too long to give up.") + assert(socket.gettime() - t < 2, "took too long to give up.") c:close() - pass("ok") + pass("ok") end ------------------------------------------------------------------------ @@ -447,16 +447,14 @@ end ------------------------------------------------------------------------ function rebind_test() - --local c ,c1 = socket.bind("localhost", 0) local c ,c1 = socket.bind("127.0.0.1", 0) if not c then pass ("failed to bind! " .. tostring(c) .. ' ' .. tostring(c1)) return end assert(c,c1) - local i, p = c:getsockname() local s, e = socket.tcp() assert(s, e) s:setoption("reuseaddr", false) - r, e = s:bind("localhost", p) + r, e = s:bind(i, p) assert(not r, "managed to rebind!") assert(e) pass("ok") @@ -476,9 +474,9 @@ function getstats_test() data:receive(c) t = t + c local r, s, a = data:getstats() - assert(r == t, "received count failed" .. tostring(r) + assert(r == t, "received count failed" .. tostring(r) .. "/" .. tostring(t)) - assert(s == t, "sent count failed" .. tostring(s) + assert(s == t, "sent count failed" .. tostring(s) .. "/" .. tostring(t)) end pass("ok") @@ -486,7 +484,7 @@ end ------------------------------------------------------------------------ -function test_nonblocking(size) +function test_nonblocking(size) reconnect() printf("testing " .. 2*size .. " bytes: ") remote(string.format([[ @@ -545,7 +543,7 @@ function test_readafterclose() data:close() data = nil ]])) - data:close() + data:close() back, err, partial = data:receive("*a") assert(back == nil and err == "closed", "should have returned 'closed'") pass("ok") @@ -555,7 +553,7 @@ function test_readafterclose() data:close() data = nil ]])) - data:close() + data:close() back, err, partial = data:receive() assert(back == nil and err == "closed", "should have returned 'closed'") pass("ok") @@ -565,7 +563,7 @@ function test_readafterclose() data:close() data = nil ]])) - data:close() + data:close() back, err, partial = data:receive(1) assert(back == nil and err == "closed", "should have returned 'closed'") pass("ok") @@ -575,7 +573,7 @@ function test_readafterclose() data:close() data = nil ]])) - data:close() + data:close() back, err, partial = data:receive(0) assert(back == nil and err == "closed", "should have returned 'closed'") pass("ok") @@ -593,7 +591,7 @@ function test_writeafterclose() while not err do sent, err, errsent, time = data:send(str) end - assert(err == "closed", "should have returned 'closed'") + assert(err == "closed", "got " .. err .. " instead of 'closed'") pass("ok") end @@ -648,18 +646,18 @@ else io.stderr:write("Warning! IPv6 does not support!\n") end end local udp_methods = { - "close", + "close", "dirty", "getfamily", "getfd", "getoption", "getpeername", "getsockname", - "receive", - "receivefrom", - "send", - "sendto", - "setfd", + "receive", + "receivefrom", + "send", + "sendto", + "setfd", "setoption", "setpeername", "setsockname", @@ -674,6 +672,9 @@ if sock then test_methods(socket.udp6(), udp_methods) else io.stderr:write("Warning! IPv6 does not support!\n") end end +test("closed connection detection: ") +test_closed() + test("partial receive") test_partialrecv() @@ -697,9 +698,6 @@ rebind_test() test("active close: ") active_close() -test("closed connection detection: ") -test_closed() - test("accept function: ") accept_timeout() accept_errors() diff --git a/test/testsrvr.lua b/test/testsrvr.lua index 72b93ab..1eb2d5b 100644 --- a/test/testsrvr.lua +++ b/test/testsrvr.lua @@ -6,7 +6,7 @@ ack = "\n"; while 1 do print("server: waiting for client connection..."); control = assert(server:accept()); - while 1 do + while 1 do command, emsg = control:receive(); if emsg == "closed" then control:close() diff --git a/test/udpconnectclnt.lua b/test/udpconnectclnt.lua index effe13a..ad6ab6a 100644 --- a/test/udpconnectclnt.lua +++ b/test/udpconnectclnt.lua @@ -1,7 +1,7 @@ local socket = require"socket" local udp = socket.udp local localhost = "127.0.0.1" -local port = arg[1] +local port = assert(arg[1], "missing port argument") se = udp(); se:setoption("reuseaddr", true) se:setsockname(localhost, 5062) From 77bba625d7aaa0f9e118879163687fcbcb0b5a7b Mon Sep 17 00:00:00 2001 From: Diego Nehab Date: Tue, 25 Aug 2015 15:41:40 -0300 Subject: [PATCH 2/2] Fixes suggested by @Florob in #147. --- doc/tcp.html | 6 ++++++ doc/udp.html | 6 ++++++ src/inet.c | 17 ++++++++++------ src/inet.h | 2 +- src/tcp.c | 52 +++++++++++++++++++---------------------------- src/udp.c | 52 +++++++++++++++++++---------------------------- test/testclnt.lua | 19 ++++++++++------- 7 files changed, 78 insertions(+), 76 deletions(-) diff --git a/doc/tcp.html b/doc/tcp.html index 6fc9900..fb627a1 100644 --- a/doc/tcp.html +++ b/doc/tcp.html @@ -65,6 +65,12 @@ href=#bind>connect, depending on the address family obtained from the resolver.

+

+Note: Before the choice between IPv4 and IPv6 happens, +the internal socket object is invalid and therefore setoption will fail. +

+

diff --git a/doc/udp.html b/doc/udp.html index e313af4..a300f2f 100644 --- a/doc/udp.html +++ b/doc/udp.html @@ -76,6 +76,12 @@ href=#setsockname>sockname, depending on the address family obtained from the resolver.

+

+Note: Before the choice between IPv4 and IPv6 happens, +the internal socket object is invalid and therefore setoption will fail. +

+

diff --git a/src/inet.c b/src/inet.c index 8f0fac2..331b800 100644 --- a/src/inet.c +++ b/src/inet.c @@ -352,8 +352,13 @@ static void inet_pushresolved(lua_State *L, struct hostent *hp) /*-------------------------------------------------------------------------*\ * Tries to create a new inet socket \*-------------------------------------------------------------------------*/ -const char *inet_trycreate(p_socket ps, int family, int type) { - return socket_strerror(socket_create(ps, family, type, 0)); +const char *inet_trycreate(p_socket ps, int family, int type, int protocol) { + const char *err = socket_strerror(socket_create(ps, family, type, protocol)); + if (err == NULL && family == AF_INET6) { + int yes = 1; + setsockopt(*ps, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&yes, sizeof(yes)); + } + return err; } /*-------------------------------------------------------------------------*\ @@ -408,8 +413,8 @@ const char *inet_tryconnect(p_socket ps, int *family, const char *address, * not enter this branch. */ if (current_family != iterator->ai_family || *ps == SOCKET_INVALID) { socket_destroy(ps); - err = socket_strerror(socket_create(ps, iterator->ai_family, - iterator->ai_socktype, iterator->ai_protocol)); + err = inet_trycreate(ps, iterator->ai_family, + iterator->ai_socktype, iterator->ai_protocol); if (err) continue; current_family = iterator->ai_family; /* set non-blocking before connect */ @@ -466,8 +471,8 @@ const char *inet_trybind(p_socket ps, int *family, const char *address, for (iterator = resolved; iterator; iterator = iterator->ai_next) { if (current_family != iterator->ai_family || *ps == SOCKET_INVALID) { socket_destroy(ps); - err = socket_strerror(socket_create(ps, iterator->ai_family, - iterator->ai_socktype, iterator->ai_protocol)); + err = inet_trycreate(ps, iterator->ai_family, + iterator->ai_socktype, iterator->ai_protocol); if (err) continue; current_family = iterator->ai_family; } diff --git a/src/inet.h b/src/inet.h index b85c20e..feb3541 100644 --- a/src/inet.h +++ b/src/inet.h @@ -24,7 +24,7 @@ int inet_open(lua_State *L); -const char *inet_trycreate(p_socket ps, int family, int type); +const char *inet_trycreate(p_socket ps, int family, int type, int protocol); const char *inet_tryconnect(p_socket ps, int *family, const char *address, const char *serv, p_timeout tm, struct addrinfo *connecthints); const char *inet_trybind(p_socket ps, int *family, const char *address, diff --git a/src/tcp.c b/src/tcp.c index 4d12f08..7bf1af5 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -355,39 +355,29 @@ static int meth_settimeout(lua_State *L) * Creates a master tcp object \*-------------------------------------------------------------------------*/ static int tcp_create(lua_State *L, int family) { - t_socket sock; - /* if family is AF_UNSPEC, we create an AF_INET socket - * but store AF_UNSPEC into tcp-family. This will allow it - * later be replaced with an AF_INET6 socket if - * trybind or tryconnect prefer it instead. */ - const char *err = inet_trycreate(&sock, family == AF_UNSPEC? - AF_INET: 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 == AF_INET6) { - int yes = 1; - setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, - (void *)&yes, sizeof(yes)); + 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); + /* if family is AF_UNSPEC, we leave the socket invalid and + * store AF_UNSPEC into family. This will allow it to later be + * replaced with an AF_INET6 or AF_INET socket upon first use. */ + tcp->sock = SOCKET_INVALID; + tcp->family = family; + 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); + if (family != AF_UNSPEC) { + const char *err = inet_trycreate(&tcp->sock, family, SOCK_STREAM, 0); + if (err != NULL) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; } - 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; + socket_setnonblocking(&tcp->sock); } + return 1; } static int global_create(lua_State *L) { diff --git a/src/udp.c b/src/udp.c index 6600859..17d932a 100644 --- a/src/udp.c +++ b/src/udp.c @@ -185,7 +185,7 @@ static int meth_sendto(lua_State *L) { return 2; } timeout_markstart(tm); - err = socket_sendto(&udp->sock, data, count, &sent, ai->ai_addr, + err = socket_sendto(&udp->sock, data, count, &sent, ai->ai_addr, (socklen_t) ai->ai_addrlen, tm); freeaddrinfo(ai); if (err != IO_DONE) { @@ -237,7 +237,7 @@ static int meth_receivefrom(lua_State *L) char portstr[6]; timeout_markstart(tm); count = MIN(count, sizeof(buffer)); - err = socket_recvfrom(&udp->sock, buffer, count, &got, (SA *) &addr, + err = socket_recvfrom(&udp->sock, buffer, count, &got, (SA *) &addr, &addr_len, tm); /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ if (err == IO_CLOSED) @@ -247,7 +247,7 @@ static int meth_receivefrom(lua_State *L) lua_pushstring(L, udp_strerror(err)); return 2; } - err = getnameinfo((struct sockaddr *)&addr, addr_len, addrstr, + err = getnameinfo((struct sockaddr *)&addr, addr_len, addrstr, INET6_ADDRSTRLEN, portstr, 6, NI_NUMERICHOST | NI_NUMERICSERV); if (err) { lua_pushnil(L); @@ -351,7 +351,7 @@ static int meth_setpeername(lua_State *L) { /* make sure we try to connect only to the same family */ connecthints.ai_family = udp->family; if (connecting) { - err = inet_tryconnect(&udp->sock, &udp->family, address, + err = inet_tryconnect(&udp->sock, &udp->family, address, port, tm, &connecthints); if (err) { lua_pushnil(L); @@ -365,7 +365,6 @@ static int meth_setpeername(lua_State *L) { inet_trydisconnect(&udp->sock, udp->family, tm); auxiliar_setclass(L, "udp{unconnected}", 1); } - /* change class to connected or unconnected depending on address */ lua_pushnumber(L, 1); return 1; } @@ -410,34 +409,25 @@ static int meth_setsockname(lua_State *L) { * Creates a master udp object \*-------------------------------------------------------------------------*/ static int udp_create(lua_State *L, int family) { - t_socket sock; - /* if family is AF_UNSPEC, we create an AF_INET socket - * but store AF_UNSPEC into tcp-family. This will allow it - * later be replaced with an AF_INET6 socket if - * trybind or tryconnect prefer it instead. */ - const char *err = inet_trycreate(&sock, family == AF_UNSPEC? - AF_INET: family, SOCK_DGRAM); - /* try to allocate a system socket */ - if (!err) { - /* allocate udp object */ - p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp)); - auxiliar_setclass(L, "udp{unconnected}", -1); - /* initialize remaining structure fields */ - socket_setnonblocking(&sock); - if (family == AF_INET6) { - int yes = 1; - setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, - (void *)&yes, sizeof(yes)); + /* allocate udp object */ + p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp)); + auxiliar_setclass(L, "udp{unconnected}", -1); + /* if family is AF_UNSPEC, we leave the socket invalid and + * store AF_UNSPEC into family. This will allow it to later be + * replaced with an AF_INET6 or AF_INET socket upon first use. */ + udp->sock = SOCKET_INVALID; + timeout_init(&udp->tm, -1, -1); + udp->family = family; + if (family != AF_UNSPEC) { + const char *err = inet_trycreate(&udp->sock, family, SOCK_DGRAM, 0); + if (err != NULL) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; } - udp->sock = sock; - timeout_init(&udp->tm, -1, -1); - udp->family = family; - return 1; - } else { - lua_pushnil(L); - lua_pushstring(L, err); - return 2; + socket_setnonblocking(&udp->sock); } + return 1; } static int global_create(lua_State *L) { diff --git a/test/testclnt.lua b/test/testclnt.lua index abf9608..ee1201f 100644 --- a/test/testclnt.lua +++ b/test/testclnt.lua @@ -304,15 +304,20 @@ function isclosed(c) end function active_close() - reconnect() - if isclosed(data) then fail("should not be closed") end - data:close() - if not isclosed(data) then fail("should be closed") end - data = nil - local udp = socket.udp() + local tcp = socket.tcp4() + if isclosed(tcp) then fail("should not be closed") end + tcp:close() + if not isclosed(tcp) then fail("should be closed") end + tcp = socket.tcp() + if not isclosed(tcp) then fail("should be closed") end + tcp = nil + local udp = socket.udp4() if isclosed(udp) then fail("should not be closed") end udp:close() if not isclosed(udp) then fail("should be closed") end + udp = socket.udp() + if not isclosed(udp) then fail("should be closed") end + udp = nil pass("ok") end @@ -368,7 +373,7 @@ function test_selectbugs() pass("invalid input: ok") local toomany = {} for i = 1, socket._SETSIZE+1 do - toomany[#toomany+1] = socket.udp() + toomany[#toomany+1] = socket.udp4() end if #toomany > socket._SETSIZE then local e = pcall(socket.select, toomany, nil, 0.1)