ipv6-ization efforts continue. Few bugs are found,
unknown number likely introduced...
diff --git a/networking/ftpgetput.c b/networking/ftpgetput.c
index 9e64ff9..9086cb1 100644
--- a/networking/ftpgetput.c
+++ b/networking/ftpgetput.c
@@ -19,7 +19,7 @@
 typedef struct ftp_host_info_s {
 	char *user;
 	char *password;
-	struct sockaddr_in *s_in;
+	struct len_and_sockaddr *lsa;
 } ftp_host_info_t;
 
 static char verbose_flag;
@@ -88,8 +88,8 @@
 	*buf_ptr = '\0';
 	port_num += xatoul_range(buf_ptr + 1, 0, 255) * 256;
 
-	server->s_in->sin_port = htons(port_num);
-	return xconnect_tcp_v4(server->s_in);
+	set_port(server->lsa, htons(port_num));
+	return xconnect_stream(server->lsa);
 }
 
 static FILE *ftp_login(ftp_host_info_t *server)
@@ -98,9 +98,9 @@
 	char buf[512];
 
 	/* Connect to the command socket */
-	control_stream = fdopen(xconnect_tcp_v4(server->s_in), "r+");
+	control_stream = fdopen(xconnect_stream(server->lsa), "r+");
 	if (control_stream == NULL) {
-		/* Extremely unlikely */
+		/* fdopen failed - extremely unlikely */
 		bb_perror_nomsg_and_die();
 	}
 
@@ -304,7 +304,6 @@
 	const char *port = "ftp";
 	/* socket to ftp server */
 	FILE *control_stream;
-	struct sockaddr_in s_in;
 	/* continue previous transfer (-c) */
 	ftp_host_info_t *server;
 
@@ -347,12 +346,10 @@
 	/* We want to do exactly _one_ DNS lookup, since some
 	 * sites (i.e. ftp.us.debian.org) use round-robin DNS
 	 * and we want to connect to only one IP... */
-	server->s_in = &s_in;
-	bb_lookup_host(&s_in, argv[0]);
-	s_in.sin_port = bb_lookup_port(port, "tcp", 21);
+	server->lsa = host2sockaddr(argv[0], bb_lookup_port(port, "tcp", 21));
 	if (verbose_flag) {
-		printf("Connecting to %s[%s]:%d\n",
-			argv[0], inet_ntoa(s_in.sin_addr), ntohs(s_in.sin_port));
+		printf("Connecting to %s [%s]\n", argv[0],
+			xmalloc_sockaddr2dotted(&server->lsa->sa, server->lsa->len));
 	}
 
 	/*  Connect/Setup/Configure the FTP session */
diff --git a/networking/tftp.c b/networking/tftp.c
index 2d28973..eaeb808 100644
--- a/networking/tftp.c
+++ b/networking/tftp.c
@@ -26,12 +26,6 @@
 #define TFTP_TIMEOUT 5	/* seconds */
 #define TFTP_NUM_RETRIES 5 /* number of retries */
 
-static const char * const MODE_OCTET = "octet";
-#define MODE_OCTET_LEN 6 /* sizeof(MODE_OCTET)*/
-
-static const char * const OPTION_BLOCKSIZE = "blksize";
-#define OPTION_BLOCKSIZE_LEN 8 /* sizeof(OPTION_BLOCKSIZE) */
-
 /* opcodes we support */
 #define TFTP_RRQ   1
 #define TFTP_WRQ   2
@@ -51,13 +45,23 @@
 	"No such user"
 };
 
-#define tftp_cmd_get ENABLE_FEATURE_TFTP_GET
-
-#if ENABLE_FEATURE_TFTP_PUT
-# define tftp_cmd_put (tftp_cmd_get+ENABLE_FEATURE_TFTP_PUT)
+#if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT
+#define USE_GETPUT(a)
+#define CMD_GET(cmd) 1
+#define CMD_PUT(cmd) 0
+#elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
+#define USE_GETPUT(a)
+#define CMD_GET(cmd) 0
+#define CMD_PUT(cmd) 1
 #else
