Unify GET_32BIT()/PUT_32BIT() et al from numerous source files into misc.h.
[u/mdw/putty] / unix / uxnet.c
index 8804b09..46959e2 100644 (file)
@@ -26,8 +26,6 @@
 # define X11_UNIX_PATH "/tmp/.X11-unix/X"
 #endif
 
-#define ipv4_is_loopback(addr) (inet_netof(addr) == IN_LOOPBACKNET)
-
 struct Socket_tag {
     struct socket_function_table *fn;
     /* the above variable absolutely *must* be the first in this structure */
@@ -36,7 +34,7 @@ struct Socket_tag {
     Plug plug;
     void *private_ptr;
     bufchain output_data;
-    int connected;
+    int connected;                    /* irrelevant for listening sockets */
     int writable;
     int frozen; /* this causes readability notifications to be ignored */
     int frozen_readable; /* this means we missed at least one readability
@@ -277,6 +275,32 @@ int sk_hostname_is_local(char *name)
     return !strcmp(name, "localhost");
 }
 
+#define ipv4_is_loopback(addr) \
+    (((addr).s_addr & htonl(0xff000000)) == htonl(0x7f000000))
+
+static int sockaddr_is_loopback(struct sockaddr *sa)
+{
+    struct sockaddr_in *sin;
+#ifndef NO_IPV6
+    struct sockaddr_in6 *sin6;
+#endif
+
+    switch (sa->sa_family) {
+      case AF_INET:
+       sin = (struct sockaddr_in *)sa;
+       return ipv4_is_loopback(sin->sin_addr);
+#ifndef NO_IPV6
+      case AF_INET6:
+       sin6 = (struct sockaddr_in6 *)sa;
+       return IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr);
+#endif
+      case AF_UNIX:
+       return TRUE;
+      default:
+       return FALSE;
+    }
+}
+
 int sk_address_is_local(SockAddr addr)
 {
 
@@ -284,14 +308,7 @@ int sk_address_is_local(SockAddr addr)
        return 0;                      /* we don't know; assume not */
     else {
 #ifndef NO_IPV6
-       if (addr->family == AF_INET)
-           return ipv4_is_loopback(
-               ((struct sockaddr_in *)addr->ai->ai_addr)->sin_addr);
-       else if (addr->family == AF_INET6)
-           return IN6_IS_ADDR_LOOPBACK(
-               &((struct sockaddr_in6 *)addr->ai->ai_addr)->sin6_addr);
-       else
-           return 0;
+       return sockaddr_is_loopback(addr->ai->ai_addr);
 #else
        struct in_addr a;
        assert(addr->family == AF_INET);
@@ -401,6 +418,7 @@ Socket sk_register(OSSocket sockfd, Plug plug)
     ret->oobpending = FALSE;
     ret->listener = 0;
     ret->addr = NULL;
+    ret->connected = 1;
 
     ret->s = sockfd;
 
@@ -705,15 +723,16 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, i
 #ifndef NO_IPV6
         hints.ai_flags = AI_NUMERICHOST;
         hints.ai_family = address_family;
-        hints.ai_socktype = 0;
+        hints.ai_socktype = SOCK_STREAM;
         hints.ai_protocol = 0;
         hints.ai_addrlen = 0;
         hints.ai_addr = NULL;
         hints.ai_canonname = NULL;
         hints.ai_next = NULL;
+       assert(port >= 0 && port <= 99999);
         sprintf(portstr, "%d", port);
         retcode = getaddrinfo(srcaddr, portstr, &hints, &ai);
-       if (retcode = 0) {
+       if (retcode == 0) {
            addr = ai->ai_addr;
            addrlen = ai->ai_addrlen;
        }
@@ -725,9 +744,8 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, i
         if (a.sin_addr.s_addr != (in_addr_t)(-1)) {
             /* Override localhost_only with specified listen addr. */
             ret->localhost_only = ipv4_is_loopback(a.sin_addr);
-            got_addr = 1;
         }
-        addr = (struct sockaddr *)a;
+        addr = (struct sockaddr *)&a;
         addrlen = sizeof(a);
         retcode = 0;
 #endif
@@ -793,42 +811,63 @@ static void sk_tcp_close(Socket sock)
     sfree(s);
 }
 
-int sk_getxdmdata(void *sock, unsigned long *ip, int *port)
+void *sk_getxdmdata(void *sock, int *lenp)
 {
     Actual_Socket s = (Actual_Socket) sock;
+#ifdef NO_IPV6
     struct sockaddr_in addr;
+#else
+    struct sockaddr_storage addr;
+    struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr;
+#endif
+    struct sockaddr *sa = (struct sockaddr *)&addr;
+    struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
     socklen_t addrlen;
+    char *buf;
+    static unsigned int unix_addr = 0xFFFFFFFF;
 
     /*
      * We must check that this socket really _is_ an Actual_Socket.
      */
     if (s->fn != &tcp_fn_table)
-       return 0;                      /* failure */
+       return NULL;                   /* failure */
 
     addrlen = sizeof(addr);
-    if (getsockname(s->s, (struct sockaddr *)&addr, &addrlen) < 0)
-       return 0;
-    switch(addr.sin_family) {
+    if (getsockname(s->s, sa, &addrlen) < 0)
+       return NULL;
+    switch(sa->sa_family) {
       case AF_INET:
-       *ip = ntohl(addr.sin_addr.s_addr);
-       *port = ntohs(addr.sin_port);
+       *lenp = 6;
+       buf = snewn(*lenp, char);
+       PUT_32BIT_MSB_FIRST(buf, ntohl(sin->sin_addr.s_addr));
+       PUT_16BIT_MSB_FIRST(buf+4, ntohs(sin->sin_port));
        break;
+#ifndef NO_IPV6
+    case AF_INET6:
+       *lenp = 6;
+       buf = snewn(*lenp, char);
+       if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+           memcpy(buf, sin6->sin6_addr.s6_addr + 12, 4);
+           PUT_16BIT_MSB_FIRST(buf+4, ntohs(sin6->sin6_port));
+       } else
+           /* This is stupid, but it's what XLib does. */
+           memset(buf, 0, 6);
+       break;
+#endif
       case AF_UNIX:
-       /*
-        * For a Unix socket, we return 0xFFFFFFFF for the IP address and
-        * our current pid for the port. Bizarre, but such is life.
-        */
-       *ip = ntohl(0xFFFFFFFF);
-       *port = getpid();
+       *lenp = 6;
+       buf = snewn(*lenp, char);
+       PUT_32BIT_MSB_FIRST(buf, unix_addr--);
+        PUT_16BIT_MSB_FIRST(buf+4, getpid());
        break;
 
        /* XXX IPV6 */
 
       default:
-       return 0;
+       return NULL;
     }
 
-    return 1;
+    return buf;
 }
 
 /*
@@ -861,11 +900,9 @@ void try_send(Actual_Socket s)
                 */
                s->writable = FALSE;
                return;
-           } else if (nsent == 0 ||
-                      err == ECONNABORTED || err == ECONNRESET) {
+           } else {
                /*
-                * If send() returns CONNABORTED or CONNRESET, we
-                * unfortunately can't just call plug_closing(),
+                * We unfortunately can't just call plug_closing(),
                 * because it's quite likely that we're currently
                 * _in_ a call from the code we'd be calling back
                 * to, so we'd have to make half the SSH code
@@ -875,11 +912,6 @@ void try_send(Actual_Socket s)
                 */
                s->pending_error = err;
                return;
-           } else {
-               /* We're inside the Unix frontend here, so we know
-                * that the frontend handle is unnecessary. */
-               logevent(NULL, strerror(err));
-               fatalbox("%s", strerror(err));
            }
        } else {
            if (s->sending_oob) {
@@ -974,12 +1006,9 @@ static int net_select_result(int fd, int event)
            ret = recv(s->s, buf, sizeof(buf), MSG_OOB);
            noise_ultralight(ret);
            if (ret <= 0) {
-               const char *str = (ret == 0 ? "Internal networking trouble" :
-                                  strerror(errno));
-               /* We're inside the Unix frontend here, so we know
-                * that the frontend handle is unnecessary. */
-               logevent(NULL, str);
-               fatalbox("%s", str);
+                return plug_closing(s->plug,
+                                   ret == 0 ? "Internal networking trouble" :
+                                   strerror(errno), errno, 0);
            } else {
                 /*
                  * Receiving actual data on a socket means we can
@@ -1009,17 +1038,22 @@ static int net_select_result(int fd, int event)
             * On a listening socket, the readability event means a
             * connection is ready to be accepted.
             */
-           struct sockaddr_in isa;
-           int addrlen = sizeof(struct sockaddr_in);
+#ifdef NO_IPV6
+           struct sockaddr_in ss;
+#else
+           struct sockaddr_storage ss;
+#endif
+           socklen_t addrlen = sizeof(ss);
            int t;  /* socket of connection */
 
-           memset(&isa, 0, sizeof(struct sockaddr_in));
-           t = accept(s->s,(struct sockaddr *)&isa,(socklen_t *) &addrlen);
+           memset(&ss, 0, addrlen);
+           t = accept(s->s, (struct sockaddr *)&ss, &addrlen);
            if (t < 0) {
                break;
            }
 
-           if (s->localhost_only && !ipv4_is_loopback(isa.sin_addr)) {
+           if (s->localhost_only &&
+               !sockaddr_is_loopback((struct sockaddr *)&ss)) {
                close(t);              /* someone let nonlocal through?! */
            } else if (plug_accepting(s->plug, t)) {
                close(t);              /* denied or error */
@@ -1198,14 +1232,16 @@ static void sk_tcp_set_frozen(Socket sock, int is_frozen)
 static void uxsel_tell(Actual_Socket s)
 {
     int rwx = 0;
-    if (!s->connected)
-       rwx |= 2;                      /* write == connect */
-    if (s->connected && !s->frozen)
-       rwx |= 1 | 4;                  /* read, except */
-    if (bufchain_size(&s->output_data))
-       rwx |= 2;                      /* write */
-    if (s->listener)
-       rwx |= 1;                      /* read == accept */
+    if (s->listener) {
+       rwx |= 1;                       /* read == accept */
+    } else {
+       if (!s->connected)
+           rwx |= 2;                   /* write == connect */
+       if (s->connected && !s->frozen)
+           rwx |= 1 | 4;               /* read, except */
+       if (bufchain_size(&s->output_data))
+           rwx |= 2;                   /* write */
+    }
     uxsel_set(s->s, rwx, net_select_result);
 }