Completely remove the 'frozen_readable' mechanism from uxnet.c. It
[u/mdw/putty] / unix / uxnet.c
index 96d3aa5..5190ef8 100644 (file)
 # define X11_UNIX_PATH "/tmp/.X11-unix/X"
 #endif
 
+/* 
+ * 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;
+    struct sockaddr_un su;
+};
+
 /*
  * We used to typedef struct Socket_tag *Socket.
  *
@@ -65,13 +80,13 @@ struct Socket_tag {
     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
-                         * notification while we were frozen */
     int localhost_only;                       /* for listening sockets */
     char oobdata[1];
     int sending_oob;
     int oobpending;                   /* is there OOB data available to read? */
     int oobinline;
+    enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof;
+    int incomingeof;
     int pending_error;                /* in case send() returns error */
     int listener;
     int nodelay, keepalive;            /* for connect()-type sockets */
@@ -301,7 +316,9 @@ static int sk_nextaddr(SockAddr addr, SockAddrStep *step)
 
 void sk_getaddr(SockAddr addr, char *buf, int buflen)
 {
-
+    /* XXX not clear what we should return for Unix-domain sockets; let's
+     * hope the question never arises */
+    assert(addr->superfamily != UNIX);
     if (addr->superfamily == UNRESOLVED) {
        strncpy(buf, addr->hostname, buflen);
        buf[buflen-1] = '\0';
@@ -336,19 +353,13 @@ int sk_hostname_is_local(char *name)
 
 static int sockaddr_is_loopback(struct sockaddr *sa)
 {
-    struct sockaddr_in *sin;
-#ifndef NO_IPV6
-    struct sockaddr_in6 *sin6;
-#endif
-
-    switch (sa->sa_family) {
+    union sockaddr_union *u = (union sockaddr_union *)sa;
+    switch (u->sa.sa_family) {
       case AF_INET:
-       sin = (struct sockaddr_in *)sa;
-       return ipv4_is_loopback(sin->sin_addr);
+       return ipv4_is_loopback(u->sin.sin_addr);
 #ifndef NO_IPV6
       case AF_INET6:
-       sin6 = (struct sockaddr_in6 *)sa;
-       return IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr);
+       return IN6_IS_ADDR_LOOPBACK(&u->sin6.sin6_addr);
 #endif
       case AF_UNIX:
        return TRUE;
@@ -359,9 +370,10 @@ static int sockaddr_is_loopback(struct sockaddr *sa)
 
 int sk_address_is_local(SockAddr addr)
 {
-
     if (addr->superfamily == UNRESOLVED)
        return 0;                      /* we don't know; assume not */
+    else if (addr->superfamily == UNIX)
+       return 1;
     else {
 #ifndef NO_IPV6
        return sockaddr_is_loopback(addr->ais->ai_addr);
@@ -376,6 +388,11 @@ int sk_address_is_local(SockAddr addr)
     }
 }
 
+int sk_address_is_special_local(SockAddr addr)
+{
+    return addr->superfamily == UNIX;
+}
+
 int sk_addrtype(SockAddr addr)
 {
     SockAddrStep step;
@@ -454,6 +471,7 @@ static void sk_tcp_flush(Socket s)
 static void sk_tcp_close(Socket s);
 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_private_ptr(Socket s, void *ptr);
 static void *sk_tcp_get_private_ptr(Socket s);
 static void sk_tcp_set_frozen(Socket s, int is_frozen);
@@ -464,6 +482,7 @@ static struct socket_function_table tcp_fn_table = {
     sk_tcp_close,
     sk_tcp_write,
     sk_tcp_write_oob,
+    sk_tcp_write_eof,
     sk_tcp_flush,
     sk_tcp_set_private_ptr,
     sk_tcp_get_private_ptr,
@@ -486,10 +505,11 @@ Socket sk_register(OSSocket sockfd, Plug plug)
     ret->writable = 1;                /* to start with */
     ret->sending_oob = 0;
     ret->frozen = 1;
-    ret->frozen_readable = 0;
     ret->localhost_only = 0;          /* unused, but best init anyway */
     ret->pending_error = 0;
     ret->oobpending = FALSE;
+    ret->outgoingeof = EOF_NO;
+    ret->incomingeof = FALSE;
     ret->listener = 0;
     ret->parent = ret->child = NULL;
     ret->addr = NULL;
@@ -513,15 +533,11 @@ Socket sk_register(OSSocket sockfd, Plug plug)
 static int try_connect(Actual_Socket sock)
 {
     int s;
-#ifndef NO_IPV6
-    struct sockaddr_in6 a6;
-#endif
-    struct sockaddr_in a;
-    struct sockaddr_un au;
-    const struct sockaddr *sa;
+    union sockaddr_union u;
+    const union sockaddr_union *sa;
     int err = 0;
     short localport;
-    int fl, salen, family;
+    int salen, family;
 
     /*
      * Remove the socket from the tree before we overwrite its
@@ -553,17 +569,32 @@ static int try_connect(Actual_Socket sock)
 
     if (sock->oobinline) {
        int b = TRUE;
-       setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (void *) &b, sizeof(b));
+       if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE,
+                       (void *) &b, sizeof(b)) < 0) {
+            err = errno;
+            close(s);
+            goto ret;
+        }
     }
 
     if (sock->nodelay) {
        int b = TRUE;
-       setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void *) &b, sizeof(b));
+       if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY,
+                       (void *) &b, sizeof(b)) < 0) {
+            err = errno;
+            close(s);
+            goto ret;
+        }
     }
 
     if (sock->keepalive) {
        int b = TRUE;
-       setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *) &b, sizeof(b));
+       if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,
+                       (void *) &b, sizeof(b)) < 0) {
+            err = errno;
+            close(s);
+            goto ret;
+        }
     }
 
     /*
@@ -575,10 +606,7 @@ static int try_connect(Actual_Socket sock)
        localport = 0;                 /* just use port 0 (ie kernel picks) */
 
     /* BSD IP stacks need sockaddr_in zeroed before filling in */
-    memset(&a,'\0',sizeof(struct sockaddr_in));
-#ifndef NO_IPV6
-    memset(&a6,'\0',sizeof(struct sockaddr_in6));
-#endif
+    memset(&u,'\0',sizeof(u));
 
     /* We don't try to bind to a local address for UNIX domain sockets.  (Why
      * do we bother doing the bind when localport == 0 anyway?) */
@@ -590,18 +618,18 @@ static int try_connect(Actual_Socket sock)
 #ifndef NO_IPV6
            if (family == AF_INET6) {
                /* XXX use getaddrinfo to get a local address? */
-               a6.sin6_family = AF_INET6;
-               a6.sin6_addr = in6addr_any;
-               a6.sin6_port = htons(localport);
-               retcode = bind(s, (struct sockaddr *) &a6, sizeof(a6));
+               u.sin6.sin6_family = AF_INET6;
+               u.sin6.sin6_addr = in6addr_any;
+               u.sin6.sin6_port = htons(localport);
+               retcode = bind(s, &u.sa, sizeof(u.sin6));
            } else
 #endif
            {
                assert(family == AF_INET);
-               a.sin_family = AF_INET;
-               a.sin_addr.s_addr = htonl(INADDR_ANY);
-               a.sin_port = htons(localport);
-               retcode = bind(s, (struct sockaddr *) &a, sizeof(a));
+               u.sin.sin_family = AF_INET;
+               u.sin.sin_addr.s_addr = htonl(INADDR_ANY);
+               u.sin.sin_port = htons(localport);
+               retcode = bind(s, &u.sa, sizeof(u.sin));
            }
            if (retcode >= 0) {
                err = 0;
@@ -632,32 +660,31 @@ static int try_connect(Actual_Socket sock)
        /* XXX would be better to have got getaddrinfo() to fill in the port. */
        ((struct sockaddr_in *)sock->step.ai->ai_addr)->sin_port =
            htons(sock->port);
-       sa = (const struct sockaddr *)sock->step.ai->ai_addr;
+       sa = (const union sockaddr_union *)sock->step.ai->ai_addr;
        salen = sock->step.ai->ai_addrlen;
        break;
       case AF_INET6:
        ((struct sockaddr_in *)sock->step.ai->ai_addr)->sin_port =
            htons(sock->port);
-       sa = (const struct sockaddr *)sock->step.ai->ai_addr;
+       sa = (const union sockaddr_union *)sock->step.ai->ai_addr;
        salen = sock->step.ai->ai_addrlen;
        break;
 #else
       case AF_INET:
-       a.sin_family = AF_INET;
-       a.sin_addr.s_addr = htonl(sock->addr->addresses[sock->step.curraddr]);
-       a.sin_port = htons((short) sock->port);
-       sa = (const struct sockaddr *)&a;
-       salen = sizeof a;
+       u.sin.sin_family = AF_INET;
+       u.sin.sin_addr.s_addr = htonl(sock->addr->addresses[sock->step.curraddr]);
+       u.sin.sin_port = htons((short) sock->port);
+       sa = &u;
+       salen = sizeof u.sin;
        break;
 #endif
       case AF_UNIX:
        assert(sock->port == 0);       /* to catch confused people */
-       assert(strlen(sock->addr->hostname) < sizeof au.sun_path);
-       memset(&au, 0, sizeof au);
-       au.sun_family = AF_UNIX;
-       strcpy(au.sun_path, sock->addr->hostname);
-       sa = (const struct sockaddr *)&au;
-       salen = sizeof au;
+       assert(strlen(sock->addr->hostname) < sizeof u.su.sun_path);
+       u.su.sun_family = AF_UNIX;
+       strcpy(u.su.sun_path, sock->addr->hostname);
+       sa = &u;
+       salen = sizeof u.su;
        break;
 
       default:
@@ -665,11 +692,9 @@ static int try_connect(Actual_Socket sock)
        exit(1); /* XXX: GCC doesn't understand assert() on some systems. */
     }
 
-    fl = fcntl(s, F_GETFL);
-    if (fl != -1)
-       fcntl(s, F_SETFL, fl | O_NONBLOCK);
+    nonblock(s);
 
-    if ((connect(s, sa, salen)) < 0) {
+    if ((connect(s, &(sa->sa), salen)) < 0) {
        if ( errno != EINPROGRESS ) {
            err = errno;
            goto ret;
@@ -715,11 +740,12 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
     ret->writable = 0;                /* to start with */
     ret->sending_oob = 0;
     ret->frozen = 0;
-    ret->frozen_readable = 0;
     ret->localhost_only = 0;          /* unused, but best init anyway */
     ret->pending_error = 0;
     ret->parent = ret->child = NULL;
     ret->oobpending = FALSE;
+    ret->outgoingeof = EOF_NO;
+    ret->incomingeof = FALSE;
     ret->listener = 0;
     ret->addr = addr;
     START_STEP(ret->addr, ret->step);
@@ -747,11 +773,10 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, i
 #ifndef NO_IPV6
     struct addrinfo hints, *ai;
     char portstr[6];
-    struct sockaddr_in6 a6;
 #endif
-    struct sockaddr *addr;
+    union sockaddr_union u;
+    union sockaddr_union *addr;
     int addrlen;
-    struct sockaddr_in a;
     Actual_Socket ret;
     int retcode;
     int address_family;
@@ -768,11 +793,12 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, i
     ret->writable = 0;                /* to start with */
     ret->sending_oob = 0;
     ret->frozen = 0;
-    ret->frozen_readable = 0;
     ret->localhost_only = local_host_only;
     ret->pending_error = 0;
     ret->parent = ret->child = NULL;
     ret->oobpending = FALSE;
+    ret->outgoingeof = EOF_NO;
+    ret->incomingeof = FALSE;
     ret->listener = 1;
     ret->addr = NULL;
 
@@ -817,7 +843,12 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, i
 
     ret->oobinline = 0;
 
-    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on));
+    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+                   (const char *)&on, sizeof(on)) < 0) {
+        ret->error = strerror(errno);
+        close(s);
+        return (Socket) ret;
+    }
 
     retcode = -1;
     addr = NULL; addrlen = -1;         /* placate optimiser */
@@ -836,52 +867,51 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, i
         sprintf(portstr, "%d", port);
         retcode = getaddrinfo(srcaddr, portstr, &hints, &ai);
        if (retcode == 0) {
-           addr = ai->ai_addr;
+           addr = (union sockaddr_union *)ai->ai_addr;
            addrlen = ai->ai_addrlen;
        }
 #else
-        memset(&a,'\0',sizeof(struct sockaddr_in));
-        a.sin_family = AF_INET;
-        a.sin_port = htons(port);
-        a.sin_addr.s_addr = inet_addr(srcaddr);
-        if (a.sin_addr.s_addr != (in_addr_t)(-1)) {
+        memset(&u,'\0',sizeof u);
+        u.sin.sin_family = AF_INET;
+        u.sin.sin_port = htons(port);
+        u.sin.sin_addr.s_addr = inet_addr(srcaddr);
+        if (u.sin.sin_addr.s_addr != (in_addr_t)(-1)) {
             /* Override localhost_only with specified listen addr. */
-            ret->localhost_only = ipv4_is_loopback(a.sin_addr);
+            ret->localhost_only = ipv4_is_loopback(u.sin.sin_addr);
         }
-        addr = (struct sockaddr *)&a;
-        addrlen = sizeof(a);
+        addr = &u;
+        addrlen = sizeof(u.sin);
         retcode = 0;
 #endif
     }
 
     if (retcode != 0) {
+        memset(&u,'\0',sizeof u);
 #ifndef NO_IPV6
         if (address_family == AF_INET6) {
-            memset(&a6,'\0',sizeof(struct sockaddr_in6));
-            a6.sin6_family = AF_INET6;
-            a6.sin6_port = htons(port);
+            u.sin6.sin6_family = AF_INET6;
+            u.sin6.sin6_port = htons(port);
             if (local_host_only)
-                a6.sin6_addr = in6addr_loopback;
+                u.sin6.sin6_addr = in6addr_loopback;
             else
-                a6.sin6_addr = in6addr_any;
-            addr = (struct sockaddr *)&a6;
-            addrlen = sizeof(a6);
+                u.sin6.sin6_addr = in6addr_any;
+            addr = &u;
+            addrlen = sizeof(u.sin6);
         } else
 #endif
         {
-            memset(&a,'\0',sizeof(struct sockaddr_in));
-            a.sin_family = AF_INET;
-            a.sin_port = htons(port);
+            u.sin.sin_family = AF_INET;
+            u.sin.sin_port = htons(port);
            if (local_host_only)
-               a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+               u.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
            else
-               a.sin_addr.s_addr = htonl(INADDR_ANY);
-            addr = (struct sockaddr *)&a;
-            addrlen = sizeof(a);
+               u.sin.sin_addr.s_addr = htonl(INADDR_ANY);
+            addr = &u;
+            addrlen = sizeof(u.sin);
         }
     }
 
-    retcode = bind(s, addr, addrlen);
+    retcode = bind(s, &addr->sa, addrlen);
     if (retcode < 0) {
         close(s);
        ret->error = strerror(errno);
@@ -946,14 +976,7 @@ static void sk_tcp_close(Socket sock)
 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;
+    union sockaddr_union u;
     socklen_t addrlen;
     char *buf;
     static unsigned int unix_addr = 0xFFFFFFFF;
@@ -964,23 +987,23 @@ void *sk_getxdmdata(void *sock, int *lenp)
     if (s->fn != &tcp_fn_table)
        return NULL;                   /* failure */
 
-    addrlen = sizeof(addr);
-    if (getsockname(s->s, sa, &addrlen) < 0)
+    addrlen = sizeof(u);
+    if (getsockname(s->s, &u.sa, &addrlen) < 0)
        return NULL;
-    switch(sa->sa_family) {
+    switch(u.sa.sa_family) {
       case AF_INET:
        *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));
+       PUT_32BIT_MSB_FIRST(buf, ntohl(u.sin.sin_addr.s_addr));
+       PUT_16BIT_MSB_FIRST(buf+4, ntohs(u.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));
+       if (IN6_IS_ADDR_V4MAPPED(&u.sin6.sin6_addr)) {
+           memcpy(buf, u.sin6.sin6_addr.s6_addr + 12, 4);
+           PUT_16BIT_MSB_FIRST(buf+4, ntohs(u.sin6.sin6_port));
        } else
            /* This is stupid, but it's what XLib does. */
            memset(buf, 0, 6);
@@ -1043,6 +1066,16 @@ void try_send(Actual_Socket s)
                 * plug_closing()) at some suitable future moment.
                 */
                s->pending_error = err;
+                /*
+                 * Immediately cease selecting on this socket, so that
+                 * we don't tight-loop repeatedly trying to do
+                 * whatever it was that went wrong.
+                 */
+                uxsel_tell(s);
+                /*
+                 * Notify the front end that it might want to call us.
+                 */
+                frontend_net_error_pending();
                return;
            }
        } else {
@@ -1058,6 +1091,20 @@ void try_send(Actual_Socket s)
            }
        }
     }
+
+    /*
+     * If we reach here, we've finished sending everything we might
+     * have needed to send. Send EOF, if we need to.
+     */
+    if (s->outgoingeof == EOF_PENDING) {
+        shutdown(s->s, SHUT_WR);
+        s->outgoingeof = EOF_SENT;
+    }
+
+    /*
+     * Also update the select status, because we don't need to select
+     * for writing any more.
+     */
     uxsel_tell(s);
 }
 
@@ -1065,6 +1112,8 @@ static int sk_tcp_write(Socket sock, const char *buf, int len)
 {
     Actual_Socket s = (Actual_Socket) sock;
 
+    assert(s->outgoingeof == EOF_NO);
+
     /*
      * Add the data to the buffer list on the socket.
      */
@@ -1089,6 +1138,8 @@ static int sk_tcp_write_oob(Socket sock, const char *buf, int len)
 {
     Actual_Socket s = (Actual_Socket) sock;
 
+    assert(s->outgoingeof == EOF_NO);
+
     /*
      * Replace the buffer list on the socket with the data.
      */
@@ -1112,6 +1163,30 @@ static int sk_tcp_write_oob(Socket sock, const char *buf, int len)
     return s->sending_oob;
 }
 
+static void sk_tcp_write_eof(Socket sock)
+{
+    Actual_Socket s = (Actual_Socket) sock;
+
+    assert(s->outgoingeof == EOF_NO);
+
+    /*
+     * Mark the socket as pending outgoing EOF.
+     */
+    s->outgoingeof = EOF_PENDING;
+
+    /*
+     * Now try sending from the start of the buffer list.
+     */
+    if (s->writable)
+       try_send(s);
+
+    /*
+     * Update the select() status to correctly reflect whether or
+     * not we should be selecting for write.
+     */
+    uxsel_tell(s);
+}
+
 static int net_select_result(int fd, int event)
 {
     int ret;
@@ -1170,27 +1245,20 @@ static int net_select_result(int fd, int event)
             * On a listening socket, the readability event means a
             * connection is ready to be accepted.
             */
-#ifdef NO_IPV6
-           struct sockaddr_in ss;
-#else
-           struct sockaddr_storage ss;
-#endif
-           socklen_t addrlen = sizeof(ss);
+           union sockaddr_union su;
+           socklen_t addrlen = sizeof(su);
            int t;  /* socket of connection */
-            int fl;
 
-           memset(&ss, 0, addrlen);
-           t = accept(s->s, (struct sockaddr *)&ss, &addrlen);
+           memset(&su, 0, addrlen);
+           t = accept(s->s, &su.sa, &addrlen);
            if (t < 0) {
                break;
            }
 
-            fl = fcntl(t, F_GETFL);
-            if (fl != -1)
-                fcntl(t, F_SETFL, fl | O_NONBLOCK);
+            nonblock(t);
 
            if (s->localhost_only &&
-               !sockaddr_is_loopback((struct sockaddr *)&ss)) {
+               !sockaddr_is_loopback(&su.sa)) {
                close(t);              /* someone let nonlocal through?! */
            } else if (plug_accepting(s->plug, t)) {
                close(t);              /* denied or error */
@@ -1204,10 +1272,8 @@ static int net_select_result(int fd, int event)
         */
 
        /* In the case the socket is still frozen, we don't even bother */
-       if (s->frozen) {
-           s->frozen_readable = 1;
+       if (s->frozen)
            break;
-       }
 
        /*
         * We have received data on the socket. For an oobinline
@@ -1246,6 +1312,8 @@ static int net_select_result(int fd, int event)
             if (err != 0)
                 return plug_closing(s->plug, strerror(err), err, 0);
        } else if (0 == ret) {
+            s->incomingeof = TRUE;     /* stop trying to read now */
+            uxsel_tell(s);
            return plug_closing(s->plug, NULL, 0, 0);
        } else {
             /*
@@ -1358,26 +1426,23 @@ static void sk_tcp_set_frozen(Socket sock, int is_frozen)
     if (s->frozen == is_frozen)
        return;
     s->frozen = is_frozen;
-    if (!is_frozen && s->frozen_readable) {
-       char c;
-       recv(s->s, &c, 1, MSG_PEEK);
-    }
-    s->frozen_readable = 0;
     uxsel_tell(s);
 }
 
 static void uxsel_tell(Actual_Socket s)
 {
     int rwx = 0;
-    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 */
+    if (!s->pending_error) {
+        if (s->listener) {
+            rwx |= 1;                  /* read == accept */
+        } else {
+            if (!s->connected)
+                rwx |= 2;              /* write == connect */
+            if (s->connected && !s->frozen && !s->incomingeof)
+                rwx |= 1 | 4;          /* read, except */
+            if (bufchain_size(&s->output_data))
+                rwx |= 2;              /* write */
+        }
     }
     uxsel_set(s->s, rwx, net_select_result);
 }
@@ -1392,6 +1457,23 @@ int net_service_lookup(char *service)
        return 0;
 }
 
+char *get_hostname(void)
+{
+    int len = 128;
+    char *hostname = NULL;
+    do {
+       len *= 2;
+       hostname = sresize(hostname, len, char);
+       if ((gethostname(hostname, len) < 0) &&
+           (errno != ENAMETOOLONG)) {
+           sfree(hostname);
+           hostname = NULL;
+           break;
+       }
+    } while (strlen(hostname) >= len-1);
+    return hostname;
+}
+
 SockAddr platform_get_x11_unix_address(const char *sockpath, int displaynum)
 {
     SockAddr ret = snew(struct SockAddr_tag);