aboutsummaryrefslogtreecommitdiff
path: root/sockutils.c
diff options
context:
space:
mode:
authorJoseph Mingrone <jrm@FreeBSD.org>2023-03-27 18:45:17 +0000
committerJoseph Mingrone <jrm@FreeBSD.org>2023-03-27 18:45:17 +0000
commit35af88c96350eb786f1198dfb6b29a171016e6bf (patch)
treee883c1f8391d5ca1afd57abd8ed9d2cd7c274b0b /sockutils.c
parent20616273d52132557e786a8aea1637be4c218a08 (diff)
downloadsrc-35af88c96350eb786f1198dfb6b29a171016e6bf.tar.gz
src-35af88c96350eb786f1198dfb6b29a171016e6bf.zip
Diffstat (limited to 'sockutils.c')
-rw-r--r--sockutils.c817
1 files changed, 646 insertions, 171 deletions
diff --git a/sockutils.c b/sockutils.c
index d3e94649d9d4..933f32670761 100644
--- a/sockutils.c
+++ b/sockutils.c
@@ -56,11 +56,7 @@
#include <errno.h> /* for the errno variable */
#include <stdio.h> /* for the stderr file */
#include <stdlib.h> /* for malloc() and free() */
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
-#else
-#define INT_MAX 2147483647
-#endif
+#include <limits.h> /* for INT_MAX */
#include "pcap-int.h"
@@ -71,7 +67,7 @@
/*
* Winsock initialization.
*
- * Ask for WinSock 2.2.
+ * Ask for Winsock 2.2.
*/
#define WINSOCK_MAJOR_VERSION 2
#define WINSOCK_MINOR_VERSION 2
@@ -97,7 +93,7 @@
*
* On Windows, send() and recv() return an int.
*
- * Wth MSVC, there *is* no ssize_t.
+ * With 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).
@@ -124,51 +120,187 @@ static int sock_ismcastaddr(const struct sockaddr *saddr);
* *
****************************************************/
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+const uint8_t *fuzzBuffer;
+size_t fuzzSize;
+size_t fuzzPos;
+
+void sock_initfuzz(const uint8_t *Data, size_t Size) {
+ fuzzPos = 0;
+ fuzzSize = Size;
+ fuzzBuffer = Data;
+}
+
+static int fuzz_recv(char *bufp, int remaining) {
+ if (remaining > fuzzSize - fuzzPos) {
+ remaining = fuzzSize - fuzzPos;
+ }
+ if (fuzzPos < fuzzSize) {
+ memcpy(bufp, fuzzBuffer + fuzzPos, remaining);
+ }
+ fuzzPos += remaining;
+ return remaining;
+}
+#endif
+
+int sock_geterrcode(void)
+{
+#ifdef _WIN32
+ return GetLastError();
+#else
+ return errno;
+#endif
+}
+
/*
- * Format an error message given an errno value (UN*X) or a WinSock error
+ * Format an error message given an errno value (UN*X) or a Winsock error
* (Windows).
*/
-void sock_fmterror(const char *caller, int errcode, char *errbuf, int errbuflen)
+void sock_vfmterrmsg(char *errbuf, size_t errbuflen, int errcode,
+ const char *fmt, va_list ap)
{
if (errbuf == NULL)
return;
#ifdef _WIN32
- pcap_fmt_errmsg_for_win32_err(errbuf, errbuflen, errcode,
- "%s", caller);
+ pcap_vfmt_errmsg_for_win32_err(errbuf, errbuflen, errcode,
+ fmt, ap);
#else
- pcap_fmt_errmsg_for_errno(errbuf, errbuflen, errcode,
- "%s", caller);
+ pcap_vfmt_errmsg_for_errno(errbuf, errbuflen, errcode,
+ fmt, ap);
#endif
}
+void sock_fmterrmsg(char *errbuf, size_t errbuflen, int errcode,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ sock_vfmterrmsg(errbuf, errbuflen, errcode, fmt, ap);
+ va_end(ap);
+}
+
/*
- * \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'.
- *
- * \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.
+ * Format an error message for the last socket error.
+ */
+void sock_geterrmsg(char *errbuf, size_t errbuflen, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ sock_vfmterrmsg(errbuf, errbuflen, sock_geterrcode(), fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Types of error.
*
- * \return No return values. The error message is returned in the 'string' parameter.
+ * These are sorted by how likely they are to be the "underlying" problem,
+ * so that lower-rated errors for a given address in a given family
+ * should not overwrite higher-rated errors for another address in that
+ * family, and higher-rated errors should overwrit elower-rated errors.
*/
-void sock_geterror(const char *caller, char *errbuf, int errbuflen)
+typedef enum {
+ SOCK_CONNERR, /* connection error */
+ SOCK_HOSTERR, /* host error */
+ SOCK_NETERR, /* network error */
+ SOCK_AFNOTSUPERR, /* address family not supported */
+ SOCK_UNKNOWNERR, /* unknown error */
+ SOCK_NOERR /* no error */
+} sock_errtype;
+
+static sock_errtype sock_geterrtype(int errcode)
{
+ switch (errcode) {
+
+#ifdef _WIN32
+ case WSAECONNRESET:
+ case WSAECONNABORTED:
+ case WSAECONNREFUSED:
+#else
+ case ECONNRESET:
+ case ECONNABORTED:
+ case ECONNREFUSED:
+#endif
+ /*
+ * Connection error; this means the problem is probably
+ * that there's no server set up on the remote machine,
+ * or that it is set up, but it's IPv4-only or IPv6-only
+ * and we're trying the wrong address family.
+ *
+ * These overwrite all other errors, as they indicate
+ * that, even if somethng else went wrong in another
+ * attempt, this probably wouldn't work even if the
+ * other problems were fixed.
+ */
+ return (SOCK_CONNERR);
+
+#ifdef _WIN32
+ case WSAENETUNREACH:
+ case WSAETIMEDOUT:
+ case WSAEHOSTDOWN:
+ case WSAEHOSTUNREACH:
+#else
+ case ENETUNREACH:
+ case ETIMEDOUT:
+ case EHOSTDOWN:
+ case EHOSTUNREACH:
+#endif
+ /*
+ * Network errors that could be IPv4-specific, IPv6-
+ * specific, or present with both.
+ *
+ * Don't overwrite connection errors, but overwrite
+ * everything else.
+ */
+ return (SOCK_HOSTERR);
+
#ifdef _WIN32
- sock_fmterror(caller, GetLastError(), errbuf, errbuflen);
+ case WSAENETDOWN:
+ case WSAENETRESET:
#else
- sock_fmterror(caller, errno, errbuf, errbuflen);
+ case ENETDOWN:
+ case ENETRESET:
#endif
+ /*
+ * Network error; this means we don't know whether
+ * there's a server set up on the remote machine,
+ * and we don't have a reason to believe that IPv6
+ * any worse or better than IPv4.
+ *
+ * These probably indicate a local failure, e.g.
+ * an interface is down.
+ *
+ * Don't overwrite connection errors or host errors,
+ * but overwrite everything else.
+ */
+ return (SOCK_NETERR);
+
+#ifdef _WIN32
+ case WSAEAFNOSUPPORT:
+#else
+ case EAFNOSUPPORT:
+#endif
+ /*
+ * "Address family not supported" probably means
+ * "No soup^WIPv6 for you!".
+ *
+ * Don't overwrite connection errors, host errors, or
+ * network errors (none of which we should get for this
+ * address family if it's not supported), but overwrite
+ * everything else.
+ */
+ return (SOCK_AFNOTSUPERR);
+
+ default:
+ /*
+ * Anything else.
+ *
+ * Don't overwrite any errors.
+ */
+ return (SOCK_UNKNOWNERR);
+ }
}
/*
@@ -201,7 +333,7 @@ int sock_init(char *errbuf, int errbuflen)
WINSOCK_MINOR_VERSION), &wsaData) != 0)
{
if (errbuf)
- pcap_snprintf(errbuf, errbuflen, "Failed to initialize Winsock\n");
+ snprintf(errbuf, errbuflen, "Failed to initialize Winsock\n");
WSACleanup();
@@ -262,6 +394,79 @@ static int sock_ismcastaddr(const struct sockaddr *saddr)
}
}
+struct addr_status {
+ struct addrinfo *info;
+ int errcode;
+ sock_errtype errtype;
+};
+
+/*
+ * Sort by IPv4 address vs. IPv6 address.
+ */
+static int compare_addrs_to_try_by_address_family(const void *a, const void *b)
+{
+ const struct addr_status *addr_a = (const struct addr_status *)a;
+ const struct addr_status *addr_b = (const struct addr_status *)b;
+
+ return addr_a->info->ai_family - addr_b->info->ai_family;
+}
+
+/*
+ * Sort by error type and, within a given error type, by error code and,
+ * within a given error code, by IPv4 address vs. IPv6 address.
+ */
+static int compare_addrs_to_try_by_status(const void *a, const void *b)
+{
+ const struct addr_status *addr_a = (const struct addr_status *)a;
+ const struct addr_status *addr_b = (const struct addr_status *)b;
+
+ if (addr_a->errtype == addr_b->errtype)
+ {
+ if (addr_a->errcode == addr_b->errcode)
+ {
+ return addr_a->info->ai_family - addr_b->info->ai_family;
+ }
+ return addr_a->errcode - addr_b->errcode;
+ }
+
+ return addr_a->errtype - addr_b->errtype;
+}
+
+static SOCKET sock_create_socket(struct addrinfo *addrinfo, char *errbuf,
+ int errbuflen)
+{
+ SOCKET sock;
+#ifdef SO_NOSIGPIPE
+ int on = 1;
+#endif
+
+ sock = socket(addrinfo->ai_family, addrinfo->ai_socktype,
+ addrinfo->ai_protocol);
+ if (sock == INVALID_SOCKET)
+ {
+ sock_geterrmsg(errbuf, errbuflen, "socket() failed");
+ 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_geterrmsg(errbuf, errbuflen,
+ "setsockopt(SO_NOSIGPIPE) failed");
+ closesocket(sock);
+ return INVALID_SOCKET;
+ }
+#endif
+ return sock;
+}
+
/*
* \brief It initializes a network connection both from the client and the server side.
*
@@ -271,7 +476,10 @@ static int sock_ismcastaddr(const struct sockaddr *saddr)
*
* In case of a server socket, the function calls socket(), bind() and listen().
*
- * This function is usually preceeded by the sock_initaddress().
+ * This function is usually preceded by the sock_initaddress().
+ *
+ * \param host: for client sockets, the host name to which we're trying
+ * to connect.
*
* \param addrinfo: pointer to an addrinfo variable which will be used to
* open the socket and such. This variable is the one returned by the previous call to
@@ -293,48 +501,33 @@ static int sock_ismcastaddr(const struct sockaddr *saddr)
* 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_open(const char *host, 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 == INVALID_SOCKET)
- {
- sock_geterror("socket()", errbuf, errbuflen);
- 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)
{
+ int on;
+
+ /*
+ * Attempt to create the socket.
+ */
+ sock = sock_create_socket(addrinfo, errbuf, errbuflen);
+ if (sock == INVALID_SOCKET)
+ {
+ return INVALID_SOCKET;
+ }
+
/*
* Allow a new server to bind the socket after the old one
* exited, even if lingering sockets are still present.
*
* Don't treat an error as a failure.
*/
- int optval = 1;
+ on = 1;
(void)setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
- (char *)&optval, sizeof (optval));
+ (char *)&on, sizeof (on));
#if defined(IPV6_V6ONLY) || defined(IPV6_BINDV6ONLY)
/*
@@ -371,11 +564,12 @@ SOCKET sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf,
#endif /* IPV6_V6ONLY */
if (addrinfo->ai_family == PF_INET6)
{
+ on = 1;
if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
(char *)&on, sizeof (int)) == -1)
{
if (errbuf)
- pcap_snprintf(errbuf, errbuflen, "setsockopt(IPV6_V6ONLY)");
+ snprintf(errbuf, errbuflen, "setsockopt(IPV6_V6ONLY)");
closesocket(sock);
return INVALID_SOCKET;
}
@@ -385,7 +579,7 @@ SOCKET sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf,
/* 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);
+ sock_geterrmsg(errbuf, errbuflen, "bind() failed");
closesocket(sock);
return INVALID_SOCKET;
}
@@ -393,7 +587,8 @@ SOCKET sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf,
if (addrinfo->ai_socktype == SOCK_STREAM)
if (listen(sock, nconn) == -1)
{
- sock_geterror("listen()", errbuf, errbuflen);
+ sock_geterrmsg(errbuf, errbuflen,
+ "listen() failed");
closesocket(sock);
return INVALID_SOCKET;
}
@@ -403,68 +598,259 @@ SOCKET sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf,
}
else /* we're the client */
{
+ struct addr_status *addrs_to_try;
struct addrinfo *tempaddrinfo;
- char *errbufptr;
- size_t bufspaceleft;
-
- tempaddrinfo = addrinfo;
- errbufptr = errbuf;
- bufspaceleft = errbuflen;
- *errbufptr = 0;
+ size_t numaddrinfos;
+ size_t i;
+ int current_af = AF_UNSPEC;
/*
- * We have to loop though all the addinfo returned.
- * For instance, we can have both IPv6 and IPv4 addresses, but the service we're trying
- * to connect to is unavailable in IPv6, so we have to try in IPv4 as well
+ * We have to loop though all the addrinfos returned.
+ * For instance, we can have both IPv6 and IPv4 addresses,
+ * but the service we're trying to connect to is unavailable
+ * in IPv6, so we have to try in IPv4 as well.
+ *
+ * How many addrinfos do we have?
*/
- while (tempaddrinfo)
+ numaddrinfos = 0;
+ for (tempaddrinfo = addrinfo; tempaddrinfo != NULL;
+ tempaddrinfo = tempaddrinfo->ai_next)
{
+ numaddrinfos++;
+ }
- if (connect(sock, tempaddrinfo->ai_addr, (int) tempaddrinfo->ai_addrlen) == -1)
- {
- size_t msglen;
- char TmpBuffer[100];
- char SocketErrorMessage[SOCK_ERRBUF_SIZE];
-
- /*
- * We have to retrieve the error message before any other socket call completes, otherwise
- * the error message is lost
- */
- sock_geterror("Connect to socket failed",
- SocketErrorMessage, sizeof(SocketErrorMessage));
+ if (numaddrinfos == 0)
+ {
+ snprintf(errbuf, errbuflen,
+ "There are no addresses in the address list");
+ return INVALID_SOCKET;
+ }
- /* Returns the numeric address of the host that triggered the error */
- sock_getascii_addrport((struct sockaddr_storage *) tempaddrinfo->ai_addr, TmpBuffer, sizeof(TmpBuffer), NULL, 0, NI_NUMERICHOST, TmpBuffer, sizeof(TmpBuffer));
+ /*
+ * Allocate an array of struct addr_status and fill it in.
+ */
+ addrs_to_try = calloc(numaddrinfos, sizeof *addrs_to_try);
+ if (addrs_to_try == NULL)
+ {
+ snprintf(errbuf, errbuflen,
+ "Out of memory connecting to %s", host);
+ return INVALID_SOCKET;
+ }
- pcap_snprintf(errbufptr, bufspaceleft,
- "Is the server properly installed on %s? %s", TmpBuffer, SocketErrorMessage);
+ for (tempaddrinfo = addrinfo, i = 0; tempaddrinfo != NULL;
+ tempaddrinfo = tempaddrinfo->ai_next, i++)
+ {
+ addrs_to_try[i].info = tempaddrinfo;
+ addrs_to_try[i].errcode = 0;
+ addrs_to_try[i].errtype = SOCK_NOERR;
+ }
- /* In case more then one 'connect' fails, we manage to keep all the error messages */
- msglen = strlen(errbufptr);
+ /*
+ * Sort the structures to put the IPv4 addresses before the
+ * IPv6 addresses; we will have to create an IPv4 socket
+ * for the IPv4 addresses and an IPv6 socket for the IPv6
+ * addresses (one of the arguments to socket() is the
+ * address/protocol family to use, and IPv4 and IPv6 are
+ * separate address/protocol families).
+ */
+ qsort(addrs_to_try, numaddrinfos, sizeof *addrs_to_try,
+ compare_addrs_to_try_by_address_family);
- errbufptr[msglen] = ' ';
- errbufptr[msglen + 1] = 0;
+ /* Start out with no socket. */
+ sock = INVALID_SOCKET;
- bufspaceleft = bufspaceleft - (msglen + 1);
- errbufptr += (msglen + 1);
+ /*
+ * Now try them all.
+ */
+ for (i = 0; i < numaddrinfos; i++)
+ {
+ tempaddrinfo = addrs_to_try[i].info;
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ break;
+#endif
+ /*
+ * If we have a socket, but it's for a
+ * different address family, close it.
+ */
+ if (sock != INVALID_SOCKET &&
+ current_af != tempaddrinfo->ai_family)
+ {
+ closesocket(sock);
+ sock = INVALID_SOCKET;
+ }
- tempaddrinfo = tempaddrinfo->ai_next;
+ /*
+ * If we don't have a socket, open one
+ * for *this* address's address family.
+ */
+ if (sock == INVALID_SOCKET)
+ {
+ sock = sock_create_socket(tempaddrinfo,
+ errbuf, errbuflen);
+ if (sock == INVALID_SOCKET)
+ {
+ free(addrs_to_try);
+ return INVALID_SOCKET;
+ }
+ }
+ if (connect(sock, tempaddrinfo->ai_addr, (int) tempaddrinfo->ai_addrlen) == -1)
+ {
+ addrs_to_try[i].errcode = sock_geterrcode();
+ addrs_to_try[i].errtype =
+ sock_geterrtype(addrs_to_try[i].errcode);
}
else
break;
}
/*
- * Check how we exit from the previous loop
- * If tempaddrinfo is equal to NULL, it means that all the connect() failed.
+ * Check how we exited from the previous loop.
+ * If tempaddrinfo is equal to NULL, it means that all
+ * the connect() attempts failed. Construct an
+ * error message.
*/
- if (tempaddrinfo == NULL)
+ if (i == numaddrinfos)
{
+ int same_error_for_all;
+ int first_error;
+
closesocket(sock);
+
+ /*
+ * Sort the statuses to group together categories
+ * of errors, errors within categories, and
+ * address families within error sets.
+ */
+ qsort(addrs_to_try, numaddrinfos, sizeof *addrs_to_try,
+ compare_addrs_to_try_by_status);
+
+ /*
+ * Are all the errors the same?
+ */
+ same_error_for_all = 1;
+ first_error = addrs_to_try[0].errcode;
+ for (i = 1; i < numaddrinfos; i++)
+ {
+ if (addrs_to_try[i].errcode != first_error)
+ {
+ same_error_for_all = 0;
+ break;
+ }
+ }
+
+ if (same_error_for_all) {
+ /*
+ * Yes. No need to show the IP
+ * addresses.
+ */
+ if (addrs_to_try[0].errtype == SOCK_CONNERR) {
+ /*
+ * Connection error; note that
+ * the daemon might not be set
+ * up correctly, or set up at all.
+ */
+ sock_fmterrmsg(errbuf, errbuflen,
+ addrs_to_try[0].errcode,
+ "Is the server properly installed? Cannot connect to %s",
+ host);
+ } else {
+ sock_fmterrmsg(errbuf, errbuflen,
+ addrs_to_try[0].errcode,
+ "Cannot connect to %s", host);
+ }
+ } else {
+ /*
+ * Show all the errors and the IP addresses
+ * to which they apply.
+ */
+ char *errbufptr;
+ size_t bufspaceleft;
+ size_t msglen;
+
+ snprintf(errbuf, errbuflen,
+ "Connect to %s failed: ", host);
+
+ msglen = strlen(errbuf);
+ errbufptr = errbuf + msglen;
+ bufspaceleft = errbuflen - msglen;
+
+ for (i = 0; i < numaddrinfos &&
+ addrs_to_try[i].errcode != SOCK_NOERR;
+ i++)
+ {
+ /*
+ * Get the string for the address
+ * and port that got this error.
+ */
+ sock_getascii_addrport((struct sockaddr_storage *) addrs_to_try[i].info->ai_addr,
+ errbufptr, (int)bufspaceleft,
+ NULL, 0, NI_NUMERICHOST, NULL, 0);
+ msglen = strlen(errbuf);
+ errbufptr = errbuf + msglen;
+ bufspaceleft = errbuflen - msglen;
+
+ if (i + 1 < numaddrinfos &&
+ addrs_to_try[i + 1].errcode == addrs_to_try[i].errcode)
+ {
+ /*
+ * There's another error
+ * after this, and it has
+ * the same error code.
+ *
+ * Append a comma, as the
+ * list of addresses with
+ * this error has another
+ * entry.
+ */
+ snprintf(errbufptr, bufspaceleft,
+ ", ");
+ }
+ else
+ {
+ /*
+ * Either there are no
+ * more errors after this,
+ * or the next error is
+ * different.
+ *
+ * Append a colon and
+ * the message for tis
+ * error, followed by a
+ * comma if there are
+ * more errors.
+ */
+ sock_fmterrmsg(errbufptr,
+ bufspaceleft,
+ addrs_to_try[i].errcode,
+ "%s", "");
+ msglen = strlen(errbuf);
+ errbufptr = errbuf + msglen;
+ bufspaceleft = errbuflen - msglen;
+
+ if (i + 1 < numaddrinfos &&
+ addrs_to_try[i + 1].errcode != SOCK_NOERR)
+ {
+ /*
+ * More to come.
+ */
+ snprintf(errbufptr,
+ bufspaceleft,
+ ", ");
+ }
+ }
+ msglen = strlen(errbuf);
+ errbufptr = errbuf + msglen;
+ bufspaceleft = errbuflen - msglen;
+ }
+ }
+ free(addrs_to_try);
return INVALID_SOCKET;
}
else
+ {
+ free(addrs_to_try);
return sock;
+ }
}
}
@@ -495,7 +881,7 @@ int sock_close(SOCKET sock, char *errbuf, int errbuflen)
*/
if (shutdown(sock, SHUT_WR))
{
- sock_geterror("shutdown()", errbuf, errbuflen);
+ sock_geterrmsg(errbuf, errbuflen, "shutdown() feiled");
/* close the socket anyway */
closesocket(sock);
return -1;
@@ -506,7 +892,7 @@ int sock_close(SOCKET sock, char *errbuf, int errbuflen)
}
/*
- * gai_errstring() has some problems:
+ * gai_strerror() has some problems:
*
* 1) on Windows, Microsoft explicitly says it's not thread-safe;
* 2) on UN*X, the Single UNIX Specification doesn't say it *is*
@@ -527,52 +913,52 @@ get_gai_errstring(char *errbuf, int errbuflen, const char *prefix, int err,
char hostport[PCAP_ERRBUF_SIZE];
if (hostname != NULL && portname != NULL)
- pcap_snprintf(hostport, PCAP_ERRBUF_SIZE, "%s:%s",
+ snprintf(hostport, PCAP_ERRBUF_SIZE, "host and port %s:%s",
hostname, portname);
else if (hostname != NULL)
- pcap_snprintf(hostport, PCAP_ERRBUF_SIZE, "%s",
+ snprintf(hostport, PCAP_ERRBUF_SIZE, "host %s",
hostname);
else if (portname != NULL)
- pcap_snprintf(hostport, PCAP_ERRBUF_SIZE, ":%s",
+ snprintf(hostport, PCAP_ERRBUF_SIZE, "port %s",
portname);
else
- pcap_snprintf(hostport, PCAP_ERRBUF_SIZE, "<no host or port!>");
+ snprintf(hostport, PCAP_ERRBUF_SIZE, "<no host or port!>");
switch (err)
{
#ifdef EAI_ADDRFAMILY
case EAI_ADDRFAMILY:
- pcap_snprintf(errbuf, errbuflen,
+ snprintf(errbuf, errbuflen,
"%sAddress family for %s not supported",
prefix, hostport);
break;
#endif
case EAI_AGAIN:
- pcap_snprintf(errbuf, errbuflen,
+ snprintf(errbuf, errbuflen,
"%s%s could not be resolved at this time",
prefix, hostport);
break;
case EAI_BADFLAGS:
- pcap_snprintf(errbuf, errbuflen,
+ snprintf(errbuf, errbuflen,
"%sThe ai_flags parameter for looking up %s had an invalid value",
prefix, hostport);
break;
case EAI_FAIL:
- pcap_snprintf(errbuf, errbuflen,
+ snprintf(errbuf, errbuflen,
"%sA non-recoverable error occurred when attempting to resolve %s",
prefix, hostport);
break;
case EAI_FAMILY:
- pcap_snprintf(errbuf, errbuflen,
+ snprintf(errbuf, errbuflen,
"%sThe address family for looking up %s was not recognized",
prefix, hostport);
break;
case EAI_MEMORY:
- pcap_snprintf(errbuf, errbuflen,
+ snprintf(errbuf, errbuflen,
"%sOut of memory trying to allocate storage when looking up %s",
prefix, hostport);
break;
@@ -589,26 +975,26 @@ get_gai_errstring(char *errbuf, int errbuflen, const char *prefix, int err,
*/
#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
case EAI_NODATA:
- pcap_snprintf(errbuf, errbuflen,
+ snprintf(errbuf, errbuflen,
"%sNo address associated with %s",
prefix, hostport);
break;
#endif
case EAI_NONAME:
- pcap_snprintf(errbuf, errbuflen,
- "%sThe host name %s couldn't be resolved",
+ snprintf(errbuf, errbuflen,
+ "%sThe %s couldn't be resolved",
prefix, hostport);
break;
case EAI_SERVICE:
- pcap_snprintf(errbuf, errbuflen,
+ snprintf(errbuf, errbuflen,
"%sThe service value specified when looking up %s as not recognized for the socket type",
prefix, hostport);
break;
case EAI_SOCKTYPE:
- pcap_snprintf(errbuf, errbuflen,
+ snprintf(errbuf, errbuflen,
"%sThe socket type specified when looking up %s as not recognized",
prefix, hostport);
break;
@@ -618,15 +1004,15 @@ get_gai_errstring(char *errbuf, int errbuflen, const char *prefix, int err,
/*
* Assumed to be UN*X.
*/
- pcap_snprintf(errbuf, errbuflen,
- "%sAn error occurred when looking up %s: %s",
- prefix, hostport, pcap_strerror(errno));
+ pcap_fmt_errmsg_for_errno(errbuf, errbuflen, errno,
+ "%sAn error occurred when looking up %s",
+ prefix, hostport);
break;
#endif
#ifdef EAI_BADHINTS
case EAI_BADHINTS:
- pcap_snprintf(errbuf, errbuflen,
+ snprintf(errbuf, errbuflen,
"%sInvalid value for hints when looking up %s",
prefix, hostport);
break;
@@ -634,7 +1020,7 @@ get_gai_errstring(char *errbuf, int errbuflen, const char *prefix, int err,
#ifdef EAI_PROTOCOL
case EAI_PROTOCOL:
- pcap_snprintf(errbuf, errbuflen,
+ snprintf(errbuf, errbuflen,
"%sResolved protocol when looking up %s is unknown",
prefix, hostport);
break;
@@ -642,14 +1028,14 @@ get_gai_errstring(char *errbuf, int errbuflen, const char *prefix, int err,
#ifdef EAI_OVERFLOW
case EAI_OVERFLOW:
- pcap_snprintf(errbuf, errbuflen,
+ snprintf(errbuf, errbuflen,
"%sArgument buffer overflow when looking up %s",
prefix, hostport);
break;
#endif
default:
- pcap_snprintf(errbuf, errbuflen,
+ snprintf(errbuf, errbuflen,
"%sgetaddrinfo() error %d when looking up %s",
prefix, err, hostport);
break;
@@ -699,13 +1085,58 @@ int sock_initaddress(const char *host, const char *port,
{
int retval;
- retval = getaddrinfo(host, port, hints, addrinfo);
+ /*
+ * We allow both the host and port to be null, but getaddrinfo()
+ * is not guaranteed to do so; to handle that, if port is null,
+ * we provide "0" as the port number.
+ *
+ * This results in better error messages from get_gai_errstring(),
+ * as those messages won't talk about a problem with the port if
+ * no port was specified.
+ */
+ retval = getaddrinfo(host, port == NULL ? "0" : port, hints, addrinfo);
if (retval != 0)
{
if (errbuf)
{
- get_gai_errstring(errbuf, errbuflen, "", retval,
- host, port);
+ if (host != NULL && port != NULL) {
+ /*
+ * Try with just a host, to distinguish
+ * between "host is bad" and "port is
+ * bad".
+ */
+ int try_retval;
+
+ try_retval = getaddrinfo(host, NULL, hints,
+ addrinfo);
+ if (try_retval == 0) {
+ /*
+ * Worked with just the host,
+ * so assume the problem is
+ * with the port.
+ *
+ * Free up the address info first.
+ */
+ freeaddrinfo(*addrinfo);
+ get_gai_errstring(errbuf, errbuflen,
+ "", retval, NULL, port);
+ } else {
+ /*
+ * Didn't work with just the host,
+ * so assume the problem is
+ * with the host.
+ */
+ get_gai_errstring(errbuf, errbuflen,
+ "", retval, host, NULL);
+ }
+ } else {
+ /*
+ * Either the host or port was null, so
+ * there's nothing to determine.
+ */
+ get_gai_errstring(errbuf, errbuflen, "",
+ retval, host, port);
+ }
}
return -1;
}
@@ -726,7 +1157,7 @@ int sock_initaddress(const char *host, const char *port,
((*addrinfo)->ai_family != PF_INET6))
{
if (errbuf)
- pcap_snprintf(errbuf, errbuflen, "getaddrinfo(): socket type not supported");
+ snprintf(errbuf, errbuflen, "getaddrinfo(): socket type not supported");
freeaddrinfo(*addrinfo);
*addrinfo = NULL;
return -1;
@@ -739,7 +1170,7 @@ int sock_initaddress(const char *host, const char *port,
(sock_ismcastaddr((*addrinfo)->ai_addr) == 0))
{
if (errbuf)
- pcap_snprintf(errbuf, errbuflen, "getaddrinfo(): multicast addresses are not valid when using TCP streams");
+ snprintf(errbuf, errbuflen, "getaddrinfo(): multicast addresses are not valid when using TCP streams");
freeaddrinfo(*addrinfo);
*addrinfo = NULL;
return -1;
@@ -775,7 +1206,7 @@ int sock_initaddress(const char *host, const char *port,
* '-2' if we got one of those errors.
* For errors, an error message is returned in the 'errbuf' variable.
*/
-int sock_send(SOCKET sock, const char *buffer, size_t size,
+int sock_send(SOCKET sock, SSL *ssl _U_NOSSL_, const char *buffer, size_t size,
char *errbuf, int errbuflen)
{
int remaining;
@@ -785,7 +1216,7 @@ int sock_send(SOCKET sock, const char *buffer, size_t size,
{
if (errbuf)
{
- pcap_snprintf(errbuf, errbuflen,
+ snprintf(errbuf, errbuflen,
"Can't send more than %u bytes with sock_send",
INT_MAX);
}
@@ -794,6 +1225,13 @@ int sock_send(SOCKET sock, const char *buffer, size_t size,
remaining = (int)size;
do {
+#ifdef HAVE_OPENSSL
+ if (ssl) return ssl_send(ssl, buffer, remaining, errbuf, errbuflen);
+#endif
+
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ nsent = remaining;
+#else
#ifdef MSG_NOSIGNAL
/*
* Send with MSG_NOSIGNAL, so that we don't get SIGPIPE
@@ -805,6 +1243,7 @@ int sock_send(SOCKET sock, const char *buffer, size_t size,
#else
nsent = send(sock, buffer, remaining, 0);
#endif
+#endif //FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if (nsent == -1)
{
@@ -828,7 +1267,8 @@ int sock_send(SOCKET sock, const char *buffer, size_t size,
*/
return -2;
}
- sock_fmterror("send()", errcode, errbuf, errbuflen);
+ sock_fmterrmsg(errbuf, errbuflen, errcode,
+ "send() failed");
#else
errcode = errno;
if (errcode == ECONNRESET || errcode == EPIPE)
@@ -840,7 +1280,8 @@ int sock_send(SOCKET sock, const char *buffer, size_t size,
*/
return -2;
}
- sock_fmterror("send()", errcode, errbuf, errbuflen);
+ sock_fmterrmsg(errbuf, errbuflen, errcode,
+ "send() failed");
#endif
return -1;
}
@@ -853,11 +1294,11 @@ int sock_send(SOCKET sock, const char *buffer, size_t size,
}
/*
- * \brief It copies the amount of data contained into 'buffer' into 'tempbuf'.
+ * \brief It copies the amount of data contained in 'data' into 'outbuf'.
* and it checks for buffer overflows.
*
- * This function basically copies 'size' bytes of data contained into 'buffer'
- * into 'tempbuf', starting at offset 'offset'. Before that, it checks that the
+ * This function basically copies 'size' bytes of data contained in 'data'
+ * into 'outbuf', starting at offset 'offset'. Before that, it checks that the
* resulting buffer will not be larger than 'totsize'. Finally, it updates
* the 'offset' variable in order to point to the first empty location of the buffer.
*
@@ -866,25 +1307,24 @@ int sock_send(SOCKET sock, const char *buffer, size_t size,
* 'offset' variable. This mode can be useful when the buffer already contains the
* data (maybe because the producer writes directly into the target buffer), so
* only the buffer overflow check has to be made.
- * In this case, both 'buffer' and 'tempbuf' can be NULL values.
+ * In this case, both 'data' and 'outbuf' can be NULL values.
*
* This function is useful in case the userland application does not know immediately
* all the data it has to write into the socket. This function provides a way to create
* the "stream" step by step, appending the new data to the old one. Then, when all the
* data has been bufferized, the application can call the sock_send() function.
*
- * \param buffer: a char pointer to a user-allocated buffer that keeps the data
- * that has to be copied.
+ * \param data: a void pointer to the data that has to be copied.
*
* \param size: number of bytes that have to be copied.
*
- * \param tempbuf: user-allocated buffer (of size 'totsize') in which data
+ * \param outbuf: user-allocated buffer (of size 'totsize') into which data
* has to be copied.
*
- * \param offset: an index into 'tempbuf' which keeps the location of its first
+ * \param offset: an index into 'outbuf' which keeps the location of its first
* empty location.
*
- * \param totsize: total size of the buffer in which data is being copied.
+ * \param totsize: total size of the buffer into which data is being copied.
*
* \param checkonly: '1' if we do not want to copy data into the buffer and we
* want just do a buffer ovreflow control, '0' if data has to be copied as well.
@@ -897,7 +1337,7 @@ int sock_send(SOCKET sock, const char *buffer, size_t size,
* 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. When the function returns, 'tempbuf' will
+ * is returned in the 'errbuf' variable. When the function returns, 'outbuf' will
* have the new string appended, and 'offset' will keep the length of that buffer.
* In case of 'checkonly == 1', data is not copied, but 'offset' is updated in any case.
*
@@ -907,17 +1347,17 @@ int sock_send(SOCKET sock, const char *buffer, size_t size,
* \warning In case of 'checkonly', be carefully to call this function *before* copying
* the data into the buffer. Otherwise, the control about the buffer overflow is useless.
*/
-int sock_bufferize(const char *buffer, int size, char *tempbuf, int *offset, int totsize, int checkonly, char *errbuf, int errbuflen)
+int sock_bufferize(const void *data, int size, char *outbuf, int *offset, int totsize, int checkonly, char *errbuf, int errbuflen)
{
if ((*offset + size) > totsize)
{
if (errbuf)
- pcap_snprintf(errbuf, errbuflen, "Not enough space in the temporary send buffer.");
+ snprintf(errbuf, errbuflen, "Not enough space in the temporary send buffer.");
return -1;
}
if (!checkonly)
- memcpy(tempbuf + (*offset), buffer, size);
+ memcpy(outbuf + (*offset), data, size);
(*offset) += size;
@@ -970,9 +1410,10 @@ int sock_bufferize(const char *buffer, int size, char *tempbuf, int *offset, int
* The error message is returned in the 'errbuf' variable.
*/
-int sock_recv(SOCKET sock, void *buffer, size_t size, int flags,
- char *errbuf, int errbuflen)
+int sock_recv(SOCKET sock, SSL *ssl _U_NOSSL_, void *buffer, size_t size,
+ int flags, char *errbuf, int errbuflen)
{
+ int recv_flags = 0;
char *bufp = buffer;
int remaining;
ssize_t nread;
@@ -985,13 +1426,16 @@ int sock_recv(SOCKET sock, void *buffer, size_t size, int flags,
{
if (errbuf)
{
- pcap_snprintf(errbuf, errbuflen,
+ snprintf(errbuf, errbuflen,
"Can't read more than %u bytes with sock_recv",
INT_MAX);
}
return -1;
}
+ if (flags & SOCK_MSG_PEEK)
+ recv_flags |= MSG_PEEK;
+
bufp = (char *) buffer;
remaining = (int) size;
@@ -1000,7 +1444,22 @@ int sock_recv(SOCKET sock, void *buffer, size_t size, int flags,
* Win32.
*/
for (;;) {
- nread = recv(sock, bufp, remaining, 0);
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ nread = fuzz_recv(bufp, remaining);
+#elif defined(HAVE_OPENSSL)
+ if (ssl)
+ {
+ /*
+ * XXX - what about MSG_PEEK?
+ */
+ nread = ssl_recv(ssl, bufp, remaining, errbuf, errbuflen);
+ if (nread == -2) return -1;
+ }
+ else
+ nread = recv(sock, bufp, remaining, recv_flags);
+#else
+ nread = recv(sock, bufp, remaining, recv_flags);
+#endif
if (nread == -1)
{
@@ -1008,7 +1467,7 @@ int sock_recv(SOCKET sock, void *buffer, size_t size, int flags,
if (errno == EINTR)
return -3;
#endif
- sock_geterror("recv()", errbuf, errbuflen);
+ sock_geterrmsg(errbuf, errbuflen, "recv() failed");
return -1;
}
@@ -1024,7 +1483,7 @@ int sock_recv(SOCKET sock, void *buffer, size_t size, int flags,
*/
if (errbuf)
{
- pcap_snprintf(errbuf, errbuflen,
+ snprintf(errbuf, errbuflen,
"The other host terminated the connection.");
}
return -1;
@@ -1058,7 +1517,7 @@ int sock_recv(SOCKET sock, void *buffer, size_t size, int flags,
*
* Returns the size of the datagram on success or -1 on error.
*/
-int sock_recv_dgram(SOCKET sock, void *buffer, size_t size,
+int sock_recv_dgram(SOCKET sock, SSL *ssl _U_NOSSL_, void *buffer, size_t size,
char *errbuf, int errbuflen)
{
ssize_t nread;
@@ -1075,20 +1534,29 @@ int sock_recv_dgram(SOCKET sock, void *buffer, size_t size,
{
if (errbuf)
{
- pcap_snprintf(errbuf, errbuflen,
+ snprintf(errbuf, errbuflen,
"Can't read more than %u bytes with sock_recv_dgram",
INT_MAX);
}
return -1;
}
+#ifdef HAVE_OPENSSL
+ // TODO: DTLS
+ if (ssl)
+ {
+ snprintf(errbuf, errbuflen, "DTLS not implemented yet");
+ return -1;
+ }
+#endif
+
/*
* 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);
+ nread = recv(sock, buffer, (int)size, 0);
if (nread == SOCKET_ERROR)
{
/*
@@ -1104,7 +1572,8 @@ int sock_recv_dgram(SOCKET sock, void *buffer, size_t size,
* supplied to us, the excess data is discarded,
* and we'll report an error.
*/
- sock_geterror("recv()", errbuf, errbuflen);
+ sock_fmterrmsg(errbuf, errbuflen, sock_geterrcode(),
+ "recv() failed");
return -1;
}
#else /* _WIN32 */
@@ -1132,12 +1601,16 @@ int sock_recv_dgram(SOCKET sock, void *buffer, size_t size,
#ifdef HAVE_STRUCT_MSGHDR_MSG_FLAGS
message.msg_flags = 0;
#endif
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ nread = fuzz_recv(buffer, size);
+#else
nread = recvmsg(sock, &message, 0);
+#endif
if (nread == -1)
{
if (errno == EINTR)
return -3;
- sock_geterror("recv()", errbuf, errbuflen);
+ sock_geterrmsg(errbuf, errbuflen, "recv() failed");
return -1;
}
#ifdef HAVE_STRUCT_MSGHDR_MSG_FLAGS
@@ -1154,7 +1627,7 @@ int sock_recv_dgram(SOCKET sock, void *buffer, size_t 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");
+ snprintf(errbuf, errbuflen, "recv(): Message too long");
return -1;
}
#endif /* HAVE_STRUCT_MSGHDR_MSG_FLAGS */
@@ -1192,7 +1665,7 @@ int sock_recv_dgram(SOCKET sock, void *buffer, size_t size,
* \return '0' if everything is fine, '-1' if some errors occurred.
* The error message is returned in the 'errbuf' variable.
*/
-int sock_discard(SOCKET sock, int size, char *errbuf, int errbuflen)
+int sock_discard(SOCKET sock, SSL *ssl, int size, char *errbuf, int errbuflen)
{
#define TEMP_BUF_SIZE 32768
@@ -1208,7 +1681,7 @@ int sock_discard(SOCKET sock, int size, char *errbuf, int errbuflen)
*/
while (size > TEMP_BUF_SIZE)
{
- if (sock_recv(sock, buffer, TEMP_BUF_SIZE, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1)
+ if (sock_recv(sock, ssl, buffer, TEMP_BUF_SIZE, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1)
return -1;
size -= TEMP_BUF_SIZE;
@@ -1220,7 +1693,7 @@ int sock_discard(SOCKET sock, int size, char *errbuf, int errbuflen)
*/
if (size)
{
- if (sock_recv(sock, buffer, size, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1)
+ if (sock_recv(sock, ssl, buffer, size, SOCK_RECEIVEALL_YES, errbuf, errbuflen) == -1)
return -1;
}
@@ -1273,7 +1746,8 @@ int sock_check_hostlist(char *hostlist, const char *sep, struct sockaddr_storage
temphostlist = strdup(hostlist);
if (temphostlist == NULL)
{
- sock_geterror("sock_check_hostlist(), malloc() failed", errbuf, errbuflen);
+ sock_geterrmsg(errbuf, errbuflen,
+ "sock_check_hostlist(), malloc() failed");
return -2;
}
@@ -1358,7 +1832,7 @@ int sock_check_hostlist(char *hostlist, const char *sep, struct sockaddr_storage
* the host wasn't in the list.
*/
if (errbuf)
- pcap_snprintf(errbuf, errbuflen, "The host is not in the allowed host list. Connection refused.");
+ snprintf(errbuf, errbuflen, "The host is not in the allowed host list. Connection refused.");
return -1;
}
}
@@ -1459,7 +1933,7 @@ int sock_getmyinfo(SOCKET sock, char *address, int addrlen, char *port, int port
if (getsockname(sock, (struct sockaddr *) &mysockaddr, &sockaddrlen) == -1)
{
- sock_geterror("getsockname()", errbuf, errbuflen);
+ sock_geterrmsg(errbuf, errbuflen, "getsockname() failed");
return 0;
}
@@ -1515,7 +1989,7 @@ int sock_getmyinfo(SOCKET sock, char *address, int addrlen, char *port, int port
* and 'port'.
* In any case, the returned strings are '0' terminated.
*/
-int sock_getascii_addrport(const struct sockaddr_storage *sockaddr, char *address, int addrlen, char *port, int portlen, int flags, char *errbuf, int errbuflen)
+int sock_getascii_addrport(const struct sockaddr_storage *sockaddr, char *address, int addrlen, char *port, int portlen, int flags, char *errbuf, size_t errbuflen)
{
socklen_t sockaddrlen;
int retval; /* Variable that keeps the return value; */
@@ -1547,7 +2021,8 @@ int sock_getascii_addrport(const struct sockaddr_storage *sockaddr, char *addres
/* If the user wants to receive an error message */
if (errbuf)
{
- sock_geterror("getnameinfo()", errbuf, errbuflen);
+ sock_geterrmsg(errbuf, errbuflen,
+ "getnameinfo() failed");
errbuf[errbuflen - 1] = 0;
}
@@ -1628,7 +2103,7 @@ int sock_present2network(const char *address, struct sockaddr_storage *sockaddr,
freeaddrinfo(addrinfo);
if (errbuf)
- pcap_snprintf(errbuf, errbuflen, "More than one socket requested; using the first one returned");
+ snprintf(errbuf, errbuflen, "More than one socket requested; using the first one returned");
return -2;
}