diff --git a/configure.in b/configure.in index e8bf8bc..a30ff09 100644 --- a/configure.in +++ b/configure.in @@ -212,20 +212,8 @@ 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)) 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..f23f913 100644 --- a/default.c +++ b/default.c @@ -943,20 +943,16 @@ 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; 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"); -#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 ""; } diff --git a/dns.c b/dns.c index d60ef18..496071a 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,9 @@ 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; if (!*name) return -1; for (n = name; *n; n++) if (*n != '.' && (*n < '0' || *n > '9')) goto nogethostbyaddr; n = name; @@ -73,26 +72,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 (getaddrinfo(name, "0", &hints, &res)) return -1; + if (getnameinfo(res->ai_addr, res->ai_addrlen, + hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST)) { + freeaddrinfo(res); + return -1; + } + 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 +186,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 +196,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 +207,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 +224,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; } 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..eaee561 100644 --- a/url.c +++ b/url.c @@ -70,7 +70,9 @@ int get_prot_info(unsigned char *prot, int *port, void (**func)(struct connectio int parse_url(unsigned char *url, int *prlen, unsigned char **user, int *uslen, unsigned char **pass, int *palen, unsigned char **host, int *holen, unsigned char **port, int *polen, unsigned char **data, int *dalen, unsigned char **post) { unsigned char *p, *q; + unsigned char *lb, *rb; unsigned char p_c[2]; + unsigned char *slash; int a; if (prlen) *prlen = 0; if (user) *user = NULL; @@ -84,6 +86,9 @@ int parse_url(unsigned char *url, int *prlen, unsigned char **user, int *uslen, if (data) *data = NULL; if (dalen) *dalen = 0; if (post) *post = NULL; + lb = strchr(url, '['); + rb = strchr(url, ']'); + if (lb && rb && lb > rb) return -1; if (!url || !(p = strchr(url, ':'))) return -1; if (prlen) *prlen = p - url; if ((a = check_protocol(url, p - url)) == -1) return -1; @@ -97,6 +102,11 @@ int parse_url(unsigned char *url, int *prlen, unsigned char **user, int *uslen, return 0; } p += 3; + if (lb && (slash = strchr(p, '/')) && slash < lb) { + /* Don't misintrepret brackets in data part of URL. E.g + * proxy://proxy-server:3128/http:[2002:dab::1]:80/ */ + lb = rb = NULL; + } q = p + strcspn(p, "@/?"); if (!*q && protocols[a].need_slash_after_host) return -1; if (*q == '@') { @@ -115,10 +125,25 @@ int parse_url(unsigned char *url, int *prlen, unsigned char **user, int *uslen, } p = q + 1; } - q = p + strcspn(p, ":/?"); + if (lb && rb) + { + q = rb; + q = rb + strcspn(rb, ":/?"); + } else + q = p + strcspn(p, ":/?"); if (!*q && protocols[a].need_slash_after_host) return -1; - if (host) *host = p; - if (holen) *holen = q - p; + if (host) { + if (lb && rb) + *host = lb + 1; + else + *host = p; + } + if (holen) { + if (lb && rb) + *holen = rb - lb - 1; + else + *holen = q - p; + } if (*q == ':') { unsigned char *pp = q + strcspn(q, "/"); int cc; @@ -152,6 +177,9 @@ 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 ommit opening bracket in case of IPv6 numeric hostname + * Now, the output string is used only as a key into the array, so it + * doesn't matter. When usage changes, we need to build proper substring. */ k = p ? p + pl : h + hl; return memacpy(z, k - z); } @@ -547,3 +575,6 @@ void add_conv_str(unsigned char **s, int *l, unsigned char *b, int ll, int encod } } +/* + * vim: noexpandtab + */