X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/8c43874c359da5024541c58c1cc7e743d4135b3d..b7a189f38294c745ae4ea6efb55891c8196e275b:/unix/uxnet.c diff --git a/unix/uxnet.c b/unix/uxnet.c index 44b09c1d..3440d19e 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,12 +57,18 @@ 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; @@ -106,77 +114,7 @@ void sk_cleanup(void) char *error_string(int error) { - switch (error) { - case EACCES: - return "Network error: Permission denied"; - case EADDRINUSE: - return "Network error: Address already in use"; - case EADDRNOTAVAIL: - return "Network error: Cannot assign requested address"; - case EAFNOSUPPORT: - return - "Network error: Address family not supported by protocol family"; - case EALREADY: - return "Network error: Operation already in progress"; - case ECONNABORTED: - return "Network error: Software caused connection abort"; - case ECONNREFUSED: - return "Network error: Connection refused"; - case ECONNRESET: - return "Network error: Connection reset by peer"; - case EDESTADDRREQ: - return "Network error: Destination address required"; - case EFAULT: - return "Network error: Bad address"; - case EHOSTDOWN: - return "Network error: Host is down"; - case EHOSTUNREACH: - return "Network error: No route to host"; - case EINPROGRESS: - return "Network error: Operation now in progress"; - case EINTR: - return "Network error: Interrupted function call"; - case EINVAL: - return "Network error: Invalid argument"; - case EISCONN: - return "Network error: Socket is already connected"; - case EMFILE: - return "Network error: Too many open files"; - case EMSGSIZE: - return "Network error: Message too long"; - case ENETDOWN: - return "Network error: Network is down"; - case ENETRESET: - return "Network error: Network dropped connection on reset"; - case ENETUNREACH: - return "Network error: Network is unreachable"; - case ENOBUFS: - return "Network error: No buffer space available"; - case ENOPROTOOPT: - return "Network error: Bad protocol option"; - case ENOTCONN: - return "Network error: Socket is not connected"; - case ENOTSOCK: - return "Network error: Socket operation on non-socket"; - case EOPNOTSUPP: - return "Network error: Operation not supported"; - case EPFNOSUPPORT: - return "Network error: Protocol family not supported"; - case EPROTONOSUPPORT: - return "Network error: Protocol not supported"; - case EPROTOTYPE: - return "Network error: Protocol wrong type for socket"; - case ESHUTDOWN: - return "Network error: Cannot send after socket shutdown"; - case ESOCKTNOSUPPORT: - return "Network error: Socket type not supported"; - case ETIMEDOUT: - return "Network error: Connection timed out"; - case EWOULDBLOCK: - return "Network error: Resource temporarily unavailable"; - default: - return "Unknown network error"; - } + return strerror(error); } SockAddr sk_namelookup(char *host, char **canonicalname) @@ -262,39 +200,78 @@ SockAddr sk_namelookup(char *host, char **canonicalname) return ret; } +SockAddr sk_nonamelookup(char *host) +{ + SockAddr ret = smalloc(sizeof(struct SockAddr_tag)); + 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) @@ -419,6 +396,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; @@ -544,7 +522,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline, 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, @@ -604,6 +582,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 @@ -612,11 +592,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 @@ -810,10 +811,12 @@ int select_result(int fd, int event) /* * If we reach here, this is an oobinline socket, which - * means we should set s->oobpending and then fall through - * to the read case. + * means we should set s->oobpending and then deal with it + * when we get called for the readability event (which + * should also occur). */ s->oobpending = TRUE; + break; case 1: /* readable; also acceptance */ if (s->listener) { /* @@ -831,8 +834,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 */ @@ -864,7 +866,7 @@ int select_result(int fd, int event) } else atmark = 1; - ret = recv(s->s, buf, sizeof(buf), 0); + ret = recv(s->s, buf, s->oobpending ? 1 : sizeof(buf), 0); noise_ultralight(ret); if (ret < 0) { if (errno == EWOULDBLOCK) {