X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/ea05787d6cde533340625d9382ba60373fda3b41..1d009ae71b849a95c86c3117d1b8f063fda8aae5:/unix/uxnet.c diff --git a/unix/uxnet.c b/unix/uxnet.c index 896f473b..0795d4d2 100644 --- a/unix/uxnet.c +++ b/unix/uxnet.c @@ -21,6 +21,8 @@ #include "network.h" #include "tree234.h" +#define ipv4_is_loopback(addr) (inet_netof(addr) == IN_LOOPBACKNET) + struct Socket_tag { struct socket_function_table *fn; /* the above variable absolutely *must* be the first in this structure */ @@ -55,16 +57,24 @@ typedef struct Socket_tag *Actual_Socket; struct SockAddr_tag { char *error; - /* address family this belongs to, AF_INET for IPv4, AF_INET6 for IPv6. */ + /* + * Which address family this address belongs to. AF_INET for + * IPv4; AF_INET6 for IPv6; AF_UNSPEC indicates that name + * resolution has not been done and a simple host name is held + * in this SockAddr structure. + */ int family; unsigned long address; /* Address IPv4 style. */ #ifdef IPV6 struct addrinfo *ai; /* Address IPv6 style. */ #endif + char hostname[512]; /* Store an unresolved host name. */ }; static tree234 *sktree; +static void uxsel_tell(Actual_Socket s); + static int cmpfortree(void *av, void *bv) { Actual_Socket a = (Actual_Socket) av, b = (Actual_Socket) bv; @@ -106,15 +116,12 @@ void sk_cleanup(void) char *error_string(int error) { - char *ret = smalloc(strlen(strerror(error)) + - strlen("Network error: ")); - sprintf(ret, "Network error: %s",strerror(error)); - return ret; + return strerror(error); } -SockAddr sk_namelookup(char *host, char **canonicalname) +SockAddr sk_namelookup(const char *host, char **canonicalname) { - SockAddr ret = smalloc(sizeof(struct SockAddr_tag)); + SockAddr ret = snew(struct SockAddr_tag); unsigned long a; struct hostent *h = NULL; char realhost[8192]; @@ -190,44 +197,84 @@ SockAddr sk_namelookup(char *host, char **canonicalname) } ret->address = ntohl(a); realhost[lenof(realhost)-1] = '\0'; - *canonicalname = smalloc(1+strlen(realhost)); + *canonicalname = snewn(1+strlen(realhost), char); strcpy(*canonicalname, realhost); return ret; } +SockAddr sk_nonamelookup(const char *host) +{ + SockAddr ret = snew(struct SockAddr_tag); + ret->error = NULL; + ret->family = AF_UNSPEC; + strncpy(ret->hostname, host, lenof(ret->hostname)); + ret->hostname[lenof(ret->hostname)-1] = '\0'; + return ret; +} + void sk_getaddr(SockAddr addr, char *buf, int buflen) { #ifdef IPV6 - if (addr->family == AF_INET) { + if (addr->family == AF_INET6) { + FIXME; /* I don't know how to get a text form of an IPv6 address. */ + } else #endif + if (addr->family == AF_INET) { struct in_addr a; a.s_addr = htonl(addr->address); strncpy(buf, inet_ntoa(a), buflen); -#ifdef IPV6 + buf[buflen-1] = '\0'; } else { - FIXME; /* I don't know how to get a text form of an IPv6 address. */ + assert(addr->family == AF_UNSPEC); + strncpy(buf, addr->hostname, buflen); + buf[buflen-1] = '\0'; } +} + +int sk_hostname_is_local(char *name) +{ + return !strcmp(name, "localhost"); +} + +int sk_address_is_local(SockAddr addr) +{ +#ifdef IPV6 + if (addr->family == AF_INET6) { + FIXME; /* someone who can compile for IPV6 had better do this bit */ + } else #endif + if (addr->family == AF_INET) { + struct in_addr a; + a.s_addr = htonl(addr->address); + return ipv4_is_loopback(a); + } else { + assert(addr->family == AF_UNSPEC); + return 0; /* we don't know; assume not */ + } } int sk_addrtype(SockAddr addr) { - return (addr->family == AF_INET ? ADDRTYPE_IPV4 : ADDRTYPE_IPV6); + return (addr->family == AF_INET ? ADDRTYPE_IPV4 : +#ifdef IPV6 + addr->family == AF_INET6 ? ADDRTYPE_IPV6 : +#endif + ADDRTYPE_NAME); } void sk_addrcopy(SockAddr addr, char *buf) { + assert(addr->family != AF_UNSPEC); #ifdef IPV6 - if (addr->family == AF_INET) { + if (addr->family == AF_INET6) { + memcpy(buf, (char*) addr->ai, 16); + } else #endif + if (addr->family == AF_INET) { struct in_addr a; a.s_addr = htonl(addr->address); memcpy(buf, (char*) &a.s_addr, 4); -#ifdef IPV6 - } else { - memcpy(buf, (char*) addr->ai, 16); } -#endif } void sk_addr_free(SockAddr addr) @@ -253,34 +300,34 @@ static void sk_tcp_flush(Socket s) } static void sk_tcp_close(Socket s); -static int sk_tcp_write(Socket s, char *data, int len); -static int sk_tcp_write_oob(Socket s, char *data, int len); +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_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_socket_error(Socket s); +static struct socket_function_table tcp_fn_table = { + sk_tcp_plug, + sk_tcp_close, + sk_tcp_write, + sk_tcp_write_oob, + sk_tcp_flush, + sk_tcp_set_private_ptr, + sk_tcp_get_private_ptr, + sk_tcp_set_frozen, + sk_tcp_socket_error +}; + Socket sk_register(void *sock, Plug plug) { - static struct socket_function_table fn_table = { - sk_tcp_plug, - sk_tcp_close, - sk_tcp_write, - sk_tcp_write_oob, - sk_tcp_flush, - sk_tcp_set_private_ptr, - sk_tcp_get_private_ptr, - sk_tcp_set_frozen, - sk_tcp_socket_error - }; - Actual_Socket ret; /* * Create Socket structure. */ - ret = smalloc(sizeof(struct Socket_tag)); - ret->fn = &fn_table; + ret = snew(struct Socket_tag); + ret->fn = &tcp_fn_table; ret->error = NULL; ret->plug = plug; bufchain_init(&ret->output_data); @@ -302,6 +349,7 @@ Socket sk_register(void *sock, Plug plug) ret->oobinline = 0; + uxsel_tell(ret); add234(sktree, ret); return (Socket) ret; @@ -310,18 +358,6 @@ Socket sk_register(void *sock, Plug plug) Socket sk_new(SockAddr addr, int port, int privport, int oobinline, int nodelay, Plug plug) { - static struct socket_function_table fn_table = { - sk_tcp_plug, - sk_tcp_close, - sk_tcp_write, - sk_tcp_write_oob, - sk_tcp_flush, - sk_tcp_set_private_ptr, - sk_tcp_get_private_ptr, - sk_tcp_set_frozen, - sk_tcp_socket_error - }; - int s; #ifdef IPV6 struct sockaddr_in6 a6; @@ -334,8 +370,8 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, /* * Create Socket structure. */ - ret = smalloc(sizeof(struct Socket_tag)); - ret->fn = &fn_table; + ret = snew(struct Socket_tag); + ret->fn = &tcp_fn_table; ret->error = NULL; ret->plug = plug; bufchain_init(&ret->output_data); @@ -352,6 +388,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, /* * Open socket. */ + assert(addr->family != AF_UNSPEC); s = socket(addr->family, SOCK_STREAM, 0); ret->s = s; @@ -444,6 +481,11 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, a.sin_port = htons((short) port); } + { + int i = 1; + ioctl(s, FIONBIO, &i); + } + if (( #ifdef IPV6 connect(s, ((addr->family == AF_INET6) ? @@ -453,13 +495,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, connect(s, (struct sockaddr *) &a, sizeof(a)) #endif ) < 0) { - /* - * FIXME: We are prepared to receive EWOULDBLOCK here, - * because we might want the connection to be made - * asynchronously; but how do we actually arrange this in - * Unix? I forget. - */ - if ( errno != EWOULDBLOCK ) { + if ( errno != EINPROGRESS ) { ret->error = error_string(errno); return (Socket) ret; } @@ -472,25 +508,14 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, ret->writable = 1; } + uxsel_tell(ret); add234(sktree, ret); return (Socket) ret; } -Socket sk_newlistener(int port, Plug plug, int local_host_only) +Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only) { - static struct socket_function_table fn_table = { - sk_tcp_plug, - sk_tcp_close, - sk_tcp_write, - sk_tcp_write_oob, - sk_tcp_flush, - sk_tcp_set_private_ptr, - sk_tcp_get_private_ptr, - sk_tcp_set_frozen, - sk_tcp_socket_error - }; - int s; #ifdef IPV6 struct sockaddr_in6 a6; @@ -504,8 +529,8 @@ Socket sk_newlistener(int port, Plug plug, int local_host_only) /* * Create Socket structure. */ - ret = smalloc(sizeof(struct Socket_tag)); - ret->fn = &fn_table; + ret = snew(struct Socket_tag); + ret->fn = &tcp_fn_table; ret->error = NULL; ret->plug = plug; bufchain_init(&ret->output_data); @@ -537,6 +562,8 @@ Socket sk_newlistener(int port, Plug plug, int local_host_only) if (addr->family == AF_INET6) { memset(&a6, 0, sizeof(a6)); a6.sin6_family = AF_INET6; + /* FIXME: srcaddr is ignored for IPv6, because I (SGT) don't + * know how to do it. :-) */ if (local_host_only) a6.sin6_addr = in6addr_loopback; else @@ -545,11 +572,32 @@ Socket sk_newlistener(int port, Plug plug, int local_host_only) } else #endif { + int got_addr = 0; a.sin_family = AF_INET; - if (local_host_only) - a.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - else - a.sin_addr.s_addr = htonl(INADDR_ANY); + + /* + * Bind to source address. First try an explicitly + * specified one... + */ + if (srcaddr) { + a.sin_addr.s_addr = inet_addr(srcaddr); + if (a.sin_addr.s_addr != INADDR_NONE) { + /* Override localhost_only with specified listen addr. */ + ret->localhost_only = ipv4_is_loopback(a.sin_addr); + got_addr = 1; + } + } + + /* + * ... and failing that, go with one of the standard ones. + */ + if (!got_addr) { + if (local_host_only) + a.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + else + a.sin_addr.s_addr = htonl(INADDR_ANY); + } + a.sin_port = htons((short)port); } #ifdef IPV6 @@ -579,6 +627,7 @@ Socket sk_newlistener(int port, Plug plug, int local_host_only) return (Socket) ret; } + uxsel_tell(ret); add234(sktree, ret); return (Socket) ret; @@ -588,11 +637,41 @@ static void sk_tcp_close(Socket sock) { Actual_Socket s = (Actual_Socket) sock; + uxsel_del(s->s); del234(sktree, s); close(s->s); sfree(s); } +int sk_getxdmdata(void *sock, unsigned long *ip, int *port) +{ + Actual_Socket s = (Actual_Socket) sock; + struct sockaddr_in addr; + socklen_t addrlen; + + /* + * We must check that this socket really _is_ an Actual_Socket. + */ + if (s->fn != &tcp_fn_table) + return 0; /* failure */ + + /* + * If we ever implement connecting to a local X server through + * a Unix socket, we return 0xFFFFFFFF for the IP address and + * our current pid for the port. Bizarre, but such is life. + */ + + addrlen = sizeof(addr); + if (getsockname(s->s, (struct sockaddr *)&addr, &addrlen) < 0 || + addr.sin_family != AF_INET) + return 0; + + *ip = ntohl(addr.sin_addr.s_addr); + *port = ntohs(addr.sin_port); + + return 1; +} + /* * The function which tries to send on a socket once it's deemed * writable. @@ -656,9 +735,10 @@ void try_send(Actual_Socket s) } } } + uxsel_tell(s); } -static int sk_tcp_write(Socket sock, char *buf, int len) +static int sk_tcp_write(Socket sock, const char *buf, int len) { Actual_Socket s = (Actual_Socket) sock; @@ -676,7 +756,7 @@ static int sk_tcp_write(Socket sock, char *buf, int len) return bufchain_size(&s->output_data); } -static int sk_tcp_write_oob(Socket sock, char *buf, int len) +static int sk_tcp_write_oob(Socket sock, const char *buf, int len) { Actual_Socket s = (Actual_Socket) sock; @@ -697,7 +777,7 @@ static int sk_tcp_write_oob(Socket sock, char *buf, int len) return s->sending_oob; } -int select_result(int fd, int event) +static int net_select_result(int fd, int event) { int ret; int err; @@ -713,11 +793,6 @@ int select_result(int fd, int event) noise_ultralight(event); switch (event) { -#ifdef FIXME_NONBLOCKING_CONNECTIONS - case FIXME: /* connected */ - s->connected = s->writable = 1; - break; -#endif case 4: /* exceptional */ if (!s->oobinline) { /* @@ -766,8 +841,7 @@ int select_result(int fd, int event) break; } - if (s->localhost_only && - ntohl(isa.sin_addr.s_addr) != INADDR_LOOPBACK) { + if (s->localhost_only && !ipv4_is_loopback(isa.sin_addr)) { close(t); /* someone let nonlocal through?! */ } else if (plug_accepting(s->plug, (void*)t)) { close(t); /* denied or error */ @@ -815,7 +889,15 @@ int select_result(int fd, int event) } break; case 2: /* writable */ - { + if (!s->connected) { + /* + * select() reports a socket as _writable_ when an + * asynchronous connection is completed. + */ + s->connected = s->writable = 1; + uxsel_tell(s); + break; + } else { int bufsize_before, bufsize_after; s->writable = 1; bufsize_before = s->sending_oob + bufchain_size(&s->output_data); @@ -909,42 +991,21 @@ static void sk_tcp_set_frozen(Socket sock, int is_frozen) recv(s->s, &c, 1, MSG_PEEK); } s->frozen_readable = 0; + uxsel_tell(s); } -/* - * For Unix select()-based frontends: enumerate all sockets - * currently active, and state whether we currently wish to receive - * select events on them for reading, writing and exceptional - * status. - */ -static void set_rwx(Actual_Socket s, int *rwx) +static void uxsel_tell(Actual_Socket s) { - int val = 0; + int rwx = 0; + if (!s->connected) + rwx |= 2; /* write == connect */ if (s->connected && !s->frozen) - val |= 1 | 4; /* read, except */ + rwx |= 1 | 4; /* read, except */ if (bufchain_size(&s->output_data)) - val |= 2; /* write */ + rwx |= 2; /* write */ if (s->listener) - val |= 1; /* read == accept */ - *rwx = val; -} - -int first_socket(int *state, int *rwx) -{ - Actual_Socket s; - *state = 0; - s = index234(sktree, (*state)++); - if (s) - set_rwx(s, rwx); - return s ? s->s : -1; -} - -int next_socket(int *state, int *rwx) -{ - Actual_Socket s = index234(sktree, (*state)++); - if (s) - set_rwx(s, rwx); - return s ? s->s : -1; + rwx |= 1; /* read == accept */ + uxsel_set(s->s, rwx, net_select_result); } int net_service_lookup(char *service)