ipsvd: use IP:PORT syntax for environment vars. Pros:
1. it's saner (matches internal libc sockaddr abstraction).
2. it's smaller.
Cons: not compatible with smarden's ipsvd.
Fix IPv6 define typos.
Stop interpreting options in prog's args.
Code size -162 bytes.
diff --git a/ipsvd/tcpudp.c b/ipsvd/tcpudp.c
index 6e8ec32..9f098c3 100644
--- a/ipsvd/tcpudp.c
+++ b/ipsvd/tcpudp.c
@@ -22,9 +22,9 @@
  *
  * udp server is hacked up by reusing TCP code. It has the following
  * limitation inherent in Unix DGRAM sockets implementation:
- * - local IP address is reptrieved (using recvmsg voodoo) but
+ * - local IP address is retrieved (using recvmsg voodoo) but
  *   child's socket is not bound to it (bind cannot be called on
- *   already bound socket). Thus you still can get outgoing packets
+ *   already bound socket). Thus it still can emit outgoing packets
  *   with wrong sorce IP...
  * - don't know how to retrieve ORIGDST for udp.
  */
@@ -100,7 +100,7 @@
 
 static void connection_status(void)
 {
-	/* UDP and "only 1 client max" TCP don't need this */
+	/* "only 1 client max" don't need this */
 	if (cmax > 1)
 		printf("%s: info: status %u/%u\n", applet_name, cnum, cmax);
 }
@@ -131,34 +131,35 @@
 	const char *instructs;
 	char *msg_per_host = NULL;
 	unsigned len_per_host = len_per_host; /* gcc */
-	int need_hostnames, need_remote_ip;
-	int tcp;
+#ifndef SSLSVD
+	struct bb_uidgid_t ugid;
+#endif
+	bool need_hostnames, need_remote_ip, tcp;
+	uint16_t local_port;
+	char *local_hostname = NULL;
+	char *remote_hostname = (char*)""; /* "" used if no -h */
+	char *local_addr = local_addr; /* gcc */
+	char *remote_addr = remote_addr; /* gcc */
+	char *remote_ip = remote_addr; /* gcc */
+	len_and_sockaddr *lsa;
+	len_and_sockaddr local, remote;
+	socklen_t sa_len;
 	int pid;
 	int sock;
 	int conn;
 	unsigned backlog = 20;
-	len_and_sockaddr *lsa;
-	len_and_sockaddr local, remote;
-	uint16_t local_port;
-	uint16_t remote_port = remote_port; /* gcc */
-	char *local_hostname = NULL;
-	char *remote_hostname = (char*)""; /* "" used if no -h */
-	char *local_ip = local_ip; /* gcc */
-	char *remote_ip = remote_ip; /* gcc */
-#ifndef SSLSVD
-	struct bb_uidgid_t ugid;
-#endif
+
 	tcp = (applet_name[0] == 't');
 
 	/* 3+ args, -i at most once, -p implies -h, -v is counter */
 	opt_complementary = "-3:?:i--i:ph:vv";
 #ifdef SSLSVD
