diff options
author | Hans Petter Selasky <hselasky@FreeBSD.org> | 2018-05-18 12:21:19 +0000 |
---|---|---|
committer | Hans Petter Selasky <hselasky@FreeBSD.org> | 2018-05-18 12:21:19 +0000 |
commit | d109bf9e4b609b5a0626b433e56db4a47dc530bb (patch) | |
tree | 48bc2a4852c42b213448a7e9d93f2e5eb6348ea0 /sockutils.c | |
parent | 2369c04eb959fda4140b71f1c78798251bf43b62 (diff) | |
download | src-d109bf9e4b609b5a0626b433e56db4a47dc530bb.tar.gz src-d109bf9e4b609b5a0626b433e56db4a47dc530bb.zip |
Import vendor revision 77da77c36e5d958f9b8d6729876a33f670de031f from:
https://github.com/the-tcpdump-group/libpcap.git
This among other minor fixes adds support for sniffing RDMA devices.
Sponsored by: Mellanox Technologies
Notes
Notes:
svn path=/vendor/libpcap/dist/; revision=333789
Diffstat (limited to 'sockutils.c')
-rw-r--r-- | sockutils.c | 471 |
1 files changed, 365 insertions, 106 deletions
diff --git a/sockutils.c b/sockutils.c index c05ff1a1b58b..079c49db2cac 100644 --- a/sockutils.c +++ b/sockutils.c @@ -31,7 +31,7 @@ */ #ifdef HAVE_CONFIG_H -#include "config.h" +#include <config.h> #endif /* @@ -50,7 +50,9 @@ * ways. */ -#include <string.h> /* for strerror() */ +#include "ftmacros.h" + +#include <string.h> #include <errno.h> /* for the errno variable */ #include <stdio.h> /* for the stderr file */ #include <stdlib.h> /* for malloc() and free() */ @@ -60,8 +62,10 @@ #define INT_MAX 2147483647 #endif -#include "portability.h" +#include "pcap-int.h" + #include "sockutils.h" +#include "portability.h" #ifdef _WIN32 /* @@ -88,6 +92,24 @@ #define SOCKET_NO_PORT_AVAILABLE "No port available" #define SOCKET_NAME_NULL_DAD "Null address (possibly DAD Phase)" +/* + * On UN*X, send() and recv() return ssize_t. + * + * On Windows, send() and recv() return an int. + * + * Wth MSVC, there *is* no ssize_t. + * + * With MinGW, there is an ssize_t type; it is either an int (32 bit) + * or a long long (64 bit). + * + * So, on Windows, if we don't have ssize_t defined, define it as an + * int, so we can use it, on all platforms, as the type of variables + * that hold the return values from send() and recv(). + */ +#if defined(_WIN32) && !defined(_SSIZE_T_DEFINED) +typedef int ssize_t; +#endif + /**************************************************** * * * Locally defined functions * @@ -103,40 +125,21 @@ static int sock_ismcastaddr(const struct sockaddr *saddr); ****************************************************/ /* - * \brief It retrieves the error message after an error occurred in the socket interface. - * - * This function is defined because of the different way errors are returned in UNIX - * and Win32. This function provides a consistent way to retrieve the error message - * (after a socket error occurred) on all the platforms. - * - * \param caller: a pointer to a user-allocated string which contains a message that has - * to be printed *before* the true error message. It could be, for example, 'this error - * comes from the recv() call at line 31'. It may be NULL. - * - * \param errbuf: a pointer to an user-allocated buffer that will contain the complete - * error message. This buffer has to be at least 'errbuflen' in length. - * It can be NULL; in this case the error cannot be printed. - * - * \param errbuflen: length of the buffer that will contains the error. The error message cannot be - * larger than 'errbuflen - 1' because the last char is reserved for the string terminator. - * - * \return No return values. The error message is returned in the 'string' parameter. + * Format an error message given an errno value (UN*X) or a WinSock error + * (Windows). */ -void sock_geterror(const char *caller, char *errbuf, int errbuflen) +void sock_fmterror(const char *caller, int errcode, char *errbuf, int errbuflen) { #ifdef _WIN32 int retval; - int code; TCHAR message[SOCK_ERRBUF_SIZE]; /* It will be char (if we're using ascii) or wchar_t (if we're using unicode) */ if (errbuf == NULL) return; - code = GetLastError(); - retval = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, - NULL, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + NULL, errcode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), message, sizeof(message) / sizeof(TCHAR), NULL); if (retval == 0) @@ -145,14 +148,13 @@ void sock_geterror(const char *caller, char *errbuf, int errbuflen) pcap_snprintf(errbuf, errbuflen, "%sUnable to get the exact error message", caller); else pcap_snprintf(errbuf, errbuflen, "Unable to get the exact error message"); - return; } else { if ((caller) && (*caller)) - pcap_snprintf(errbuf, errbuflen, "%s%s (code %d)", caller, message, code); + pcap_snprintf(errbuf, errbuflen, "%s%s (code %d)", caller, message, errcode); else - pcap_snprintf(errbuf, errbuflen, "%s (code %d)", message, code); + pcap_snprintf(errbuf, errbuflen, "%s (code %d)", message, errcode); } #else char *message; @@ -160,12 +162,45 @@ void sock_geterror(const char *caller, char *errbuf, int errbuflen) if (errbuf == NULL) return; - message = strerror(errno); + message = strerror(errcode); if ((caller) && (*caller)) - pcap_snprintf(errbuf, errbuflen, "%s%s (code %d)", caller, message, errno); + pcap_snprintf(errbuf, errbuflen, "%s%s (code %d)", caller, message, errcode); else - pcap_snprintf(errbuf, errbuflen, "%s (code %d)", message, errno); + pcap_snprintf(errbuf, errbuflen, "%s (code %d)", message, errcode); +#endif +} + +/* + * \brief It retrieves the error message after an error occurred in the socket interface. + * + * This function is defined because of the different way errors are returned in UNIX + * and Win32. This function provides a consistent way to retrieve the error message + * (after a socket error occurred) on all the platforms. + * + * \param caller: a pointer to a user-allocated string which contains a message that has + * to be printed *before* the true error message. It could be, for example, 'this error + * comes from the recv() call at line 31'. It may be NULL. + * + * \param errbuf: a pointer to an user-allocated buffer that will contain the complete + * error message. This buffer has to be at least 'errbuflen' in length. + * It can be NULL; in this case the error cannot be printed. + * + * \param errbuflen: length of the buffer that will contains the error. The error message cannot be + * larger than 'errbuflen - 1' because the last char is reserved for the string terminator. + * + * \return No return values. The error message is returned in the 'string' parameter. + */ +void sock_geterror(const char *caller, char *errbuf, int errbuflen) +{ +#ifdef _WIN32 + if (errbuf == NULL) + return; + sock_fmterror(caller, GetLastError(), errbuf, errbuflen); +#else + if (errbuf == NULL) + return; + sock_fmterror(caller, errno, errbuf, errbuflen); #endif } @@ -185,9 +220,9 @@ void sock_geterror(const char *caller, char *errbuf, int errbuflen) * \return '0' if everything is fine, '-1' if some errors occurred. The error message is returned * in the 'errbuf' variable. */ +#ifdef _WIN32 int sock_init(char *errbuf, int errbuflen) { -#ifdef _WIN32 if (sockcount == 0) { WSADATA wsaData; /* helper variable needed to initialize Winsock */ @@ -205,8 +240,10 @@ int sock_init(char *errbuf, int errbuflen) } sockcount++; +#else +int sock_init(char *errbuf _U_, int errbuflen _U_) +{ #endif - return 0; } @@ -277,56 +314,102 @@ static int sock_ismcastaddr(const struct sockaddr *saddr) * larger than 'errbuflen - 1' because the last char is reserved for the string terminator. * * \return the socket that has been opened (that has to be used in the following sockets calls) - * if everything is fine, '0' if some errors occurred. The error message is returned + * if everything is fine, INVALID_SOCKET if some errors occurred. The error message is returned * in the 'errbuf' variable. */ SOCKET sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf, int errbuflen) { SOCKET sock; +#if defined(SO_NOSIGPIPE) || defined(IPV6_V6ONLY) || defined(IPV6_BINDV6ONLY) + int on = 1; +#endif sock = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol); - if (sock == -1) + if (sock == INVALID_SOCKET) { sock_geterror("socket(): ", errbuf, errbuflen); - return -1; + return INVALID_SOCKET; } + /* + * Disable SIGPIPE, if we have SO_NOSIGPIPE. We don't want to + * have to deal with signals if the peer closes the connection, + * especially in client programs, which may not even be aware that + * they're sending to sockets. + */ +#ifdef SO_NOSIGPIPE + if (setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, (char *)&on, + sizeof (int)) == -1) + { + sock_geterror("setsockopt(SO_NOSIGPIPE)", errbuf, errbuflen); + closesocket(sock); + return INVALID_SOCKET; + } +#endif /* This is a server socket */ if (server) { -#ifdef BSD +#if defined(IPV6_V6ONLY) || defined(IPV6_BINDV6ONLY) /* - * Force the use of IPv6-only addresses; in BSD you can accept both v4 and v6 - * connections if you have a "NULL" pointer as the nodename in the getaddrinfo() - * This behavior is not clear in the RFC 2553, so each system implements the - * bind() differently from this point of view + * Force the use of IPv6-only addresses. + * + * RFC 3493 indicates that you can support IPv4 on an + * IPv6 socket: + * + * https://tools.ietf.org/html/rfc3493#section-3.7 + * + * and that this is the default behavior. This means + * that if we first create an IPv6 socket bound to the + * "any" address, it is, in effect, also bound to the + * IPv4 "any" address, so when we create an IPv4 socket + * and try to bind it to the IPv4 "any" address, it gets + * EADDRINUSE. + * + * Not all network stacks support IPv4 on IPv6 sockets; + * pre-NT 6 Windows stacks don't support it, and the + * OpenBSD stack doesn't support it for security reasons + * (see the OpenBSD inet6(4) man page). Therefore, we + * don't want to rely on this behavior. + * + * So we try to disable it, using either the IPV6_V6ONLY + * option from RFC 3493: + * + * https://tools.ietf.org/html/rfc3493#section-5.3 + * + * or the IPV6_BINDV6ONLY option from older UN*Xes. */ +#ifndef IPV6_V6ONLY + /* For older systems */ + #define IPV6_V6ONLY IPV6_BINDV6ONLY +#endif /* IPV6_V6ONLY */ if (addrinfo->ai_family == PF_INET6) { - int on; - - if (setsockopt(sock, IPPROTO_IPV6, IPV6_BINDV6ONLY, (char *)&on, sizeof (int)) == -1) + if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, + (char *)&on, sizeof (int)) == -1) { if (errbuf) - pcap_snprintf(errbuf, errbuflen, "setsockopt(IPV6_BINDV6ONLY)"); - return -1; + pcap_snprintf(errbuf, errbuflen, "setsockopt(IPV6_V6ONLY)"); + closesocket(sock); + return INVALID_SOCKET; } } -#endif +#endif /* defined(IPV6_V6ONLY) || defined(IPV6_BINDV6ONLY) */ /* WARNING: if the address is a mcast one, I should place the proper Win32 code here */ if (bind(sock, addrinfo->ai_addr, (int) addrinfo->ai_addrlen) != 0) { sock_geterror("bind(): ", errbuf, errbuflen); - return -1; + closesocket(sock); + return INVALID_SOCKET; } if (addrinfo->ai_socktype == SOCK_STREAM) if (listen(sock, nconn) == -1) { sock_geterror("listen(): ", errbuf, errbuflen); - return -1; + closesocket(sock); + return INVALID_SOCKET; } /* server side ended */ @@ -391,7 +474,7 @@ SOCKET sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf, if (tempaddrinfo == NULL) { closesocket(sock); - return -1; + return INVALID_SOCKET; } else return sock; @@ -513,6 +596,8 @@ int sock_initaddress(const char *host, const char *port, { if (errbuf) pcap_snprintf(errbuf, errbuflen, "getaddrinfo(): socket type not supported"); + freeaddrinfo(*addrinfo); + *addrinfo = NULL; return -1; } @@ -524,6 +609,8 @@ int sock_initaddress(const char *host, const char *port, { if (errbuf) pcap_snprintf(errbuf, errbuflen, "getaddrinfo(): multicast addresses are not valid when using TCP streams"); + freeaddrinfo(*addrinfo); + *addrinfo = NULL; return -1; } @@ -552,39 +639,84 @@ int sock_initaddress(const char *host, const char *port, * \param errbuflen: length of the buffer that will contains the error. The error message cannot be * larger than 'errbuflen - 1' because the last char is reserved for the string terminator. * - * \return '0' if everything is fine, '-1' if some errors occurred. The error message is returned - * in the 'errbuf' variable. + * \return '0' if everything is fine, '-1' if an error other than + * "connection reset" or "peer has closed the receive side" occurred, + * '-2' if we got one of those errors. + * For errors, an error message is returned in the 'errbuf' variable. */ -int sock_send(SOCKET socket, const char *buffer, int size, char *errbuf, int errbuflen) +int sock_send(SOCKET sock, const char *buffer, size_t size, + char *errbuf, int errbuflen) { - int nsent; - -send: -#ifdef linux - /* - * Another pain... in Linux there's this flag - * MSG_NOSIGNAL - * Requests not to send SIGPIPE on errors on stream-oriented - * sockets when the other end breaks the connection. - * The EPIPE error is still returned. - */ - nsent = send(socket, buffer, size, MSG_NOSIGNAL); -#else - nsent = send(socket, buffer, size, 0); -#endif + int remaining; + ssize_t nsent; - if (nsent == -1) + if (size > INT_MAX) { - sock_geterror("send(): ", errbuf, errbuflen); + if (errbuf) + { + pcap_snprintf(errbuf, errbuflen, + "Can't send more than %u bytes with sock_recv", + INT_MAX); + } return -1; } + remaining = (int)size; - if (nsent != size) - { - size -= nsent; + do { +#ifdef MSG_NOSIGNAL + /* + * Send with MSG_NOSIGNAL, so that we don't get SIGPIPE + * on errors on stream-oriented sockets when the other + * end breaks the connection. + * The EPIPE error is still returned. + */ + nsent = send(sock, buffer, remaining, MSG_NOSIGNAL); +#else + nsent = send(sock, buffer, remaining, 0); +#endif + + if (nsent == -1) + { + /* + * If the client closed the connection out from + * under us, there's no need to log that as an + * error. + */ + int errcode; + +#ifdef _WIN32 + errcode = GetLastError(); + if (errcode == WSAECONNRESET || + errcode == WSAECONNABORTED) + { + /* + * WSAECONNABORTED appears to be the error + * returned in Winsock when you try to send + * on a connection where the peer has closed + * the receive side. + */ + return -2; + } + sock_fmterror("send(): ", errcode, errbuf, errbuflen); +#else + errcode = errno; + if (errcode == ECONNRESET || errcode == EPIPE) + { + /* + * EPIPE is what's returned on UN*X when + * you try to send on a connection when + * the peer has closed the receive side. + */ + return -2; + } + sock_fmterror("send(): ", errcode, errbuf, errbuflen); +#endif + return -1; + } + + remaining -= nsent; buffer += nsent; - goto send; - } + } while (remaining != 0); return 0; } @@ -682,9 +814,19 @@ int sock_bufferize(const char *buffer, int size, char *tempbuf, int *offset, int * \param size: size of the allocated buffer. WARNING: this indicates the number of bytes * that we are expecting to be read. * - * \param receiveall: if '0' (or SOCK_RECEIVEALL_NO), it returns as soon as some data - * is ready; otherwise, (or SOCK_RECEIVEALL_YES) it waits until 'size' data has been - * received (in case the socket does not have enough data available). + * \param flags: + * + * SOCK_RECEIVALL_XXX: + * + * if SOCK_RECEIVEALL_NO, return as soon as some data is ready + * if SOCK_RECEIVALL_YES, wait until 'size' data has been + * received (in case the socket does not have enough data available). + * + * SOCK_EOF_XXX: + * + * if SOCK_EOF_ISNT_ERROR, if the first read returns 0, just return 0, + * and return an error on any subsequent read that returns 0; + * if SOCK_EOF_IS_ERROR, if any read returns 0, return an error. * * \param errbuf: a pointer to an user-allocated buffer that will contain the complete * error message. This buffer has to be at least 'errbuflen' in length. @@ -697,17 +839,7 @@ int sock_bufferize(const char *buffer, int size, char *tempbuf, int *offset, int * The error message is returned in the 'errbuf' variable. */ -/* - * On UN*X, recv() returns ssize_t. - * On Windows, there *is* no ssize_t, and it returns an int. - * Define ssize_t as int on Windows so we can use it as the return value - * from recv(). - */ -#ifdef _WIN32 -typedef int ssize_t; -#endif - -int sock_recv(SOCKET sock, void *buffer, size_t size, int receiveall, +int sock_recv(SOCKET sock, void *buffer, size_t size, int flags, char *errbuf, int errbuflen) { char *bufp = buffer; @@ -716,13 +848,17 @@ int sock_recv(SOCKET sock, void *buffer, size_t size, int receiveall, if (size == 0) { - SOCK_ASSERT("I have been requested to read zero bytes", 1); + SOCK_DEBUG_MESSAGE("I have been requested to read zero bytes"); return 0; } if (size > INT_MAX) { - pcap_snprintf(errbuf, errbuflen, "Can't read more than %u bytes with sock_recv", - INT_MAX); + if (errbuf) + { + pcap_snprintf(errbuf, errbuflen, + "Can't read more than %u bytes with sock_recv", + INT_MAX); + } return -1; } @@ -748,19 +884,30 @@ int sock_recv(SOCKET sock, void *buffer, size_t size, int receiveall, if (nread == 0) { - if (errbuf) + if ((flags & SOCK_EOF_IS_ERROR) || + (remaining != (int) size)) { - pcap_snprintf(errbuf, errbuflen, - "The other host terminated the connection."); + /* + * Either we've already read some data, + * or we're always supposed to return + * an error on EOF. + */ + if (errbuf) + { + pcap_snprintf(errbuf, errbuflen, + "The other host terminated the connection."); + } + return -1; } - return -1; + else + return 0; } /* * Do we want to read the amount requested, or just return * what we got? */ - if (!receiveall) + if (!(flags & SOCK_RECEIVEALL_YES)) { /* * Just return what we got. @@ -777,6 +924,121 @@ int sock_recv(SOCKET sock, void *buffer, size_t size, int receiveall, } /* + * Receives a datagram from a socket. + * + * Returns the size of the datagram on success or -1 on error. + */ +int sock_recv_dgram(SOCKET sock, void *buffer, size_t size, + char *errbuf, int errbuflen) +{ + ssize_t nread; +#ifndef _WIN32 + struct msghdr message; + struct iovec iov; +#endif + + if (size == 0) + { + SOCK_DEBUG_MESSAGE("I have been requested to read zero bytes"); + return 0; + } + if (size > INT_MAX) + { + if (errbuf) + { + pcap_snprintf(errbuf, errbuflen, + "Can't read more than %u bytes with sock_recv_dgram", + INT_MAX); + } + return -1; + } + + /* + * This should be a datagram socket, so we should get the + * entire datagram in one recv() or recvmsg() call, and + * don't need to loop. + */ +#ifdef _WIN32 + nread = recv(sock, buffer, size, 0); + if (nread == SOCKET_ERROR) + { + /* + * To quote the MSDN documentation for recv(), + * "If the datagram or message is larger than + * the buffer specified, the buffer is filled + * with the first part of the datagram, and recv + * generates the error WSAEMSGSIZE. For unreliable + * protocols (for example, UDP) the excess data is + * lost..." + * + * So if the message is bigger than the buffer + * supplied to us, the excess data is discarded, + * and we'll report an error. + */ + sock_geterror("recv(): ", errbuf, errbuflen); + return -1; + } +#else /* _WIN32 */ + /* + * The Single UNIX Specification says that a recv() on + * a socket for a message-oriented protocol will discard + * the excess data. It does *not* indicate that the + * receive will fail with, for example, EMSGSIZE. + * + * Therefore, we use recvmsg(), which appears to be + * the only way to get a "message truncated" indication + * when receiving a message for a message-oriented + * protocol. + */ + message.msg_name = NULL; /* we don't care who it's from */ + message.msg_namelen = 0; + iov.iov_base = buffer; + iov.iov_len = size; + message.msg_iov = &iov; + message.msg_iovlen = 1; +#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL + message.msg_control = NULL; /* we don't care about control information */ + message.msg_controllen = 0; +#endif +#ifdef HAVE_STRUCT_MSGHDR_MSG_FLAGS + message.msg_flags = 0; +#endif + nread = recvmsg(sock, &message, 0); + if (nread == -1) + { + if (errno == EINTR) + return -3; + sock_geterror("recv(): ", errbuf, errbuflen); + return -1; + } +#ifdef HAVE_STRUCT_MSGHDR_MSG_FLAGS + /* + * XXX - Solaris supports this, but only if you ask for the + * X/Open version of recvmsg(); should we use that, or will + * that cause other problems? + */ + if (message.msg_flags & MSG_TRUNC) + { + /* + * Message was bigger than the specified buffer size. + * + * Report this as an error, as the Microsoft documentation + * implies we'd do in a similar case on Windows. + */ + pcap_snprintf(errbuf, errbuflen, "recv(): Message too long"); + return -1; + } +#endif /* HAVE_STRUCT_MSGHDR_MSG_FLAGS */ +#endif /* _WIN32 */ + + /* + * The size we're reading fits in an int, so the return value + * will fit in an int. + */ + return (int)nread; +} + +/* * \brief It discards N bytes that are currently waiting to be read on the current socket. * * This function is useful in case we receive a message we cannot understand (e.g. @@ -833,7 +1095,7 @@ int sock_discard(SOCKET sock, int size, char *errbuf, int errbuflen) return -1; } - SOCK_ASSERT("I'm currently discarding data\n", 1); + SOCK_DEBUG_MESSAGE("I'm currently discarding data\n"); return 0; } @@ -908,7 +1170,7 @@ int sock_check_hostlist(char *hostlist, const char *sep, struct sockaddr_storage if (errbuf) pcap_snprintf(errbuf, errbuflen, "getaddrinfo() %s", gai_strerror(retval)); - SOCK_ASSERT(errbuf, 1); + SOCK_DEBUG_MESSAGE(errbuf); /* Get next token */ token = pcap_strtok_r(NULL, sep, &lasts); @@ -922,6 +1184,7 @@ int sock_check_hostlist(char *hostlist, const char *sep, struct sockaddr_storage if (sock_cmpaddr(from, (struct sockaddr_storage *) ai_next->ai_addr) == 0) { free(temphostlist); + freeaddrinfo(addrinfo); return 0; } @@ -1051,13 +1314,9 @@ int sock_getmyinfo(SOCKET sock, char *address, int addrlen, char *port, int port sock_geterror("getsockname(): ", errbuf, errbuflen); return 0; } - else - { - /* Returns the numeric address of the host that triggered the error */ - return sock_getascii_addrport(&mysockaddr, address, addrlen, port, portlen, flags, errbuf, errbuflen); - } - return 0; + /* Returns the numeric address of the host that triggered the error */ + return sock_getascii_addrport(&mysockaddr, address, addrlen, port, portlen, flags, errbuf, errbuflen); } /* |