X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=unix%2Fuxnet.c;h=47490a1eefe235de82fdcf35d8a70c24c5a2ee35;hb=769ce54734f0359068a72d71bca99da9d5d3555e;hp=0bd0f9f5efb06055ea6ab897450ce3b95aebf30b;hpb=6f6e9db932589470975ecc23261cd57eb9e710be;p=PuTTY.git diff --git a/unix/uxnet.c b/unix/uxnet.c index 0bd0f9f5..47490a1e 100644 --- a/unix/uxnet.c +++ b/unix/uxnet.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include #define DEFINE_PLUG_METHOD_MACROS #include "putty.h" @@ -35,14 +37,12 @@ * 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; }; @@ -75,7 +75,6 @@ struct Socket_tag { const char *error; int s; Plug plug; - void *private_ptr; bufchain output_data; int connected; /* irrelevant for listening sockets */ int writable; @@ -127,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 /* @@ -222,7 +224,11 @@ SockAddr sk_namelookup(const char *host, char **canonicalname, int address_famil hints.ai_addr = NULL; hints.ai_canonname = NULL; hints.ai_next = NULL; - err = getaddrinfo(host, NULL, &hints, &ret->ais); + { + char *trimmed_host = host_strduptrim(host); /* strip [] on literals */ + err = getaddrinfo(trimmed_host, NULL, &hints, &ret->ais); + sfree(trimmed_host); + } if (err != 0) { ret->error = gai_strerror(err); return ret; @@ -338,6 +344,43 @@ 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) { + return FALSE; + } else { + return TRUE; + } +} + int sk_hostname_is_local(const char *name) { return !strcmp(name, "localhost") || @@ -469,9 +512,8 @@ 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); +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 = { @@ -481,10 +523,9 @@ static struct socket_function_table tcp_fn_table = { sk_tcp_write_oob, sk_tcp_write_eof, sk_tcp_flush, - sk_tcp_set_private_ptr, - sk_tcp_get_private_ptr, 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) @@ -548,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. @@ -715,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; } @@ -765,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; @@ -799,6 +848,7 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, i ret->incomingeof = FALSE; ret->listener = 1; ret->addr = NULL; + ret->s = -1; /* * Translate address_family from platform-independent constants @@ -863,7 +913,11 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, i hints.ai_next = NULL; assert(port >= 0 && port <= 99999); sprintf(portstr, "%d", port); - retcode = getaddrinfo(srcaddr, portstr, &hints, &ai); + { + char *trimmed_addr = host_strduptrim(srcaddr); + retcode = getaddrinfo(trimmed_addr, portstr, &hints, &ai); + sfree(trimmed_addr); + } if (retcode == 0) { addr = (union sockaddr_union *)ai->ai_addr; addrlen = ai->ai_addrlen; @@ -910,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); @@ -1317,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); @@ -1353,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; @@ -1373,22 +1460,6 @@ static int net_select_result(int fd, int event) return 1; } -/* - * Each socket abstraction contains a `void *' private field in - * which the client can keep state. - */ -static void sk_tcp_set_private_ptr(Socket sock, void *ptr) -{ - Actual_Socket s = (Actual_Socket) sock; - s->private_ptr = ptr; -} - -static void *sk_tcp_get_private_ptr(Socket sock) -{ - Actual_Socket s = (Actual_Socket) sock; - return s->private_ptr; -} - /* * Special error values are returned from sk_namelookup and sk_new * if there's a problem. These functions extract an error message, @@ -1413,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; @@ -1544,6 +1661,7 @@ Socket new_unix_listener(SockAddr listenaddr, Plug plug) ret->incomingeof = FALSE; ret->listener = 1; ret->addr = listenaddr; + ret->s = -1; assert(listenaddr->superfamily == UNIX);