]> asedeno.scripts.mit.edu Git - PuTTY.git/blobdiff - unix/uxnet.c
Report the right address in connection setup errors.
[PuTTY.git] / unix / uxnet.c
index 1fe78436115878ce98174c6d5d9de8e158db4208..47490a1eefe235de82fdcf35d8a70c24c5a2ee35 100644 (file)
@@ -16,6 +16,8 @@
 #include <netinet/tcp.h>
 #include <netdb.h>
 #include <sys/un.h>
+#include <pwd.h>
+#include <grp.h>
 
 #define DEFINE_PLUG_METHOD_MACROS
 #include "putty.h"
  * Access to sockaddr types without breaking C strict aliasing rules.
  */
 union sockaddr_union {
-#ifdef NO_IPV6
-    struct sockaddr_in storage;
-#else
     struct sockaddr_storage storage;
-    struct sockaddr_in6 sin6;
-#endif
     struct sockaddr sa;
     struct sockaddr_in sin;
+#ifndef NO_IPV6
+    struct sockaddr_in6 sin6;
+#endif
     struct sockaddr_un su;
 };
 
@@ -126,9 +126,12 @@ struct SockAddr_tag {
      (addr)->superfamily == UNIX ? AF_UNIX : \
      (step).ai ? (step).ai->ai_family : AF_INET)
 #else
+/* Here we gratuitously reference 'step' to avoid gcc warnings about
+ * 'set but not used' when compiling -DNO_IPV6 */
 #define SOCKADDR_FAMILY(addr, step) \
     ((addr)->superfamily == UNRESOLVED ? AF_UNSPEC : \
-     (addr)->superfamily == UNIX ? AF_UNIX : AF_INET)
+     (addr)->superfamily == UNIX ? AF_UNIX : \
+     (step).curraddr ? AF_INET : AF_INET)
 #endif
 
 /*
@@ -341,6 +344,34 @@ void sk_getaddr(SockAddr addr, char *buf, int buflen)
     }
 }
 
+/*
+ * This constructs a SockAddr that points at one specific sub-address
+ * of a parent SockAddr. The returned SockAddr does not own all its
+ * own memory: it points into the old one's data structures, so it
+ * MUST NOT be used after the old one is freed, and it MUST NOT be
+ * passed to sk_addr_free. (The latter is why it's returned by value
+ * rather than dynamically allocated - that should clue in anyone
+ * writing a call to it that something is weird about it.)
+ */
+static struct SockAddr_tag sk_extractaddr_tmp(
+    SockAddr addr, const SockAddrStep *step)
+{
+    struct SockAddr_tag toret;
+    toret = *addr;                    /* structure copy */
+    toret.refcount = 1;
+
+    if (addr->superfamily == IP) {
+#ifndef NO_IPV6
+        toret.ais = step->ai;
+#else
+       assert(SOCKADDR_FAMILY(addr, *step) == AF_INET);
+        toret.addresses += step->curraddr;
+#endif
+    }
+
+    return toret;
+}
+
 int sk_addr_needs_port(SockAddr addr)
 {
     if (addr->superfamily == UNRESOLVED || addr->superfamily == UNIX) {
@@ -482,6 +513,7 @@ static int sk_tcp_write(Socket s, const char *data, int len);
 static int sk_tcp_write_oob(Socket s, const char *data, int len);
 static void sk_tcp_write_eof(Socket s);
 static void sk_tcp_set_frozen(Socket s, int is_frozen);
+static char *sk_tcp_peer_info(Socket s);
 static const char *sk_tcp_socket_error(Socket s);
 
 static struct socket_function_table tcp_fn_table = {
@@ -492,7 +524,8 @@ static struct socket_function_table tcp_fn_table = {
     sk_tcp_write_eof,
     sk_tcp_flush,
     sk_tcp_set_frozen,
-    sk_tcp_socket_error
+    sk_tcp_socket_error,
+    sk_tcp_peer_info,
 };
 
 static Socket sk_tcp_accept(accept_ctx_t ctx, Plug plug)
@@ -556,7 +589,11 @@ static int try_connect(Actual_Socket sock)
     if (sock->s >= 0)
         close(sock->s);
 
-    plug_log(sock->plug, 0, sock->addr, sock->port, NULL, 0);
+    {
+        struct SockAddr_tag thisaddr = sk_extractaddr_tmp(
+            sock->addr, &sock->step);
+        plug_log(sock->plug, 0, &thisaddr, sock->port, NULL, 0);
+    }
 
     /*
      * Open socket.
@@ -723,8 +760,11 @@ static int try_connect(Actual_Socket sock)
      */
     add234(sktree, sock);
 
-    if (err)
-       plug_log(sock->plug, 1, sock->addr, sock->port, strerror(err), err);
+    if (err) {
+        struct SockAddr_tag thisaddr = sk_extractaddr_tmp(
+            sock->addr, &sock->step);
+       plug_log(sock->plug, 1, &thisaddr, sock->port, strerror(err), err);
+    }
     return err;
 }
 
@@ -773,11 +813,12 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
     return (Socket) ret;
 }
 
-Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, int orig_address_family)
+Socket sk_newlistener(const char *srcaddr, int port, Plug plug,
+                      int local_host_only, int orig_address_family)
 {
     int s;
 #ifndef NO_IPV6
-    struct addrinfo hints, *ai;
+    struct addrinfo hints, *ai = NULL;
     char portstr[6];
 #endif
     union sockaddr_union u;
@@ -923,6 +964,12 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, i
     }
 
     retcode = bind(s, &addr->sa, addrlen);
