X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/7555d6a50b05d96de39b5e95cf11a8f05f0c4fd9..9f77212d7f268f8380781727b7fff520349891cd:/windows/winnet.c diff --git a/windows/winnet.c b/windows/winnet.c index f6f7bab0..19babb0f 100644 --- a/windows/winnet.c +++ b/windows/winnet.c @@ -96,6 +96,10 @@ static int cmpfortree(void *av, void *bv) return -1; if (as > bs) return +1; + if (a < b) + return -1; + if (a > b) + return +1; return 0; } @@ -115,7 +119,7 @@ static int cmpforsearch(void *av, void *bv) typedef rettype (WINAPI *t_##name) params; \ linkage t_##name p_##name #define GET_WINSOCK_FUNCTION(module, name) \ - p_##name = (t_##name) GetProcAddress(module, #name) + p_##name = module ? (t_##name) GetProcAddress(module, #name) : NULL DECL_WINSOCK_FUNCTION(NOTHING, int, WSAAsyncSelect, (SOCKET, HWND, u_int, long)); @@ -166,34 +170,87 @@ DECL_WINSOCK_FUNCTION(static, int, getnameinfo, (const struct sockaddr FAR * sa, socklen_t salen, char FAR * host, size_t hostlen, char FAR * serv, size_t servlen, int flags)); +DECL_WINSOCK_FUNCTION(static, char *, gai_strerror, (int ecode)); +DECL_WINSOCK_FUNCTION(static, int, WSAAddressToStringA, + (LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFO, + LPTSTR, LPDWORD)); #endif -static HMODULE winsock_module; +static HMODULE winsock_module = NULL; +static WSADATA wsadata; #ifndef NO_IPV6 -static HMODULE wship6_module; +static HMODULE winsock2_module = NULL; +static HMODULE wship6_module = NULL; #endif -void sk_init(void) +int sk_startup(int hi, int lo) { WORD winsock_ver; - WSADATA wsadata; - winsock_ver = MAKEWORD(2, 0); - winsock_module = LoadLibrary("WS2_32.DLL"); + winsock_ver = MAKEWORD(hi, lo); + + if (p_WSAStartup(winsock_ver, &wsadata)) { + return FALSE; + } + + if (LOBYTE(wsadata.wVersion) != LOBYTE(winsock_ver)) { + return FALSE; + } + +#ifdef NET_SETUP_DIAGNOSTICS + { + char buf[80]; + sprintf(buf, "Using WinSock %d.%d", hi, lo); + logevent(NULL, buf); + } +#endif + return TRUE; +} + +void sk_init(void) +{ +#ifndef NO_IPV6 + winsock2_module = +#endif + winsock_module = LoadLibrary("WS2_32.DLL"); if (!winsock_module) { winsock_module = LoadLibrary("WSOCK32.DLL"); - winsock_ver = MAKEWORD(1, 1); } if (!winsock_module) fatalbox("Unable to load any WinSock library"); #ifndef NO_IPV6 - wship6_module = LoadLibrary("wship6.dll"); - if (wship6_module) { - GET_WINSOCK_FUNCTION(wship6_module, getaddrinfo); - GET_WINSOCK_FUNCTION(wship6_module, freeaddrinfo); - GET_WINSOCK_FUNCTION(wship6_module, getnameinfo); + /* Check if we have getaddrinfo in Winsock */ + if (GetProcAddress(winsock_module, "getaddrinfo") != NULL) { +#ifdef NET_SETUP_DIAGNOSTICS + logevent(NULL, "Native WinSock IPv6 support detected"); +#endif + GET_WINSOCK_FUNCTION(winsock_module, getaddrinfo); + GET_WINSOCK_FUNCTION(winsock_module, freeaddrinfo); + GET_WINSOCK_FUNCTION(winsock_module, getnameinfo); + GET_WINSOCK_FUNCTION(winsock_module, gai_strerror); + } else { + /* Fall back to wship6.dll for Windows 2000 */ + wship6_module = LoadLibrary("wship6.dll"); + if (wship6_module) { +#ifdef NET_SETUP_DIAGNOSTICS + logevent(NULL, "WSH IPv6 support detected"); +#endif + GET_WINSOCK_FUNCTION(wship6_module, getaddrinfo); + GET_WINSOCK_FUNCTION(wship6_module, freeaddrinfo); + GET_WINSOCK_FUNCTION(wship6_module, getnameinfo); + GET_WINSOCK_FUNCTION(wship6_module, gai_strerror); + } else { +#ifdef NET_SETUP_DIAGNOSTICS + logevent(NULL, "No IPv6 support detected"); +#endif + } } + GET_WINSOCK_FUNCTION(winsock2_module, WSAAddressToStringA); +#else +#ifdef NET_SETUP_DIAGNOSTICS + logevent(NULL, "PuTTY was built without IPv6 support"); +#endif #endif GET_WINSOCK_FUNCTION(winsock_module, WSAAsyncSelect); @@ -223,14 +280,12 @@ void sk_init(void) GET_WINSOCK_FUNCTION(winsock_module, recv); GET_WINSOCK_FUNCTION(winsock_module, WSAIoctl); - if (p_WSAStartup(winsock_ver, &wsadata)) { + /* Try to get the best WinSock version we can get */ + if (!sk_startup(2,2) && + !sk_startup(2,0) && + !sk_startup(1,1)) { fatalbox("Unable to initialise WinSock"); } - if (LOBYTE(wsadata.wVersion) != LOBYTE(winsock_ver)) { - p_WSACleanup(); - fatalbox("WinSock version is incompatible with %d.%d", - LOBYTE(winsock_ver), HIBYTE(winsock_ver)); - } sktree = newtree234(cmpfortree); } @@ -251,8 +306,10 @@ void sk_cleanup(void) p_WSACleanup(); if (winsock_module) FreeLibrary(winsock_module); +#ifndef NO_IPV6 if (wship6_module) FreeLibrary(wship6_module); +#endif } char *winsock_error_string(int error) @@ -351,6 +408,10 @@ SockAddr sk_namelookup(const char *host, char **canonicalname, address_family == ADDRTYPE_IPV6 ? AF_INET6 : #endif AF_UNSPEC); +#ifndef NO_IPV6 + ret->ai = ret->ais = NULL; +#endif + ret->addresses = NULL; ret_family = AF_UNSPEC; *realhost = '\0'; @@ -361,14 +422,21 @@ SockAddr sk_namelookup(const char *host, char **canonicalname, */ if (p_getaddrinfo) { struct addrinfo hints; +#ifdef NET_SETUP_DIAGNOSTICS + logevent(NULL, "Using getaddrinfo() for resolving"); +#endif memset(&hints, 0, sizeof(hints)); hints.ai_family = ret->family; + hints.ai_flags = AI_CANONNAME; if ((err = p_getaddrinfo(host, NULL, &hints, &ret->ais)) == 0) ret_family = ret->ais->ai_family; ret->ai = ret->ais; } else #endif { +#ifdef NET_SETUP_DIAGNOSTICS + logevent(NULL, "Using gethostbyname() for resolving"); +#endif /* * Otherwise use the IPv4-only gethostbyname... * (NOTE: we don't use gethostbyname as a fallback!) @@ -384,7 +452,7 @@ SockAddr sk_namelookup(const char *host, char **canonicalname, err == WSAHOST_NOT_FOUND ? "Host does not exist" : err == WSATRY_AGAIN ? "Host not found" : #ifndef NO_IPV6 - p_getaddrinfo ? "getaddrinfo: unknown error" : + p_getaddrinfo&&p_gai_strerror ? p_gai_strerror(err) : #endif "gethostbyname: unknown error"); } else { @@ -401,17 +469,10 @@ SockAddr sk_namelookup(const char *host, char **canonicalname, (char *) &((SOCKADDR_IN *) ret->ai-> ai_addr)->sin_addr, sizeof(a)); - /* Now let's find that canonicalname... */ - if (p_getnameinfo) { - if (p_getnameinfo - ((struct sockaddr *) ret->ai->ai_addr, - ret->family == - AF_INET ? sizeof(SOCKADDR_IN) : - sizeof(SOCKADDR_IN6), realhost, - sizeof(realhost), NULL, 0, 0) != 0) { - strncpy(realhost, host, sizeof(realhost)); - } - } + if (ret->ai->ai_canonname) + strncpy(realhost, ret->ai->ai_canonname, lenof(realhost)); + else + strncpy(realhost, host, lenof(realhost)); } /* We used the IPv4-only gethostbyname()... */ else @@ -454,6 +515,11 @@ SockAddr sk_nonamelookup(const char *host) SockAddr ret = snew(struct SockAddr_tag); ret->error = NULL; ret->family = AF_UNSPEC; +#ifndef NO_IPV6 + ret->ai = ret->ais = NULL; +#endif + ret->addresses = NULL; + ret->naddresses = 0; strncpy(ret->hostname, host, lenof(ret->hostname)); ret->hostname[lenof(ret->hostname)-1] = '\0'; return ret; @@ -483,28 +549,11 @@ void sk_getaddr(SockAddr addr, char *buf, int buflen) { #ifndef NO_IPV6 if (addr->ai) { - /* Try to get the WSAAddressToStringA() function from wship6.dll */ - /* This way one doesn't need to have IPv6 dll's to use PuTTY and - * it will fallback to IPv4. */ - typedef int (CALLBACK * FADDRTOSTR) (LPSOCKADDR lpsaAddress, - DWORD dwAddressLength, - LPWSAPROTOCOL_INFO lpProtocolInfo, - OUT LPTSTR lpszAddressString, - IN OUT LPDWORD lpdwAddressStringLength - ); - FADDRTOSTR fAddrToStr = NULL; - - HINSTANCE dllWS2 = LoadLibrary("ws2_32.dll"); - if (dllWS2) { - fAddrToStr = (FADDRTOSTR)GetProcAddress(dllWS2, - "WSAAddressToStringA"); - if (fAddrToStr) { - fAddrToStr(addr->ai->ai_addr, addr->ai->ai_addrlen, - NULL, buf, &buflen); - } - else strncpy(buf, "IPv6", buflen); - FreeLibrary(dllWS2); - } + if (p_WSAAddressToStringA) { + p_WSAAddressToStringA(addr->ai->ai_addr, addr->ai->ai_addrlen, + NULL, buf, &buflen); + } else + strncpy(buf, "IPv6", buflen); } else #endif if (addr->family == AF_INET) { @@ -563,10 +612,18 @@ int sk_address_is_local(SockAddr addr) } else #endif if (addr->family == AF_INET) { - struct in_addr a; - assert(addr->addresses && addr->curraddr < addr->naddresses); - a.s_addr = p_htonl(addr->addresses[addr->curraddr]); - return ipv4_is_local_addr(a); +#ifndef NO_IPV6 + if (addr->ai) { + return ipv4_is_local_addr(((struct sockaddr_in *)addr->ai->ai_addr) + ->sin_addr); + } else +#endif + { + struct in_addr a; + assert(addr->addresses && addr->curraddr < addr->naddresses); + a.s_addr = p_htonl(addr->addresses[addr->curraddr]); + return ipv4_is_local_addr(a); + } } else { assert(addr->family == AF_UNSPEC); return 0; /* we don't know; assume not */ @@ -735,6 +792,14 @@ static DWORD try_connect(Actual_Socket sock) family = AF_INET; } + /* + * Remove the socket from the tree before we overwrite its + * internal socket id, because that forms part of the tree's + * sorting criterion. We'll add it back before exiting this + * function, whether we changed anything or not. + */ + del234(sktree, sock); + s = p_socket(family, SOCK_STREAM, 0); sock->s = s; @@ -824,6 +889,8 @@ static DWORD try_connect(Actual_Socket sock) a6.sin6_port = p_htons((short) sock->port); a6.sin6_addr = ((struct sockaddr_in6 *) sock->addr->ai->ai_addr)->sin6_addr; + a6.sin6_flowinfo = ((struct sockaddr_in6 *) sock->addr->ai->ai_addr)->sin6_flowinfo; + a6.sin6_scope_id = ((struct sockaddr_in6 *) sock->addr->ai->ai_addr)->sin6_scope_id; } else { a.sin_family = AF_INET; a.sin_addr = @@ -877,11 +944,15 @@ static DWORD try_connect(Actual_Socket sock) sock->writable = 1; } - add234(sktree, sock); - err = 0; ret: + + /* + * No matter what happened, put the socket back in the tree. + */ + add234(sktree, sock); + if (err) plug_log(sock->plug, 1, sock->addr, sock->port, sock->error, err); return err; @@ -1418,10 +1489,11 @@ int select_result(WPARAM wParam, LPARAM lParam) #ifndef NO_IPV6 if (isa.ss_family == AF_INET && s->localhost_only && - !ipv4_is_local_addr(((struct sockaddr_in *)&isa)->sin_addr)) { + !ipv4_is_local_addr(((struct sockaddr_in *)&isa)->sin_addr)) #else - if (s->localhost_only && !ipv4_is_local_addr(isa.sin_addr)) { + if (s->localhost_only && !ipv4_is_local_addr(isa.sin_addr)) #endif + { p_closesocket(t); /* dodgy WinSock let nonlocal through */ } else if (plug_accepting(s->plug, (void*)t)) { p_closesocket(t); /* denied or error */ @@ -1507,13 +1579,27 @@ static void sk_tcp_set_frozen(Socket sock, int is_frozen) if (s->frozen == is_frozen) return; s->frozen = is_frozen; - if (!is_frozen && s->frozen_readable) { - char c; - p_recv(s->s, &c, 1, MSG_PEEK); + if (!is_frozen) { + do_select(s->s, 1); + if (s->frozen_readable) { + char c; + p_recv(s->s, &c, 1, MSG_PEEK); + } } s->frozen_readable = 0; } +void socket_reselect_all(void) +{ + Actual_Socket s; + int i; + + for (i = 0; (s = index234(sktree, i)) != NULL; i++) { + if (!s->frozen) + do_select(s->s, 1); + } +} + /* * For Plink: enumerate all sockets currently active. */