Blob Blame History Raw
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
+ */