X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/68a49acbf3f96fbbbc385620655dcb577e62c328..b7a189f38294c745ae4ea6efb55891c8196e275b:/winnet.c diff --git a/winnet.c b/winnet.c index 430a47bb..e94075b5 100644 --- a/winnet.c +++ b/winnet.c @@ -55,6 +55,9 @@ #include "network.h" #include "tree234.h" +#define ipv4_is_loopback(addr) \ + ((ntohl(addr.s_addr) & 0xFF000000L) == 0x7F000000L) + struct Socket_tag { struct socket_function_table *fn; /* the above variable absolutely *must* be the first in this structure */ @@ -87,12 +90,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; @@ -353,39 +362,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) @@ -520,6 +568,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; @@ -654,7 +703,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, @@ -708,14 +757,14 @@ Socket sk_newlistener(int port, Plug plug, int local_host_only) ret->oobinline = 0; - setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)); - #ifdef IPV6 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 @@ -724,11 +773,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 @@ -901,6 +971,25 @@ int select_result(WPARAM wParam, LPARAM lParam) u_long atmark; /* wParam is the socket itself */ + + /* + * One user has reported an assertion failure in tree234 which + * indicates a null element pointer has been passed to a + * find*234 function. The following find234 is the only one in + * the whole program that I can see being capable of doing + * this, hence I'm forced to conclude that WinSock is capable + * of sending me netevent messages with wParam==0. I want to + * know what the rest of the message is if it does so! + */ + if (wParam == 0) { + char *str; + str = dupprintf("Strange WinSock message: wp=%08x lp=%08x", + (int)wParam, (int)lParam); + logevent(NULL, str); + connection_fatal(NULL, str); + sfree(str); + } + s = find234(sktree, (void *) wParam, cmpforsearch); if (!s) return 1; /* boggle */ @@ -1028,8 +1117,7 @@ int select_result(WPARAM wParam, LPARAM lParam) break; } - if (s->localhost_only && - ntohl(isa.sin_addr.s_addr) != INADDR_LOOPBACK) { + if (s->localhost_only && !ipv4_is_loopback(isa.sin_addr)) { closesocket(t); /* dodgy WinSock let nonlocal through */ } else if (plug_accepting(s->plug, (void*)t)) { closesocket(t); /* denied or error */