mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-02-03 21:52:24 +00:00
Reorganise sk_namelookup (on both platforms).
I just tried to trace through the Windows version's control flow in response to a confusing bug report, and found that the control flow itself was so confusing I couldn't make sense of it. Why are we choosing between getaddrinfo and gethostbyname via #ifndef NO_IPV6, then re-converging control flow and diverging a second time to report the error? So I rewrote the whole thing to have completely separate sections of code dealing with the three resolution strategies, each with its own dedicated error reporting system. And then I checked the Unix version and found it was about as confusing, so I rewrote that too in the same style. Now the two are mostly the same, except for details: Unix has an override at the top for a Unix socket pathname, Windows has to cope with getaddrinfo maybe not being found at run time (so the other cases aren't in the #else clause), and Windows uses the same error reporting for both lookup functions whereas Unix has to use the appropriate gai_strerror or hstrerror.
This commit is contained in:
parent
67204ffd0b
commit
e22df74545
154
unix/network.c
154
unix/network.c
@ -184,103 +184,89 @@ void sk_cleanup(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SockAddr *sk_namelookup(const char *host, char **canonicalname, int address_family)
|
SockAddr *sk_namelookup(const char *host, char **canonicalname,
|
||||||
|
int address_family)
|
||||||
{
|
{
|
||||||
|
*canonicalname = NULL;
|
||||||
|
|
||||||
if (host[0] == '/') {
|
if (host[0] == '/') {
|
||||||
*canonicalname = dupstr(host);
|
*canonicalname = dupstr(host);
|
||||||
return unix_sock_addr(host);
|
return unix_sock_addr(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
SockAddr *ret = snew(SockAddr);
|
SockAddr *addr = snew(SockAddr);
|
||||||
#ifndef NO_IPV6
|
memset(addr, 0, sizeof(SockAddr));
|
||||||
struct addrinfo hints;
|
addr->superfamily = UNRESOLVED;
|
||||||
int err;
|
addr->refcount = 1;
|
||||||
#else
|
|
||||||
unsigned long a;
|
|
||||||
struct hostent *h = NULL;
|
|
||||||
int n;
|
|
||||||
#endif
|
|
||||||
strbuf *realhost = strbuf_new();
|
|
||||||
|
|
||||||
/* Clear the structure and default to IPv4. */
|
|
||||||
memset(ret, 0, sizeof(SockAddr));
|
|
||||||
ret->superfamily = UNRESOLVED;
|
|
||||||
ret->error = NULL;
|
|
||||||
ret->refcount = 1;
|
|
||||||
|
|
||||||
#ifndef NO_IPV6
|
#ifndef NO_IPV6
|
||||||
hints.ai_flags = AI_CANONNAME;
|
/*
|
||||||
hints.ai_family = (address_family == ADDRTYPE_IPV4 ? AF_INET :
|
* Use getaddrinfo, as long as it's available. This should handle
|
||||||
address_family == ADDRTYPE_IPV6 ? AF_INET6 :
|
* both IPv4 and IPv6 address literals, and hostnames, in one
|
||||||
AF_UNSPEC);
|
* unified API.
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
*/
|
||||||
hints.ai_protocol = 0;
|
|
||||||
hints.ai_addrlen = 0;
|
|
||||||
hints.ai_addr = NULL;
|
|
||||||
hints.ai_canonname = NULL;
|
|
||||||
hints.ai_next = NULL;
|
|
||||||
{
|
{
|
||||||
char *trimmed_host = host_strduptrim(host); /* strip [] on literals */
|
struct addrinfo hints;
|
||||||
err = getaddrinfo(trimmed_host, NULL, &hints, &ret->ais);
|
memset(&hints, 0, sizeof(hints));
|
||||||
sfree(trimmed_host);
|
hints.ai_family = (address_family == ADDRTYPE_IPV4 ? AF_INET :
|
||||||
}
|
address_family == ADDRTYPE_IPV6 ? AF_INET6 :
|
||||||
if (err != 0) {
|
AF_UNSPEC);
|
||||||
ret->error = gai_strerror(err);
|
hints.ai_flags = AI_CANONNAME;
|
||||||
strbuf_free(realhost);
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
ret->superfamily = IP;
|
|
||||||
|
|
||||||
if (ret->ais->ai_canonname != NULL)
|
/* strip [] on IPv6 address literals */
|
||||||
put_fmt(realhost, "%s", ret->ais->ai_canonname);
|
char *trimmed_host = host_strduptrim(host);
|
||||||
else
|
int err = getaddrinfo(trimmed_host, NULL, &hints, &addr->ais);
|
||||||
put_fmt(realhost, "%s", host);
|
sfree(trimmed_host);
|
||||||
#else
|
|
||||||
if ((a = inet_addr(host)) == (unsigned long)(in_addr_t)(-1)) {
|
if (addr->ais) {
|
||||||
/*
|
addr->superfamily = IP;
|
||||||
* Otherwise use the IPv4-only gethostbyname... (NOTE:
|
if (addr->ais->ai_canonname)
|
||||||
* we don't use gethostbyname as a fallback!)
|
*canonicalname = dupstr(addr->ais->ai_canonname);
|
||||||
*/
|
else
|
||||||
if (ret->superfamily == UNRESOLVED) {
|
*canonicalname = dupstr(host);
|
||||||
/*debug("Resolving \"%s\" with gethostbyname() (IPv4 only)...\n", host); */
|
} else {
|
||||||
if ( (h = gethostbyname(host)) )
|
addr->error = gai_strerror(err);
|
||||||
ret->superfamily = IP;
|
|
||||||
}
|
}
|
||||||
if (ret->superfamily == UNRESOLVED) {
|
return addr;
|
||||||
ret->error = (h_errno == HOST_NOT_FOUND ||
|
|
||||||
h_errno == NO_DATA ||
|
|
||||||
h_errno == NO_ADDRESS ? "Host does not exist" :
|
|
||||||
h_errno == TRY_AGAIN ?
|
|
||||||
"Temporary name service failure" :
|
|
||||||
"gethostbyname: unknown error");
|
|
||||||
strbuf_free(realhost);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
/* This way we are always sure the h->h_name is valid :) */
|
|
||||||
strbuf_clear(realhost);
|
|
||||||
put_fmt(realhost, "%s", h->h_name);
|
|
||||||
for (n = 0; h->h_addr_list[n]; n++);
|
|
||||||
ret->addresses = snewn(n, unsigned long);
|
|
||||||
ret->naddresses = n;
|
|
||||||
for (n = 0; n < ret->naddresses; n++) {
|
|
||||||
memcpy(&a, h->h_addr_list[n], sizeof(a));
|
|
||||||
ret->addresses[n] = ntohl(a);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* This must be a numeric IPv4 address because it caused a
|
|
||||||
* success return from inet_addr.
|
|
||||||
*/
|
|
||||||
ret->superfamily = IP;
|
|
||||||
strbuf_clear(realhost);
|
|
||||||
put_fmt(realhost, "%s", host);
|
|
||||||
ret->addresses = snew(unsigned long);
|
|
||||||
ret->naddresses = 1;
|
|
||||||
ret->addresses[0] = ntohl(a);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
/*
|
||||||
|
* Failing that (if IPv6 support was not compiled in), try the
|
||||||
|
* old-fashioned approach, which is to start by manually checking
|
||||||
|
* for an IPv4 literal and then use gethostbyname.
|
||||||
|
*/
|
||||||
|
unsigned long a = inet_addr(host);
|
||||||
|
if (a != (unsigned long) INADDR_NONE) {
|
||||||
|
addr->addresses = snew(unsigned long);
|
||||||
|
addr->naddresses = 1;
|
||||||
|
addr->addresses[0] = ntohl(a);
|
||||||
|
addr->superfamily = IP;
|
||||||
|
*canonicalname = dupstr(host);
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct hostent *h = gethostbyname(host);
|
||||||
|
if (h) {
|
||||||
|
addr->superfamily = IP;
|
||||||
|
|
||||||
|
size_t n;
|
||||||
|
for (n = 0; h->h_addr_list[n]; n++);
|
||||||
|
addr->addresses = snewn(n, unsigned long);
|
||||||
|
addr->naddresses = n;
|
||||||
|
for (n = 0; n < addr->naddresses; n++) {
|
||||||
|
uint32_t a;
|
||||||
|
memcpy(&a, h->h_addr_list[n], sizeof(a));
|
||||||
|
addr->addresses[n] = ntohl(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
*canonicalname = dupstr(h->h_name);
|
||||||
|
} else {
|
||||||
|
addr->error = hstrerror(h_errno);
|
||||||
|
}
|
||||||
|
return addr;
|
||||||
#endif
|
#endif
|
||||||
*canonicalname = strbuf_to_str(realhost);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SockAddr *sk_nonamelookup(const char *host)
|
SockAddr *sk_nonamelookup(const char *host)
|
||||||
|
@ -452,118 +452,95 @@ const char *winsock_error_string(int error)
|
|||||||
return win_strerror(error);
|
return win_strerror(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline const char *namelookup_strerror(DWORD err)
|
||||||
|
{
|
||||||
|
/* PuTTY has traditionally translated a few of the likely error
|
||||||
|
* messages into more concise strings than the standard Windows ones */
|
||||||
|
return (err == WSAENETDOWN ? "Network is down" :
|
||||||
|
err == WSAHOST_NOT_FOUND ? "Host does not exist" :
|
||||||
|
err == WSATRY_AGAIN ? "Host not found" :
|
||||||
|
win_strerror(err));
|
||||||
|
}
|
||||||
|
|
||||||
SockAddr *sk_namelookup(const char *host, char **canonicalname,
|
SockAddr *sk_namelookup(const char *host, char **canonicalname,
|
||||||
int address_family)
|
int address_family)
|
||||||
{
|
{
|
||||||
SockAddr *ret = snew(SockAddr);
|
*canonicalname = NULL;
|
||||||
unsigned long a;
|
|
||||||
char realhost[8192];
|
|
||||||
int hint_family;
|
|
||||||
|
|
||||||
/* Default to IPv4. */
|
SockAddr *addr = snew(SockAddr);
|
||||||
hint_family = (address_family == ADDRTYPE_IPV4 ? AF_INET :
|
memset(addr, 0, sizeof(SockAddr));
|
||||||
#ifndef NO_IPV6
|
addr->superfamily = UNRESOLVED;
|
||||||
address_family == ADDRTYPE_IPV6 ? AF_INET6 :
|
addr->refcount = 1;
|
||||||
#endif
|
|
||||||
AF_UNSPEC);
|
|
||||||
|
|
||||||
/* Clear the structure and default to IPv4. */
|
|
||||||
memset(ret, 0, sizeof(SockAddr));
|
|
||||||
#ifndef NO_IPV6
|
#ifndef NO_IPV6
|
||||||
ret->ais = NULL;
|
/*
|
||||||
#endif
|
* Use getaddrinfo, as long as it's available. This should handle
|
||||||
ret->addresses = NULL;
|
* both IPv4 and IPv6 address literals, and hostnames, in one
|
||||||
ret->superfamily = UNRESOLVED;
|
* unified API.
|
||||||
ret->refcount = 1;
|
*/
|
||||||
*realhost = '\0';
|
if (p_getaddrinfo) {
|
||||||
|
struct addrinfo hints;
|
||||||
|
memset(&hints, 0, sizeof(hints));
|
||||||
|
hints.ai_family = (address_family == ADDRTYPE_IPV4 ? AF_INET :
|
||||||
|
address_family == ADDRTYPE_IPV6 ? AF_INET6 :
|
||||||
|
AF_UNSPEC);
|
||||||
|
hints.ai_flags = AI_CANONNAME;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
|
||||||
if ((a = p_inet_addr(host)) == (unsigned long) INADDR_NONE) {
|
/* strip [] on IPv6 address literals */
|
||||||
struct hostent *h = NULL;
|
char *trimmed_host = host_strduptrim(host);
|
||||||
int err = 0;
|
int err = p_getaddrinfo(trimmed_host, NULL, &hints, &addr->ais);
|
||||||
#ifndef NO_IPV6
|
sfree(trimmed_host);
|
||||||
/*
|
|
||||||
* Use getaddrinfo when it's available
|
if (addr->ais) {
|
||||||
*/
|
addr->superfamily = IP;
|
||||||
if (p_getaddrinfo) {
|
if (addr->ais->ai_canonname)
|
||||||
struct addrinfo hints;
|
*canonicalname = dupstr(addr->ais->ai_canonname);
|
||||||
memset(&hints, 0, sizeof(hints));
|
|
||||||
hints.ai_family = hint_family;
|
|
||||||
hints.ai_flags = AI_CANONNAME;
|
|
||||||
{
|
|
||||||
/* strip [] on IPv6 address literals */
|
|
||||||
char *trimmed_host = host_strduptrim(host);
|
|
||||||
err = p_getaddrinfo(trimmed_host, NULL, &hints, &ret->ais);
|
|
||||||
sfree(trimmed_host);
|
|
||||||
}
|
|
||||||
if (err == 0)
|
|
||||||
ret->superfamily = IP;
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Otherwise use the IPv4-only gethostbyname...
|
|
||||||
* (NOTE: we don't use gethostbyname as a fallback!)
|
|
||||||
*/
|
|
||||||
if ( (h = p_gethostbyname(host)) )
|
|
||||||
ret->superfamily = IP;
|
|
||||||
else
|
else
|
||||||
err = p_WSAGetLastError();
|
*canonicalname = dupstr(host);
|
||||||
}
|
|
||||||
|
|
||||||
if (ret->superfamily != IP) {
|
|
||||||
ret->error = (err == WSAENETDOWN ? "Network is down" :
|
|
||||||
err == WSAHOST_NOT_FOUND ? "Host does not exist" :
|
|
||||||
err == WSATRY_AGAIN ? "Host not found" :
|
|
||||||
win_strerror(err));
|
|
||||||
} else {
|
} else {
|
||||||
ret->error = NULL;
|
addr->error = namelookup_strerror(err);
|
||||||
|
|
||||||
#ifndef NO_IPV6
|
|
||||||
/* If we got an address info use that... */
|
|
||||||
if (ret->ais) {
|
|
||||||
/* Are we in IPv4 fallback mode? */
|
|
||||||
/* We put the IPv4 address into the a variable so we can further-on use the IPv4 code... */
|
|
||||||
if (ret->ais->ai_family == AF_INET)
|
|
||||||
memcpy(&a,
|
|
||||||
(char *) &((SOCKADDR_IN *) ret->ais->
|
|
||||||
ai_addr)->sin_addr, sizeof(a));
|
|
||||||
|
|
||||||
if (ret->ais->ai_canonname)
|
|
||||||
strncpy(realhost, ret->ais->ai_canonname, lenof(realhost));
|
|
||||||
else
|
|
||||||
strncpy(realhost, host, lenof(realhost));
|
|
||||||
}
|
|
||||||
/* We used the IPv4-only gethostbyname()... */
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
int n;
|
|
||||||
for (n = 0; h->h_addr_list[n]; n++);
|
|
||||||
ret->addresses = snewn(n, unsigned long);
|
|
||||||
ret->naddresses = n;
|
|
||||||
for (n = 0; n < ret->naddresses; n++) {
|
|
||||||
memcpy(&a, h->h_addr_list[n], sizeof(a));
|
|
||||||
ret->addresses[n] = p_ntohl(a);
|
|
||||||
}
|
|
||||||
memcpy(&a, h->h_addr, sizeof(a));
|
|
||||||
/* This way we are always sure the h->h_name is valid :) */
|
|
||||||
strncpy(realhost, h->h_name, sizeof(realhost));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
return addr;
|
||||||
/*
|
|
||||||
* This must be a numeric IPv4 address because it caused a
|
|
||||||
* success return from inet_addr.
|
|
||||||
*/
|
|
||||||
ret->addresses = snewn(1, unsigned long);
|
|
||||||
ret->naddresses = 1;
|
|
||||||
ret->addresses[0] = p_ntohl(a);
|
|
||||||
ret->superfamily = IP;
|
|
||||||
strncpy(realhost, host, sizeof(realhost));
|
|
||||||
}
|
}
|
||||||
realhost[lenof(realhost)-1] = '\0';
|
#endif
|
||||||
*canonicalname = dupstr(realhost);
|
|
||||||
return ret;
|
/*
|
||||||
|
* Failing that (if IPv6 support was not compiled in, or if
|
||||||
|
* getaddrinfo turned out to be unavailable at run time), try the
|
||||||
|
* old-fashioned approach, which is to start by manually checking
|
||||||
|
* for an IPv4 literal and then use gethostbyname.
|
||||||
|
*/
|
||||||
|
unsigned long a = p_inet_addr(host);
|
||||||
|
if (a != (unsigned long) INADDR_NONE) {
|
||||||
|
addr->addresses = snew(unsigned long);
|
||||||
|
addr->naddresses = 1;
|
||||||
|
addr->addresses[0] = p_ntohl(a);
|
||||||
|
addr->superfamily = IP;
|
||||||
|
*canonicalname = dupstr(host);
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct hostent *h = p_gethostbyname(host);
|
||||||
|
if (h) {
|
||||||
|
addr->superfamily = IP;
|
||||||
|
|
||||||
|
size_t n;
|
||||||
|
for (n = 0; h->h_addr_list[n]; n++);
|
||||||
|
addr->addresses = snewn(n, unsigned long);
|
||||||
|
addr->naddresses = n;
|
||||||
|
for (n = 0; n < addr->naddresses; n++) {
|
||||||
|
uint32_t a;
|
||||||
|
memcpy(&a, h->h_addr_list[n], sizeof(a));
|
||||||
|
addr->addresses[n] = p_ntohl(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
*canonicalname = dupstr(h->h_name);
|
||||||
|
} else {
|
||||||
|
DWORD err = p_WSAGetLastError();
|
||||||
|
addr->error = namelookup_strerror(err);
|
||||||
|
}
|
||||||
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SockAddr *sk_special_addr(SuperFamily superfamily, const char *name)
|
static SockAddr *sk_special_addr(SuperFamily superfamily, const char *name)
|
||||||
|
Loading…
Reference in New Issue
Block a user