]> The Tcpdump Group git mirrors - libpcap/commitdiff
rpcap: fix sock_open() issues.
authorGuy Harris <[email protected]>
Mon, 1 Aug 2022 21:25:42 +0000 (14:25 -0700)
committerGuy Harris <[email protected]>
Mon, 1 Aug 2022 21:25:42 +0000 (14:25 -0700)
When connecting as a client, don't create one socket, using the address
family of the first entry in the address list, and use that for all
entries; on most if not all platforms, an AF_INET socket can't be used
to connect to an IPv6 address and an AF_INET6 socket can't be used to
connect to an IPv4 address.  Instead, construct a table of the entries
in the address list, sort it by address family, and cycle through the
entries.  If there is no socket yet, create it based on the current
entry's address family; if there is a socket, but it's for a different
address family than the current entry's address family, close it and
open a new one.

If connecting fails for all addresses, don't just construct a long
barely-readable error message consisting of the full errors for each
failure.  Instead, construct one that, for each error code, has a list
of the addresses that got that error code; if all the failures had the
same error code, just show the host name, not the complete list of
addresses.

Clean up some error handling routines - fix names, allow some to take a
printf-style argument list, etc..

fmtutils.c
fmtutils.h
pcap-rpcap.c
rpcapd/daemon.c
rpcapd/rpcapd.c
sockutils.c
sockutils.h

index 5c7ddadfe1918df413acbe658d534e3476e23e86..2d3576244107602913c853c2868608b698848611 100644 (file)
@@ -270,13 +270,21 @@ pcap_fmt_errmsg_for_errno(char *errbuf, size_t errbuflen, int errnum,
     const char *fmt, ...)
 {
        va_list ap;
+
+       va_start(ap, fmt);
+       pcap_vfmt_errmsg_for_errno(errbuf, errbuflen, errnum, fmt, ap);
+       va_end(ap);
+}
+
+void
+pcap_vfmt_errmsg_for_errno(char *errbuf, size_t errbuflen, int errnum,
+    const char *fmt, va_list ap)
+{
        size_t msglen;
        char *p;
        size_t errbuflen_remaining;
 
-       va_start(ap, fmt);
-       vsnprintf(errbuf, errbuflen, fmt, ap);
-       va_end(ap);
+       (void)vsnprintf(errbuf, errbuflen, fmt, ap);
        msglen = strlen(errbuf);
 
        /*
@@ -378,6 +386,16 @@ pcap_fmt_errmsg_for_win32_err(char *errbuf, size_t errbuflen, DWORD errnum,
     const char *fmt, ...)
 {
        va_list ap;
+
+       va_start(ap, fmt);
+       pcap_vfmt_errmsg_for_win32_err(errbuf, errbuflen, errnum, fmt, ap);
+       va_end(ap);
+}
+
+void
+pcap_vfmt_errmsg_for_win32_err(char *errbuf, size_t errbuflen, DWORD errnum,
+    const char *fmt, va_list ap)
+{
        size_t msglen;
        char *p;
        size_t errbuflen_remaining;
@@ -385,9 +403,7 @@ pcap_fmt_errmsg_for_win32_err(char *errbuf, size_t errbuflen, DWORD errnum,
        wchar_t utf_16_errbuf[PCAP_ERRBUF_SIZE];
        size_t utf_8_len;
 
-       va_start(ap, fmt);
        vsnprintf(errbuf, errbuflen, fmt, ap);
-       va_end(ap);
        msglen = strlen(errbuf);
 
        /*
index ba0f66ca06846bbed90514b30022d63c76619762..4fa344865205911753ffbb0438f2f51ee1ed2580 100644 (file)
@@ -34,6 +34,8 @@
 #ifndef fmtutils_h
 #define        fmtutils_h
 
+#include <stdarg.h>    /* we declare varargs functions */
+
 #include "pcap/funcattrs.h"
 
 #ifdef __cplusplus
@@ -44,10 +46,14 @@ void        pcap_fmt_set_encoding(unsigned int);
 
 void   pcap_fmt_errmsg_for_errno(char *, size_t, int,
     PCAP_FORMAT_STRING(const char *), ...) PCAP_PRINTFLIKE(4, 5);
+void   pcap_vfmt_errmsg_for_errno(char *, size_t, int,
+    PCAP_FORMAT_STRING(const char *), va_list) PCAP_PRINTFLIKE(4, 0);
 
 #ifdef _WIN32
 void   pcap_fmt_errmsg_for_win32_err(char *, size_t, DWORD,
     PCAP_FORMAT_STRING(const char *), ...) PCAP_PRINTFLIKE(4, 5);
+void   pcap_vfmt_errmsg_for_win32_err(char *, size_t, DWORD,
+    PCAP_FORMAT_STRING(const char *), va_list) PCAP_PRINTFLIKE(4, 0);
 #endif
 
 #ifdef __cplusplus
index 5978de802f1bc6d41826787240213903add67e03..5b45b829bf0d01a76c6b920622227a030a1ef0d3 100644 (file)
@@ -434,7 +434,8 @@ static int pcap_read_nocb_remote(pcap_t *p, struct pcap_pkthdr *pkt_header, u_ch
                                return 0;
                        }
 #endif
-                       sock_geterror("select()", p->errbuf, PCAP_ERRBUF_SIZE);
+                       sock_geterrmsg(p->errbuf, PCAP_ERRBUF_SIZE,
+                           "select() failed");
                        return -1;
                }
        }
