diff -urNp links-2.6-orig/bfu.c links-2.6/bfu.c
--- links-2.6-orig/bfu.c 2012-04-09 05:09:00.000000000 +0200
+++ links-2.6/bfu.c 2012-06-08 09:44:30.276915109 +0200
@@ -1526,15 +1526,16 @@ int check_local_ip_address(struct dialog
{
int s;
int rs;
+ struct sockaddr_storage sockaddr;
unsigned char *p = di->cdata;
if (!*p) {
return 0;
}
- if (numeric_ip_address(p, NULL) == -1) {
+ if (numeric_ip_address(p, &sockaddr) == -1) {
msg_box(dlg->win->term, NULL, TEXT_(T_BAD_IP_ADDRESS), AL_CENTER, TEXT_(T_INVALID_IP_ADDRESS_SYNTAX), NULL, 1, TEXT_(T_CANCEL), NULL, B_ENTER | B_ESC);
return 1;
}
- s = socket_and_bind(p);
+ s = socket_and_bind(p, sockaddr.ss_family, SOCK_STREAM, 0);
if (s != -1) {
EINTRLOOP(rs, close(s));
} else {
diff -urNp links-2.6-orig/configure.in links-2.6/configure.in
--- links-2.6-orig/configure.in 2012-06-08 09:43:20.207817752 +0200
+++ links-2.6/configure.in 2012-06-08 09:44:30.279914737 +0200
@@ -400,37 +400,12 @@ if test "$cf_result" = no; then
AC_CHECK_LIB(socket, setsockopt)
fi
-#AC_MSG_CHECKING([for gethostbyname])
-#AC_TRY_LINK([#include <netdb.h>], [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)
- cf_result="$ac_cv_lib_socket_gethostbyname"
- if test "$ac_cv_lib_socket_gethostbyname" = no; then
- AC_CHECK_LIB(nsl, gethostbyname)
- cf_result="$ac_cv_lib_nsl_gethostbyname"
- fi
-fi
-test "$cf_result" = yes && AC_DEFINE(HAVE_GETHOSTBYNAME)
-
-if test "$cf_result" = yes && test "$ac_cv_have_watcom" = yes -o "`uname -s`" = SunOS; then
- AC_CACHE_CHECK([for flawed gethostbyname], ac_cv_gethostbyname_bug,
- AC_TRY_RUN([
- #include <netdb.h>
- int main()
- {
- return !gethostbyname("www.gnu.org");
- }
- ], ac_cv_gethostbyname_bug=no, ac_cv_gethostbyname_bug=yes, ac_cv_gethostbyname_bug="$ac_cv_have_watcom")
- )
- test "$ac_cv_gethostbyname_bug" = yes && AC_DEFINE(HAVE_GETHOSTBYNAME_BUG)
-fi
-
-AC_HAVE_FUNCS(gethostbyaddr)
+AC_CHECK_FUNCS(getaddrinfo getnameinfo freeaddrinfo, [],
+ AC_ERROR([RFC 3493 (getaddrinfo() etc.) not supported]))
AC_HAVE_FUNCS(dhcp_option)
-AC_HAVE_FUNCS(herror)
+AC_CHECK_FUNC(gai_strerror, AC_DEFINE([HAVE_GAI_STRERROR], [1],
+ [Define if you have gai_strerror(3) function]))
AC_HAVE_FUNCS(cfmakeraw)
AC_HAVE_FUNCS(cygwin_conv_to_full_win32_path)
diff -urNp links-2.6-orig/connect.c links-2.6/connect.c
--- links-2.6-orig/connect.c 2012-06-08 09:43:20.200790122 +0200
+++ links-2.6/connect.c 2012-06-08 09:44:30.283915502 +0200
@@ -43,24 +43,22 @@ static void exception(struct connection
retry_connection(c);
}
-int socket_and_bind(unsigned char *address)
+/* Create a socket of given family, type, and protocol. If @address is
+ * provided, bind it to the address.
+ * Return the socket, or -1 in case of error. */
+int socket_and_bind(unsigned char *address, int family, int type, int protocol)
{
int s;
int rs;
- EINTRLOOP(s, socket(PF_INET, SOCK_STREAM, IPPROTO_TCP));
+ EINTRLOOP(s, socket(family, type, protocol));
if (s == -1)
return -1;
if (address && *address) {
- struct sockaddr_in sa;
- ip__address addr;
- if (numeric_ip_address(address, &addr) == -1) {
+ struct sockaddr_storage sa;
+ if (numeric_ip_address(address, &sa) == -1) {
errno = EINVAL;
return -1;
}
- memset(&sa, 0, sizeof(struct sockaddr_in));
- sa.sin_family = AF_INET;
- sa.sin_addr.s_addr = addr;
- sa.sin_port = htons(0);
EINTRLOOP(rs, bind(s, (struct sockaddr *)(void *)&sa, sizeof sa));
if (rs) {
int sv_errno = errno;
@@ -83,7 +81,7 @@ void close_socket(int *s)
struct conn_info {
void (*func)(struct connection *);
- ip__address addr;
+ char addr[NI_MAXHOST];
int port;
int *sock;
int real_port;
@@ -106,6 +104,7 @@ void make_connection(struct connection *
host = stracpy(p);
real_port = port;
port = 1080;
+ /* TODO: Parse proxy specification as IPv6 */
if ((p = strchr(host, ':'))) {
*p++ = 0;
if (!*p) goto badu;
@@ -137,18 +136,22 @@ void make_connection(struct connection *
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);
}
-int get_pasv_socket(struct connection *c, int cc, int *sock, unsigned char *port)
+/* Create new listening socket bound to the same address as socket @cc.
+ * New socket is returned in @sock, address of new socket in @port. Both must
+ * be prellocated.
+ * Return 0, -1 in case of error. */
+int get_pasv_socket(struct connection *c, int cc, int *sock, struct sockaddr_storage *port)
{
int s;
int rs;
- struct sockaddr_in sa;
- struct sockaddr_in sb;
+ struct sockaddr_storage sa;
+ struct sockaddr_storage sb;
socklen_t len = sizeof(sa);
memset(&sa, 0, sizeof sa);
memset(&sb, 0, sizeof sb);
@@ -159,12 +162,22 @@ int get_pasv_socket(struct connection *c
retry_connection(c);
return -1;
}
- EINTRLOOP(s, socket(PF_INET, SOCK_STREAM, IPPROTO_TCP));
+ EINTRLOOP(s, socket(sa.ss_family, SOCK_STREAM, 0));
if (s == -1) goto e;
EINTRLOOP(rs, fcntl(s, F_SETFL, O_NONBLOCK));
*sock = s;
- memcpy(&sb, &sa, sizeof(struct sockaddr_in));
- sb.sin_port = htons(0);
+ memcpy(&sb, &sa, sizeof(sa));
+ switch (sb.ss_family) {
+ case AF_INET:
+ ((struct sockaddr_in *) &sb)->sin_port = 0;
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *) &sb)->sin6_port = 0;
+ break;
+ default:
+ errno = EAFNOSUPPORT;
+ goto e;
+ }
EINTRLOOP(rs, bind(s, (struct sockaddr *)(void *)&sb, sizeof sb));
if (rs) goto e;
len = sizeof(sa);
@@ -172,8 +185,7 @@ int get_pasv_socket(struct connection *c
if (rs) goto e;
EINTRLOOP(rs, listen(s, 1));
if (rs) goto e;
- memcpy(port, &sa.sin_addr.s_addr, 4);
- memcpy(port + 4, &sa.sin_port, 2);
+ memcpy(port, &sa, sizeof(*port));
return 0;
}
@@ -306,38 +318,62 @@ static void dns_found(struct connection
int s;
int rs;
struct conn_info *b = c->newconn;
- struct sockaddr_in sa;
+ struct addrinfo hints, *res, *res0;
+ char pbuf[NI_MAXSERV];
if (state) {
setcstate(c, S_NO_DNS);
abort_connection(c);
return;
}
- if ((s = socket_and_bind(bind_ip_address)) == -1) {
- setcstate(c, get_error_from_errno(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, get_error_from_errno(EADDRNOTAVAIL));
retry_connection(c);
return;
}
- EINTRLOOP(rs, fcntl(s, F_SETFL, O_NONBLOCK));
- *b->sock = s;
- memset(&sa, 0, sizeof(struct sockaddr_in));
- sa.sin_family = AF_INET;
- sa.sin_addr.s_addr = b->addr;
- sa.sin_port = htons(b->port);
- EINTRLOOP(rs, connect(s, (struct sockaddr *)(void *)&sa, sizeof sa));
- if (rs) {
- if (errno != EALREADY && errno != EINPROGRESS) {
+
+ for (res = res0; res != NULL; res = res->ai_next) {
+ s = socket_and_bind(bind_ip_address, res->ai_family, res->ai_socktype,
+ res->ai_protocol);
+ if (-1 == s) {
+ if (res->ai_next) {
+ continue;
+ } else {
+ setcstate(c, get_error_from_errno(errno));
+ retry_connection(c);
+ freeaddrinfo(res0);
+ return;
+ }
+ }
+ EINTRLOOP(rs, fcntl(s, F_SETFL, O_NONBLOCK));
+ *b->sock = s;
+ EINTRLOOP(rs, connect(s, res->ai_addr, res->ai_addrlen));
+ if (rs) {
+ if (res->ai_next) {
+ close(s);
+ continue;
+ } else if (errno != EALREADY && errno != EINPROGRESS) {
#ifdef BEOS
- if (errno == EWOULDBLOCK) errno = ETIMEDOUT;
+ if (errno == EWOULDBLOCK) errno = ETIMEDOUT;
#endif
- setcstate(c, get_error_from_errno(errno));
- retry_connection(c);
- return;
+ setcstate(c, get_error_from_errno(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);
}
- set_handlers(s, NULL, (void(*)(void *))connected, (void(*)(void *))exception, c);
- setcstate(c, S_CONN);
- } else {
- connected(c);
+ break;
}
+ freeaddrinfo(res0);
}
static void connected(struct connection *c)
@@ -581,3 +617,7 @@ void kill_buffer_data(struct read_buffer
memmove(rb->data, rb->data + n, rb->len - n);
rb->len -= n;
}
+
+/*
+ * vim: noexpandtab
+ */
diff -urNp links-2.6-orig/default.c links-2.6/default.c
--- links-2.6-orig/default.c 2012-04-04 23:27:07.000000000 +0200
+++ links-2.6/default.c 2012-06-08 09:44:30.288941454 +0200
@@ -1256,20 +1256,20 @@ static unsigned char *gen_cmd(struct opt
static unsigned char *lookup_cmd(struct option *o, unsigned char ***argv, int *argc)
{
- ip__address addr;
- unsigned char *p = (unsigned char *)&addr;
+ char addr[NI_MAXHOST];
+ int retval;
if (!*argc) return "Parameter expected";
if (*argc >= 2) return "Too many parameters";
(*argv)++; (*argc)--;
- if (do_real_lookup(*(*argv - 1), &addr)) {
-#if defined(HAVE_GETHOSTBYNAME) && defined(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 "";
}
@@ -1505,7 +1505,7 @@ fprintf(stdout, "%s%s%s%s%s%s\n",
" Password for anonymous ftp access.\n"
"\n"
" -ftp.use-passive <0>/<1>\n"
-" Use ftp PASV command to bypass firewalls.\n"
+" Use ftp EPSV command to bypass firewalls.\n"
"\n"
" -ftp.fast <0>/<1>\n"
" Send more ftp commands simultaneously. Faster response when\n"
@@ -1767,7 +1767,7 @@ int max_connections_to_host = 8;
int max_tries = 3;
int receive_timeout = 120;
int unrestartable_receive_timeout = 600;
-unsigned char bind_ip_address[16] = "";
+unsigned char bind_ip_address[NI_MAXHOST] = "";
int async_lookup = 1;
int download_utime = 0;
@@ -1861,7 +1861,7 @@ static struct option links_options[] = {
{1, gen_cmd, num_rd, num_wr, 0, 16, &max_tries, "retries", "retries"},
{1, gen_cmd, num_rd, num_wr, 1, 9999, &receive_timeout, "receive_timeout", "receive-timeout"},
{1, gen_cmd, num_rd, num_wr, 1, 9999, &unrestartable_receive_timeout, "unrestartable_receive_timeout", "unrestartable-receive-timeout"},
- {1, gen_cmd, ip_rd, str_wr, 0, 16, bind_ip_address, "bind_address", "bind-address"},
+ {1, gen_cmd, ip_rd, str_wr, 0, NI_MAXHOST, bind_ip_address, "bind_address", "bind-address"},
{1, gen_cmd, num_rd, num_wr, 0, 1, &async_lookup, "async_dns", "async-dns"},
{1, gen_cmd, num_rd, num_wr, 0, 1, &download_utime, "download_utime", "download-utime"},
{1, gen_cmd, num_rd, num_wr, 0, 999, &max_format_cache_entries, "format_cache_size", "format-cache-size"},
@@ -2067,3 +2067,6 @@ void save_url_history(void)
return;
}
+/*
+ * vim: noexpandtab
+ */
diff -urNp links-2.6-orig/dns.c links-2.6/dns.c
--- links-2.6-orig/dns.c 2012-03-30 06:26:00.000000000 +0200
+++ links-2.6/dns.c 2012-06-08 09:44:30.291914828 +0200
@@ -5,15 +5,11 @@
#include "links.h"
-#if defined(HAVE_GETHOSTBYNAME_BUG) || !defined(HAVE_GETHOSTBYNAME)
-#define EXTERNAL_LOOKUP
-#endif
-
struct dnsentry {
struct dnsentry *next;
struct dnsentry *prev;
ttime get_time;
- ip__address addr;
+ char addr[NI_MAXHOST];
unsigned char name[1];
};
@@ -30,138 +26,67 @@ struct dnsquery {
void (*xfn)(struct dnsquery *, int);
int h;
struct dnsquery **s;
- ip__address *addr;
+ char *addr;
unsigned char name[1];
};
static struct list_head dns_cache = {&dns_cache, &dns_cache};
-static int get_addr_byte(unsigned char **ptr, unsigned char *res, unsigned char stp)
-{
- unsigned u = 0;
- if (!(**ptr >= '0' && **ptr <= '9')) return -1;
- while (**ptr >= '0' && **ptr <= '9') {
- u = u * 10 + **ptr - '0';
- if (u >= 256) return -1;
- (*ptr)++;
+/* Convert numeric adress to socket address.
+ * @name is the ASCII represantion of the numeric address.
+ * @host is prealocated socket address. Pass NULL if you do not care.
+ * Return 0 if the address is valid, otherwise -1. */
+int numeric_ip_address(unsigned char *name, struct sockaddr_storage *host)
+{
+ struct addrinfo hints, *res;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICHOST;
+
+ if (getaddrinfo(name, NULL, &hints, &res)) return -1;
+ if (host) {
+ memcpy(host, res->ai_addr, res->ai_addrlen);
}
- if (stp != 255 && **ptr != stp) return -1;
- (*ptr)++;
- *res = u;
- return 0;
-}
-
-int numeric_ip_address(unsigned char *name, ip__address *host)
-{
- ip__address dummy;
- if (!host) host = &dummy;
- if (get_addr_byte(&name, ((unsigned char *)host + 0), '.')) return -1;
- if (get_addr_byte(&name, ((unsigned char *)host + 1), '.')) return -1;
- if (get_addr_byte(&name, ((unsigned char *)host + 2), '.')) return -1;
- if (get_addr_byte(&name, ((unsigned char *)host + 3), 0)) return -1;
+ freeaddrinfo(res);
return 0;
}
-#ifdef EXTERNAL_LOOKUP
-
-static int do_external_lookup(unsigned char *name, ip__address *host)
-{
- unsigned char buffer[1024];
- unsigned char sink[16];
- int rd;
- int pi[2];
- pid_t f;
- unsigned char *n;
- int rs;
- if (c_pipe(pi) == -1)
- return -1;
- EINTRLOOP(f, fork());
- if (f == -1) {
- EINTRLOOP(rs, close(pi[0]));
- EINTRLOOP(rs, close(pi[1]));
- return -1;
- }
- if (!f) {
- EINTRLOOP(rs, close(pi[0]));
- EINTRLOOP(rs, dup2(pi[1], 1));
- if (rs == -1) _exit(1);
- EINTRLOOP(rs, dup2(pi[1], 2));
- if (rs == -1) _exit(1);
- EINTRLOOP(rs, close(pi[1]));
- EINTRLOOP(rs, execlp("host", "host", name, NULL));
- EINTRLOOP(rs, execl("/usr/sbin/host", "host", name, NULL));
- _exit(1);
- }
- EINTRLOOP(rs, close(pi[1]));
- rd = hard_read(pi[0], buffer, sizeof buffer - 1);
- if (rd >= 0) buffer[rd] = 0;
- if (rd > 0) {
- while (hard_read(pi[0], sink, sizeof sink) > 0);
- }
- EINTRLOOP(rs, close(pi[0]));
- /* Don't wait for the process, we already have sigchld handler that
- * does cleanup.
- * waitpid(f, NULL, 0); */
- if (rd < 0) return -1;
- /*fprintf(stderr, "query: '%s', result: %s\n", name, buffer);*/
- while ((n = strstr(buffer, name))) {
- memset(n, '-', strlen(name));
- }
- for (n = buffer; n < buffer + rd; n++) {
- if (*n >= '0' && *n <= '9') {
- if (get_addr_byte(&n, ((unsigned char *)host + 0), '.')) goto skip_addr;
- if (get_addr_byte(&n, ((unsigned char *)host + 1), '.')) goto skip_addr;
- if (get_addr_byte(&n, ((unsigned char *)host + 2), '.')) goto skip_addr;
- if (get_addr_byte(&n, ((unsigned char *)host + 3), 255)) goto skip_addr;
- return 0;
-skip_addr:
- if (n >= buffer + rd) break;
- }
- }
- return -1;
-}
-
-#endif
-
-int do_real_lookup(unsigned char *name, ip__address *host)
+/* Resolve @name and return its first IP address as ASCII representation in
+ * @host which must be pre-allocated to NI_MAXHOST bytes.
+ * Return getnameinfo(3) error code, or zero on success. */
+/* TODO: Support multi-homed domain names, link scope addresses. */
+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;
- if (!numeric_ip_address(name, host)) return 0;
-#ifdef HAVE_GETHOSTBYADDR
- if (!(hst = gethostbyaddr(name, strlen(name), AF_INET)))
-#endif
- {
- nogethostbyaddr:
-#ifdef HAVE_GETHOSTBYNAME
- if (!(hst = gethostbyname(name)))
-#endif
- {
-#ifdef EXTERNAL_LOOKUP
- return do_external_lookup(name, host);
-#endif
- return -1;
- }
- }
- memcpy(host, hst->h_addr_list[0], sizeof(ip__address));
- return 0;
+ int retval;
+ struct addrinfo hints, *res;
+ char hbuf[NI_MAXHOST];
+ if (!*name) return EAI_NONAME;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ if ((retval = getaddrinfo(name, NULL, &hints, &res))) return retval;
+ retval = getnameinfo(res->ai_addr, res->ai_addrlen, host, NI_MAXHOST,
+ NULL, 0, NI_NUMERICHOST);
+ freeaddrinfo(res);
+ return retval;
}
#ifndef NO_ASYNC_LOOKUP
static void lookup_fn(unsigned char *name, int h)
{
- ip__address host;
- if (do_real_lookup(name, &host)) return;
- hard_write(h, (unsigned char *)&host, sizeof(ip__address));
+ char host[NI_MAXHOST];
+ if (do_real_lookup(name, host)) return;
+ hard_write(h, (unsigned char *)host, NI_MAXHOST);
}
static void end_real_lookup(struct dnsquery *q)
{
int r = 1;
int rs;
- if (!q->addr || hard_read(q->h, (unsigned char *)q->addr, sizeof(ip__address)) != sizeof(ip__address)) goto end;
+ if (!q->addr || hard_read(q->h, (unsigned char *)q->addr, NI_MAXHOST) != NI_MAXHOST) goto end;
r = 0;
end:
@@ -189,7 +114,7 @@ static int do_lookup(struct dnsquery *q,
#ifndef NO_ASYNC_LOOKUP
sync_lookup:
#endif
- r = do_real_lookup(q->name, q->addr);
+ r = -!!do_real_lookup(q->name, q->addr);
q->xfn(q, r);
return 0;
#ifndef NO_ASYNC_LOOKUP
@@ -252,7 +177,7 @@ static void end_dns_lookup(struct dnsque
}
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;
}
@@ -262,7 +187,7 @@ static void end_dns_lookup(struct dnsque
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:
@@ -273,7 +198,7 @@ static void end_dns_lookup(struct dnsque
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;
retry:
@@ -293,13 +218,13 @@ int find_host_no_cache(unsigned char *na
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;
}
@@ -353,3 +278,7 @@ void init_dns(void)
{
register_cache_upcall(shrink_dns_cache, "dns");
}
+
+/*
+ * vim: noexpandtab
+ */
diff -urNp links-2.6-orig/ftp.c links-2.6/ftp.c
--- links-2.6-orig/ftp.c 2012-03-30 06:26:00.000000000 +0200
+++ links-2.6/ftp.c 2012-06-08 09:44:30.294914833 +0200
@@ -212,12 +212,45 @@ static void ftp_pass_info(struct connect
else ftp_send_retr_req(c, S_GETH);
}
+/* Add EPRT FTP command (RFC 2428) to @string with @length parametrized by
+ * @port. Return 0, -1 in case of error (unknown protocol family or similar). */
+static int ftp_add_eprt(unsigned char **string, int *length, const struct sockaddr_storage *port){
+ unsigned char host_string[NI_MAXHOST];
+ unsigned char port_string[NI_MAXSERV];
+ unsigned char *protocol_string;
+ if (getnameinfo((const struct sockaddr *)port, (socklen_t) sizeof(*port),
+ (char *)host_string, NI_MAXHOST, (char *)port_string, NI_MAXSERV,
+ NI_NUMERICHOST|NI_NUMERICSERV)) return -1;
+ switch (port->ss_family) {
+ /* Numbers assigned by RFC 1070. */
+ case AF_INET:
+ protocol_string = "1";
+ break;
+ case AF_INET6:
+ protocol_string = "2";
+ /* If link-local address, do not send local interface name. */
+ char *interface = strchr(host_string, '%');
+ if (interface) *interface = '\0';
+ break;
+ default:
+ return -1;
+ }
+ add_to_str(string, length, "EPRT |");
+ add_to_str(string, length, protocol_string);
+ add_chr_to_str(string, length, '|');
+ add_to_str(string, length, host_string);
+ add_chr_to_str(string, length, '|');
+ add_to_str(string, length, port_string);
+ add_to_str(string, length, "|\r\n");
+ return 0;
+}
+
static struct ftp_connection_info *add_file_cmd_to_str(struct connection *c)
{
unsigned char *d = get_url_data(c->url);
unsigned char *de;
int del;
- unsigned char pc[6];
+ struct sockaddr_storage pc;
int ps;
struct ftp_connection_info *inf, *inf2;
unsigned char *s;
@@ -238,7 +271,7 @@ static struct ftp_connection_info *add_f
inf->pasv = ftp_options.passive_ftp;
if (*c->socks_proxy) inf->pasv = 1;
c->info = inf;
- if (!inf->pasv) if ((ps = get_pasv_socket(c, c->sock1, &c->sock2, pc))) {
+ if (!inf->pasv) if ((ps = get_pasv_socket(c, c->sock1, &c->sock2, &pc))) {
mem_free(d);
return NULL;
}
@@ -255,21 +288,13 @@ static struct ftp_connection_info *add_f
inf->pending_commands = 4;
add_to_str(&s, &l, "TYPE A\r\n");
if (!inf->pasv) {
- add_to_str(&s, &l, "PORT ");
- add_num_to_str(&s, &l, pc[0]);
- add_chr_to_str(&s, &l, ',');
- add_num_to_str(&s, &l, pc[1]);
- add_chr_to_str(&s, &l, ',');
- add_num_to_str(&s, &l, pc[2]);
- add_chr_to_str(&s, &l, ',');
- add_num_to_str(&s, &l, pc[3]);
- add_chr_to_str(&s, &l, ',');
- add_num_to_str(&s, &l, pc[4]);
- add_chr_to_str(&s, &l, ',');
- add_num_to_str(&s, &l, pc[5]);
- add_to_str(&s, &l, "\r\n");
+ if (ftp_add_eprt(&s, &l, &pc)) {
+ mem_free(d);
+ mem_free(s);
+ return NULL;
+ }
} else {
- add_to_str(&s, &l, "PASV\r\n");
+ add_to_str(&s, &l, "EPSV\r\n");
}
add_to_str(&s, &l, "CWD /");
add_bytes_to_str(&s, &l, d, de - d);
@@ -280,21 +305,13 @@ static struct ftp_connection_info *add_f
inf->pending_commands = 3;
add_to_str(&s, &l, "TYPE I\r\n");
if (!inf->pasv) {
- add_to_str(&s, &l, "PORT ");
- add_num_to_str(&s, &l, pc[0]);
- add_chr_to_str(&s, &l, ',');
- add_num_to_str(&s, &l, pc[1]);
- add_chr_to_str(&s, &l, ',');
- add_num_to_str(&s, &l, pc[2]);
- add_chr_to_str(&s, &l, ',');
- add_num_to_str(&s, &l, pc[3]);
- add_chr_to_str(&s, &l, ',');
- add_num_to_str(&s, &l, pc[4]);
- add_chr_to_str(&s, &l, ',');
- add_num_to_str(&s, &l, pc[5]);
- add_to_str(&s, &l, "\r\n");
+ if (ftp_add_eprt(&s, &l, &pc)) {
+ mem_free(d);
+ mem_free(s);
+ return NULL;
+ }
} else {
- add_to_str(&s, &l, "PASV\r\n");
+ add_to_str(&s, &l, "EPSV\r\n");
}
if (c->from && c->no_cache < NC_IF_MOD) {
add_to_str(&s, &l, "REST ");
@@ -361,38 +378,37 @@ static void ftp_retr_file(struct connect
}
}
if (inf->pending_commands > 1) {
- unsigned char pc[6];
+ int port = 0;
if (inf->pasv && inf->opc - (inf->pending_commands - 1) == 2) {
int i = 3, j;
- while (i < rb->len) {
- if (rb->data[i] >= '0' && rb->data[i] <= '9') {
- for (j = 0; j < 6; j++) {
- int n = 0;
- while (rb->data[i] >= '0' && rb->data[i] <= '9') {
- n = n * 10 + rb->data[i] - '0';
- if (n >= 256) goto no_pasv;
- if (++i >= rb->len) goto no_pasv;
- }
- pc[j] = n;
- if (j != 5) {
- if (rb->data[i] != ',') goto xa;
- if (++i >= rb->len) goto xa;
- if (rb->data[i] < '0' || rb->data[i] > '9') {
- xa:
- if (j != 1) goto no_pasv;
- pc[4] = pc[0];
- pc[5] = pc[1];
- pc[0] = pc[1] = pc[2] = pc[3] = 0;
- goto pasv_ok;
- }
- }
- }
- goto pasv_ok;
- }
+ /* RFC 2428 defines message: <text> (<d><d><d><tcp_port><d>)
+ * where <d> is one-character delimiter and only port can be
+ * returned by server. */
+ /* Search from the end (<text> could contain paranthesis) of first
+ * line in rb->data. */
+ /* Find end of line */
+ while (i < rb->len && rb->data[i] != '\r' && rb->data[i] != '\n')
+ i++;
+ if (i >= rb->len) goto no_pasv;
+ /* Find preceding closing parenthesis */
+ while (i >= 3 && rb->data[i] != ')') i--;
+ if (i < 3) goto no_pasv;
+ /* Delimit port number */
+ j = --i;
+ i--;
+ while (i >= 3 && rb->data[i] != rb->data[j]) i--;
+ if (i < 3) goto no_pasv;
+ i++;
+ /* The port number string is rb->data[i..j-1] */
+ while (i < j) {
+ if (rb->data[i] < '0' || rb->data[i] > '9') goto no_pasv;
+ port = port * 10 + rb->data[i] - '0';
+ if (port < 0) goto no_pasv; /* Integer overflow */
i++;
}
+ goto pasv_ok;
no_pasv:
- memset(pc, 0, sizeof pc);
+ port = 0;
pasv_ok:;
}
g = get_ftp_response(c, rb, 0);
@@ -405,12 +421,12 @@ static void ftp_retr_file(struct connect
case 2: /* PORT */
if (g >= 400) { setcstate(c, S_FTP_PORT); abort_connection(c); return; }
if (inf->pasv) {
- if (!pc[4] && !pc[5]) {
+ if (port == 0) {
setcstate(c, S_FTP_ERROR);
retry_connection(c);
return;
}
- make_connection(c, (pc[4] << 8) + pc[5], &c->sock2, created_data_connection);
+ make_connection(c, port, &c->sock2, created_data_connection);
}
goto rep;
case 3: /* REST / CWD */
@@ -790,3 +806,6 @@ static void ftp_end_request(struct conne
add_keepalive_socket(c, FTP_KEEPALIVE_TIMEOUT);
}
+/*
+ * vim: noexpandtab
+ */
diff -urNp links-2.6-orig/http.c links-2.6/http.c
--- links-2.6-orig/http.c 2012-04-04 23:27:07.000000000 +0200
+++ links-2.6/http.c 2012-06-08 09:44:30.297914934 +0200
@@ -259,7 +259,17 @@ static void http_send_header(struct conn
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, ':')) {
+ /* Do not send local interface name.
+ * TODO: Do it, only if we know this is IPv6 address. */
+ char *interface = strchr(h, '%');
+ if (interface) *interface = '\0';
+ 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, ":");
@@ -1040,3 +1050,7 @@ static void http_get_header(struct conne
rb->close = 1;
read_from_socket(c, c->sock1, rb, http_got_header);
}
+
+/*
+ * vim: noexpandtab
+ */
diff -urNp links-2.6-orig/intl/english.lng links-2.6/intl/english.lng
--- links-2.6-orig/intl/english.lng 2012-04-09 05:09:02.000000000 +0200
+++ links-2.6/intl/english.lng 2012-06-08 09:46:13.159917776 +0200
@@ -216,7 +216,7 @@ T_ERROR_OPENING_FILE, "Error opening fil
T_BAD_FTP_RESPONSE, "Bad FTP response",
T_FTP_SERVICE_UNAVAILABLE, "FTP service unavailable",
T_BAD_FTP_LOGIN, "Bad FTP login",
-T_FTP_PORT_COMMAND_FAILED, "FTP PORT command failed",
+T_FTP_PORT_COMMAND_FAILED, "FTP EPRT command failed",
T_FILE_NOT_FOUND, "File not found",
T_FTP_FILE_ERROR, "FTP file error",
T_UNKNOWN_ERROR, "Unknown error",
diff -urNp links-2.6-orig/language.inc links-2.6/language.inc
--- links-2.6-orig/language.inc 2012-04-09 05:09:00.000000000 +0200
+++ links-2.6/language.inc 2012-06-08 09:44:30.312940885 +0200
@@ -220,7 +220,7 @@ static struct translation translation_en
{T_BAD_FTP_RESPONSE, "Bad FTP response" },
{T_FTP_SERVICE_UNAVAILABLE, "FTP service unavailable" },
{T_BAD_FTP_LOGIN, "Bad FTP login" },
- {T_FTP_PORT_COMMAND_FAILED, "FTP PORT command failed" },
+ {T_FTP_PORT_COMMAND_FAILED, "FTP EPRT command failed" },
{T_FILE_NOT_FOUND, "File not found" },
{T_FTP_FILE_ERROR, "FTP file error" },
{T_UNKNOWN_ERROR, "Unknown error" },
diff -urNp links-2.6-orig/links.1 links-2.6/links.1
--- links-2.6-orig/links.1 2012-01-24 17:14:37.000000000 +0100
+++ links-2.6/links.1 2012-06-08 09:44:30.316939443 +0200
@@ -247,7 +247,7 @@ Password for anonymous ftp access.
.TP
\f3-ftp.use-passive \f2<0>/<1>\f1
-Use ftp PASV command to bypass firewalls.
+Use ftp EPSV command to bypass firewalls.
.TP
\f3-ftp.fast \f2<0>/<1>\f1
diff -urNp links-2.6-orig/links.h links-2.6/links.h
--- links-2.6-orig/links.h 2012-04-09 05:09:00.000000000 +0200
+++ links-2.6/links.h 2012-06-08 09:44:30.320942363 +0200
@@ -727,12 +727,10 @@ void set_sigcld(void);
/* dns.c */
-typedef unsigned ip__address;
-
-int numeric_ip_address(unsigned char *name, ip__address *host);
-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 numeric_ip_address(unsigned char *name, struct sockaddr_storage *host);
+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 **);
unsigned long dns_info(int type);
void init_dns(void);
@@ -1026,10 +1024,10 @@ struct read_buffer {
unsigned char data[1];
};
-int socket_and_bind(unsigned char *address);
+int socket_and_bind(unsigned char *address, int family, int type, int protocol);
void close_socket(int *);
void make_connection(struct connection *, int, int *, void (*)(struct connection *));
-int get_pasv_socket(struct connection *, int, int *, unsigned char *);
+int get_pasv_socket(struct connection *, int, int *, struct sockaddr_storage *);
void write_to_socket(struct connection *, int, unsigned char *, int, void (*)(struct connection *));
struct read_buffer *alloc_read_buffer(struct connection *c);
void read_from_socket(struct connection *, int, struct read_buffer *, void (*)(struct connection *, struct read_buffer *));
@@ -4133,7 +4131,7 @@ extern int max_connections_to_host;
extern int max_tries;
extern int receive_timeout;
extern int unrestartable_receive_timeout;
-extern unsigned char bind_ip_address[16];
+extern unsigned char bind_ip_address[NI_MAXHOST];
extern int async_lookup;
extern int download_utime;
diff -urNp links-2.6-orig/url.c links-2.6/url.c
--- links-2.6-orig/url.c 2012-01-24 17:14:38.000000000 +0100
+++ links-2.6/url.c 2012-06-08 09:44:30.323940116 +0200
@@ -110,11 +110,22 @@ int parse_url(unsigned char *url, int *p
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;
@@ -148,6 +159,10 @@ unsigned char *get_host_and_pass(unsigne
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 will 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);
}
@@ -573,3 +588,6 @@ void add_conv_str(unsigned char **s, int
}
}
+/*
+ * vim: noexpandtab
+ */