X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/c24e27c631bae3139724e5a0951a12a956353bca..6d983afb8617ac09740fb954e3149518d791ee93:/unix/uxnet.c diff --git a/unix/uxnet.c b/unix/uxnet.c index d01a44d9..a82c9860 100644 --- a/unix/uxnet.c +++ b/unix/uxnet.c @@ -31,6 +31,16 @@ # define X11_UNIX_PATH "/tmp/.X11-unix/X" #endif +/* + * We used to typedef struct Socket_tag *Socket. + * + * Since we have made the networking abstraction slightly more + * abstract, Socket no longer means a tcp socket (it could mean + * an ssl socket). So now we must use Actual_Socket when we know + * we are talking about a tcp socket. + */ +typedef struct Socket_tag *Actual_Socket; + struct Socket_tag { struct socket_function_table *fn; /* the above variable absolutely *must* be the first in this structure */ @@ -54,18 +64,15 @@ struct Socket_tag { int nodelay, keepalive; /* for connect()-type sockets */ int privport, port; /* and again */ SockAddr addr; + /* + * We sometimes need pairs of Socket structures to be linked: + * if we are listening on the same IPv6 and v4 port, for + * example. So here we define `parent' and `child' pointers to + * track this link. + */ + Actual_Socket parent, child; }; -/* - * We used to typedef struct Socket_tag *Socket. - * - * Since we have made the networking abstraction slightly more - * abstract, Socket no longer means a tcp socket (it could mean - * an ssl socket). So now we must use Actual_Socket when we know - * we are talking about a tcp socket. - */ -typedef struct Socket_tag *Actual_Socket; - struct SockAddr_tag { const char *error; /* @@ -97,6 +104,10 @@ static int cmpfortree(void *av, void *bv) return -1; if (as > bs) return +1; + if (a < b) + return -1; + if (a > b) + return +1; return 0; } @@ -422,6 +433,7 @@ Socket sk_register(OSSocket sockfd, Plug plug) ret->pending_error = 0; ret->oobpending = FALSE; ret->listener = 0; + ret->parent = ret->child = NULL; ret->addr = NULL; ret->connected = 1; @@ -453,6 +465,14 @@ static int try_connect(Actual_Socket sock) short localport; int fl, salen; + /* + * Remove the socket from the tree before we overwrite its + * internal socket id, because that forms part of the tree's + * sorting criterion. We'll add it back before exiting this + * function, whether we changed anything or not. + */ + del234(sktree, sock); + if (sock->s >= 0) close(sock->s); @@ -470,6 +490,8 @@ static int try_connect(Actual_Socket sock) goto ret; } + cloexec(s); + if (sock->oobinline) { int b = TRUE; setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (void *) &b, sizeof(b)); @@ -603,9 +625,14 @@ static int try_connect(Actual_Socket sock) } uxsel_tell(sock); - add234(sktree, sock); ret: + + /* + * No matter what happened, put the socket back in the tree. + */ + add234(sktree, sock); + if (err) plug_log(sock->plug, 1, sock->addr, sock->port, strerror(err), err); return err; @@ -632,6 +659,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, 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->listener = 0; ret->addr = addr; @@ -653,7 +681,7 @@ 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 address_family) +Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, int orig_address_family) { int s; #ifndef NO_IPV6 @@ -666,6 +694,7 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, i struct sockaddr_in a; Actual_Socket ret; int retcode; + int address_family; int on = 1; /* @@ -682,6 +711,7 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, i ret->frozen_readable = 0; ret->localhost_only = local_host_only; ret->pending_error = 0; + ret->parent = ret->child = NULL; ret->oobpending = FALSE; ret->listener = 1; ret->addr = NULL; @@ -690,9 +720,9 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, i * Translate address_family from platform-independent constants * into local reality. */ - address_family = (address_family == ADDRTYPE_IPV4 ? AF_INET : + address_family = (orig_address_family == ADDRTYPE_IPV4 ? AF_INET : #ifndef NO_IPV6 - address_family == ADDRTYPE_IPV6 ? AF_INET6 : + orig_address_family == ADDRTYPE_IPV6 ? AF_INET6 : #endif AF_UNSPEC); @@ -723,6 +753,8 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, i return (Socket) ret; } + cloexec(s); + ret->oobinline = 0; setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)); @@ -802,6 +834,32 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, i return (Socket) ret; } +#ifndef NO_IPV6 + /* + * If we were given ADDRTYPE_UNSPEC, we must also create an + * IPv4 listening socket and link it to this one. + */ + if (address_family == AF_INET6 && orig_address_family == ADDRTYPE_UNSPEC) { + Actual_Socket other; + + other = (Actual_Socket) sk_newlistener(srcaddr, port, plug, + local_host_only, ADDRTYPE_IPV4); + + if (other) { + if (!other->error) { + other->parent = ret; + ret->child = other; + } else { + /* If we couldn't create a listening socket on IPv4 as well + * as IPv6, we must return an error overall. */ + close(s); + sfree(ret); + return (Socket) other; + } + } + } +#endif + ret->s = s; uxsel_tell(ret); @@ -814,6 +872,9 @@ static void sk_tcp_close(Socket sock) { Actual_Socket s = (Actual_Socket) sock; + if (s->child) + sk_tcp_close((Socket)s->child); + uxsel_del(s->s); del234(sktree, s); close(s->s); @@ -1056,6 +1117,7 @@ static int net_select_result(int fd, int event) #endif socklen_t addrlen = sizeof(ss); int t; /* socket of connection */ + int fl; memset(&ss, 0, addrlen); t = accept(s->s, (struct sockaddr *)&ss, &addrlen); @@ -1063,6 +1125,10 @@ static int net_select_result(int fd, int event) break; } + fl = fcntl(t, F_GETFL); + if (fl != -1) + fcntl(t, F_SETFL, fl | O_NONBLOCK); + if (s->localhost_only && !sockaddr_is_loopback((struct sockaddr *)&ss)) { close(t); /* someone let nonlocal through?! */ @@ -1266,15 +1332,29 @@ int net_service_lookup(char *service) return 0; } -SockAddr platform_get_x11_unix_address(int displaynum, char **canonicalname) +SockAddr platform_get_x11_unix_address(const char *display, int displaynum, + char **canonicalname) { SockAddr ret = snew(struct SockAddr_tag); int n; memset(ret, 0, sizeof *ret); ret->family = AF_UNIX; - n = snprintf(ret->hostname, sizeof ret->hostname, - "%s%d", X11_UNIX_PATH, displaynum); + /* + * Mac OS X Leopard uses an innovative X display naming + * convention in which the entire display name is the path to + * the Unix socket, including the trailing :0 which only + * _looks_ like a display number. Heuristically, I think + * detecting this by means of a leading slash ought to be + * adequate. + */ + if (display[0] == '/') { + n = snprintf(ret->hostname, sizeof ret->hostname, + "%s", display); + } else { + n = snprintf(ret->hostname, sizeof ret->hostname, + "%s%d", X11_UNIX_PATH, displaynum); + } if(n < 0) ret->error = "snprintf failed"; else if(n >= sizeof ret->hostname)