From 7b4730cbfdb156e7a99310c94f15bb73df941949 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Fri, 22 Nov 2002 13:02:51 +0000 Subject: [PATCH] Karl Heyes: patches for better networking code. IPv6 support (complete? Not sure). svn path=/trunk/net/; revision=4114 --- src/net/resolver.c | 274 ++++++++++++++++++---------------- src/net/sock.c | 360 +++++++++++++++++++++++++++++++++------------ src/net/sock.h | 23 ++- 3 files changed, 429 insertions(+), 228 deletions(-) diff --git a/src/net/resolver.c b/src/net/resolver.c index 3711b217..2f5a0128 100644 --- a/src/net/resolver.c +++ b/src/net/resolver.c @@ -13,174 +13,188 @@ #ifndef _WIN32 #include #include -#include #include #include #else #include -#define sethostent(x) #endif +#include "../thread/thread.h" #include "resolver.h" #include "sock.h" /* internal function */ -static void _lock_resolver(void); -static void _unlock_resolver(void); -static char *_lookup(const char *what, char *buff, int len); static int _isip(const char *what); /* internal data */ -#ifdef _WIN32 -#define mutex_t CRITICAL_SECTION -#else -#define mutex_t pthread_mutex_t -#endif - static mutex_t _resolver_mutex; static int _initialized = 0; +#ifdef HAVE_INET_PTON +static int _isip(const char *what) +{ + union { + struct in_addr v4addr; + struct in6_addr v6addr; + } addr_u; + + if (inet_pton(AF_INET, what, &addr_u.v4addr) <= 0) + return inet_pton(AF_INET6, what, &addr_u.v6addr) > 0 ? 1 : 0; + + return 1; +} + +#else +static int _isip(const char *what) +{ + struct in_addr inp; + + return inet_aton(what, &inp); +} +#endif + + +#if defined (HAVE_GETNAMEINFO) && defined (HAVE_GETADDRINFO) char *resolver_getname(const char *ip, char *buff, int len) { - if (!_isip(ip)) { - strncpy(buff, ip, len); - return buff; - } + struct addrinfo *head = NULL, hints; + char *ret = NULL; - return _lookup(ip, buff, len); + if (!_isip(ip)) { + strncpy(buff, ip, len); + buff [len-1] = '\0'; + return buff; + } + + memset (&hints, 0, sizeof (hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_CANONNAME; + if (getaddrinfo (ip, NULL, &hints, &head)) + return NULL; + + if (head) + { + if (getnameinfo(head->ai_addr, head->ai_addrlen, buff, len, NULL, + 0, NI_NAMEREQD) == 0) + ret = buff; + + freeaddrinfo (head); + } + + return ret; +} + + +char *resolver_getip(const char *name, char *buff, int len) +{ + struct addrinfo *head, hints; + char *ret = NULL; + + if (_isip(name)) { + strncpy(buff, name, len); + buff [len-1] = '\0'; + return buff; + } + + memset (&hints, 0, sizeof (hints)); + hints . ai_family = AF_UNSPEC; + hints . ai_socktype = SOCK_STREAM; + if (getaddrinfo (name, NULL, &hints, &head)) + return NULL; + + if (head) + { + if (getnameinfo(head->ai_addr, head->ai_addrlen, buff, len, NULL, + 0, NI_NUMERICHOST) == 0) + ret = buff; + freeaddrinfo (head); + } + + return ret; +} + +#else + +char *resolver_getname(const char *ip, char *buff, int len) +{ + struct hostent *host; + char *ret = NULL; + struct in_addr addr; + + if (! _isip(ip)) + { + strncpy(buff, ip, len); + buff [len-1] = '\0'; + return buff; + } + + thread_mutex_lock(&_resolver_mutex); + if (inet_aton (ip, &addr)) { + if ((host=gethostbyaddr (&addr, sizeof (struct in_addr), AF_INET))) + { + ret = strncpy (buff, host->h_name, len); + buff [len-1] = '\0'; + } + } + + thread_mutex_unlock(&_resolver_mutex); + return ret; } char *resolver_getip(const char *name, char *buff, int len) { - if (_isip(name)) { - strncpy(buff, name, len); - return buff; - } + struct hostent *host; + char *ret = NULL; - return _lookup(name, buff, len); + if (_isip(name)) + { + strncpy(buff, name, len); + buff [len-1] = '\0'; + return buff; + } + thread_mutex_lock(&_resolver_mutex); + host = gethostbyname(name); + if (host) + { + char * temp = inet_ntoa(*(struct in_addr *)host->h_addr); + ret = strncpy(buff, temp, len); + buff [len-1] = '\0'; + } + thread_mutex_unlock(&_resolver_mutex); + + return ret; } - -static int _isip(const char *what) -{ -#ifdef HAVE_IPV6 - union { - struct in_addr v4addr; - struct in6_addr v6addr; - } addr_u; - - if (inet_pton(AF_INET, what, &addr_u.v4addr) <= 0) - return inet_pton(AF_INET6, what, &addr_u.v6addr) > 0 ? 1 : 0; - - return 1; -#else - struct in_addr inp; - - return inet_aton(what, &inp); #endif -} -static char *_lookup(const char *what, char *buff, int len) -{ - /* linux doesn't appear to have getipnodebyname as of glibc-2.2.3, so the IPV6 lookup is untested */ -#ifdef HAVE_GETIPNODEBYNAME - int err; -#else - struct in_addr inp; -#endif - struct hostent *host = NULL; - char *temp; - - /* do a little sanity checking */ - if (what == NULL || buff == NULL || len <= 0) - return NULL; - -#ifdef HAVE_GETIPNODEBYNAME - host = getipnodebyname(what, AF_INET6, AI_DEFAULT, &err); - if (host) { - if (_isip(what)) - strncpy(buff, host->h_name, len); - else - inet_ntop(host->h_addrtype, host->h_addr_list[0], buff, len); - - freehostent(host); - } else - buff = NULL; -#else - if (_isip(what)) { - /* gotta lock calls for now, since gethostbyname and such - * aren't threadsafe */ - _lock_resolver(); - host = gethostbyaddr((char *)&inp, sizeof(struct in_addr), AF_INET); - _unlock_resolver(); - if (host == NULL) { - buff = NULL; - } else { - strncpy(buff, host->h_name, len); - } - } else { - _lock_resolver(); - host = gethostbyname(what); - _unlock_resolver(); - - if (host == NULL) { - buff = NULL; - } else { - // still need to be locked here? - temp = inet_ntoa(*(struct in_addr *)host->h_addr); - strncpy(buff, temp, len); - } - } - -#endif - return buff; -} void resolver_initialize() { - /* initialize the lib if we havne't done so already */ + /* initialize the lib if we havne't done so already */ - if (!_initialized) { - _initialized = 1; -#ifndef _WIN32 - pthread_mutex_init(&_resolver_mutex, NULL); -#else - InitializeCriticalSection(&_resolver_mutex); + if (!_initialized) + { + _initialized = 1; + thread_mutex_create (&_resolver_mutex); + + /* keep dns connects (TCP) open */ +#ifdef HAVE_SETHOSTENT + sethostent(1); #endif - - /* keep dns connects (TCP) open */ - sethostent(1); - } + } } void resolver_shutdown(void) { - if (_initialized) { -#ifndef _WIN32 - pthread_mutex_destroy(&_resolver_mutex); -#else - DeleteCriticalSection(&_resolver_mutex); + if (_initialized) + { + thread_mutex_destroy(&_resolver_mutex); + _initialized = 0; +#ifdef HAVE_ENDHOSTENT + endhostent(); #endif - - _initialized = 0; - } + } } -static void _lock_resolver() -{ -#ifndef _WIN32 - pthread_mutex_lock(&_resolver_mutex); -#else - EnterCriticalSection(&_resolver_mutex); -#endif -} - -static void _unlock_resolver() -{ -#ifndef _WIN32 - pthread_mutex_unlock(&_resolver_mutex); -#else - LeaveCriticalSection(&_resolver_mutex); -#endif -} diff --git a/src/net/sock.c b/src/net/sock.c index 3a4c00c7..4e776c9e 100644 --- a/src/net/sock.c +++ b/src/net/sock.c @@ -47,10 +47,6 @@ #include "sock.h" #include "resolver.h" -#ifndef _WIN32 -extern int errno; -#endif - /* sock_initialize ** ** initializes the socket library. you must call this @@ -118,7 +114,24 @@ int sock_error(void) */ int sock_recoverable(int error) { - return (error == 0 || error == EAGAIN || error == EINTR || error == EINPROGRESS || error == EWOULDBLOCK); + return (error == 0 || error == EAGAIN || error == EINTR || + error == EINPROGRESS || error == EWOULDBLOCK); +} + +int sock_stalled (int error) +{ + return error == EAGAIN || error == EINPROGRESS || error == EWOULDBLOCK || + error == EALREADY; +} + +int sock_success (int error) +{ + return error == 0; +} + +int sock_connect_pending (int error) +{ + return error == EINPROGRESS || error == EALREADY; } /* sock_valid_socket @@ -127,13 +140,14 @@ int sock_recoverable(int error) */ int sock_valid_socket(sock_t sock) { - int ret; - int optval, optlen; + int ret; + int optval; + socklen_t optlen; - optlen = sizeof(int); - ret = getsockopt(sock, SOL_SOCKET, SO_TYPE, &optval, &optlen); + optlen = sizeof(int); + ret = getsockopt(sock, SOL_SOCKET, SO_TYPE, &optval, &optlen); - return (ret == 0); + return (ret == 0); } /* inet_aton @@ -180,13 +194,15 @@ int sock_set_blocking(sock_t sock, const int block) int sock_set_nolinger(sock_t sock) { struct linger lin = { 0, 0 }; - return setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&lin, sizeof(struct linger)); + return setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&lin, + sizeof(struct linger)); } int sock_set_keepalive(sock_t sock) { int keepalive = 1; - return setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, sizeof(int)); + return setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, + sizeof(int)); } /* sock_close @@ -202,12 +218,51 @@ int sock_close(sock_t sock) #endif } +/* sock_writev + * + * write multiple buffers at once, return bytes actually written + */ +#ifdef HAVE_WRITEV + +ssize_t sock_writev (int sock, const struct iovec *iov, const size_t count) +{ + return writev (sock, iov, count); +} + +#else + +ssize_t sock_writev (int sock, const struct iovec *iov, const size_t count) +{ + int i = count, accum = 0, ret; + const struct iovec *v = iov; + + while (i) + { + if (v->iov_base && v->iov_len) + { + ret = sock_write_bytes (sock, v->iov_base, v->iov_len); + if (ret == -1 && accum==0) + return -1; + if (ret == -1) + ret = 0; + accum += ret; + if (ret < (int)v->iov_len) + break; + } + v++; + i--; + } + return accum; +} + +#endif + /* sock_write_bytes ** ** write bytes to the socket ** this function will _NOT_ block */ -int sock_write_bytes(sock_t sock, const char *buff, const int len) +int sock_write_bytes(sock_t sock, const void *buff, const size_t len) { /* sanity check */ if (!buff) { @@ -301,97 +356,207 @@ int sock_read_line(sock_t sock, char *buff, const int len) } } -/* sock_connect_wto -** -** Connect to hostname on specified port and return the created socket. -** timeout specifies the maximum time to wait for this to finish and -** returns when it expires whether it connected or not -** setting timeout to 0 disable the timeout. +/* see if a connection can be written to +** return -1 unable to check +** return 0 for not yet +** return 1 for ok */ +int sock_connected (int sock, unsigned timeout) +{ + fd_set wfds; + int val = SOCK_ERROR; + socklen_t size = sizeof val; + struct timeval tv; + + tv.tv_sec = timeout; + tv.tv_usec = 0; + + FD_ZERO(&wfds); + FD_SET(sock, &wfds); + + switch (select(sock + 1, NULL, &wfds, NULL, &tv)) + { + case 0: return SOCK_TIMEOUT; + default: if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &val, &size) < 0) + val = SOCK_ERROR; + case -1: return val; + } +} + +#ifdef HAVE_GETADDRINFO + +int sock_connect_non_blocking (const char *hostname, const unsigned port) +{ + int sock = SOCK_ERROR; + struct addrinfo *ai, *head, hints; + char service[8]; + + memset (&hints, 0, sizeof (hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + snprintf (service, sizeof (service), "%u", port); + + if (getaddrinfo (hostname, service, &hints, &head)) + return SOCK_ERROR; + + ai = head; + while (ai) + { + if ((sock = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol)) + > -1) + { + sock_set_blocking (sock, SOCK_NONBLOCK); + if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0 && + !sock_connect_pending(sock_error())) + { + sock_close (sock); + sock = SOCK_ERROR; + } + else + break; + } + ai = ai->ai_next; + } + if (head) freeaddrinfo (head); + + return sock; +} + + sock_t sock_connect_wto(const char *hostname, const int port, const int timeout) { - sock_t sock; - struct sockaddr_in sin, server; - char ip[20]; + int sock = SOCK_ERROR; + struct addrinfo *ai, *head, hints; + char service[8]; - if (!hostname || !hostname[0]) { - return SOCK_ERROR; - } else if (port <= 0) { - return SOCK_ERROR; - } - - sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock == SOCK_ERROR) { - sock_close(sock); - return SOCK_ERROR; - } + memset (&hints, 0, sizeof (hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + snprintf (service, sizeof (service), "%u", port); - memset(&sin, 0, sizeof(struct sockaddr_in)); - memset(&server, 0, sizeof(struct sockaddr_in)); + if (getaddrinfo (hostname, service, &hints, &head)) + return SOCK_ERROR; - if (!resolver_getip(hostname, ip, 20)) - return SOCK_ERROR; - - if (inet_aton(ip, (struct in_addr *)&sin.sin_addr) == 0) { - sock_close(sock); - return SOCK_ERROR; - } + ai = head; + while (ai) + { + if ((sock = socket (ai->ai_family, ai->ai_socktype, ai->ai_protocol)) + > -1) + { + if (timeout) + { + sock_set_blocking (sock, SOCK_NONBLOCK); + if (connect (sock, ai->ai_addr, ai->ai_addrlen) < 0) + { + int ret = sock_connected (sock, timeout); + if (ret <= 0) + { + sock_close (sock); + sock = SOCK_ERROR; + } + } + sock_set_blocking(sock, SOCK_BLOCK); + } + else + { + if (connect (sock, ai->ai_addr, ai->ai_addrlen) < 0) + { + sock_close (sock); + sock = SOCK_ERROR; + } + } + } + ai = ai->ai_next; + } + if (head) freeaddrinfo (head); - memcpy(&server.sin_addr, &sin.sin_addr, sizeof(struct sockaddr_in)); - - server.sin_family = AF_INET; - server.sin_port = htons(port); - - /* if we have a timeout, use select, if not, use connect straight. */ - /* dunno if this is portable, and it sure is complicated for such a - simple thing to want to do. damn BSD sockets! */ - if (timeout > 0) { - fd_set wfds; - struct timeval tv; - int retval; - int val; - int valsize = sizeof(int); - - FD_ZERO(&wfds); - FD_SET(sock, &wfds); - tv.tv_sec = timeout; - tv.tv_usec = 0; - - sock_set_blocking(sock, SOCK_NONBLOCK); - retval = connect(sock, (struct sockaddr *)&server, sizeof(server)); - if (retval == 0) { - sock_set_blocking(sock, SOCK_BLOCK); - return sock; - } else { - if (!sock_recoverable(sock_error())) { - sock_close(sock); - return SOCK_ERROR; - } - } - - if (select(sock + 1, NULL, &wfds, NULL, &tv)) { - retval = getsockopt(sock, SOL_SOCKET, SO_ERROR, (void *)&val, (int *)&valsize); - if ((retval == 0) && (val == 0)) { - sock_set_blocking(sock, SOCK_BLOCK); - return sock; - } else { - sock_close(sock); - return SOCK_ERROR; - } - } else { - sock_close(sock); - return SOCK_ERROR; - } - } else { - if (connect(sock, (struct sockaddr *)&server, sizeof(server)) == 0) { - return sock; - } else { - sock_close(sock); - return SOCK_ERROR; - } - } + return sock; } +#else + + +int sock_try_connection (int sock, const char *hostname, const unsigned port) +{ + struct sockaddr_in sin, server; + char ip[20]; + + if (!hostname || !hostname[0] || port == 0) + return -1; + + memset(&sin, 0, sizeof(struct sockaddr_in)); + memset(&server, 0, sizeof(struct sockaddr_in)); + + if (!resolver_getip(hostname, ip, 20)) + { + sock_close (sock); + return -1; + } + + if (inet_aton(ip, (struct in_addr *)&sin.sin_addr) == 0) + { + sock_close(sock); + return -1; + } + + memcpy(&server.sin_addr, &sin.sin_addr, sizeof(struct sockaddr_in)); + + server.sin_family = AF_INET; + server.sin_port = htons(port); + + return connect(sock, (struct sockaddr *)&server, sizeof(server)); +} + +int sock_connect_non_blocking (const char *hostname, const unsigned port) +{ + int sock; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == -1) + return -1; + + sock_set_blocking (sock, SOCK_NONBLOCK); + sock_try_connection (sock, hostname, port); + + return sock; +} + +sock_t sock_connect_wto(const char *hostname, const int port, const int timeout) +{ + int sock; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == -1) + return -1; + + if (timeout) + { + sock_set_blocking (sock, SOCK_NONBLOCK); + if (sock_try_connection (sock, hostname, port) < 0) + { + int ret = sock_connected (sock, timeout); + if (ret <= 0) + { + sock_close (sock); + return SOCK_ERROR; + } + } + sock_set_blocking(sock, SOCK_BLOCK); + } + else + { + if (sock_try_connection (sock, hostname, port) < 0) + { + sock_close (sock); + sock = SOCK_ERROR; + } + } + return sock; +} +#endif + + /* sock_get_server_socket ** ** create a socket for incoming requests on a specified port and @@ -426,7 +591,8 @@ sock_t sock_get_server_socket(const int port, char *sinterface) if (inet_pton(AF_INET, ip, &((struct sockaddr_in*)&sa)->sin_addr) > 0) { ((struct sockaddr_in*)&sa)->sin_family = AF_INET; ((struct sockaddr_in*)&sa)->sin_port = htons(port); - } else if (inet_pton(AF_INET6, ip, &((struct sockaddr_in6*)&sa)->sin6_addr) > 0) { + } else if (inet_pton(AF_INET6, ip, + &((struct sockaddr_in6*)&sa)->sin6_addr) > 0) { sa_family = AF_INET6; sa_len = sizeof (struct sockaddr_in6); ((struct sockaddr_in6*)&sa)->sin6_family = AF_INET6; @@ -480,7 +646,7 @@ int sock_accept(sock_t serversock, char *ip, int len) { struct sockaddr_in sin; int ret; - int slen; + socklen_t slen; if (!sock_valid_socket(serversock)) return SOCK_ERROR; diff --git a/src/net/sock.h b/src/net/sock.h index bb242613..72a5a4bf 100644 --- a/src/net/sock.h +++ b/src/net/sock.h @@ -26,9 +26,25 @@ #include #endif +#ifdef HAVE_SYS_UIO_H +#include +#else +#ifndef _SYS_UIO_H +struct iovec +{ + void *iov_base; + size_t iov_len; +}; +#endif +#endif + typedef int sock_t; +/* The following values are based on unix avoiding errno value clashes */ +#define SOCK_SUCCESS 0 #define SOCK_ERROR -1 +#define SOCK_TIMEOUT -2 + #define SOCK_BLOCK 0 #define SOCK_NONBLOCK 1 @@ -41,6 +57,7 @@ void sock_shutdown(void); char *sock_get_localip(char *buff, int len); int sock_error(void); int sock_recoverable(int error); +int sock_stalled (int error); int sock_valid_socket(sock_t sock); int sock_set_blocking(sock_t sock, const int block); int sock_set_nolinger(sock_t sock); @@ -49,11 +66,15 @@ int sock_close(sock_t sock); /* Connection related socket functions */ sock_t sock_connect_wto(const char *hostname, const int port, const int timeout); +int sock_connect_non_blocking (const char *host, const unsigned port); +int sock_connected (int sock, unsigned timeout); /* Socket write functions */ -int sock_write_bytes(sock_t sock, const char *buff, const int len); +int sock_write_bytes(sock_t sock, const void *buff, const size_t len); int sock_write(sock_t sock, const char *fmt, ...); int sock_write_string(sock_t sock, const char *buff); +ssize_t sock_writev (int sock, const struct iovec *iov, const size_t count); + /* Socket read functions */ int sock_read_bytes(sock_t sock, char *buff, const int len);