X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=unix%2Fuxnet.c;h=47490a1eefe235de82fdcf35d8a70c24c5a2ee35;hb=769ce54734f0359068a72d71bca99da9d5d3555e;hp=4d796f5d5cc0eb3b85dcd7cdd16073d3f58e157d;hpb=e00a004e64d9de3f8ff5c4eb745faa326c307057;p=PuTTY.git diff --git a/unix/uxnet.c b/unix/uxnet.c index 4d796f5d..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; }; @@ -126,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 /* @@ -331,7 +334,9 @@ void sk_getaddr(SockAddr addr, char *buf, int buflen) } #else struct in_addr a; - assert(SOCKADDR_FAMILY(addr, ignored_macro_parameter) == AF_INET); + SockAddrStep step; + START_STEP(addr, step); + assert(SOCKADDR_FAMILY(addr, step) == AF_INET); a.s_addr = htonl(addr->addresses[0]); strncpy(buf, inet_ntoa(a), buflen); buf[buflen-1] = '\0'; @@ -339,6 +344,34 @@ 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) { @@ -386,7 +419,9 @@ int sk_address_is_local(SockAddr addr) return sockaddr_is_loopback(addr->ais->ai_addr); #else struct in_addr a; - assert(SOCKADDR_FAMILY(addr, ignored_macro_parameter) == AF_INET); + SockAddrStep step; + START_STEP(addr, step); + assert(SOCKADDR_FAMILY(addr, step) == AF_INET); a.s_addr = htonl(addr->addresses[0]); return ipv4_is_loopback(a); #endif @@ -400,8 +435,10 @@ int sk_address_is_special_local(SockAddr addr) int sk_addrtype(SockAddr addr) { + SockAddrStep step; int family; - family = SOCKADDR_FAMILY(addr, ignored_macro_parameter); + START_STEP(addr, step); + family = SOCKADDR_FAMILY(addr, step); return (family == AF_INET ? ADDRTYPE_IPV4 : #ifndef NO_IPV6 @@ -476,6 +513,7 @@ 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_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 = { @@ -486,7 +524,8 @@ static struct socket_function_table tcp_fn_table = { sk_tcp_write_eof, sk_tcp_flush, 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) @@ -550,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. @@ -717,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; } @@ -767,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; @@ -917,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); @@ -1324,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); @@ -1360,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; @@ -1404,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;