-# define tftp_cmd_put 0
+#define USE_GETPUT(a) a
+/* masks coming from getpot32 */
+#define CMD_GET(cmd) (cmd & 1)
+#define CMD_PUT(cmd) (cmd & 2)
 #endif
+/* NB: in the code below
+ * CMD_GET(cmd) and CMD_GET(cmd) are mutually exclusive
+ */
 
 
 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
@@ -70,8 +74,9 @@
 	 * to use blocksizes smaller than 22 octets.
 	 */
 
-	if ((bufsize && (blocksize > bufsize)) ||
-		(blocksize < 8) || (blocksize > 65564)) {
+	if ((bufsize && (blocksize > bufsize))
+	 || (blocksize < 8) || (blocksize > 65564)
+	) {
 		bb_error_msg("bad blocksize");
 		return 0;
 	}
@@ -86,7 +91,6 @@
 	int k;
 
 	while (len > 0) {
-
 		/* Make sure the options are terminated correctly */
 
 		for (k = 0; k < len; k++) {
@@ -122,22 +126,23 @@
 
 #endif
 
-static int tftp(const int cmd, const struct hostent *host,
-		   const char *remotefile, const int localfd,
-		   const unsigned short port, int tftp_bufsize)
+static int tftp(
+#if ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
+		const int cmd,
+#endif
+		const len_and_sockaddr *peer_lsa,
+		const char *remotefile, const int localfd,
+		const unsigned port, int tftp_bufsize)
 {
-	struct sockaddr_in sa;
-	struct sockaddr_in from;
 	struct timeval tv;
-	socklen_t fromlen;
 	fd_set rfds;
 	int socketfd;
 	int len;
 	int opcode = 0;
 	int finished = 0;
 	int timeout = TFTP_NUM_RETRIES;
-	unsigned short block_nr = 1;
-	unsigned short tmp;
+	uint16_t block_nr = 1;
+	uint16_t tmp;
 	char *cp;
 
 	USE_FEATURE_TFTP_BLOCKSIZE(int want_option_ack = 0;)
@@ -149,42 +154,24 @@
 	char *xbuf = xmalloc(tftp_bufsize += 4);
 	char *rbuf = xmalloc(tftp_bufsize);
 
-	if ((socketfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
-		/* need to unlink the localfile, so don't use xsocket here. */
-		bb_perror_msg("socket");
-		return EXIT_FAILURE;
-	}
-
-	len = sizeof(sa);
-
-	memset(&sa, 0, len);
-	xbind(socketfd, (struct sockaddr *)&sa, len);
-
-	sa.sin_family = host->h_addrtype;
-	sa.sin_port = port;
-	memcpy(&sa.sin_addr, (struct in_addr *) host->h_addr,
-		   sizeof(sa.sin_addr));
+	socketfd = xsocket(peer_lsa->sa.sa_family, SOCK_DGRAM, 0);
 
 	/* build opcode */
-	if (cmd & tftp_cmd_get) {
+	opcode = TFTP_WRQ;
+	if (CMD_GET(cmd)) {
 		opcode = TFTP_RRQ;
 	}
-	if (cmd & tftp_cmd_put) {
-		opcode = TFTP_WRQ;
-	}
 
 	while (1) {
 
 		cp = xbuf;
 
 		/* first create the opcode part */
-		*((unsigned short *) cp) = htons(opcode);
+		*((uint16_t*)cp) = htons(opcode);
 		cp += 2;
 
 		/* add filename and mode */
-		if (((cmd & tftp_cmd_get) && (opcode == TFTP_RRQ)) ||
-			((cmd & tftp_cmd_put) && (opcode == TFTP_WRQ)))
-		{
+		if (CMD_GET(cmd) ? (opcode == TFTP_RRQ) : (opcode == TFTP_WRQ)) {
 			int too_long = 0;
 
 			/* see if the filename fits into xbuf
@@ -198,14 +185,14 @@
 				cp += len;
 			}
 
-			if (too_long || ((&xbuf[tftp_bufsize - 1] - cp) < MODE_OCTET_LEN)) {
+			if (too_long || (&xbuf[tftp_bufsize - 1] - cp) < sizeof("octet")) {
 				bb_error_msg("remote filename too long");
 				break;
 			}
 
 			/* add "mode" part of the package */
-			memcpy(cp, MODE_OCTET, MODE_OCTET_LEN);
-			cp += MODE_OCTET_LEN;
+			memcpy(cp, "octet", sizeof("octet"));
+			cp += sizeof("octet");
 
 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
 
@@ -219,8 +206,8 @@
 				}
 
 				/* add "blksize" + number of blocks  */
-				memcpy(cp, OPTION_BLOCKSIZE, OPTION_BLOCKSIZE_LEN);
-				cp += OPTION_BLOCKSIZE_LEN;
+				memcpy(cp, "blksize", sizeof("blksize"));
+				cp += sizeof("blksize");
 				cp += snprintf(cp, 6, "%d", len) + 1;
 
 				want_option_ack = 1;
@@ -230,16 +217,12 @@
 
 		/* add ack and data */
 
-		if (((cmd & tftp_cmd_get) && (opcode == TFTP_ACK)) ||
-			((cmd & tftp_cmd_put) && (opcode == TFTP_DATA))) {
-
-			*((unsigned short *) cp) = htons(block_nr);
-
+		if (CMD_GET(cmd) ? (opcode == TFTP_ACK) : (opcode == TFTP_DATA)) {
+			*((uint16_t*)cp) = htons(block_nr);
 			cp += 2;
-
 			block_nr++;
 
-			if ((cmd & tftp_cmd_put) && (opcode == TFTP_DATA)) {
+			if (CMD_PUT(cmd) && (opcode == TFTP_DATA)) {
 				len = full_read(localfd, cp, tftp_bufsize - 4);
 
 				if (len < 0) {
@@ -261,9 +244,7 @@
 
 		timeout = TFTP_NUM_RETRIES;	/* re-initialize */
 		do {
-
 			len = cp - xbuf;
-
 #if ENABLE_DEBUG_TFTP
 			fprintf(stderr, "sending %u bytes\n", len);
 			for (cp = xbuf; cp < &xbuf[len]; cp++)
@@ -271,7 +252,7 @@
 			fprintf(stderr, "\n");
 #endif
 			if (sendto(socketfd, xbuf, len, 0,
-					   (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+					&peer_lsa->sa, peer_lsa->len) < 0) {
 				bb_perror_msg("send");
 				len = -1;
 				break;
@@ -284,9 +265,6 @@
 
 			/* receive packet */
 
-			memset(&from, 0, sizeof(from));
-			fromlen = sizeof(from);
-
 			tv.tv_sec = TFTP_TIMEOUT;
 			tv.tv_usec = 0;
 
@@ -294,30 +272,41 @@
 			FD_SET(socketfd, &rfds);
 
 			switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
-			case 1:
-				len = recvfrom(socketfd, rbuf, tftp_bufsize, 0,
-							   (struct sockaddr *) &from, &fromlen);
+				struct sockaddr *from;
+				socklen_t fromlen;
 
+			case 1:
+				fromlen = peer_lsa->len;
+				from = alloca(fromlen);
+				memset(from, 0, fromlen);
+
+				len = recvfrom(socketfd, rbuf, tftp_bufsize, 0,
+							from, &fromlen);
 				if (len < 0) {
 					bb_perror_msg("recvfrom");
 					break;
 				}
-
 				timeout = 0;
-
-				if (sa.sin_port == port) {
-					sa.sin_port = from.sin_port;
+				if (from->sa_family == peer_lsa->sa.sa_family) {
+#if ENABLE_FEATURE_IPV6
+					if (from->sa_family == AF_INET6
+					 && ((struct sockaddr_in6*)from)->sin6_port == port
+					)
+						break;
+#endif
+					/* Non-internet sockets are ok */
+					if (from->sa_family != AF_INET)
+						break;
+					if (((struct sockaddr_in*)from)->sin_port == port)
+						break;
 				}
-				if (sa.sin_port == from.sin_port) {
-					break;
-				}
-
-				/* fall-through for bad packets! */
-				/* discard the packet - treat as timeout */
+				/* family doesn't match, or
+				 * it is INET[v6] and port doesn't match -
+				 * fall-through for bad packets!
+				 * (discard the packet - treat as timeout) */
 				timeout = TFTP_NUM_RETRIES;
 			case 0:
 				bb_error_msg("timeout");
-
 				timeout--;
 				if (timeout == 0) {
 					len = -1;
@@ -331,14 +320,14 @@
 
 		} while (timeout && (len >= 0));
 
-		if ((finished) || (len < 0)) {
+		if (finished || (len < 0)) {
 			break;
 		}
 
 		/* process received packet */
 
-		opcode = ntohs(*((unsigned short *) rbuf));
-		tmp = ntohs(*((unsigned short *) &rbuf[2]));
+		opcode = ntohs( ((uint16_t*)rbuf)[0] );
+		tmp = ntohs( ((uint16_t*)rbuf)[1] );
 
 #if ENABLE_DEBUG_TFTP
 		fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, tmp);
@@ -352,7 +341,6 @@
 				rbuf[tftp_bufsize - 1] = '\0';
 			} else if (tmp < (sizeof(tftp_bb_error_msg)
 							  / sizeof(char *))) {
-
 				msg = tftp_bb_error_msg[tmp];
 			}
 
@@ -368,25 +356,22 @@
 			want_option_ack = 0;
 
 			if (opcode == TFTP_OACK) {
-
 				/* server seems to support options */
-
 				char *res;
 
-				res = tftp_option_get(&rbuf[2], len - 2, OPTION_BLOCKSIZE);
+				res = tftp_option_get(&rbuf[2], len - 2, "blksize");
 
 				if (res) {
 					int blksize = xatoi_u(res);
 
 					if (tftp_blocksize_check(blksize, tftp_bufsize - 4)) {
-
-						if (cmd & tftp_cmd_put) {
+						if (CMD_PUT(cmd)) {
 							opcode = TFTP_DATA;
 						} else {
 							opcode = TFTP_ACK;
 						}
 #if ENABLE_DEBUG_TFTP
-						fprintf(stderr, "using %s %u\n", OPTION_BLOCKSIZE,
+						fprintf(stderr, "using blksize %u\n",
 								blksize);
 #endif
 						tftp_bufsize = blksize + 4;
@@ -407,10 +392,8 @@
 		}
 #endif
 
-		if ((cmd & tftp_cmd_get) && (opcode == TFTP_DATA)) {
-
+		if (CMD_GET(cmd) && (opcode == TFTP_DATA)) {
 			if (tmp == block_nr) {
-
 				len = full_write(localfd, &rbuf[4], len - 4);
 
 				if (len < 0) {
@@ -430,6 +413,7 @@
 				--block_nr;
 				opcode = TFTP_ACK;
 				continue;
+// tmp==(block_nr-1) and (tmp+1)==block_nr is always same, I think. wtf?
 			} else if (tmp + 1 == block_nr) {
 				/* Server lost our TFTP_ACK.  Resend it */
 				block_nr = tmp;
@@ -438,9 +422,8 @@
 			}
 		}
 
-		if ((cmd & tftp_cmd_put) && (opcode == TFTP_ACK)) {
-
-			if (tmp == (unsigned short) (block_nr - 1)) {
+		if (CMD_PUT(cmd) && (opcode == TFTP_ACK)) {
+			if (tmp == (uint16_t) (block_nr - 1)) {
 				if (finished) {
 					break;
 				}
@@ -462,61 +445,32 @@
 
 int tftp_main(int argc, char **argv)
 {
-	struct hostent *host = NULL;
+	len_and_sockaddr *peer_lsa;
 	const char *localfile = NULL;
 	const char *remotefile = NULL;
+#if ENABLE_FEATURE_TFTP_BLOCKSIZE
+	const char *sblocksize = NULL;
+#endif
 	int port;
-	int cmd = 0;
+	USE_GETPUT(int cmd;)
 	int fd = -1;
 	int flags = 0;
 	int result;
 	int blocksize = TFTP_BLOCKSIZE_DEFAULT;
 
-	/* figure out what to pass to getopt */
+	/* -p or -g is mandatory, and they are mutually exclusive */
+	opt_complementary = "" USE_FEATURE_TFTP_GET("g:") USE_FEATURE_TFTP_PUT("p:")
+			USE_GETPUT("?g--p:p--g");
 
-#if ENABLE_FEATURE_TFTP_BLOCKSIZE
-	char *sblocksize = NULL;
+	USE_GETPUT(cmd =) getopt32(argc, argv,
+			USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p")
+				"l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"),
+			&localfile, &remotefile
+			USE_FEATURE_TFTP_BLOCKSIZE(, &sblocksize));
 
-#define BS "b:"
-#define BS_ARG , &sblocksize
-#else
-#define BS
-#define BS_ARG
-#endif
-
-#if ENABLE_FEATURE_TFTP_GET
-#define GET "g"
-#define GET_COMPL ":g"
-#else
-#define GET
-#define GET_COMPL
-#endif
-
-#if ENABLE_FEATURE_TFTP_PUT
-#define PUT "p"
-#define PUT_COMPL ":p"
-#else
-#define PUT
-#define PUT_COMPL
-#endif
-
-#if defined(CONFIG_FEATURE_TFTP_GET) && defined(CONFIG_FEATURE_TFTP_PUT)
-	opt_complementary = GET_COMPL PUT_COMPL ":?g--p:p--g";
-#elif defined(CONFIG_FEATURE_TFTP_GET) || defined(CONFIG_FEATURE_TFTP_PUT)
-	opt_complementary = GET_COMPL PUT_COMPL;
-#endif
-
-	cmd = getopt32(argc, argv, GET PUT "l:r:" BS, &localfile, &remotefile BS_ARG);
-
-	cmd &= (tftp_cmd_get | tftp_cmd_put);
-#if ENABLE_FEATURE_TFTP_GET
-	if (cmd == tftp_cmd_get)
+	flags = O_RDONLY;
+	if (CMD_GET(cmd))
 		flags = O_WRONLY | O_CREAT | O_TRUNC;
-#endif
-#if ENABLE_FEATURE_TFTP_PUT
-	if (cmd == tftp_cmd_put)
-		flags = O_RDONLY;
-#endif
 
 #if ENABLE_FEATURE_TFTP_BLOCKSIZE
 	if (sblocksize) {
@@ -535,30 +489,31 @@
 		bb_show_usage();
 
 	if (localfile == NULL || LONE_DASH(localfile)) {
-		fd = (cmd == tftp_cmd_get) ? STDOUT_FILENO : STDIN_FILENO;
+		fd = CMD_GET(cmd) ? STDOUT_FILENO : STDIN_FILENO;
 	} else {
-		fd = open(localfile, flags, 0644); /* fail below */
-	}
-	if (fd < 0) {
-		bb_perror_msg_and_die("local file");
+		fd = xopen3(localfile, flags, 0644);
 	}
 
-	host = xgethostbyname(argv[optind]);
 	port = bb_lookup_port(argv[optind + 1], "udp", 69);
+	peer_lsa = host2sockaddr(argv[optind], port);
 
 #if ENABLE_DEBUG_TFTP
-	fprintf(stderr, "using server \"%s\", remotefile \"%s\", "
-			"localfile \"%s\".\n",
-			inet_ntoa(*((struct in_addr *) host->h_addr)),
+	fprintf(stderr, "using server \"%s\", "
+			"remotefile \"%s\", localfile \"%s\".\n",
+			xmalloc_sockaddr2dotted(&peer_lsa->sa, peer_lsa->len),
 			remotefile, localfile);
 #endif
 
-	result = tftp(cmd, host, remotefile, fd, port, blocksize);
+	result = tftp(
+#if ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
+		cmd,
+#endif
+		peer_lsa, remotefile, fd, port, blocksize);
 
-	if (!(fd == STDOUT_FILENO || fd == STDIN_FILENO)) {
+	if (fd > 1) {
 		if (ENABLE_FEATURE_CLEAN_UP)
 			close(fd);
-		if (cmd == tftp_cmd_get && result != EXIT_SUCCESS)
+		if (CMD_GET(cmd) && result != EXIT_SUCCESS)
 			unlink(localfile);
 	}
 	return result;