@@ -1140,7 +1141,8 @@ static int pcap_startcapture_remote(pcap_t *fp)
        saddrlen = sizeof(struct sockaddr_storage);
        if (getpeername(pr->rmt_sockctrl, (struct sockaddr *) &saddr, &saddrlen) == -1)
        {
-               sock_geterror("getsockname()", fp->errbuf, PCAP_ERRBUF_SIZE);
+               sock_geterrmsg(fp->errbuf, PCAP_ERRBUF_SIZE,
+                   "getsockname() failed");
                goto error_nodiscard;
        }
        ai_family = ((struct sockaddr_storage *) &saddr)->ss_family;
@@ -1149,7 +1151,8 @@ static int pcap_startcapture_remote(pcap_t *fp)
        if (getnameinfo((struct sockaddr *) &saddr, saddrlen, host,
                sizeof(host), NULL, 0, NI_NUMERICHOST))
        {
-               sock_geterror("getnameinfo()", fp->errbuf, PCAP_ERRBUF_SIZE);
+               sock_geterrmsg(fp->errbuf, PCAP_ERRBUF_SIZE,
+                   "getnameinfo() failed");
                goto error_nodiscard;
        }
 
@@ -1175,7 +1178,7 @@ static int pcap_startcapture_remote(pcap_t *fp)
                if (sock_initaddress(NULL, NULL, &hints, &addrinfo, fp->errbuf, PCAP_ERRBUF_SIZE) == -1)
                        goto error_nodiscard;
 
