+++ /dev/null
-/*\r
- * Unix networking abstraction.\r
- */\r
-\r
-#include <stdio.h>\r
-#include <stdlib.h>\r
-#include <assert.h>\r
-#include <errno.h>\r
-#include <fcntl.h>\r
-#include <unistd.h>\r
-#include <sys/types.h>\r
-#include <sys/socket.h>\r
-#include <sys/ioctl.h>\r
-#include <arpa/inet.h>\r
-#include <netinet/in.h>\r
-#include <netinet/tcp.h>\r
-#include <netdb.h>\r
-#include <sys/un.h>\r
-\r
-#define DEFINE_PLUG_METHOD_MACROS\r
-#include "putty.h"\r
-#include "network.h"\r
-#include "tree234.h"\r
-\r
-/* Solaris needs <sys/sockio.h> for SIOCATMARK. */\r
-#ifndef SIOCATMARK\r
-#include <sys/sockio.h>\r
-#endif\r
-\r
-#ifndef X11_UNIX_PATH\r
-# define X11_UNIX_PATH "/tmp/.X11-unix/X"\r
-#endif\r
-\r
-/* \r
- * Access to sockaddr types without breaking C strict aliasing rules.\r
- */\r
-union sockaddr_union {\r
-#ifdef NO_IPV6\r
- struct sockaddr_in storage;\r
-#else\r
- struct sockaddr_storage storage;\r
- struct sockaddr_in6 sin6;\r
-#endif\r
- struct sockaddr sa;\r
- struct sockaddr_in sin;\r
- struct sockaddr_un su;\r
-};\r
-\r
-/*\r
- * We used to typedef struct Socket_tag *Socket.\r
- *\r
- * Since we have made the networking abstraction slightly more\r
- * abstract, Socket no longer means a tcp socket (it could mean\r
- * an ssl socket). So now we must use Actual_Socket when we know\r
- * we are talking about a tcp socket.\r
- */\r
-typedef struct Socket_tag *Actual_Socket;\r
-\r
-/*\r
- * Mutable state that goes with a SockAddr: stores information\r
- * about where in the list of candidate IP(v*) addresses we've\r
- * currently got to.\r
- */\r
-typedef struct SockAddrStep_tag SockAddrStep;\r
-struct SockAddrStep_tag {\r
-#ifndef NO_IPV6\r
- struct addrinfo *ai; /* steps along addr->ais */\r
-#endif\r
- int curraddr;\r
-};\r
-\r
-struct Socket_tag {\r
- struct socket_function_table *fn;\r
- /* the above variable absolutely *must* be the first in this structure */\r
- const char *error;\r
- int s;\r
- Plug plug;\r
- void *private_ptr;\r
- bufchain output_data;\r
- int connected; /* irrelevant for listening sockets */\r
- int writable;\r
- int frozen; /* this causes readability notifications to be ignored */\r
- int frozen_readable; /* this means we missed at least one readability\r
- * notification while we were frozen */\r
- int localhost_only; /* for listening sockets */\r
- char oobdata[1];\r
- int sending_oob;\r
- int oobpending; /* is there OOB data available to read? */\r
- int oobinline;\r
- int pending_error; /* in case send() returns error */\r
- int listener;\r
- int nodelay, keepalive; /* for connect()-type sockets */\r
- int privport, port; /* and again */\r
- SockAddr addr;\r
- SockAddrStep step;\r
- /*\r
- * We sometimes need pairs of Socket structures to be linked:\r
- * if we are listening on the same IPv6 and v4 port, for\r
- * example. So here we define `parent' and `child' pointers to\r
- * track this link.\r
- */\r
- Actual_Socket parent, child;\r
-};\r
-\r
-struct SockAddr_tag {\r
- int refcount;\r
- const char *error;\r
- enum { UNRESOLVED, UNIX, IP } superfamily;\r
-#ifndef NO_IPV6\r
- struct addrinfo *ais; /* Addresses IPv6 style. */\r
-#else\r
- unsigned long *addresses; /* Addresses IPv4 style. */\r
- int naddresses;\r
-#endif\r
- char hostname[512]; /* Store an unresolved host name. */\r
-};\r
-\r
-/*\r
- * Which address family this address belongs to. AF_INET for IPv4;\r
- * AF_INET6 for IPv6; AF_UNSPEC indicates that name resolution has\r
- * not been done and a simple host name is held in this SockAddr\r
- * structure.\r
- */\r
-#ifndef NO_IPV6\r
-#define SOCKADDR_FAMILY(addr, step) \\r
- ((addr)->superfamily == UNRESOLVED ? AF_UNSPEC : \\r
- (addr)->superfamily == UNIX ? AF_UNIX : \\r
- (step).ai ? (step).ai->ai_family : AF_INET)\r
-#else\r
-#define SOCKADDR_FAMILY(addr, step) \\r
- ((addr)->superfamily == UNRESOLVED ? AF_UNSPEC : \\r
- (addr)->superfamily == UNIX ? AF_UNIX : AF_INET)\r
-#endif\r
-\r
-/*\r
- * Start a SockAddrStep structure to step through multiple\r
- * addresses.\r
- */\r
-#ifndef NO_IPV6\r
-#define START_STEP(addr, step) \\r
- ((step).ai = (addr)->ais, (step).curraddr = 0)\r
-#else\r
-#define START_STEP(addr, step) \\r
- ((step).curraddr = 0)\r
-#endif\r
-\r
-static tree234 *sktree;\r
-\r
-static void uxsel_tell(Actual_Socket s);\r
-\r
-static int cmpfortree(void *av, void *bv)\r
-{\r
- Actual_Socket a = (Actual_Socket) av, b = (Actual_Socket) bv;\r
- int as = a->s, bs = b->s;\r
- if (as < bs)\r
- return -1;\r
- if (as > bs)\r
- return +1;\r
- if (a < b)\r
- return -1;\r
- if (a > b)\r
- return +1;\r
- return 0;\r
-}\r
-\r
-static int cmpforsearch(void *av, void *bv)\r
-{\r
- Actual_Socket b = (Actual_Socket) bv;\r
- int as = *(int *)av, bs = b->s;\r
- if (as < bs)\r
- return -1;\r
- if (as > bs)\r
- return +1;\r
- return 0;\r
-}\r
-\r
-void sk_init(void)\r
-{\r
- sktree = newtree234(cmpfortree);\r
-}\r
-\r
-void sk_cleanup(void)\r
-{\r
- Actual_Socket s;\r
- int i;\r
-\r
- if (sktree) {\r
- for (i = 0; (s = index234(sktree, i)) != NULL; i++) {\r
- close(s->s);\r
- }\r
- }\r
-}\r
-\r
-SockAddr sk_namelookup(const char *host, char **canonicalname, int address_family)\r
-{\r
- SockAddr ret = snew(struct SockAddr_tag);\r
-#ifndef NO_IPV6\r
- struct addrinfo hints;\r
- int err;\r
-#else\r
- unsigned long a;\r
- struct hostent *h = NULL;\r
- int n;\r
-#endif\r
- char realhost[8192];\r
-\r
- /* Clear the structure and default to IPv4. */\r
- memset(ret, 0, sizeof(struct SockAddr_tag));\r
- ret->superfamily = UNRESOLVED;\r
- *realhost = '\0';\r
- ret->error = NULL;\r
- ret->refcount = 1;\r
-\r
-#ifndef NO_IPV6\r
- hints.ai_flags = AI_CANONNAME;\r
- hints.ai_family = (address_family == ADDRTYPE_IPV4 ? AF_INET :\r
- address_family == ADDRTYPE_IPV6 ? AF_INET6 :\r
- AF_UNSPEC);\r
- hints.ai_socktype = SOCK_STREAM;\r
- hints.ai_protocol = 0;\r
- hints.ai_addrlen = 0;\r
- hints.ai_addr = NULL;\r
- hints.ai_canonname = NULL;\r
- hints.ai_next = NULL;\r
- err = getaddrinfo(host, NULL, &hints, &ret->ais);\r
- if (err != 0) {\r
- ret->error = gai_strerror(err);\r
- return ret;\r
- }\r
- ret->superfamily = IP;\r
- *realhost = '\0';\r
- if (ret->ais->ai_canonname != NULL)\r
- strncat(realhost, ret->ais->ai_canonname, sizeof(realhost) - 1);\r
- else\r
- strncat(realhost, host, sizeof(realhost) - 1);\r
-#else\r
- if ((a = inet_addr(host)) == (unsigned long)(in_addr_t)(-1)) {\r
- /*\r
- * Otherwise use the IPv4-only gethostbyname... (NOTE:\r
- * we don't use gethostbyname as a fallback!)\r
- */\r
- if (ret->superfamily == UNRESOLVED) {\r
- /*debug(("Resolving \"%s\" with gethostbyname() (IPv4 only)...\n", host)); */\r
- if ( (h = gethostbyname(host)) )\r
- ret->superfamily = IP;\r
- }\r
- if (ret->superfamily == UNRESOLVED) {\r
- ret->error = (h_errno == HOST_NOT_FOUND ||\r
- h_errno == NO_DATA ||\r
- h_errno == NO_ADDRESS ? "Host does not exist" :\r
- h_errno == TRY_AGAIN ?\r
- "Temporary name service failure" :\r
- "gethostbyname: unknown error");\r
- return ret;\r
- }\r
- /* This way we are always sure the h->h_name is valid :) */\r
- strncpy(realhost, h->h_name, sizeof(realhost));\r
- for (n = 0; h->h_addr_list[n]; n++);\r
- ret->addresses = snewn(n, unsigned long);\r
- ret->naddresses = n;\r
- for (n = 0; n < ret->naddresses; n++) {\r
- memcpy(&a, h->h_addr_list[n], sizeof(a));\r
- ret->addresses[n] = ntohl(a);\r
- }\r
- } else {\r
- /*\r
- * This must be a numeric IPv4 address because it caused a\r
- * success return from inet_addr.\r
- */\r
- ret->superfamily = IP;\r
- strncpy(realhost, host, sizeof(realhost));\r
- ret->addresses = snew(unsigned long);\r
- ret->naddresses = 1;\r
- ret->addresses[0] = ntohl(a);\r
- }\r
-#endif\r
- realhost[lenof(realhost)-1] = '\0';\r
- *canonicalname = snewn(1+strlen(realhost), char);\r
- strcpy(*canonicalname, realhost);\r
- return ret;\r
-}\r
-\r
-SockAddr sk_nonamelookup(const char *host)\r
-{\r
- SockAddr ret = snew(struct SockAddr_tag);\r
- ret->error = NULL;\r
- ret->superfamily = UNRESOLVED;\r
- strncpy(ret->hostname, host, lenof(ret->hostname));\r
- ret->hostname[lenof(ret->hostname)-1] = '\0';\r
-#ifndef NO_IPV6\r
- ret->ais = NULL;\r
-#else\r
- ret->addresses = NULL;\r
-#endif\r
- ret->refcount = 1;\r
- return ret;\r
-}\r
-\r
-static int sk_nextaddr(SockAddr addr, SockAddrStep *step)\r
-{\r
-#ifndef NO_IPV6\r
- if (step->ai && step->ai->ai_next) {\r
- step->ai = step->ai->ai_next;\r
- return TRUE;\r
- } else\r
- return FALSE;\r
-#else\r
- if (step->curraddr+1 < addr->naddresses) {\r
- step->curraddr++;\r
- return TRUE;\r
- } else {\r
- return FALSE;\r
- }\r
-#endif \r
-}\r
-\r
-void sk_getaddr(SockAddr addr, char *buf, int buflen)\r
-{\r
- /* XXX not clear what we should return for Unix-domain sockets; let's\r
- * hope the question never arises */\r
- assert(addr->superfamily != UNIX);\r
- if (addr->superfamily == UNRESOLVED) {\r
- strncpy(buf, addr->hostname, buflen);\r
- buf[buflen-1] = '\0';\r
- } else {\r
-#ifndef NO_IPV6\r
- if (getnameinfo(addr->ais->ai_addr, addr->ais->ai_addrlen, buf, buflen,\r
- NULL, 0, NI_NUMERICHOST) != 0) {\r
- buf[0] = '\0';\r
- strncat(buf, "<unknown>", buflen - 1);\r
- }\r
-#else\r
- struct in_addr a;\r
- SockAddrStep step;\r
- START_STEP(addr, step);\r
- assert(SOCKADDR_FAMILY(addr, step) == AF_INET);\r
- a.s_addr = htonl(addr->addresses[0]);\r
- strncpy(buf, inet_ntoa(a), buflen);\r
- buf[buflen-1] = '\0';\r
-#endif\r
- }\r
-}\r
-\r
-int sk_hostname_is_local(char *name)\r
-{\r
- return !strcmp(name, "localhost") ||\r
- !strcmp(name, "::1") ||\r
- !strncmp(name, "127.", 4);\r
-}\r
-\r
-#define ipv4_is_loopback(addr) \\r
- (((addr).s_addr & htonl(0xff000000)) == htonl(0x7f000000))\r
-\r
-static int sockaddr_is_loopback(struct sockaddr *sa)\r
-{\r
- union sockaddr_union *u = (union sockaddr_union *)sa;\r
- switch (u->sa.sa_family) {\r
- case AF_INET:\r
- return ipv4_is_loopback(u->sin.sin_addr);\r
-#ifndef NO_IPV6\r
- case AF_INET6:\r
- return IN6_IS_ADDR_LOOPBACK(&u->sin6.sin6_addr);\r
-#endif\r
- case AF_UNIX:\r
- return TRUE;\r
- default:\r
- return FALSE;\r
- }\r
-}\r
-\r
-int sk_address_is_local(SockAddr addr)\r
-{\r
- if (addr->superfamily == UNRESOLVED)\r
- return 0; /* we don't know; assume not */\r
- else if (addr->superfamily == UNIX)\r
- return 1;\r
- else {\r
-#ifndef NO_IPV6\r
- return sockaddr_is_loopback(addr->ais->ai_addr);\r
-#else\r
- struct in_addr a;\r
- SockAddrStep step;\r
- START_STEP(addr, step);\r
- assert(SOCKADDR_FAMILY(addr, step) == AF_INET);\r
- a.s_addr = htonl(addr->addresses[0]);\r
- return ipv4_is_loopback(a);\r
-#endif\r
- }\r
-}\r
-\r
-int sk_addrtype(SockAddr addr)\r
-{\r
- SockAddrStep step;\r
- int family;\r
- START_STEP(addr, step);\r
- family = SOCKADDR_FAMILY(addr, step);\r
-\r
- return (family == AF_INET ? ADDRTYPE_IPV4 :\r
-#ifndef NO_IPV6\r
- family == AF_INET6 ? ADDRTYPE_IPV6 :\r
-#endif\r
- ADDRTYPE_NAME);\r
-}\r
-\r
-void sk_addrcopy(SockAddr addr, char *buf)\r
-{\r
- SockAddrStep step;\r
- int family;\r
- START_STEP(addr, step);\r
- family = SOCKADDR_FAMILY(addr, step);\r
-\r
-#ifndef NO_IPV6\r
- if (family == AF_INET)\r
- memcpy(buf, &((struct sockaddr_in *)step.ai->ai_addr)->sin_addr,\r
- sizeof(struct in_addr));\r
- else if (family == AF_INET6)\r
- memcpy(buf, &((struct sockaddr_in6 *)step.ai->ai_addr)->sin6_addr,\r
- sizeof(struct in6_addr));\r
- else\r
- assert(FALSE);\r
-#else\r
- struct in_addr a;\r
-\r
- assert(family == AF_INET);\r
- a.s_addr = htonl(addr->addresses[step.curraddr]);\r
- memcpy(buf, (char*) &a.s_addr, 4);\r
-#endif\r
-}\r
-\r
-void sk_addr_free(SockAddr addr)\r
-{\r
- if (--addr->refcount > 0)\r
- return;\r
-#ifndef NO_IPV6\r
- if (addr->ais != NULL)\r
- freeaddrinfo(addr->ais);\r
-#else\r
- sfree(addr->addresses);\r
-#endif\r
- sfree(addr);\r
-}\r
-\r
-SockAddr sk_addr_dup(SockAddr addr)\r
-{\r
- addr->refcount++;\r
- return addr;\r
-}\r
-\r
-static Plug sk_tcp_plug(Socket sock, Plug p)\r
-{\r
- Actual_Socket s = (Actual_Socket) sock;\r
- Plug ret = s->plug;\r
- if (p)\r
- s->plug = p;\r
- return ret;\r
-}\r
-\r
-static void sk_tcp_flush(Socket s)\r
-{\r
- /*\r
- * We send data to the socket as soon as we can anyway,\r
- * so we don't need to do anything here. :-)\r
- */\r
-}\r
-\r
-static void sk_tcp_close(Socket s);\r
-static int sk_tcp_write(Socket s, const char *data, int len);\r
-static int sk_tcp_write_oob(Socket s, const char *data, int len);\r
-static void sk_tcp_set_private_ptr(Socket s, void *ptr);\r
-static void *sk_tcp_get_private_ptr(Socket s);\r
-static void sk_tcp_set_frozen(Socket s, int is_frozen);\r
-static const char *sk_tcp_socket_error(Socket s);\r
-\r
-static struct socket_function_table tcp_fn_table = {\r
- sk_tcp_plug,\r
- sk_tcp_close,\r
- sk_tcp_write,\r
- sk_tcp_write_oob,\r
- sk_tcp_flush,\r
- sk_tcp_set_private_ptr,\r
- sk_tcp_get_private_ptr,\r
- sk_tcp_set_frozen,\r
- sk_tcp_socket_error\r
-};\r
-\r
-Socket sk_register(OSSocket sockfd, Plug plug)\r
-{\r
- Actual_Socket ret;\r
-\r
- /*\r
- * Create Socket structure.\r
- */\r
- ret = snew(struct Socket_tag);\r
- ret->fn = &tcp_fn_table;\r
- ret->error = NULL;\r
- ret->plug = plug;\r
- bufchain_init(&ret->output_data);\r
- ret->writable = 1; /* to start with */\r
- ret->sending_oob = 0;\r
- ret->frozen = 1;\r
- ret->frozen_readable = 0;\r
- ret->localhost_only = 0; /* unused, but best init anyway */\r
- ret->pending_error = 0;\r
- ret->oobpending = FALSE;\r
- ret->listener = 0;\r
- ret->parent = ret->child = NULL;\r
- ret->addr = NULL;\r
- ret->connected = 1;\r
-\r
- ret->s = sockfd;\r
-\r
- if (ret->s < 0) {\r
- ret->error = strerror(errno);\r
- return (Socket) ret;\r
- }\r
-\r
- ret->oobinline = 0;\r
-\r
- uxsel_tell(ret);\r
- add234(sktree, ret);\r
-\r
- return (Socket) ret;\r
-}\r
-\r
-static int try_connect(Actual_Socket sock)\r
-{\r
- int s;\r
- union sockaddr_union u;\r
- const union sockaddr_union *sa;\r
- int err = 0;\r
- short localport;\r
- int fl, salen, family;\r
-\r
- /*\r
- * Remove the socket from the tree before we overwrite its\r
- * internal socket id, because that forms part of the tree's\r
- * sorting criterion. We'll add it back before exiting this\r
- * function, whether we changed anything or not.\r
- */\r
- del234(sktree, sock);\r
-\r
- if (sock->s >= 0)\r
- close(sock->s);\r
-\r
- plug_log(sock->plug, 0, sock->addr, sock->port, NULL, 0);\r
-\r
- /*\r
- * Open socket.\r
- */\r
- family = SOCKADDR_FAMILY(sock->addr, sock->step);\r
- assert(family != AF_UNSPEC);\r
- s = socket(family, SOCK_STREAM, 0);\r
- sock->s = s;\r
-\r
- if (s < 0) {\r
- err = errno;\r
- goto ret;\r
- }\r
-\r
- cloexec(s);\r
-\r
- if (sock->oobinline) {\r
- int b = TRUE;\r
- setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (void *) &b, sizeof(b));\r
- }\r
-\r
- if (sock->nodelay) {\r
- int b = TRUE;\r
- setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void *) &b, sizeof(b));\r
- }\r
-\r
- if (sock->keepalive) {\r
- int b = TRUE;\r
- setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *) &b, sizeof(b));\r
- }\r
-\r
- /*\r
- * Bind to local address.\r
- */\r
- if (sock->privport)\r
- localport = 1023; /* count from 1023 downwards */\r
- else\r
- localport = 0; /* just use port 0 (ie kernel picks) */\r
-\r
- /* BSD IP stacks need sockaddr_in zeroed before filling in */\r
- memset(&u,'\0',sizeof(u));\r
-\r
- /* We don't try to bind to a local address for UNIX domain sockets. (Why\r
- * do we bother doing the bind when localport == 0 anyway?) */\r
- if (family != AF_UNIX) {\r
- /* Loop round trying to bind */\r
- while (1) {\r
- int retcode;\r
-\r
-#ifndef NO_IPV6\r
- if (family == AF_INET6) {\r
- /* XXX use getaddrinfo to get a local address? */\r
- u.sin6.sin6_family = AF_INET6;\r
- u.sin6.sin6_addr = in6addr_any;\r
- u.sin6.sin6_port = htons(localport);\r
- retcode = bind(s, &u.sa, sizeof(u.sin6));\r
- } else\r
-#endif\r
- {\r
- assert(family == AF_INET);\r
- u.sin.sin_family = AF_INET;\r
- u.sin.sin_addr.s_addr = htonl(INADDR_ANY);\r
- u.sin.sin_port = htons(localport);\r
- retcode = bind(s, &u.sa, sizeof(u.sin));\r
- }\r
- if (retcode >= 0) {\r
- err = 0;\r
- break; /* done */\r
- } else {\r
- err = errno;\r
- if (err != EADDRINUSE) /* failed, for a bad reason */\r
- break;\r
- }\r
- \r
- if (localport == 0)\r
- break; /* we're only looping once */\r
- localport--;\r
- if (localport == 0)\r
- break; /* we might have got to the end */\r
- }\r
- \r
- if (err)\r
- goto ret;\r
- }\r
-\r
- /*\r
- * Connect to remote address.\r
- */\r
- switch(family) {\r
-#ifndef NO_IPV6\r
- case AF_INET:\r
- /* XXX would be better to have got getaddrinfo() to fill in the port. */\r
- ((struct sockaddr_in *)sock->step.ai->ai_addr)->sin_port =\r
- htons(sock->port);\r
- sa = (const union sockaddr_union *)sock->step.ai->ai_addr;\r
- salen = sock->step.ai->ai_addrlen;\r
- break;\r
- case AF_INET6:\r
- ((struct sockaddr_in *)sock->step.ai->ai_addr)->sin_port =\r
- htons(sock->port);\r
- sa = (const union sockaddr_union *)sock->step.ai->ai_addr;\r
- salen = sock->step.ai->ai_addrlen;\r
- break;\r
-#else\r
- case AF_INET:\r
- u.sin.sin_family = AF_INET;\r
- u.sin.sin_addr.s_addr = htonl(sock->addr->addresses[sock->step.curraddr]);\r
- u.sin.sin_port = htons((short) sock->port);\r
- sa = &u;\r
- salen = sizeof u.sin;\r
- break;\r
-#endif\r
- case AF_UNIX:\r
- assert(sock->port == 0); /* to catch confused people */\r
- assert(strlen(sock->addr->hostname) < sizeof u.su.sun_path);\r
- u.su.sun_family = AF_UNIX;\r
- strcpy(u.su.sun_path, sock->addr->hostname);\r
- sa = &u;\r
- salen = sizeof u.su;\r
- break;\r
-\r
- default:\r
- assert(0 && "unknown address family");\r
- exit(1); /* XXX: GCC doesn't understand assert() on some systems. */\r
- }\r
-\r
- fl = fcntl(s, F_GETFL);\r
- if (fl != -1)\r
- fcntl(s, F_SETFL, fl | O_NONBLOCK);\r
-\r
- if ((connect(s, &(sa->sa), salen)) < 0) {\r
- if ( errno != EINPROGRESS ) {\r
- err = errno;\r
- goto ret;\r
- }\r
- } else {\r
- /*\r
- * If we _don't_ get EWOULDBLOCK, the connect has completed\r
- * and we should set the socket as connected and writable.\r
- */\r
- sock->connected = 1;\r
- sock->writable = 1;\r
- }\r
-\r
- uxsel_tell(sock);\r
-\r
- ret:\r
-\r
- /*\r
- * No matter what happened, put the socket back in the tree.\r
- */\r
- add234(sktree, sock);\r
-\r
- if (err)\r
- plug_log(sock->plug, 1, sock->addr, sock->port, strerror(err), err);\r
- return err;\r
-}\r
-\r
-Socket sk_new(SockAddr addr, int port, int privport, int oobinline,\r
- int nodelay, int keepalive, Plug plug)\r
-{\r
- Actual_Socket ret;\r
- int err;\r
-\r
- /*\r
- * Create Socket structure.\r
- */\r
- ret = snew(struct Socket_tag);\r
- ret->fn = &tcp_fn_table;\r
- ret->error = NULL;\r
- ret->plug = plug;\r
- bufchain_init(&ret->output_data);\r
- ret->connected = 0; /* to start with */\r
- ret->writable = 0; /* to start with */\r
- ret->sending_oob = 0;\r
- ret->frozen = 0;\r
- ret->frozen_readable = 0;\r
- ret->localhost_only = 0; /* unused, but best init anyway */\r
- ret->pending_error = 0;\r
- ret->parent = ret->child = NULL;\r
- ret->oobpending = FALSE;\r
- ret->listener = 0;\r
- ret->addr = addr;\r
- START_STEP(ret->addr, ret->step);\r
- ret->s = -1;\r
- ret->oobinline = oobinline;\r
- ret->nodelay = nodelay;\r
- ret->keepalive = keepalive;\r
- ret->privport = privport;\r
- ret->port = port;\r
-\r
- err = 0;\r
- do {\r
- err = try_connect(ret);\r
- } while (err && sk_nextaddr(ret->addr, &ret->step));\r
-\r
- if (err)\r
- ret->error = strerror(err);\r
-\r
- return (Socket) ret;\r
-}\r
-\r
-Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only, int orig_address_family)\r
-{\r
- int s;\r
-#ifndef NO_IPV6\r
- struct addrinfo hints, *ai;\r
- char portstr[6];\r
-#endif\r
- union sockaddr_union u;\r
- union sockaddr_union *addr;\r
- int addrlen;\r
- Actual_Socket ret;\r
- int retcode;\r
- int address_family;\r
- int on = 1;\r
-\r
- /*\r
- * Create Socket structure.\r
- */\r
- ret = snew(struct Socket_tag);\r
- ret->fn = &tcp_fn_table;\r
- ret->error = NULL;\r
- ret->plug = plug;\r
- bufchain_init(&ret->output_data);\r
- ret->writable = 0; /* to start with */\r
- ret->sending_oob = 0;\r
- ret->frozen = 0;\r
- ret->frozen_readable = 0;\r
- ret->localhost_only = local_host_only;\r
- ret->pending_error = 0;\r
- ret->parent = ret->child = NULL;\r
- ret->oobpending = FALSE;\r
- ret->listener = 1;\r
- ret->addr = NULL;\r
-\r
- /*\r
- * Translate address_family from platform-independent constants\r
- * into local reality.\r
- */\r
- address_family = (orig_address_family == ADDRTYPE_IPV4 ? AF_INET :\r
-#ifndef NO_IPV6\r
- orig_address_family == ADDRTYPE_IPV6 ? AF_INET6 :\r
-#endif\r
- AF_UNSPEC);\r
-\r
-#ifndef NO_IPV6\r
- /* Let's default to IPv6.\r
- * If the stack doesn't support IPv6, we will fall back to IPv4. */\r
- if (address_family == AF_UNSPEC) address_family = AF_INET6;\r
-#else\r
- /* No other choice, default to IPv4 */\r
- if (address_family == AF_UNSPEC) address_family = AF_INET;\r
-#endif\r
-\r
- /*\r
- * Open socket.\r
- */\r
- s = socket(address_family, SOCK_STREAM, 0);\r
-\r
-#ifndef NO_IPV6\r
- /* If the host doesn't support IPv6 try fallback to IPv4. */\r
- if (s < 0 && address_family == AF_INET6) {\r
- address_family = AF_INET;\r
- s = socket(address_family, SOCK_STREAM, 0);\r
- }\r
-#endif\r
-\r
- if (s < 0) {\r
- ret->error = strerror(errno);\r
- return (Socket) ret;\r
- }\r
-\r
- cloexec(s);\r
-\r
- ret->oobinline = 0;\r
-\r
- setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on));\r
-\r
- retcode = -1;\r
- addr = NULL; addrlen = -1; /* placate optimiser */\r
-\r
- if (srcaddr != NULL) {\r
-#ifndef NO_IPV6\r
- hints.ai_flags = AI_NUMERICHOST;\r
- hints.ai_family = address_family;\r
- hints.ai_socktype = SOCK_STREAM;\r
- hints.ai_protocol = 0;\r
- hints.ai_addrlen = 0;\r
- hints.ai_addr = NULL;\r
- hints.ai_canonname = NULL;\r
- hints.ai_next = NULL;\r
- assert(port >= 0 && port <= 99999);\r
- sprintf(portstr, "%d", port);\r
- retcode = getaddrinfo(srcaddr, portstr, &hints, &ai);\r
- if (retcode == 0) {\r
- addr = (union sockaddr_union *)ai->ai_addr;\r
- addrlen = ai->ai_addrlen;\r
- }\r
-#else\r
- memset(&u,'\0',sizeof u);\r
- u.sin.sin_family = AF_INET;\r
- u.sin.sin_port = htons(port);\r
- u.sin.sin_addr.s_addr = inet_addr(srcaddr);\r
- if (u.sin.sin_addr.s_addr != (in_addr_t)(-1)) {\r
- /* Override localhost_only with specified listen addr. */\r
- ret->localhost_only = ipv4_is_loopback(u.sin.sin_addr);\r
- }\r
- addr = &u;\r
- addrlen = sizeof(u.sin);\r
- retcode = 0;\r
-#endif\r
- }\r
-\r
- if (retcode != 0) {\r
- memset(&u,'\0',sizeof u);\r
-#ifndef NO_IPV6\r
- if (address_family == AF_INET6) {\r
- u.sin6.sin6_family = AF_INET6;\r
- u.sin6.sin6_port = htons(port);\r
- if (local_host_only)\r
- u.sin6.sin6_addr = in6addr_loopback;\r
- else\r
- u.sin6.sin6_addr = in6addr_any;\r
- addr = &u;\r
- addrlen = sizeof(u.sin6);\r
- } else\r
-#endif\r
- {\r
- u.sin.sin_family = AF_INET;\r
- u.sin.sin_port = htons(port);\r
- if (local_host_only)\r
- u.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);\r
- else\r
- u.sin.sin_addr.s_addr = htonl(INADDR_ANY);\r
- addr = &u;\r
- addrlen = sizeof(u.sin);\r
- }\r
- }\r
-\r
- retcode = bind(s, &addr->sa, addrlen);\r
- if (retcode < 0) {\r
- close(s);\r
- ret->error = strerror(errno);\r
- return (Socket) ret;\r
- }\r
-\r
- if (listen(s, SOMAXCONN) < 0) {\r
- close(s);\r
- ret->error = strerror(errno);\r
- return (Socket) ret;\r
- }\r
-\r
-#ifndef NO_IPV6\r
- /*\r
- * If we were given ADDRTYPE_UNSPEC, we must also create an\r
- * IPv4 listening socket and link it to this one.\r
- */\r
- if (address_family == AF_INET6 && orig_address_family == ADDRTYPE_UNSPEC) {\r
- Actual_Socket other;\r
-\r
- other = (Actual_Socket) sk_newlistener(srcaddr, port, plug,\r
- local_host_only, ADDRTYPE_IPV4);\r
-\r
- if (other) {\r
- if (!other->error) {\r
- other->parent = ret;\r
- ret->child = other;\r
- } else {\r
- /* If we couldn't create a listening socket on IPv4 as well\r
- * as IPv6, we must return an error overall. */\r
- close(s);\r
- sfree(ret);\r
- return (Socket) other;\r
- }\r
- }\r
- }\r
-#endif\r
-\r
- ret->s = s;\r
-\r
- uxsel_tell(ret);\r
- add234(sktree, ret);\r
-\r
- return (Socket) ret;\r
-}\r
-\r
-static void sk_tcp_close(Socket sock)\r
-{\r
- Actual_Socket s = (Actual_Socket) sock;\r
-\r
- if (s->child)\r
- sk_tcp_close((Socket)s->child);\r
-\r
- uxsel_del(s->s);\r
- del234(sktree, s);\r
- close(s->s);\r
- if (s->addr)\r
- sk_addr_free(s->addr);\r
- sfree(s);\r
-}\r
-\r
-void *sk_getxdmdata(void *sock, int *lenp)\r
-{\r
- Actual_Socket s = (Actual_Socket) sock;\r
- union sockaddr_union u;\r
- socklen_t addrlen;\r
- char *buf;\r
- static unsigned int unix_addr = 0xFFFFFFFF;\r
-\r
- /*\r
- * We must check that this socket really _is_ an Actual_Socket.\r
- */\r
- if (s->fn != &tcp_fn_table)\r
- return NULL; /* failure */\r
-\r
- addrlen = sizeof(u);\r
- if (getsockname(s->s, &u.sa, &addrlen) < 0)\r
- return NULL;\r
- switch(u.sa.sa_family) {\r
- case AF_INET:\r
- *lenp = 6;\r
- buf = snewn(*lenp, char);\r
- PUT_32BIT_MSB_FIRST(buf, ntohl(u.sin.sin_addr.s_addr));\r
- PUT_16BIT_MSB_FIRST(buf+4, ntohs(u.sin.sin_port));\r
- break;\r
-#ifndef NO_IPV6\r
- case AF_INET6:\r
- *lenp = 6;\r
- buf = snewn(*lenp, char);\r
- if (IN6_IS_ADDR_V4MAPPED(&u.sin6.sin6_addr)) {\r
- memcpy(buf, u.sin6.sin6_addr.s6_addr + 12, 4);\r
- PUT_16BIT_MSB_FIRST(buf+4, ntohs(u.sin6.sin6_port));\r
- } else\r
- /* This is stupid, but it's what XLib does. */\r
- memset(buf, 0, 6);\r
- break;\r
-#endif\r
- case AF_UNIX:\r
- *lenp = 6;\r
- buf = snewn(*lenp, char);\r
- PUT_32BIT_MSB_FIRST(buf, unix_addr--);\r
- PUT_16BIT_MSB_FIRST(buf+4, getpid());\r
- break;\r
-\r
- /* XXX IPV6 */\r
-\r
- default:\r
- return NULL;\r
- }\r
-\r
- return buf;\r
-}\r
-\r
-/*\r
- * The function which tries to send on a socket once it's deemed\r
- * writable.\r
- */\r
-void try_send(Actual_Socket s)\r
-{\r
- while (s->sending_oob || bufchain_size(&s->output_data) > 0) {\r
- int nsent;\r
- int err;\r
- void *data;\r
- int len, urgentflag;\r
-\r
- if (s->sending_oob) {\r
- urgentflag = MSG_OOB;\r
- len = s->sending_oob;\r
- data = &s->oobdata;\r
- } else {\r
- urgentflag = 0;\r
- bufchain_prefix(&s->output_data, &data, &len);\r
- }\r
- nsent = send(s->s, data, len, urgentflag);\r
- noise_ultralight(nsent);\r
- if (nsent <= 0) {\r
- err = (nsent < 0 ? errno : 0);\r
- if (err == EWOULDBLOCK) {\r
- /*\r
- * Perfectly normal: we've sent all we can for the moment.\r
- */\r
- s->writable = FALSE;\r
- return;\r
- } else {\r
- /*\r
- * We unfortunately can't just call plug_closing(),\r
- * because it's quite likely that we're currently\r
- * _in_ a call from the code we'd be calling back\r
- * to, so we'd have to make half the SSH code\r
- * reentrant. Instead we flag a pending error on\r
- * the socket, to be dealt with (by calling\r
- * plug_closing()) at some suitable future moment.\r
- */\r
- s->pending_error = err;\r
- return;\r
- }\r
- } else {\r
- if (s->sending_oob) {\r
- if (nsent < len) {\r
- memmove(s->oobdata, s->oobdata+nsent, len-nsent);\r
- s->sending_oob = len - nsent;\r
- } else {\r
- s->sending_oob = 0;\r
- }\r
- } else {\r
- bufchain_consume(&s->output_data, nsent);\r
- }\r
- }\r
- }\r
- uxsel_tell(s);\r
-}\r
-\r
-static int sk_tcp_write(Socket sock, const char *buf, int len)\r
-{\r
- Actual_Socket s = (Actual_Socket) sock;\r
-\r
- /*\r
- * Add the data to the buffer list on the socket.\r
- */\r
- bufchain_add(&s->output_data, buf, len);\r
-\r
- /*\r
- * Now try sending from the start of the buffer list.\r
- */\r
- if (s->writable)\r
- try_send(s);\r
-\r
- /*\r
- * Update the select() status to correctly reflect whether or\r
- * not we should be selecting for write.\r
- */\r
- uxsel_tell(s);\r
-\r
- return bufchain_size(&s->output_data);\r
-}\r
-\r
-static int sk_tcp_write_oob(Socket sock, const char *buf, int len)\r
-{\r
- Actual_Socket s = (Actual_Socket) sock;\r
-\r
- /*\r
- * Replace the buffer list on the socket with the data.\r
- */\r
- bufchain_clear(&s->output_data);\r
- assert(len <= sizeof(s->oobdata));\r
- memcpy(s->oobdata, buf, len);\r
- s->sending_oob = len;\r
-\r
- /*\r
- * Now try sending from the start of the buffer list.\r
- */\r
- if (s->writable)\r
- try_send(s);\r
-\r
- /*\r
- * Update the select() status to correctly reflect whether or\r
- * not we should be selecting for write.\r
- */\r
- uxsel_tell(s);\r
-\r
- return s->sending_oob;\r
-}\r
-\r
-static int net_select_result(int fd, int event)\r
-{\r
- int ret;\r
- char buf[20480]; /* nice big buffer for plenty of speed */\r
- Actual_Socket s;\r
- u_long atmark;\r
-\r
- /* Find the Socket structure */\r
- s = find234(sktree, &fd, cmpforsearch);\r
- if (!s)\r
- return 1; /* boggle */\r
-\r
- noise_ultralight(event);\r
-\r
- switch (event) {\r
- case 4: /* exceptional */\r
- if (!s->oobinline) {\r
- /*\r
- * On a non-oobinline socket, this indicates that we\r
- * can immediately perform an OOB read and get back OOB\r
- * data, which we will send to the back end with\r
- * type==2 (urgent data).\r
- */\r
- ret = recv(s->s, buf, sizeof(buf), MSG_OOB);\r
- noise_ultralight(ret);\r
- if (ret <= 0) {\r
- return plug_closing(s->plug,\r
- ret == 0 ? "Internal networking trouble" :\r
- strerror(errno), errno, 0);\r
- } else {\r
- /*\r
- * Receiving actual data on a socket means we can\r
- * stop falling back through the candidate\r
- * addresses to connect to.\r
- */\r
- if (s->addr) {\r
- sk_addr_free(s->addr);\r
- s->addr = NULL;\r
- }\r
- return plug_receive(s->plug, 2, buf, ret);\r
- }\r
- break;\r
- }\r
-\r
- /*\r
- * If we reach here, this is an oobinline socket, which\r
- * means we should set s->oobpending and then deal with it\r
- * when we get called for the readability event (which\r
- * should also occur).\r
- */\r
- s->oobpending = TRUE;\r
- break;\r
- case 1: /* readable; also acceptance */\r
- if (s->listener) {\r
- /*\r
- * On a listening socket, the readability event means a\r
- * connection is ready to be accepted.\r
- */\r
- union sockaddr_union su;\r
- socklen_t addrlen = sizeof(su);\r
- int t; /* socket of connection */\r
- int fl;\r
-\r
- memset(&su, 0, addrlen);\r
- t = accept(s->s, &su.sa, &addrlen);\r
- if (t < 0) {\r
- break;\r
- }\r
-\r
- fl = fcntl(t, F_GETFL);\r
- if (fl != -1)\r
- fcntl(t, F_SETFL, fl | O_NONBLOCK);\r
-\r
- if (s->localhost_only &&\r
- !sockaddr_is_loopback(&su.sa)) {\r
- close(t); /* someone let nonlocal through?! */\r
- } else if (plug_accepting(s->plug, t)) {\r
- close(t); /* denied or error */\r
- }\r
- break;\r
- }\r
-\r
- /*\r
- * If we reach here, this is not a listening socket, so\r
- * readability really means readability.\r
- */\r
-\r
- /* In the case the socket is still frozen, we don't even bother */\r
- if (s->frozen) {\r
- s->frozen_readable = 1;\r
- break;\r
- }\r
-\r
- /*\r
- * We have received data on the socket. For an oobinline\r
- * socket, this might be data _before_ an urgent pointer,\r
- * in which case we send it to the back end with type==1\r
- * (data prior to urgent).\r
- */\r
- if (s->oobinline && s->oobpending) {\r
- atmark = 1;\r
- if (ioctl(s->s, SIOCATMARK, &atmark) == 0 && atmark)\r
- s->oobpending = FALSE; /* clear this indicator */\r
- } else\r
- atmark = 1;\r
-\r
- ret = recv(s->s, buf, s->oobpending ? 1 : sizeof(buf), 0);\r
- noise_ultralight(ret);\r
- if (ret < 0) {\r
- if (errno == EWOULDBLOCK) {\r
- break;\r
- }\r
- }\r
- if (ret < 0) {\r
- /*\r
- * An error at this point _might_ be an error reported\r
- * by a non-blocking connect(). So before we return a\r
- * panic status to the user, let's just see whether\r
- * that's the case.\r
- */\r
- int err = errno;\r
- if (s->addr) {\r
- plug_log(s->plug, 1, s->addr, s->port, strerror(err), err);\r
- while (s->addr && sk_nextaddr(s->addr, &s->step)) {\r
- err = try_connect(s);\r
- }\r
- }\r
- if (err != 0)\r
- return plug_closing(s->plug, strerror(err), err, 0);\r
- } else if (0 == ret) {\r
- return plug_closing(s->plug, NULL, 0, 0);\r
- } else {\r
- /*\r
- * Receiving actual data on a socket means we can\r
- * stop falling back through the candidate\r
- * addresses to connect to.\r
- */\r
- if (s->addr) {\r
- sk_addr_free(s->addr);\r
- s->addr = NULL;\r
- }\r
- return plug_receive(s->plug, atmark ? 0 : 1, buf, ret);\r
- }\r
- break;\r
- case 2: /* writable */\r
- if (!s->connected) {\r
- /*\r
- * select() reports a socket as _writable_ when an\r
- * asynchronous connection is completed.\r
- */\r
- s->connected = s->writable = 1;\r
- uxsel_tell(s);\r
- break;\r
- } else {\r
- int bufsize_before, bufsize_after;\r
- s->writable = 1;\r
- bufsize_before = s->sending_oob + bufchain_size(&s->output_data);\r
- try_send(s);\r
- bufsize_after = s->sending_oob + bufchain_size(&s->output_data);\r
- if (bufsize_after < bufsize_before)\r
- plug_sent(s->plug, bufsize_after);\r
- }\r
- break;\r
- }\r
-\r
- return 1;\r
-}\r
-\r
-/*\r
- * Deal with socket errors detected in try_send().\r
- */\r
-void net_pending_errors(void)\r
-{\r
- int i;\r
- Actual_Socket s;\r
-\r
- /*\r
- * This might be a fiddly business, because it's just possible\r
- * that handling a pending error on one socket might cause\r
- * others to be closed. (I can't think of any reason this might\r
- * happen in current SSH implementation, but to maintain\r
- * generality of this network layer I'll assume the worst.)\r
- * \r
- * So what we'll do is search the socket list for _one_ socket\r
- * with a pending error, and then handle it, and then search\r
- * the list again _from the beginning_. Repeat until we make a\r
- * pass with no socket errors present. That way we are\r
- * protected against the socket list changing under our feet.\r
- */\r
-\r
- do {\r
- for (i = 0; (s = index234(sktree, i)) != NULL; i++) {\r
- if (s->pending_error) {\r
- /*\r
- * An error has occurred on this socket. Pass it to the\r
- * plug.\r
- */\r
- plug_closing(s->plug, strerror(s->pending_error),\r
- s->pending_error, 0);\r
- break;\r
- }\r
- }\r
- } while (s);\r
-}\r
-\r
-/*\r
- * Each socket abstraction contains a `void *' private field in\r
- * which the client can keep state.\r
- */\r
-static void sk_tcp_set_private_ptr(Socket sock, void *ptr)\r
-{\r
- Actual_Socket s = (Actual_Socket) sock;\r
- s->private_ptr = ptr;\r
-}\r
-\r
-static void *sk_tcp_get_private_ptr(Socket sock)\r
-{\r
- Actual_Socket s = (Actual_Socket) sock;\r
- return s->private_ptr;\r
-}\r
-\r
-/*\r
- * Special error values are returned from sk_namelookup and sk_new\r
- * if there's a problem. These functions extract an error message,\r
- * or return NULL if there's no problem.\r
- */\r
-const char *sk_addr_error(SockAddr addr)\r
-{\r
- return addr->error;\r
-}\r
-static const char *sk_tcp_socket_error(Socket sock)\r
-{\r
- Actual_Socket s = (Actual_Socket) sock;\r
- return s->error;\r
-}\r
-\r
-static void sk_tcp_set_frozen(Socket sock, int is_frozen)\r
-{\r
- Actual_Socket s = (Actual_Socket) sock;\r
- if (s->frozen == is_frozen)\r
- return;\r
- s->frozen = is_frozen;\r
- if (!is_frozen && s->frozen_readable) {\r
- char c;\r
- recv(s->s, &c, 1, MSG_PEEK);\r
- }\r
- s->frozen_readable = 0;\r
- uxsel_tell(s);\r
-}\r
-\r
-static void uxsel_tell(Actual_Socket s)\r
-{\r
- int rwx = 0;\r
- if (s->listener) {\r
- rwx |= 1; /* read == accept */\r
- } else {\r
- if (!s->connected)\r
- rwx |= 2; /* write == connect */\r
- if (s->connected && !s->frozen)\r
- rwx |= 1 | 4; /* read, except */\r
- if (bufchain_size(&s->output_data))\r
- rwx |= 2; /* write */\r
- }\r
- uxsel_set(s->s, rwx, net_select_result);\r
-}\r
-\r
-int net_service_lookup(char *service)\r
-{\r
- struct servent *se;\r
- se = getservbyname(service, NULL);\r
- if (se != NULL)\r
- return ntohs(se->s_port);\r
- else\r
- return 0;\r
-}\r
-\r
-char *get_hostname(void)\r
-{\r
- int len = 128;\r
- char *hostname = NULL;\r
- do {\r
- len *= 2;\r
- hostname = sresize(hostname, len, char);\r
- if ((gethostname(hostname, len) < 0) &&\r
- (errno != ENAMETOOLONG)) {\r
- sfree(hostname);\r
- hostname = NULL;\r
- break;\r
- }\r
- } while (strlen(hostname) >= len-1);\r
- return hostname;\r
-}\r
-\r
-SockAddr platform_get_x11_unix_address(const char *sockpath, int displaynum)\r
-{\r
- SockAddr ret = snew(struct SockAddr_tag);\r
- int n;\r
-\r
- memset(ret, 0, sizeof *ret);\r
- ret->superfamily = UNIX;\r
- /*\r
- * In special circumstances (notably Mac OS X Leopard), we'll\r
- * have been passed an explicit Unix socket path.\r
- */\r
- if (sockpath) {\r
- n = snprintf(ret->hostname, sizeof ret->hostname,\r
- "%s", sockpath);\r
- } else {\r
- n = snprintf(ret->hostname, sizeof ret->hostname,\r
- "%s%d", X11_UNIX_PATH, displaynum);\r
- }\r
-\r
- if (n < 0)\r
- ret->error = "snprintf failed";\r
- else if (n >= sizeof ret->hostname)\r
- ret->error = "X11 UNIX name too long";\r
-\r
-#ifndef NO_IPV6\r
- ret->ais = NULL;\r
-#else\r
- ret->addresses = NULL;\r
- ret->naddresses = 0;\r
-#endif\r
- ret->refcount = 1;\r
- return ret;\r
-}\r