diff --git a/configure.in b/configure.in index e8bf8bc..f599ce8 100644 --- a/configure.in +++ b/configure.in @@ -212,23 +212,12 @@ if test "$cf_result" = no; then AC_CHECK_LIB(socket, setsockopt) fi -#AC_MSG_CHECKING([for gethostbyname]) -#AC_TRY_LINK([#include ], [gethostbyname("")], cf_result=yes, cf_result=no) -#AC_MSG_RESULT($cf_result) -AC_CHECK_FUNC(gethostbyname, cf_result=yes, cf_result=no) -if test "$cf_result" = no; then - AC_CHECK_LIB(socket, gethostbyname) - if test "$ac_cv_lib_socket_gethostbyname" = no; then - AC_CHECK_LIB(nsl, gethostbyname) - if test "$ac_cv_lib_nsl_gethostbyname" = no; then - AC_ERROR([gethostbyname function not present]) - fi - fi -fi -AC_CHECK_FUNC(gethostbyaddr, AC_DEFINE(HAVE_GETHOSTBYADDR)) +AC_CHECK_FUNCS(getaddrinfo getnameinfo freeaddrinfo, [], + AC_ERROR([RFC 3493 (getaddrinfo() etc.) not supported])) AC_CHECK_FUNC(dhcp_option, AC_DEFINE(HAVE_DHCP_OPTION)) -AC_CHECK_FUNC(herror, AC_DEFINE(HAVE_HERROR)) +AC_CHECK_FUNC(gai_strerror, AC_DEFINE([HAVE_GAI_STRERROR], [1], + [Define if you have gai_strerror(3) function])) AC_CHECK_FUNC(cfmakeraw, AC_DEFINE(HAVE_CFMAKERAW)) AC_HAVE_FUNCS(cygwin_conv_to_full_win32_path) diff --git a/connect.c b/connect.c index 7cdd050..475f9be 100644 --- a/connect.c +++ b/connect.c @@ -55,8 +55,8 @@ void connected(struct connection *); struct conn_info { void (*func)(struct connection *); - struct sockaddr_in sa; - ip__address addr; + struct sockaddr_storage sa; + char addr[NI_MAXHOST]; int port; int *sock; int real_port; @@ -110,8 +110,8 @@ void make_connection(struct connection *c, int port, int *sock, void (*func)(str log_data("\nCONNECTION: ", 13); log_data(host, strlen(host)); log_data("\n", 1); - if (c->no_cache >= NC_RELOAD) as = find_host_no_cache(host, &b->addr, &c->dnsquery, (void(*)(void *, int))dns_found, c); - else as = find_host(host, &b->addr, &c->dnsquery, (void(*)(void *, int))dns_found, c); + if (c->no_cache >= NC_RELOAD) as = find_host_no_cache(host, b->addr, &c->dnsquery, (void(*)(void *, int))dns_found, c); + else as = find_host(host, b->addr, &c->dnsquery, (void(*)(void *, int))dns_found, c); mem_free(host); if (as) setcstate(c, S_DNS); } @@ -262,34 +262,59 @@ void handle_socks_reply(struct connection *c) void dns_found(struct connection *c, int state) { int s; + struct addrinfo hints, *res, *res0; struct conn_info *b = c->newconn; + char pbuf[NI_MAXSERV]; if (state) { setcstate(c, S_NO_DNS); abort_connection(c); return; } - if ((s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { - setcstate(c, -errno); + sprintf(pbuf, "%d", b->port); + pbuf[sizeof(pbuf)-1] = '\0'; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + if (getaddrinfo(b->addr, pbuf, &hints, &res0)) { + setcstate(c, -EADDRNOTAVAIL); retry_connection(c); return; } - *b->sock = s; - fcntl(s, F_SETFL, O_NONBLOCK); - memset(&b->sa, 0, sizeof(struct sockaddr_in)); - b->sa.sin_family = AF_INET; - b->sa.sin_addr.s_addr = b->addr; - b->sa.sin_port = htons(b->port); - if (connect(s, (struct sockaddr *)(void *)&b->sa, sizeof b->sa)) { - if (errno != EALREADY && errno != EINPROGRESS) { - setcstate(c, -errno); - retry_connection(c); - return; - } - set_handlers(s, NULL, (void(*)(void *))connected, (void(*)(void *))exception, c); - setcstate(c, S_CONN); - } else { - connected(c); + + for (res = res0; res != NULL; res = res->ai_next) { + if ((s = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) + == -1) { + if (res->ai_next) + continue; + else { + setcstate(c, -errno); + retry_connection(c); + freeaddrinfo(res0); + return; + } + } + *b->sock = s; + fcntl(s, F_SETFL, O_NONBLOCK); + memcpy(&b->sa, res->ai_addr, res->ai_addrlen); + if (connect(s, res->ai_addr, res->ai_addrlen)) { + if (res->ai_next) { + close(s); + continue; + } else if (errno != EALREADY && errno != EINPROGRESS) { + setcstate(c, -errno); + retry_connection(c); + freeaddrinfo(res0); + return; + } + set_handlers(s, NULL, (void(*)(void *))connected, + (void(*)(void *))exception, c); + setcstate(c, S_CONN); + } else { + connected(c); + } + break; } + freeaddrinfo(res0); } void connected(struct connection *c) diff --git a/default.c b/default.c index 9c699a6..5e6ce96 100644 --- a/default.c +++ b/default.c @@ -943,20 +943,21 @@ unsigned char *gen_cmd(struct option *o, unsigned char ***argv, int *argc) unsigned char *lookup_cmd(struct option *o, unsigned char ***argv, int *argc) { - ip__address addr; + char addr; unsigned char *p = (unsigned char *)&addr; + int retval; if (!*argc) return "Parameter expected"; if (*argc >= 2) return "Too many parameters"; (*argv)++; (*argc)--; - if (do_real_lookup(*(*argv - 1), &addr)) { -#ifdef HAVE_HERROR - herror("error"); + if ((retval = do_real_lookup(*(*argv - 1), &addr))) { +#ifdef HAVE_GAI_STRERROR + fprintf(stderr, "error: host not found: %s\n", gai_strerror(retval)); #else fprintf(stderr, "error: host not found\n"); #endif return ""; } - printf("%d.%d.%d.%d\n", (int)p[0], (int)p[1], (int)p[2], (int)p[3]); + printf("%s\n", addr); fflush(stdout); return ""; } @@ -1653,3 +1654,6 @@ void save_url_history(void) return; } +/* + * vim: noexpandtab + */ diff --git a/dns.c b/dns.c index d60ef18..4127b32 100644 --- a/dns.c +++ b/dns.c @@ -9,7 +9,7 @@ struct dnsentry { struct dnsentry *next; struct dnsentry *prev; ttime get_time; - ip__address addr; + char addr[NI_MAXHOST]; char name[1]; }; @@ -28,7 +28,7 @@ struct dnsquery { void (*xfn)(struct dnsquery *, int); int h; struct dnsquery **s; - ip__address *addr; + char *addr; char name[1]; }; @@ -60,10 +60,10 @@ int shrink_dns_cache(int); void failed_real_lookup(struct dnsquery *); -int do_real_lookup(unsigned char *name, ip__address *host) +int do_real_lookup(unsigned char *name, char *host) { unsigned char *n; - struct hostent *hst; + int retval; if (!*name) return -1; for (n = name; *n; n++) if (*n != '.' && (*n < '0' || *n > '9')) goto nogethostbyaddr; n = name; @@ -73,26 +73,34 @@ int do_real_lookup(unsigned char *name, ip__address *host) if (get_addr_byte(&n, ((unsigned char *)host + 3), 0)) goto skip_addr; return 0; skip_addr: -#ifdef HAVE_GETHOSTBYADDR - if (!(hst = gethostbyaddr (name, strlen(name), AF_INET))) -#endif - nogethostbyaddr: if (!(hst = gethostbyname(name))) - return -1; - memcpy(host, hst->h_addr_list[0], sizeof(ip__address)); + nogethostbyaddr: ; + struct addrinfo hints, *res; + char hbuf[NI_MAXHOST]; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + if ((retval = getaddrinfo(name, NULL, &hints, &res))) return retval; + if ((retval = getnameinfo(res->ai_addr, res->ai_addrlen, + hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST))) { + freeaddrinfo(res); + return retval; + } + freeaddrinfo(res); + memcpy(host, hbuf, NI_MAXHOST-1); return 0; } void lookup_fn(unsigned char *name, int h) { - ip__address host; - if (do_real_lookup(name, &host)) return; - write(h, &host, sizeof(ip__address)); + char host[NI_MAXHOST]; + if (do_real_lookup(name, host)) return; + write(h, host, NI_MAXHOST); } void end_real_lookup(struct dnsquery *q) { int r = 1; - if (!q->addr || read(q->h, q->addr, sizeof(ip__address)) != sizeof(ip__address)) goto end; + if (!q->addr || read(q->h, q->addr, NI_MAXHOST) != NI_MAXHOST) goto end; r = 0; end: @@ -179,7 +187,7 @@ void end_dns_lookup(struct dnsquery *q, int a) } if (!find_in_dns_cache(q->name, &dnsentry)) { if (a) { - memcpy(q->addr, &dnsentry->addr, sizeof(ip__address)); + memcpy(q->addr, &dnsentry->addr, NI_MAXHOST); a = 0; goto e; } @@ -189,7 +197,7 @@ void end_dns_lookup(struct dnsquery *q, int a) if (a) goto e; dnsentry = mem_alloc(sizeof(struct dnsentry) + strlen(q->name) + 1); strcpy(dnsentry->name, q->name); - memcpy(&dnsentry->addr, q->addr, sizeof(ip__address)); + memcpy(&dnsentry->addr, q->addr, NI_MAXHOST); dnsentry->get_time = get_time(); add_to_list(dns_cache, dnsentry); e: @@ -200,7 +208,7 @@ void end_dns_lookup(struct dnsquery *q, int a) fn(data, a); } -int find_host_no_cache(unsigned char *name, ip__address *addr, void **qp, void (*fn)(void *, int), void *data) +int find_host_no_cache(unsigned char *name, char *addr, void **qp, void (*fn)(void *, int), void *data) { struct dnsquery *q; if (!(q = malloc(sizeof(struct dnsquery) + strlen(name) + 1))) { @@ -217,13 +225,13 @@ int find_host_no_cache(unsigned char *name, ip__address *addr, void **qp, void ( return do_queued_lookup(q); } -int find_host(unsigned char *name, ip__address *addr, void **qp, void (*fn)(void *, int), void *data) +int find_host(unsigned char *name, char *addr, void **qp, void (*fn)(void *, int), void *data) { struct dnsentry *dnsentry; if (qp) *qp = NULL; if (!find_in_dns_cache(name, &dnsentry)) { if ((uttime)get_time() - (uttime)dnsentry->get_time > DNS_TIMEOUT) goto timeout; - memcpy(addr, &dnsentry->addr, sizeof(ip__address)); + memcpy(addr, &dnsentry->addr, NI_MAXHOST); fn(data, 0); return 0; } @@ -265,3 +273,7 @@ void init_dns(void) { register_cache_upcall(shrink_dns_cache, "dns"); } + +/* + * vim: noexpandtab + */ diff --git a/http.c b/http.c index 4432d58..fd68ae4 100644 --- a/http.c +++ b/http.c @@ -241,7 +241,13 @@ void http_send_header(struct connection *c) else add_to_str(&hdr, &l, " HTTP/1.0\r\n"); if ((h = get_host_name(host))) { add_to_str(&hdr, &l, "Host: "); - add_to_str(&hdr, &l, h); + if (strchr(h, ':') != strrchr(h, ':')) { + add_to_str(&hdr, &l, "["); + add_to_str(&hdr, &l, h); + add_to_str(&hdr, &l, "]"); + } else + add_to_str(&hdr, &l, h); + mem_free(h); if ((h = get_port_str(host))) { add_to_str(&hdr, &l, ":"); diff --git a/links.h b/links.h index dde69dc..b5c6833 100644 --- a/links.h +++ b/links.h @@ -907,11 +907,9 @@ void set_sigcld(void); /* dns.c */ -typedef unsigned ip__address; - -int do_real_lookup(unsigned char *, ip__address *); -int find_host(unsigned char *, ip__address *, void **, void (*)(void *, int), void *); -int find_host_no_cache(unsigned char *, ip__address *, void **, void (*)(void *, int), void *); +int do_real_lookup(unsigned char *, char *); +int find_host(unsigned char *, char *, void **, void (*)(void *, int), void *); +int find_host_no_cache(unsigned char *, char *, void **, void (*)(void *, int), void *); void kill_dns_request(void **); void init_dns(void); diff --git a/url.c b/url.c index f793579..76f913f 100644 --- a/url.c +++ b/url.c @@ -114,11 +114,22 @@ int parse_url(unsigned char *url, int *prlen, unsigned char **user, int *uslen, if (palen) *palen = q - pp - 1; } p = q + 1; - } - q = p + strcspn(p, ":/?"); - if (!*q && protocols[a].need_slash_after_host) return -1; + } + if (*p == '[') { + q = strchr(++p, ']'); + if (!q++ || (*q != ':' && *q != '/' && *q != '?' && *q != '\0')){ + /* Missing closing bracket or garbage after that */ + return -1; + } + if (holen) *holen = q - p - 1; + } else { + q = p + strcspn(p, ":/?"); + if (holen) *holen = q - p; + } if (host) *host = p; - if (holen) *holen = q - p; + /* XXX: the q needs to point after host including ']' here. + * Thus *holen computation is specific to every upper if-branche */ + if (!*q && protocols[a].need_slash_after_host) return -1; if (*q == ':') { unsigned char *pp = q + strcspn(q, "/"); int cc; @@ -152,6 +163,10 @@ unsigned char *get_host_and_pass(unsigned char *url) int hl, pl; if (parse_url(url, NULL, &u, NULL, NULL, NULL, &h, &hl, &p, &pl, NULL, NULL, NULL)) return NULL; z = u ? u : h; + /* XXX: This wil omit opening bracket in case of IPv6 numeric hostname + * Now, the output string is used only as a key into a list, so it + * doesn't matter. When usage changes, we need to build properly balanced + * substring. */ k = p ? p + pl : h + hl; return memacpy(z, k - z); } @@ -547,3 +562,6 @@ void add_conv_str(unsigned char **s, int *l, unsigned char *b, int ll, int encod } } +/* + * vim: noexpandtab + */