X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/89e97516fedf1d0a7fe9c569bb569fa7ea872afa..4efd5997084ad8fbac105b01da833846f57f8e80:/unix/uxnet.c diff --git a/unix/uxnet.c b/unix/uxnet.c index 2b3d8cbc..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,7 +490,7 @@ static int try_connect(Actual_Socket sock) goto ret; } - fcntl(s, F_SETFD, FD_CLOEXEC); + cloexec(s); if (sock->oobinline) { int b = TRUE; @@ -605,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; @@ -634,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; @@ -655,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 @@ -668,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; /* @@ -684,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; @@ -692,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); @@ -725,7 +753,7 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, i return (Socket) ret; } - fcntl(s, F_SETFD, FD_CLOEXEC); + cloexec(s); ret->oobinline = 0; @@ -806,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); @@ -818,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); @@ -1060,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); @@ -1067,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?! */ @@ -1270,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)