X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=unix%2Fuxnet.c;h=903faeb096744e20e943fec174a2f0063166ad9a;hb=b73c1c1deb9e0c6080ce27d70191a7242886bea3;hp=449ea38a7e40abb9db6cdb8a8f27031407fc4d53;hpb=489590cbd434facbb800e66a78a840f185ed7496;p=PuTTY.git diff --git a/unix/uxnet.c b/unix/uxnet.c index 449ea38a..903faeb0 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,15 @@ void sk_getaddr(SockAddr addr, char *buf, int buflen) } } +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 +484,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 +495,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) @@ -765,11 +778,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; @@ -864,7 +878,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; @@ -911,6 +929,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); @@ -1318,21 +1342,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); @@ -1354,11 +1364,48 @@ 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. + */ + assert(s->addr); + plug_log(s->plug, 1, s->addr, 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; @@ -1374,22 +1421,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, @@ -1414,6 +1445,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;