-               if ((sockdata = sock_open(addrinfo, SOCKOPEN_SERVER,
+               if ((sockdata = sock_open(NULL, addrinfo, SOCKOPEN_SERVER,
                        1 /* max 1 connection in queue */, fp->errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
                        goto error_nodiscard;
 
@@ -1187,7 +1190,8 @@ static int pcap_startcapture_remote(pcap_t *fp)
                saddrlen = sizeof(struct sockaddr_storage);
                if (getsockname(sockdata, (struct sockaddr *) &saddr, &saddrlen) == -1)
                {
-                       sock_geterror("getsockname()", fp->errbuf, PCAP_ERRBUF_SIZE);
+                       sock_geterrmsg(fp->errbuf, PCAP_ERRBUF_SIZE,
+                           "getsockname() failed");
                        goto error_nodiscard;
                }
 
@@ -1299,7 +1303,7 @@ static int pcap_startcapture_remote(pcap_t *fp)
                        if (sock_initaddress(host, portstring, &hints, &addrinfo, fp->errbuf, PCAP_ERRBUF_SIZE) == -1)
                                goto error;
 
-                       if ((sockdata = sock_open(addrinfo, SOCKOPEN_CLIENT, 0, fp->errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
+                       if ((sockdata = sock_open(host, addrinfo, SOCKOPEN_CLIENT, 0, fp->errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
                                goto error;
 
                        /* addrinfo is no longer used */
@@ -1317,7 +1321,8 @@ static int pcap_startcapture_remote(pcap_t *fp)
 
                        if (socktemp == INVALID_SOCKET)
                        {
-                               sock_geterror("accept()", fp->errbuf, PCAP_ERRBUF_SIZE);
+                               sock_geterrmsg(fp->errbuf, PCAP_ERRBUF_SIZE,
+                                   "accept() failed");
                                goto error;
                        }
 
@@ -1351,7 +1356,8 @@ static int pcap_startcapture_remote(pcap_t *fp)
        res = getsockopt(sockdata, SOL_SOCKET, SO_RCVBUF, (char *)&sockbufsize, &itemp);
        if (res == -1)
        {
-               sock_geterror("pcap_startcapture_remote(): getsockopt() failed", fp->errbuf, PCAP_ERRBUF_SIZE);
+               sock_geterrmsg(fp->errbuf, PCAP_ERRBUF_SIZE,
+                   "pcap_startcapture_remote(): getsockopt() failed");
                goto error;
        }
 
@@ -1730,14 +1736,16 @@ static int pcap_createfilter_norpcappkt(pcap_t *fp, struct bpf_program *prog)
                saddrlen = sizeof(struct sockaddr_storage);
                if (getpeername(pr->rmt_sockctrl, (struct sockaddr *) &saddr, &saddrlen) == -1)
                {
-                       sock_geterror("getpeername()", fp->errbuf, PCAP_ERRBUF_SIZE);
+                       sock_geterrmsg(fp->errbuf, PCAP_ERRBUF_SIZE,
+                           "getpeername() failed");
                        return -1;
                }
 
                if (getnameinfo((struct sockaddr *) &saddr, saddrlen, peeraddress,
                        sizeof(peeraddress), peerctrlport, sizeof(peerctrlport), NI_NUMERICHOST | NI_NUMERICSERV))
                {
-                       sock_geterror("getnameinfo()", fp->errbuf, PCAP_ERRBUF_SIZE);
+                       sock_geterrmsg(fp->errbuf, PCAP_ERRBUF_SIZE,
+                           "getnameinfo() failed");
                        return -1;
                }
 
@@ -1745,7 +1753,8 @@ static int pcap_createfilter_norpcappkt(pcap_t *fp, struct bpf_program *prog)
                /* Get the name/port of the current host */
                if (getsockname(pr->rmt_sockctrl, (struct sockaddr *) &saddr, &saddrlen) == -1)
                {
-                       sock_geterror("getsockname()", fp->errbuf, PCAP_ERRBUF_SIZE);
+                       sock_geterrmsg(fp->errbuf, PCAP_ERRBUF_SIZE,
+                           "getsockname() failed");
                        return -1;
                }
 
@@ -1753,21 +1762,24 @@ static int pcap_createfilter_norpcappkt(pcap_t *fp, struct bpf_program *prog)
                if (getnameinfo((struct sockaddr *) &saddr, saddrlen, myaddress,
                        sizeof(myaddress), myctrlport, sizeof(myctrlport), NI_NUMERICHOST | NI_NUMERICSERV))
                {
-                       sock_geterror("getnameinfo()", fp->errbuf, PCAP_ERRBUF_SIZE);
+                       sock_geterrmsg(fp->errbuf, PCAP_ERRBUF_SIZE,
+                           "getnameinfo() failed");
                        return -1;
                }
 
                /* Let's now check the data port */
                if (getsockname(pr->rmt_sockdata, (struct sockaddr *) &saddr, &saddrlen) == -1)
                {
-                       sock_geterror("getsockname()", fp->errbuf, PCAP_ERRBUF_SIZE);
+                       sock_geterrmsg(fp->errbuf, PCAP_ERRBUF_SIZE,
+                           "getsockname() failed");
                        return -1;
                }
 
                /* Get the local port the system picked up */
                if (getnameinfo((struct sockaddr *) &saddr, saddrlen, NULL, 0, mydataport, sizeof(mydataport), NI_NUMERICSERV))
                {
-                       sock_geterror("getnameinfo()", fp->errbuf, PCAP_ERRBUF_SIZE);
+                       sock_geterrmsg(fp->errbuf, PCAP_ERRBUF_SIZE,
+                           "getnameinfo() failed");
                        return -1;
                }
 
@@ -2326,7 +2338,7 @@ rpcap_setup_session(const char *source, struct pcap_rmtauth *auth,
                                return -1;
                }
 
-               if ((*sockctrlp = sock_open(addrinfo, SOCKOPEN_CLIENT, 0,
+               if ((*sockctrlp = sock_open(host, addrinfo, SOCKOPEN_CLIENT, 0,
                    errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
                {
                        freeaddrinfo(addrinfo);
@@ -2944,7 +2956,7 @@ SOCKET pcap_remoteact_accept_ex(const char *address, const char *port, const cha
        }
 
 
-       if ((sockmain = sock_open(addrinfo, SOCKOPEN_SERVER, 1, errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
+       if ((sockmain = sock_open(NULL, addrinfo, SOCKOPEN_SERVER, 1, errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
        {
                freeaddrinfo(addrinfo);
                return (SOCKET)-2;
@@ -2963,7 +2975,7 @@ SOCKET pcap_remoteact_accept_ex(const char *address, const char *port, const cha
 
        if (sockctrl == INVALID_SOCKET)
        {
-               sock_geterror("accept()", errbuf, PCAP_ERRBUF_SIZE);
+               sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE, "accept() failed");
                return (SOCKET)-2;
        }
 
@@ -2987,7 +2999,8 @@ SOCKET pcap_remoteact_accept_ex(const char *address, const char *port, const cha
        /* Get the numeric for of the name of the connecting host */
        if (getnameinfo((struct sockaddr *) &from, fromlen, connectinghost, RPCAP_HOSTLIST_SIZE, NULL, 0, NI_NUMERICHOST))
        {
-               sock_geterror("getnameinfo()", errbuf, PCAP_ERRBUF_SIZE);
+               sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE,
+                   "getnameinfo() failed");
                rpcap_senderror(sockctrl, ssl, 0, PCAP_ERR_REMOTEACCEPT, errbuf, NULL);
 #ifdef HAVE_OPENSSL
                if (ssl)
@@ -3253,7 +3266,8 @@ int pcap_remoteact_list(char *hostlist, char sep, int size, char *errbuf)
                        /*      if (getnameinfo( (struct sockaddr *) &temp->host, sizeof (struct sockaddr_storage), hoststr, */
                        /*              RPCAP_HOSTLIST_SIZE, NULL, 0, NI_NUMERICHOST) ) */
                {
-                       /*      sock_geterror("getnameinfo()", errbuf, PCAP_ERRBUF_SIZE); */
+                       /*      sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE, */
+                       /*          "getnameinfo() failed");             */
                        return -1;
                }
 
index f74192ae96c3fd7204a103050f3701b08ba08082..806602e66a48aec45097579060895199b163055f 100644 (file)
@@ -449,7 +449,8 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients,
                if (getpeername(pars.sockctrl, (struct sockaddr *)&from,
                    &fromlen) == -1)
                {
-                       sock_geterror("getpeername()", errmsgbuf, PCAP_ERRBUF_SIZE);
+                       sock_geterrmsg(errmsgbuf, PCAP_ERRBUF_SIZE,
+                           "getpeername() failed");
                        if (rpcap_senderror(pars.sockctrl, pars.ssl, 0, PCAP_ERR_NETW, errmsgbuf, errbuf) == -1)
                                rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
                        goto end;
@@ -523,7 +524,8 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients,
                        retval = select((int)pars.sockctrl + 1, &rfds, NULL, NULL, &tv);
                        if (retval == -1)
                        {
-                               sock_geterror("select() failed", errmsgbuf, PCAP_ERRBUF_SIZE);
+                               sock_geterrmsg(errmsgbuf, PCAP_ERRBUF_SIZE,
+                                   "select() failed");
                                if (rpcap_senderror(pars.sockctrl, pars.ssl, 0, PCAP_ERR_NETW, errmsgbuf, errbuf) == -1)
                                        rpcapd_log(LOGPRIO_ERROR, "Send to client failed: %s", errbuf);
                                goto end;
@@ -763,7 +765,8 @@ daemon_serviceloop(SOCKET sockctrl, int isactive, char *passiveClients,
 #endif
                        if (retval == -1)
                        {
-                               sock_geterror("select() failed", errmsgbuf, PCAP_ERRBUF_SIZE);
+                               sock_geterrmsg(errmsgbuf, PCAP_ERRBUF_SIZE,
+                                   "select() failed");
                                if (rpcap_senderror(pars.sockctrl, pars.ssl,
                                    0, PCAP_ERR_NETW,
                                    errmsgbuf, errbuf) == -1)
@@ -2054,7 +2057,8 @@ daemon_msg_startcap_req(uint8_t ver, struct daemon_slpars *pars, uint32_t plen,
        saddrlen = sizeof(struct sockaddr_storage);
        if (getpeername(pars->sockctrl, (struct sockaddr *) &saddr, &saddrlen) == -1)
        {
-               sock_geterror("getpeername()", errmsgbuf, PCAP_ERRBUF_SIZE);
+               sock_geterrmsg(errmsgbuf, PCAP_ERRBUF_SIZE,
+                   "getpeername() failed");
                goto error;
        }
 
@@ -2071,14 +2075,15 @@ daemon_msg_startcap_req(uint8_t ver, struct daemon_slpars *pars, uint32_t plen,
                if (getnameinfo((struct sockaddr *) &saddr, saddrlen, peerhost,
                                sizeof(peerhost), NULL, 0, NI_NUMERICHOST))
                {
-                       sock_geterror("getnameinfo()", errmsgbuf, PCAP_ERRBUF_SIZE);
+                       sock_geterrmsg(errmsgbuf, PCAP_ERRBUF_SIZE,
+                           "getnameinfo() failed");
                        goto error;
                }
 
                if (sock_initaddress(peerhost, portdata, &hints, &addrinfo, errmsgbuf, PCAP_ERRBUF_SIZE) == -1)
                        goto error;
 
-               if ((session->sockdata = sock_open(addrinfo, SOCKOPEN_CLIENT, 0, errmsgbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
+               if ((session->sockdata = sock_open(peerhost, addrinfo, SOCKOPEN_CLIENT, 0, errmsgbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
                        goto error;
        }
        else            // Data connection is opened by the client toward the server
@@ -2089,14 +2094,15 @@ daemon_msg_startcap_req(uint8_t ver, struct daemon_slpars *pars, uint32_t plen,
                if (sock_initaddress(NULL, NULL, &hints, &addrinfo, errmsgbuf, PCAP_ERRBUF_SIZE) == -1)
                        goto error;
 
-               if ((session->sockdata = sock_open(addrinfo, SOCKOPEN_SERVER, 1 /* max 1 connection in queue */, errmsgbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
+               if ((session->sockdata = sock_open(NULL, addrinfo, SOCKOPEN_SERVER, 1 /* max 1 connection in queue */, errmsgbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
                        goto error;
 
                // get the complete sockaddr structure used in the data connection
                saddrlen = sizeof(struct sockaddr_storage);
                if (getsockname(session->sockdata, (struct sockaddr *) &saddr, &saddrlen) == -1)
                {
-                       sock_geterror("getsockname()", errmsgbuf, PCAP_ERRBUF_SIZE);
+                       sock_geterrmsg(errmsgbuf, PCAP_ERRBUF_SIZE,
+                           "getsockname() failed");
                        goto error;
                }
 
@@ -2104,7 +2110,8 @@ daemon_msg_startcap_req(uint8_t ver, struct daemon_slpars *pars, uint32_t plen,
                if (getnameinfo((struct sockaddr *) &saddr, saddrlen, NULL,
                                0, portdata, sizeof(portdata), NI_NUMERICSERV))
                {
-                       sock_geterror("getnameinfo()", errmsgbuf, PCAP_ERRBUF_SIZE);
+                       sock_geterrmsg(errmsgbuf, PCAP_ERRBUF_SIZE,
+                           "getnameinfo() failed");
                        goto error;
                }
        }
@@ -2172,7 +2179,8 @@ daemon_msg_startcap_req(uint8_t ver, struct daemon_slpars *pars, uint32_t plen,
 
                if (socktemp == INVALID_SOCKET)
                {
-                       sock_geterror("accept()", errbuf, PCAP_ERRBUF_SIZE);
+                       sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE,
+                          "accept() failed");
                        rpcapd_log(LOGPRIO_ERROR, "Accept of data connection failed: %s",
                            errbuf);
                        goto error;
index 19da87f836fc9231b8bac1ff1d564a2098745648..bf52891885dc00c9bb4f89704259eeeb18b6e30e 100644 (file)
@@ -361,8 +361,8 @@ int main(int argc, char *argv[])
        state_change_event = CreateEvent(NULL, FALSE, FALSE, NULL);
        if (state_change_event == NULL)
        {
-               sock_geterror("Can't create state change event", errbuf,
-                   PCAP_ERRBUF_SIZE);
+               sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE,
+                   "Can't create state change event");
                rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
                exit(2);
        }
@@ -372,8 +372,8 @@ int main(int argc, char *argv[])
        //
        if (!SetConsoleCtrlHandler(main_ctrl_event, TRUE))
        {
-               sock_geterror("Can't set control handler", errbuf,
-                   PCAP_ERRBUF_SIZE);
+               sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE,
+                   "Can't set control handler");
                rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
                exit(2);
        }
@@ -427,8 +427,8 @@ int main(int argc, char *argv[])
                sockctrl = dup(0);
                if (sockctrl == -1)
                {
-                       sock_geterror("Can't dup standard input", errbuf,
-                           PCAP_ERRBUF_SIZE);
+                       sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE,
+                           "Can't dup standard input");
                        rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
                        exit(2);
                }
@@ -623,7 +623,7 @@ void main_startup(void)
                        SOCKET sock;
                        struct listen_sock *sock_info;
 
-                       if ((sock = sock_open(tempaddrinfo, SOCKOPEN_SERVER, SOCKET_MAXCONN, errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
+                       if ((sock = sock_open(NULL, tempaddrinfo, SOCKOPEN_SERVER, SOCKET_MAXCONN, errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
                        {
                                switch (tempaddrinfo->ai_family)
                                {
@@ -736,8 +736,8 @@ send_state_change_event(void)
 
        if (!SetEvent(state_change_event))
        {
-               sock_geterror("SetEvent on shutdown event failed", errbuf,
-                   PCAP_ERRBUF_SIZE);
+               sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE,
+                   "SetEvent on shutdown event failed");
                rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
        }
 }
@@ -904,15 +904,15 @@ accept_connections(void)
                event = WSACreateEvent();
                if (event == WSA_INVALID_EVENT)
                {
-                       sock_geterror("Can't create socket event", errbuf,
-                           PCAP_ERRBUF_SIZE);
+                       sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE,
+                           "Can't create socket event");
                        rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
                        exit(2);
                }
                if (WSAEventSelect(sock_info->sock, event, FD_ACCEPT) == SOCKET_ERROR)
                {
-                       sock_geterror("Can't setup socket event", errbuf,
-                           PCAP_ERRBUF_SIZE);
+                       sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE,
+                           "Can't setup socket event");
                        rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
                        exit(2);
                }
@@ -930,8 +930,8 @@ accept_connections(void)
                    WSA_INFINITE, FALSE);
                if (ret == WSA_WAIT_FAILED)
                {
-                       sock_geterror("WSAWaitForMultipleEvents failed", errbuf,
-                           PCAP_ERRBUF_SIZE);
+                       sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE,
+                           "WSAWaitForMultipleEvents failed");
                        rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
                        exit(2);
                }
@@ -970,8 +970,8 @@ accept_connections(void)
                        if (WSAEnumNetworkEvents(sock_info->sock,
                            events[i], &network_events) == SOCKET_ERROR)
                        {
-                               sock_geterror("WSAEnumNetworkEvents failed",
-                                   errbuf, PCAP_ERRBUF_SIZE);
+                               sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE,
+                                   "WSAEnumNetworkEvents failed");
                                rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
                                exit(2);
                        }
@@ -985,10 +985,10 @@ accept_connections(void)
                                        //
                                        // Yes - report it and keep going.
                                        //
-                                       sock_fmterror("Socket error",
+                                       sock_fmterrmsg(errbuf,
+                                           PCAP_ERRBUF_SIZE,
                                            network_events.iErrorCode[FD_ACCEPT_BIT],
-                                           errbuf,
-                                           PCAP_ERRBUF_SIZE);
+                                           "Socket error");
                                        rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
                                        continue;
                                }
@@ -1174,7 +1174,7 @@ accept_connection(SOCKET listen_sock)
 
                // Don't check for errors here, since the error can be due to the fact that the thread
                // has been killed
-               sock_geterror("accept()", errbuf, PCAP_ERRBUF_SIZE);
+               sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE, "accept() failed");
                rpcapd_log(LOGPRIO_ERROR, "Accept of control connection from client failed: %s",
                    errbuf);
                return;
@@ -1198,14 +1198,16 @@ accept_connection(SOCKET listen_sock)
        //
        if (WSAEventSelect(sockctrl, NULL, 0) == SOCKET_ERROR)
        {
-               sock_geterror("WSAEventSelect()", errbuf, PCAP_ERRBUF_SIZE);
+               sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE,
+                   "WSAEventSelect() failed");
                rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
                sock_close(sockctrl, NULL, 0);
                return;
        }
        if (ioctlsocket(sockctrl, FIONBIO, &off) == SOCKET_ERROR)
        {
-               sock_geterror("ioctlsocket(FIONBIO)", errbuf, PCAP_ERRBUF_SIZE);
+               sock_geterrmsg(errbuf, PCAP_ERRBUF_SIZE,
+                   "ioctlsocket(FIONBIO) failed");
                rpcapd_log(LOGPRIO_ERROR, "%s", errbuf);
                sock_close(sockctrl, NULL, 0);
                return;
@@ -1358,7 +1360,7 @@ main_active(void *ptr)
        {
                int activeclose;
 
-               if ((sockctrl = sock_open(addrinfo, SOCKOPEN_CLIENT, 0, errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
+               if ((sockctrl = sock_open(activepars->address, addrinfo, SOCKOPEN_CLIENT, 0, errbuf, PCAP_ERRBUF_SIZE)) == INVALID_SOCKET)
                {
                        rpcapd_log(LOGPRIO_DEBUG, "%s", errbuf);
 
index 52a5ee826fd7a3054aad4894480bbe19a8686008..75bd7b64782bfae3de2ad5eb63daffea8354c83f 100644 (file)
@@ -143,52 +143,165 @@ static int fuzz_recv(char *bufp, int 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
  * (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 addres 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
-       sock_fmterror(caller, GetLastError(), errbuf, errbuflen);
+       case WSAECONNRESET:
+       case WSAECONNABORTED:
+       case WSAECONNREFUSED:
 #else
-       sock_fmterror(caller, errno, errbuf, errbuflen);
+       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
+       case WSAENETDOWN:
+       case WSAENETRESET:
+#else
+       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);
+       }
+} 
 
 /*
  * \brief This function initializes the socket mechanism if it hasn't
@@ -281,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.
  *
@@ -292,6 +478,9 @@ static int sock_ismcastaddr(const struct sockaddr *saddr)
  *
  * 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
  * sock_initaddress().
@@ -312,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)
                /*
@@ -390,6 +564,7 @@ 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)
                        {
@@ -404,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;
                }
@@ -412,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;
                        }
@@ -422,70 +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)
                {
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
-                       break;
-#endif
-                       if (connect(sock, tempaddrinfo->ai_addr, (int) tempaddrinfo->ai_addrlen) == -1)
-                       {
-                               size_t msglen;
-                               char TmpBuffer[100];
-                               char SocketErrorMessage[SOCK_ERRBUF_SIZE];
+                       numaddrinfos++;
+               }
 
-                               /*
-                                * 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;
+               }
 
-                               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 ahve 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 numeric address athat
+                                        * this erro.
+                                        */
+                                       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;
+               }
        }
 }
 
@@ -516,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;
@@ -902,7 +1267,8 @@ int sock_send(SOCKET sock, SSL *ssl _U_NOSSL_, 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)
@@ -914,7 +1280,8 @@ int sock_send(SOCKET sock, SSL *ssl _U_NOSSL_, const char *buffer, size_t size,
                                 */
                                return -2;
                        }
-                       sock_fmterror("send()", errcode, errbuf, errbuflen);
+                       sock_fmterrmsg(errbuf, errbuflen, errcode,
+                           "send() failed");
 #endif
                        return -1;
                }
@@ -1100,7 +1467,7 @@ int sock_recv(SOCKET sock, SSL *ssl _U_NOSSL_, void *buffer, size_t size,
                        if (errno == EINTR)
                                return -3;
 #endif
-                       sock_geterror("recv()", errbuf, errbuflen);
+                       sock_geterrmsg(errbuf, errbuflen, "recv() failed");
                        return -1;
                }
 
@@ -1205,7 +1572,8 @@ int sock_recv_dgram(SOCKET sock, SSL *ssl _U_NOSSL_, 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 */
@@ -1242,7 +1610,7 @@ int sock_recv_dgram(SOCKET sock, SSL *ssl _U_NOSSL_, void *buffer, size_t size,
        {
                if (errno == EINTR)
                        return -3;
-               sock_geterror("recv()", errbuf, errbuflen);
+               sock_geterrmsg(errbuf, errbuflen, "recv() failed");
                return -1;
        }
 #ifdef HAVE_STRUCT_MSGHDR_MSG_FLAGS
@@ -1378,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;
                }
 
@@ -1564,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;
        }
 
@@ -1620,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; */
@@ -1652,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;
                }
 
index 5e3ac49ca606693b363a4670ed2ec12efedd1dc8..a488d8fcb4ff0604c99dea52b8d4a9d1636b675c 100644 (file)
 #pragma once
 #endif
 
+#include <stdarg.h>    /* we declare varargs functions */
+
+#include "pcap/funcattrs.h"
+
 #include "pcap/socket.h"
 
 #ifndef _WIN32
@@ -127,8 +131,13 @@ extern "C" {
 
 int sock_init(char *errbuf, int errbuflen);
 void sock_cleanup(void);
-void sock_fmterror(const char *caller, int errcode, char *errbuf, int errbuflen);
-void sock_geterror(const char *caller, char *errbuf, int errbufsize);
+int sock_geterrcode(void);
+void sock_vfmterrmsg(char *errbuf, size_t errbuflen, int errcode,
+    PCAP_FORMAT_STRING(const char *fmt), va_list ap) PCAP_PRINTFLIKE(4, 0);
+void sock_fmterrmsg(char *errbuf, size_t errbuflen, int errcode,
+    PCAP_FORMAT_STRING(const char *fmt), ...) PCAP_PRINTFLIKE(4, 5);
+void sock_geterrmsg(char *errbuf, size_t errbuflen,
+    PCAP_FORMAT_STRING(const char *fmt), ...)  PCAP_PRINTFLIKE(3, 4);
 int sock_initaddress(const char *address, const char *port,
     struct addrinfo *hints, struct addrinfo **addrinfo,
     char *errbuf, int errbuflen);
@@ -136,7 +145,7 @@ int sock_recv(SOCKET sock, SSL *, void *buffer, size_t size, int receiveall,
     char *errbuf, int errbuflen);
 int sock_recv_dgram(SOCKET sock, SSL *, void *buffer, size_t size,
     char *errbuf, int errbuflen);
-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);
 int sock_close(SOCKET sock, char *errbuf, int errbuflen);
 
 int sock_send(SOCKET sock, SSL *, const char *buffer, size_t size,
@@ -148,7 +157,7 @@ int sock_cmpaddr(struct sockaddr_storage *first, struct sockaddr_storage *second
 
 int sock_getmyinfo(SOCKET sock, 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, 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);
 int sock_present2network(const char *address, struct sockaddr_storage *sockaddr, int addr_family, char *errbuf, int errbuflen);
 
 #ifdef __cplusplus