diff options
author | Philip Paeps <philip@FreeBSD.org> | 2019-10-06 04:26:37 +0000 |
---|---|---|
committer | Philip Paeps <philip@FreeBSD.org> | 2019-10-06 04:26:37 +0000 |
commit | 30a580a870fabfea51e4b970c488e58bd1515ce4 (patch) | |
tree | cf18fcdc7c1818418ee5fc375ca4ba2094583a41 /sockutils.c | |
parent | d109bf9e4b609b5a0626b433e56db4a47dc530bb (diff) | |
download | src-30a580a870fabfea51e4b970c488e58bd1515ce4.tar.gz src-30a580a870fabfea51e4b970c488e58bd1515ce4.zip |
Notes
Diffstat (limited to 'sockutils.c')
-rw-r--r-- | sockutils.c | 330 |
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; } |