+
+#ifndef NO_IPV6
+    if (ai)
+        freeaddrinfo(ai);
+#endif
+
     if (retcode < 0) {
         close(s);
        ret->error = strerror(errno);
@@ -1330,21 +1377,7 @@ static int net_select_result(int fd, int event)
            }
        }
        if (ret < 0) {
-            /*
-             * An error at this point _might_ be an error reported
-             * by a non-blocking connect(). So before we return a
-             * panic status to the user, let's just see whether
-             * that's the case.
-             */
-            int err = errno;
-           if (s->addr) {
-               plug_log(s->plug, 1, s->addr, s->port, strerror(err), err);
-               while (s->addr && sk_nextaddr(s->addr, &s->step)) {
-                   err = try_connect(s);
-               }
-           }
-            if (err != 0)
-                return plug_closing(s->plug, strerror(err), err, 0);
+            return plug_closing(s->plug, strerror(errno), errno, 0);
        } else if (0 == ret) {
             s->incomingeof = TRUE;     /* stop trying to read now */
             uxsel_tell(s);
@@ -1366,11 +1399,52 @@ static int net_select_result(int fd, int event)
        if (!s->connected) {
            /*
             * select() reports a socket as _writable_ when an
-            * asynchronous connection is completed.
+            * asynchronous connect() attempt either completes or
+            * fails. So first we must find out which.
             */
+            {
+                int err;
+                socklen_t errlen = sizeof(err);
+                char *errmsg = NULL;
+                if (getsockopt(s->s, SOL_SOCKET, SO_ERROR, &err, &errlen)<0) {
+                    errmsg = dupprintf("getsockopt(SO_ERROR): %s",
+                                       strerror(errno));
+                    err = errno;       /* got to put something in here */
+                } else if (err != 0) {
+                    errmsg = dupstr(strerror(err));
+                }
+                if (errmsg) {
+                    /*
+                     * The asynchronous connection attempt failed.
+                     * Report the problem via plug_log, and try again
+                     * with the next candidate address, if we have
+                     * more than one.
+                     */
+                    struct SockAddr_tag thisaddr;
+                    assert(s->addr);
+
+                    thisaddr = sk_extractaddr_tmp(s->addr, &s->step);
+                    plug_log(s->plug, 1, &thisaddr, s->port, errmsg, err);
+
+                    while (err && s->addr && sk_nextaddr(s->addr, &s->step)) {
+                        err = try_connect(s);
+                    }
+                    if (err)
+                        return plug_closing(s->plug, strerror(err), err, 0);
+                    if (!s->connected)
+                        return 0;      /* another async attempt in progress */
+                }
+            }
+
+            /*
+             * If we get here, we've managed to make a connection.
+             */
+            if (s->addr) {
+                sk_addr_free(s->addr);
+                s->addr = NULL;
+            }
            s->connected = s->writable = 1;
            uxsel_tell(s);
-           break;
        } else {
            int bufsize_before, bufsize_after;
            s->writable = 1;
@@ -1410,6 +1484,52 @@ static void sk_tcp_set_frozen(Socket sock, int is_frozen)
     uxsel_tell(s);
 }
 
+static char *sk_tcp_peer_info(Socket sock)
+{
+    Actual_Socket s = (Actual_Socket) sock;
+    union sockaddr_union addr;
+    socklen_t addrlen = sizeof(addr);
+#ifndef NO_IPV6
+    char buf[INET6_ADDRSTRLEN];
+#endif
+
+    if (getpeername(s->s, &addr.sa, &addrlen) < 0)
+        return NULL;
+    if (addr.storage.ss_family == AF_INET) {
+        return dupprintf
+            ("%s:%d",
+             inet_ntoa(addr.sin.sin_addr),
+             (int)ntohs(addr.sin.sin_port));
+#ifndef NO_IPV6
+    } else if (addr.storage.ss_family == AF_INET6) {
+        return dupprintf
+            ("[%s]:%d",
+             inet_ntop(AF_INET6, &addr.sin6.sin6_addr, buf, sizeof(buf)),
+             (int)ntohs(addr.sin6.sin6_port));
+#endif
+    } else if (addr.storage.ss_family == AF_UNIX) {
+        /*
+         * For Unix sockets, the source address is unlikely to be
+         * helpful. Instead, we try SO_PEERCRED and try to get the
+         * source pid.
+         */
+        int pid, uid, gid;
+        if (so_peercred(s->s, &pid, &uid, &gid)) {
+            char uidbuf[64], gidbuf[64];
+            sprintf(uidbuf, "%d", uid);
+            sprintf(gidbuf, "%d", gid);
+            struct passwd *pw = getpwuid(uid);
+            struct group *gr = getgrgid(gid);
+            return dupprintf("pid %d (%s:%s)", pid,
+                             pw ? pw->pw_name : uidbuf,
+                             gr ? gr->gr_name : gidbuf);
+        }
+        return NULL;
+    } else {
+        return NULL;
+    }
+}
+
 static void uxsel_tell(Actual_Socket s)
 {
     int rwx = 0;