aboutsummaryrefslogtreecommitdiff
path: root/sockutils.c
diff options
context:
space:
mode:
authorPhilip Paeps <philip@FreeBSD.org>2019-10-06 04:26:37 +0000
committerPhilip Paeps <philip@FreeBSD.org>2019-10-06 04:26:37 +0000
commit30a580a870fabfea51e4b970c488e58bd1515ce4 (patch)
treecf18fcdc7c1818418ee5fc375ca4ba2094583a41 /sockutils.c
parentd109bf9e4b609b5a0626b433e56db4a47dc530bb (diff)
downloadsrc-30a580a870fabfea51e4b970c488e58bd1515ce4.tar.gz
src-30a580a870fabfea51e4b970c488e58bd1515ce4.zip
Notes
Diffstat (limited to 'sockutils.c')
-rw-r--r--sockutils.c330
1 files changed, 239 insertions, 91 deletions
diff --git a/sockutils.c b/sockutils.c
index 079c49db2cac..d3e94649d9d4 100644
--- a/sockutils.c
+++ b/sockutils.c
@@ -100,7 +100,7 @@
* 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).
+ * 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
@@ -130,44 +130,15 @@ static int sock_ismcastaddr(const struct sockaddr *saddr);
*/
void sock_fmterror(const char *caller, int errcode, char *errbuf, int errbuflen)
{
-#ifdef _WIN32
- int retval;
- 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;
- retval = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
- FORMAT_MESSAGE_MAX_WIDTH_MASK,
- NULL, errcode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- message, sizeof(message) / sizeof(TCHAR), NULL);
-
- if (retval == 0)
- {
- if ((caller) && (*caller))
- pcap_snprintf(errbuf, errbuflen, "%sUnable to get the exact error message", caller);
- else
- pcap_snprintf(errbuf, errbuflen, "Unable to get the exact error message");
- }
- else
- {
- if ((caller) && (*caller))
- pcap_snprintf(errbuf, errbuflen, "%s%s (code %d)", caller, message, errcode);
- else
- pcap_snprintf(errbuf, errbuflen, "%s (code %d)", message, errcode);
- }
+#ifdef _WIN32
+ pcap_fmt_errmsg_for_win32_err(errbuf, errbuflen, errcode,
+ "%s", caller);
#else
- char *message;
-
- if (errbuf == NULL)
- return;
-
- message = strerror(errcode);
-
- if ((caller) && (*caller))
- pcap_snprintf(errbuf, errbuflen, "%s%s (code %d)", caller, message, errcode);
- else
- pcap_snprintf(errbuf, errbuflen, "%s (code %d)", message, errcode);
+ pcap_fmt_errmsg_for_errno(errbuf, errbuflen, errcode,
+ "%s", caller);
#endif
}
@@ -180,7 +151,7 @@ void sock_fmterror(const char *caller, int errcode, char *errbuf, int errbuflen)
*
* \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.
+ * 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.
@@ -194,31 +165,30 @@ void sock_fmterror(const char *caller, int errcode, char *errbuf, int errbuflen)
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
}
/*
- * \brief It initializes sockets.
+ * \brief This function initializes the socket mechanism if it hasn't
+ * already been initialized or reinitializes it after it has been
+ * cleaned up.
*
- * This function is pretty useless on UNIX, since socket initialization is not required.
- * However it is required on Win32. In UNIX, this function appears to be completely empty.
+ * On UN*Xes, it doesn't need to do anything; on Windows, it needs to
+ * initialize Winsock.
*
- * \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 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 no error message is supplied.
*
- * \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.
+ * \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 some errors occurred. The
+ * error message is returned in the buffer pointed to by 'errbuf' variable.
*/
#ifdef _WIN32
int sock_init(char *errbuf, int errbuflen)
@@ -240,18 +210,24 @@ int sock_init(char *errbuf, int errbuflen)
}
sockcount++;
+ return 0;
+}
#else
int sock_init(char *errbuf _U_, int errbuflen _U_)
{
-#endif
+ /*
+ * Nothing to do on UN*Xes.
+ */
return 0;
}
+#endif
/*
- * \brief It deallocates sockets.
+ * \brief This function cleans up the socket mechanism if we have no
+ * sockets left open.
*
- * This function is pretty useless on UNIX, since socket deallocation is not required.
- * However it is required on Win32. In UNIX, this function appears to be completely empty.
+ * On UN*Xes, it doesn't need to do anything; on Windows, it needs
+ * to clean up Winsock.
*
* \return No error values.
*/
@@ -327,7 +303,7 @@ SOCKET sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf,
sock = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
if (sock == INVALID_SOCKET)
{
- sock_geterror("socket(): ", errbuf, errbuflen);
+ sock_geterror("socket()", errbuf, errbuflen);
return INVALID_SOCKET;
}
@@ -350,6 +326,16 @@ SOCKET sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf,
/* This is a server socket */
if (server)
{
+ /*
+ * 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;
+ (void)setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&optval, sizeof (optval));
+
#if defined(IPV6_V6ONLY) || defined(IPV6_BINDV6ONLY)
/*
* Force the use of IPv6-only addresses.
@@ -399,7 +385,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_geterror("bind()", errbuf, errbuflen);
closesocket(sock);
return INVALID_SOCKET;
}
@@ -407,7 +393,7 @@ 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_geterror("listen()", errbuf, errbuflen);
closesocket(sock);
return INVALID_SOCKET;
}
@@ -444,13 +430,14 @@ SOCKET sock_open(struct addrinfo *addrinfo, int server, int nconn, char *errbuf,
* We have to retrieve the error message before any other socket call completes, otherwise
* the error message is lost
*/
- sock_geterror(NULL, SocketErrorMessage, sizeof(SocketErrorMessage));
+ sock_geterror("Connect to socket failed",
+ SocketErrorMessage, sizeof(SocketErrorMessage));
/* 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));
pcap_snprintf(errbufptr, bufspaceleft,
- "Is the server properly installed on %s? connect() failed: %s", TmpBuffer, SocketErrorMessage);
+ "Is the server properly installed on %s? %s", TmpBuffer, SocketErrorMessage);
/* In case more then one 'connect' fails, we manage to keep all the error messages */
msglen = strlen(errbufptr);
@@ -508,7 +495,7 @@ int sock_close(SOCKET sock, char *errbuf, int errbuflen)
*/
if (shutdown(sock, SHUT_WR))
{
- sock_geterror("shutdown(): ", errbuf, errbuflen);
+ sock_geterror("shutdown()", errbuf, errbuflen);
/* close the socket anyway */
closesocket(sock);
return -1;
@@ -519,6 +506,157 @@ int sock_close(SOCKET sock, char *errbuf, int errbuflen)
}
/*
+ * gai_errstring() 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*
+ * thread-safe, so an implementation might use a static buffer
+ * for unknown error codes;
+ * 3) the error message for the most likely error, EAI_NONAME, is
+ * truly horrible on several platforms ("nodename nor servname
+ * provided, or not known"? It's typically going to be "not
+ * known", not "oopsie, I passed null pointers for the host name
+ * and service name", not to mention they forgot the "neither");
+ *
+ * so we roll our own.
+ */
+static void
+get_gai_errstring(char *errbuf, int errbuflen, const char *prefix, int err,
+ const char *hostname, const char *portname)
+{
+ char hostport[PCAP_ERRBUF_SIZE];
+
+ if (hostname != NULL && portname != NULL)
+ pcap_snprintf(hostport, PCAP_ERRBUF_SIZE, "%s:%s",
+ hostname, portname);
+ else if (hostname != NULL)
+ pcap_snprintf(hostport, PCAP_ERRBUF_SIZE, "%s",
+ hostname);
+ else if (portname != NULL)
+ pcap_snprintf(hostport, PCAP_ERRBUF_SIZE, ":%s",
+ portname);
+ else
+ pcap_snprintf(hostport, PCAP_ERRBUF_SIZE, "<no host or port!>");
+ switch (err)
+ {
+#ifdef EAI_ADDRFAMILY
+ case EAI_ADDRFAMILY:
+ pcap_snprintf(errbuf, errbuflen,
+ "%sAddress family for %s not supported",
+ prefix, hostport);
+ break;
+#endif
+
+ case EAI_AGAIN:
+ pcap_snprintf(errbuf, errbuflen,
+ "%s%s could not be resolved at this time",
+ prefix, hostport);
+ break;
+
+ case EAI_BADFLAGS:
+ pcap_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,
+ "%sA non-recoverable error occurred when attempting to resolve %s",
+ prefix, hostport);
+ break;
+
+ case EAI_FAMILY:
+ pcap_snprintf(errbuf, errbuflen,
+ "%sThe address family for looking up %s was not recognized",
+ prefix, hostport);
+ break;
+
+ case EAI_MEMORY:
+ pcap_snprintf(errbuf, errbuflen,
+ "%sOut of memory trying to allocate storage when looking up %s",
+ prefix, hostport);
+ break;
+
+ /*
+ * RFC 2553 had both EAI_NODATA and EAI_NONAME.
+ *
+ * RFC 3493 has only EAI_NONAME.
+ *
+ * Some implementations define EAI_NODATA and EAI_NONAME
+ * to the same value, others don't. If EAI_NODATA is
+ * defined and isn't the same as EAI_NONAME, we handle
+ * EAI_NODATA.
+ */
+#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
+ case EAI_NODATA:
+ pcap_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",
+ prefix, hostport);
+ break;
+
+ case EAI_SERVICE:
+ pcap_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,
+ "%sThe socket type specified when looking up %s as not recognized",
+ prefix, hostport);
+ break;
+
+#ifdef EAI_SYSTEM
+ case EAI_SYSTEM:
+ /*
+ * Assumed to be UN*X.
+ */
+ pcap_snprintf(errbuf, errbuflen,
+ "%sAn error occurred when looking up %s: %s",
+ prefix, hostport, pcap_strerror(errno));
+ break;
+#endif
+
+#ifdef EAI_BADHINTS
+ case EAI_BADHINTS:
+ pcap_snprintf(errbuf, errbuflen,
+ "%sInvalid value for hints when looking up %s",
+ prefix, hostport);
+ break;
+#endif
+
+#ifdef EAI_PROTOCOL
+ case EAI_PROTOCOL:
+ pcap_snprintf(errbuf, errbuflen,
+ "%sResolved protocol when looking up %s is unknown",
+ prefix, hostport);
+ break;
+#endif
+
+#ifdef EAI_OVERFLOW
+ case EAI_OVERFLOW:
+ pcap_snprintf(errbuf, errbuflen,
+ "%sArgument buffer overflow when looking up %s",
+ prefix, hostport);
+ break;
+#endif
+
+ default:
+ pcap_snprintf(errbuf, errbuflen,
+ "%sgetaddrinfo() error %d when looking up %s",
+ prefix, err, hostport);
+ break;
+ }
+}
+
+/*
* \brief Checks that the address, port and flags given are valids and it returns an 'addrinfo' structure.
*
* This function basically calls the getaddrinfo() calls, and it performs a set of sanity checks
@@ -564,17 +702,10 @@ int sock_initaddress(const char *host, const char *port,
retval = getaddrinfo(host, port, hints, addrinfo);
if (retval != 0)
{
- /*
- * if the getaddrinfo() fails, you have to use gai_strerror(), instead of using the standard
- * error routines (errno) in UNIX; Winsock suggests using the GetLastError() instead.
- */
if (errbuf)
{
-#ifdef _WIN32
- sock_geterror("getaddrinfo(): ", errbuf, errbuflen);
-#else
- pcap_snprintf(errbuf, errbuflen, "getaddrinfo() %s", gai_strerror(retval));
-#endif
+ get_gai_errstring(errbuf, errbuflen, "", retval,
+ host, port);
}
return -1;
}
@@ -655,7 +786,7 @@ int sock_send(SOCKET sock, const char *buffer, size_t size,
if (errbuf)
{
pcap_snprintf(errbuf, errbuflen,
- "Can't send more than %u bytes with sock_recv",
+ "Can't send more than %u bytes with sock_send",
INT_MAX);
}
return -1;
@@ -697,7 +828,7 @@ int sock_send(SOCKET sock, const char *buffer, size_t size,
*/
return -2;
}
- sock_fmterror("send(): ", errcode, errbuf, errbuflen);
+ sock_fmterror("send()", errcode, errbuf, errbuflen);
#else
errcode = errno;
if (errcode == ECONNRESET || errcode == EPIPE)
@@ -709,7 +840,7 @@ int sock_send(SOCKET sock, const char *buffer, size_t size,
*/
return -2;
}
- sock_fmterror("send(): ", errcode, errbuf, errbuflen);
+ sock_fmterror("send()", errcode, errbuf, errbuflen);
#endif
return -1;
}
@@ -848,7 +979,6 @@ int sock_recv(SOCKET sock, void *buffer, size_t size, int flags,
if (size == 0)
{
- SOCK_DEBUG_MESSAGE("I have been requested to read zero bytes");
return 0;
}
if (size > INT_MAX)
@@ -878,7 +1008,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_geterror("recv()", errbuf, errbuflen);
return -1;
}
@@ -939,7 +1069,6 @@ int sock_recv_dgram(SOCKET sock, void *buffer, size_t size,
if (size == 0)
{
- SOCK_DEBUG_MESSAGE("I have been requested to read zero bytes");
return 0;
}
if (size > INT_MAX)
@@ -975,7 +1104,7 @@ 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_geterror("recv()", errbuf, errbuflen);
return -1;
}
#else /* _WIN32 */
@@ -1008,7 +1137,7 @@ int sock_recv_dgram(SOCKET sock, void *buffer, size_t size,
{
if (errno == EINTR)
return -3;
- sock_geterror("recv(): ", errbuf, errbuflen);
+ sock_geterror("recv()", errbuf, errbuflen);
return -1;
}
#ifdef HAVE_STRUCT_MSGHDR_MSG_FLAGS
@@ -1095,8 +1224,6 @@ int sock_discard(SOCKET sock, int size, char *errbuf, int errbuflen)
return -1;
}
- SOCK_DEBUG_MESSAGE("I'm currently discarding data\n");
-
return 0;
}
@@ -1137,6 +1264,7 @@ int sock_check_hostlist(char *hostlist, const char *sep, struct sockaddr_storage
struct addrinfo *addrinfo, *ai_next;
char *temphostlist;
char *lasts;
+ int getaddrinfo_failed = 0;
/*
* The problem is that strtok modifies the original variable by putting '0' at the end of each token
@@ -1164,13 +1292,19 @@ int sock_check_hostlist(char *hostlist, const char *sep, struct sockaddr_storage
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
- retval = getaddrinfo(token, "0", &hints, &addrinfo);
+ retval = getaddrinfo(token, NULL, &hints, &addrinfo);
if (retval != 0)
{
if (errbuf)
- pcap_snprintf(errbuf, errbuflen, "getaddrinfo() %s", gai_strerror(retval));
+ get_gai_errstring(errbuf, errbuflen,
+ "Allowed host list error: ",
+ retval, token, NULL);
- SOCK_DEBUG_MESSAGE(errbuf);
+ /*
+ * Note that at least one call to getaddrinfo()
+ * failed.
+ */
+ getaddrinfo_failed = 1;
/* Get next token */
token = pcap_strtok_r(NULL, sep, &lasts);
@@ -1208,11 +1342,25 @@ int sock_check_hostlist(char *hostlist, const char *sep, struct sockaddr_storage
addrinfo = NULL;
}
- if (errbuf)
- pcap_snprintf(errbuf, errbuflen, "The host is not in the allowed host list. Connection refused.");
-
free(temphostlist);
- return -1;
+
+ if (getaddrinfo_failed) {
+ /*
+ * At least one getaddrinfo() call failed;
+ * treat that as an error, so rpcapd knows
+ * that it should log it locally as well
+ * as telling the client about it.
+ */
+ return -2;
+ } else {
+ /*
+ * All getaddrinfo() calls succeeded, but
+ * the host wasn't in the list.
+ */
+ if (errbuf)
+ pcap_snprintf(errbuf, errbuflen, "The host is not in the allowed host list. Connection refused.");
+ return -1;
+ }
}
/* No hostlist, so we have to return 'empty list' */
@@ -1311,7 +1459,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_geterror("getsockname()", errbuf, errbuflen);
return 0;
}
@@ -1389,7 +1537,7 @@ int sock_getascii_addrport(const struct sockaddr_storage *sockaddr, char *addres
(memcmp(&((struct sockaddr_in6 *) sockaddr)->sin6_addr, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", sizeof(struct in6_addr)) == 0))
{
if (address)
- strlcpy(address, SOCKET_NAME_NULL_DAD, addrlen);
+ pcap_strlcpy(address, SOCKET_NAME_NULL_DAD, addrlen);
return retval;
}
}
@@ -1399,19 +1547,19 @@ 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_geterror("getnameinfo()", errbuf, errbuflen);
errbuf[errbuflen - 1] = 0;
}
if (address)
{
- strlcpy(address, SOCKET_NO_NAME_AVAILABLE, addrlen);
+ pcap_strlcpy(address, SOCKET_NO_NAME_AVAILABLE, addrlen);
address[addrlen - 1] = 0;
}
if (port)
{
- strlcpy(port, SOCKET_NO_PORT_AVAILABLE, portlen);
+ pcap_strlcpy(port, SOCKET_NO_PORT_AVAILABLE, portlen);
port[portlen - 1] = 0;
}