-	getopt32(argc, argv, "c:C:i:x:u:l:Eb:hpt:vU:/:Z:K:",
+	getopt32(argc, argv, "+c:C:i:x:u:l:Eb:hpt:vU:/:Z:K:",
 		&str_c, &str_C, &instructs, &instructs, &user, &local_hostname,
 		&str_b, &str_t, &ssluser, &root, &cert, &key, &verbose
 	);
 #else
-	getopt32(argc, argv, "c:C:i:x:u:l:Eb:hpt:v",
+	getopt32(argc, argv, "+c:C:i:x:u:l:Eb:hpt:v",
 		&str_c, &str_C, &instructs, &instructs, &user, &local_hostname,
 		&str_b, &str_t, &verbose
 	);
@@ -213,10 +214,8 @@
 	if (option_mask32 & OPT_u)
 		if (!uidgid_get(&sslugid, ssluser, 1)) {
 			if (errno) {
-				xfunc_exitcode = 100;
 				bb_perror_msg_and_die("fatal: cannot get user/group: %s", ssluser);
 			}
-			xfunc_exitcode = 111;
 			bb_error_msg_and_die("fatal: unknown user/group '%s'", ssluser);
 		}
 	if (!cert) cert = "./cert.pem";
@@ -244,7 +243,8 @@
 	lsa = xhost2sockaddr(argv[0], local_port);
 	sock = xsocket(lsa->sa.sa_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
 	setsockopt_reuseaddr(sock);
-	xbind(sock, &lsa->sa, lsa->len);
+	sa_len = lsa->len; /* I presume sockaddr len stays the same */
+	xbind(sock, &lsa->sa, sa_len);
 	if (tcp)
 		xlisten(sock, backlog);
 	else /* udp: needed for recv_from_to to work: */
@@ -260,9 +260,7 @@
 #endif
 
 	if (verbose) {
-		/* we do it only for ":port" cosmetics... oh well */
-		char *addr = xmalloc_sockaddr2dotted(&lsa->sa, lsa->len);
-
+		char *addr = xmalloc_sockaddr2dotted(&lsa->sa, sa_len);
 		printf("%s: info: listening on %s", applet_name, addr);
 		free(addr);
 #ifndef SSLSVD
@@ -287,10 +285,14 @@
  again2:
 	sig_unblock(SIGCHLD);
 	if (tcp) {
-		remote.len = lsa->len;
+		remote.len = sa_len;
 		conn = accept(sock, &remote.sa, &remote.len);
-	} else
-		conn = recv_from_to(sock, NULL, 0, MSG_PEEK, &remote.sa, &local.sa, lsa->len);
+	} else {
+		/* In case we won't be able to recover local below.
+		 * Also sets port - recv_from_to is unable to do it. */
+		local = *lsa;
+		conn = recv_from_to(sock, NULL, 0, MSG_PEEK, &remote.sa, &local.sa, sa_len);
+	}
 	sig_block(SIGCHLD);
 	if (conn < 0) {
 		if (errno != EINTR)
@@ -302,7 +304,7 @@
 	if (max_per_host) {
 		/* Drop connection immediately if cur_per_host > max_per_host
 		 * (minimizing load under SYN flood) */
-		remote_ip = xmalloc_sockaddr2dotted_noport(&remote.sa, lsa->len);
+		remote_ip = xmalloc_sockaddr2dotted_noport(&remote.sa, sa_len);
 		cur_per_host = ipsvd_perhost_add(remote_ip, max_per_host, &hccp);
 		if (cur_per_host > max_per_host) {
 			/* ipsvd_perhost_add detected that max is exceeded
@@ -319,16 +321,21 @@
 
 	if (!tcp) {
 		/* Voodoo magic: making udp sockets each receive its own
-		 * packets is not trivial */
+		 * packets is not trivial, and I still not sure
+		 * I do it 100% right.
+		 * 1) we have to do it before fork()
+		 * 2) order is important - is it right now? */
 
-		/* Make plain write work for this socket by supplying default
+		/* Make plain write/send work for this socket by supplying default
 		 * destination address. This also restricts incoming packets
 		 * to ones coming from this remote IP. */
-		xconnect(0, &remote.sa, lsa->len);
+		xconnect(0, &remote.sa, sa_len);
+	/* hole? at this point we have no wildcard udp socket...
+	 * can this cause clients to get "port unreachable" icmp? */
 		/* Open new non-connected UDP socket for further clients */
 		sock = xsocket(lsa->sa.sa_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
 		setsockopt_reuseaddr(sock);
-		xbind(sock, &lsa->sa, lsa->len);
+		xbind(sock, &lsa->sa, sa_len);
 		socket_want_pktinfo(sock);
 	}
 
@@ -355,50 +362,44 @@
 	if (tcp)
 		close(sock);
 
-	if (need_remote_ip) {
-		if (!max_per_host)
-			remote_ip = xmalloc_sockaddr2dotted_noport(&remote.sa, lsa->len);
-		/* else it is already done */
-		remote_port = get_nport(&remote.sa);
-		remote_port = ntohs(remote_port);
-	}
+	if (need_remote_ip)
+		remote_addr = xmalloc_sockaddr2dotted(&remote.sa, sa_len);
 
 	if (need_hostnames) {
 		if (option_mask32 & OPT_h) {
-			remote_hostname = xmalloc_sockaddr2host(&remote.sa, lsa->len);
+			remote_hostname = xmalloc_sockaddr2host_noport(&remote.sa, sa_len);
 			if (!remote_hostname) {
-				bb_error_msg("warning: cannot look up hostname for %s", remote_ip);
+				bb_error_msg("warning: cannot look up hostname for %s", remote_addr);
 				remote_hostname = (char*)"";
 			}
 		}
 		/* Find out local IP peer connected to.
 		 * Errors ignored (I'm not paranoid enough to imagine kernel
 		 * which doesn't know local IP). */
-		if (tcp)
+		if (tcp) {
+			local.len = sa_len;
 			getsockname(0, &local.sa, &local.len);
-		local_ip = xmalloc_sockaddr2dotted_noport(&local.sa, lsa->len);
-		local_port = get_nport(&local.sa);
-		local_port = ntohs(local_port);
+		}
+		local_addr = xmalloc_sockaddr2dotted(&local.sa, sa_len);
 		if (!local_hostname) {
-			local_hostname = xmalloc_sockaddr2host_noport(&local.sa, lsa->len);
+			local_hostname = xmalloc_sockaddr2host_noport(&local.sa, sa_len);
 			if (!local_hostname)
-				bb_error_msg_and_die("warning: cannot look up hostname for %s"+9, local_ip);
+				bb_error_msg_and_die("warning: cannot look up hostname for %s"+9, local_addr);
 		}
 	}
 
 	if (verbose) {
 		pid = getpid();
-		printf("%s: info: pid %u from %s\n", applet_name, pid, remote_ip);
+		printf("%s: info: pid %u from %s\n", applet_name, pid, remote_addr);
 		if (max_per_host)
 			printf("%s: info: concurrency %u %s %u/%u\n",
 				applet_name, pid, remote_ip, cur_per_host, max_per_host);
-		printf("%s: info: start %u %s:%s :%s:%s:%u\n",
+		printf("%s: info: start %u %s:%s :%s:%s\n",
 			applet_name, pid,
-			local_hostname, local_ip,
-			remote_hostname, remote_ip, (unsigned)remote_port);
+			local_hostname, local_addr,
+			remote_hostname, remote_addr);
 	}
 
-// TODO: stop splitiing port# from IP?
 	if (!(option_mask32 & OPT_E)) {
 		/* setup ucspi env */
 		const char *proto = tcp ? "TCP" : "UDP";
@@ -408,19 +409,14 @@
 		 * an outbond connection to local handler, and it needs
 		 * to know where it originally tried to connect */
 		if (tcp && getsockopt(0, SOL_IP, SO_ORIGINAL_DST, &lsa->sa, &lsa->len) == 0) {
-			char *ip = xmalloc_sockaddr2dotted_noport(&lsa->sa, lsa->len);
-			unsigned port = get_nport(&lsa->sa);
-			port = ntohs(port);
-			xsetenv("TCPORIGDSTIP", ip);
-			xsetenv("TCPORIGDSTPORT", utoa(port));
-			free(ip);
+			char *addr = xmalloc_sockaddr2dotted(&lsa->sa, sa_len);
+			xsetenv("TCPORIGDSTADDR", addr);
+			free(addr);
 		}
 		xsetenv("PROTO", proto);
-		xsetenv_proto(proto, "LOCALIP", local_ip);
-		xsetenv_proto(proto, "LOCALPORT", utoa(local_port));
+		xsetenv_proto(proto, "LOCALADDR", local_addr);
 		xsetenv_proto(proto, "LOCALHOST", local_hostname);
-		xsetenv_proto(proto, "REMOTEIP", remote_ip);
-		xsetenv_proto(proto, "REMOTEPORT", utoa(remote_port));
+		xsetenv_proto(proto, "REMOTEADDR", remote_addr);
 		if (option_mask32 & OPT_h) {
 			xsetenv_proto(proto, "REMOTEHOST", remote_hostname);
 		}
diff --git a/ipsvd/udp_io.c b/ipsvd/udp_io.c
index 68999d4..2efc159 100644
--- a/ipsvd/udp_io.c
+++ b/ipsvd/udp_io.c
@@ -18,12 +18,13 @@
 #ifdef IP_PKTINFO
 	setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &const_int_1, sizeof(int));
 #endif
-#ifdef IPV6_PKTINFO
+#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
 	setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &const_int_1, sizeof(int));
 #endif
 }
 
 
+#ifdef UNUSED
 ssize_t
 send_to_from(int fd, void *buf, size_t len, int flags,
 		const struct sockaddr *from, const struct sockaddr *to,
@@ -34,8 +35,11 @@
 #else
 	struct iovec iov[1];
 	struct msghdr msg;
-	char cbuf[LSA_SIZEOF_SA];
-	/* actually, max(sizeof(in_pktinfo),sizeof(in6_pktinfo)) */
+	char cbuf[sizeof(struct in_pktinfo)
+#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
+		| sizeof(struct in6_pktinfo) /* (a|b) is poor man's max(a,b) */
+#endif
+	];
 	struct cmsghdr* cmsgptr;
 
 	if (from->sa_family != AF_INET
@@ -73,11 +77,11 @@
 		/* pktptr->ipi_ifindex = 0; -- already done by memset(cbuf...) */
 		pktptr->ipi_spec_dst = ((struct sockaddr_in*)from)->sin_addr;
 	}
-#if ENABLE_FEATURE_IPV6 && defined(IP6_PKTINFO)
+#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
 	else if (to->sa_family == AF_INET6 && from->sa_family == AF_INET6) {
 		struct in6_pktinfo *pktptr;
 		cmsgptr->cmsg_level = IPPROTO_IPV6;
-		cmsgptr->cmsg_type = IP6_PKTINFO;
+		cmsgptr->cmsg_type = IPV6_PKTINFO;
 		cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
 		pktptr = (struct in6_pktinfo *)(CMSG_DATA(cmsgptr));
 		/* pktptr->ipi6_ifindex = 0; -- already done by memset(cbuf...) */
@@ -87,8 +91,12 @@
 	return sendmsg(fd, &msg, flags);
 #endif
 }
+#endif /* UNUSED */
 
-/* NB: this will never set port# in *to! */
+/* NB: this will never set port# in 'to'!
+ * _Only_ IP/IPv6 address part of 'to' is _maybe_ modified.
+ * Typical usage is to preinit it with "default" value
+ * before calling recv_from_to(). */
 ssize_t
 recv_from_to(int fd, void *buf, size_t len, int flags,
 		struct sockaddr *from, struct sockaddr *to,
@@ -123,7 +131,6 @@
 		return recv_length;
 
 	/* Here we try to retrieve destination IP and memorize it */
-	memset(to, 0, sa_size);
 	for (cmsgptr = CMSG_FIRSTHDR(&msg);
 			cmsgptr != NULL;
 			cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)
@@ -138,9 +145,9 @@
 #undef pktinfo
 			break;
 		}
-#if ENABLE_FEATURE_IPV6 && defined(IP6_PKTINFO)
+#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
 		if (cmsgptr->cmsg_level == IPPROTO_IPV6
-		 && cmsgptr->cmsg_type == IP6_PKTINFO
+		 && cmsgptr->cmsg_type == IPV6_PKTINFO
 		) {
 #define pktinfo(cmsgptr) ( (struct in6_pktinfo*)(CMSG_DATA(cmsgptr)) )
 			to->sa_family = AF_INET6;