diff -uNr ucspi-tcp-0.88.orig/FILES ucspi-tcp-0.88/FILES
--- ucspi-tcp-0.88.orig/FILES	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/FILES	2009-08-04 17:45:59.000000000 -0500
@@ -216,3 +216,40 @@
 warn-auto.sh
 warn-shsgr
 x86cpuid.c
+dns_ip6.c
+dns_ipq6.c
+dns_nd6.c
+dns_sortip6.c
+fmt_xlong.c
+ip6_fmt.c
+ip6_scan.c
+scan_0x.c
+socket_accept6.c
+socket_bind6.c
+socket_conn6.c
+socket_local6.c
+socket_recv6.c
+socket_remote6.c
+socket_send6.c
+socket_tcp6.c
+timeoutconn6.c
+tryip6.c
+haveip6.h2
+haveip6.h1
+remoteinfo6.c
+addcr.1
+argv0.1
+date@.1
+delcr.1
+finger@.1
+fixcr.1
+http@.1
+mconnect.1
+recordio.1
+tcp-environ.5
+tcpcat.1
+tcpclient.1
+tcprules.1
+tcprulescheck.1
+tcpserver.1
+who@.1
diff -uNr ucspi-tcp-0.88.orig/Makefile ucspi-tcp-0.88/Makefile
--- ucspi-tcp-0.88.orig/Makefile	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/Makefile	2009-08-04 17:45:59.000000000 -0500
@@ -76,12 +76,14 @@
 makelib byte_chr.o byte_copy.o byte_cr.o byte_diff.o byte_rchr.o \
 byte_zero.o case_diffb.o case_diffs.o fmt_ulong.o ip4_fmt.o \
 ip4_scan.o scan_ulong.o str_chr.o str_diff.o str_len.o str_start.o \
-uint16_pack.o uint16_unpack.o uint32_pack.o uint32_unpack.o
+uint16_pack.o uint16_unpack.o uint32_pack.o uint32_unpack.o \
+ip6_fmt.o scan_ip6.o scan_xlong.o fmt_xlong.o
 	./makelib byte.a byte_chr.o byte_copy.o byte_cr.o \
 	byte_diff.o byte_rchr.o byte_zero.o case_diffb.o \
 	case_diffs.o fmt_ulong.o ip4_fmt.o ip4_scan.o scan_ulong.o \
 	str_chr.o str_diff.o str_len.o str_start.o uint16_pack.o \
-	uint16_unpack.o uint32_pack.o uint32_unpack.o
+	uint16_unpack.o uint32_pack.o uint32_unpack.o ip6_fmt.o \
+	scan_ip6.o scan_xlong.o fmt_xlong.o
 
 byte_chr.o: \
 compile byte_chr.c byte.h
@@ -181,11 +183,13 @@
 dns.a: \
 makelib dns_dfd.o dns_domain.o dns_dtda.o dns_ip.o dns_ipq.o \
 dns_name.o dns_nd.o dns_packet.o dns_random.o dns_rcip.o dns_rcrw.o \
-dns_resolve.o dns_sortip.o dns_transmit.o dns_txt.o
+dns_resolve.o dns_sortip.o dns_transmit.o dns_txt.o dns_ip6.o \
+dns_sortip6.o dns_nd6.o dns_ipq6.o
 	./makelib dns.a dns_dfd.o dns_domain.o dns_dtda.o dns_ip.o \
 	dns_ipq.o dns_name.o dns_nd.o dns_packet.o dns_random.o \
 	dns_rcip.o dns_rcrw.o dns_resolve.o dns_sortip.o \
-	dns_transmit.o dns_txt.o
+	dns_transmit.o dns_txt.o dns_ip6.o dns_sortip6.o dns_nd6.o \
+	dns_ipq6.o
 
 dns_dfd.o: \
 compile dns_dfd.c error.h alloc.h byte.h dns.h stralloc.h gen_alloc.h \
@@ -257,7 +261,7 @@
 dns_transmit.o: \
 compile dns_transmit.c socket.h uint16.h alloc.h error.h byte.h \
 readwrite.h uint16.h dns.h stralloc.h gen_alloc.h iopause.h taia.h \
-tai.h uint64.h taia.h
+tai.h uint64.h taia.h uint32.h
 	./compile dns_transmit.c
 
 dns_txt.o: \
@@ -498,9 +502,15 @@
 remoteinfo.o: \
 compile remoteinfo.c fmt.h buffer.h socket.h uint16.h error.h \
 iopause.h taia.h tai.h uint64.h timeoutconn.h uint16.h remoteinfo.h \
-stralloc.h gen_alloc.h uint16.h
+stralloc.h gen_alloc.h uint16.h uint32.h
 	./compile remoteinfo.c
 
+remoteinfo6.o: \
+compile remoteinfo6.c fmt.h buffer.h socket.h uint16.h error.h \
+iopause.h taia.h tai.h uint64.h timeoutconn.h uint16.h remoteinfo.h \
+stralloc.h gen_alloc.h uint16.h uint32.h
+	./compile remoteinfo6.c
+
 rts: \
 warn-auto.sh rts.sh conf-home
 	cat warn-auto.sh rts.sh \
@@ -557,43 +567,43 @@
 	rm -f trylsock.o trylsock
 
 socket_accept.o: \
-compile socket_accept.c byte.h socket.h uint16.h
+compile socket_accept.c byte.h socket.h uint16.h uint32.h
 	./compile socket_accept.c
 
 socket_bind.o: \
-compile socket_bind.c byte.h socket.h uint16.h
+compile socket_bind.c byte.h socket.h uint16.h uint32.h
 	./compile socket_bind.c
 
 socket_conn.o: \
-compile socket_conn.c readwrite.h byte.h socket.h uint16.h
+compile socket_conn.c readwrite.h byte.h socket.h uint16.h uint32.h
 	./compile socket_conn.c
 
 socket_delay.o: \
-compile socket_delay.c socket.h uint16.h
+compile socket_delay.c socket.h uint16.h uint32.h
 	./compile socket_delay.c
 
 socket_listen.o: \
-compile socket_listen.c socket.h uint16.h
+compile socket_listen.c socket.h uint16.h uint32.h
 	./compile socket_listen.c
 
 socket_local.o: \
-compile socket_local.c byte.h socket.h uint16.h
+compile socket_local.c byte.h socket.h uint16.h uint32.h
 	./compile socket_local.c
 
 socket_opts.o: \
-compile socket_opts.c socket.h uint16.h
+compile socket_opts.c socket.h uint16.h uint32.h
 	./compile socket_opts.c
 
 socket_remote.o: \
-compile socket_remote.c byte.h socket.h uint16.h
+compile socket_remote.c byte.h socket.h uint16.h uint32.h
 	./compile socket_remote.c
 
 socket_tcp.o: \
-compile socket_tcp.c ndelay.h socket.h uint16.h
+compile socket_tcp.c ndelay.h socket.h uint16.h uint32.h
 	./compile socket_tcp.c
 
 socket_udp.o: \
-compile socket_udp.c ndelay.h socket.h uint16.h
+compile socket_udp.c ndelay.h socket.h uint16.h uint32.h
 	./compile socket_udp.c
 
 str_chr.o: \
@@ -710,9 +720,9 @@
 	chmod 755 tcpcat
 
 tcpclient: \
-load tcpclient.o remoteinfo.o timeoutconn.o dns.a time.a unix.a \
-byte.a socket.lib
-	./load tcpclient remoteinfo.o timeoutconn.o dns.a time.a \
+load tcpclient.o remoteinfo6.o dns.a time.a unix.a \
+byte.a socket.lib byte.h timeoutconn6.o
+	./load tcpclient remoteinfo6.o timeoutconn6.o dns.a time.a \
 	unix.a byte.a  `cat socket.lib`
 
 tcpclient.o: \
@@ -720,7 +730,7 @@
 scan.h str.h ip4.h uint16.h socket.h uint16.h fd.h stralloc.h \
 gen_alloc.h buffer.h error.h strerr.h pathexec.h timeoutconn.h \
 uint16.h remoteinfo.h stralloc.h uint16.h dns.h stralloc.h iopause.h \
-taia.h tai.h uint64.h taia.h
+taia.h tai.h uint64.h taia.h uint32.h
 	./compile tcpclient.c
 
 tcprules: \
@@ -742,9 +752,9 @@
 	./compile tcprulescheck.c
 
 tcpserver: \
-load tcpserver.o rules.o remoteinfo.o timeoutconn.o cdb.a dns.a \
+load tcpserver.o rules.o remoteinfo6.o timeoutconn6.o cdb.a dns.a \
 time.a unix.a byte.a socket.lib
-	./load tcpserver rules.o remoteinfo.o timeoutconn.o cdb.a \
+	./load tcpserver rules.o remoteinfo6.o timeoutconn6.o cdb.a \
 	dns.a time.a unix.a byte.a  `cat socket.lib`
 
 tcpserver.o: \
@@ -753,7 +763,7 @@
 alloc.h buffer.h error.h strerr.h sgetopt.h subgetopt.h pathexec.h \
 socket.h uint16.h ndelay.h remoteinfo.h stralloc.h uint16.h rules.h \
 stralloc.h sig.h dns.h stralloc.h iopause.h taia.h tai.h uint64.h \
-taia.h
+taia.h uint32.h
 	./compile tcpserver.c
 
 time.a: \
@@ -765,9 +775,14 @@
 
 timeoutconn.o: \
 compile timeoutconn.c ndelay.h socket.h uint16.h iopause.h taia.h \
-tai.h uint64.h error.h timeoutconn.h uint16.h
+tai.h uint64.h error.h timeoutconn.h uint16.h uint32.h
 	./compile timeoutconn.c
 
+timeoutconn6.o: \
+compile timeoutconn6.c ndelay.h socket.h uint16.h iopause.h taia.h \
+tai.h uint64.h error.h timeoutconn.h uint16.h uint32.h
+	./compile timeoutconn6.c
+
 uint16_pack.o: \
 compile uint16_pack.c uint16.h
 	./compile uint16_pack.c
@@ -806,7 +821,12 @@
 socket_opts.o socket_remote.o socket_tcp.o socket_udp.o \
 stralloc_cat.o stralloc_catb.o stralloc_cats.o stralloc_copy.o \
 stralloc_eady.o stralloc_opyb.o stralloc_opys.o stralloc_pend.o \
-strerr_die.o strerr_sys.o subgetopt.o wait_nohang.o wait_pid.o
+strerr_die.o strerr_sys.o subgetopt.o wait_nohang.o wait_pid.o \
+socket_conn6.o socket_bind6.o socket_accept6.o socket_recv6.o \
+socket_send6.o socket_local6.o socket_remote6.o socket_tcp6.o \
+socket_getifname.o socket_getifidx.o socket_v4mappedprefix.o \
+socket_ip4loopback.o socket_v6any.o socket_v6loopback.o \
+socket_udp6.o
 	./makelib unix.a alloc.o alloc_re.o buffer.o buffer_0.o \
 	buffer_1.o buffer_2.o buffer_copy.o buffer_get.o \
 	buffer_put.o env.o error.o error_str.o fd_copy.o fd_move.o \
@@ -819,7 +839,12 @@
 	socket_udp.o stralloc_cat.o stralloc_catb.o stralloc_cats.o \
 	stralloc_copy.o stralloc_eady.o stralloc_opyb.o \
 	stralloc_opys.o stralloc_pend.o strerr_die.o strerr_sys.o \
-	subgetopt.o wait_nohang.o wait_pid.o
+	subgetopt.o wait_nohang.o wait_pid.o socket_conn6.o \
+	socket_bind6.o socket_accept6.o socket_recv6.o socket_send6.o \
+	socket_local6.o socket_remote6.o socket_tcp6.o \
+	socket_getifname.o socket_getifidx.o socket_v4mappedprefix.o \
+	socket_ip4loopback.o socket_v6any.o socket_v6loopback.o \
+	socket_udp6.o
 
 wait_nohang.o: \
 compile wait_nohang.c haswaitp.h
@@ -835,3 +860,110 @@
 	| sed s}HOME}"`head -1 conf-home`"}g \
 	> who@
 	chmod 755 who@
+
+socket_conn6.o: \
+compile socket_conn6.c socket.h uint16.h haveip6.h error.h ip6.h \
+uint32.h
+	./compile socket_conn6.c
+
+socket_bind6.o: \
+compile socket_bind6.c socket.h uint16.h haveip6.h error.h ip6.h \
+uint32.h
+	./compile socket_bind6.c
+
+socket_accept6.o: \
+compile socket_accept6.c socket.h uint16.h haveip6.h error.h ip6.h \
+uint32.h
+	./compile socket_accept6.c
+
+socket_recv6.o: \
+compile socket_recv6.c socket.h uint16.h haveip6.h error.h ip6.h \
+uint32.h
+	./compile socket_recv6.c
+
+socket_send6.o: \
+compile socket_send6.c socket.h uint16.h haveip6.h error.h uint32.h
+	./compile socket_send6.c
+
+socket_local6.o: \
+compile socket_local6.c socket.h uint16.h haveip6.h error.h uint32.h
+	./compile socket_local6.c
+
+socket_remote6.o: \
+compile socket_remote6.c socket.h uint16.h haveip6.h error.h uint32.h
+	./compile socket_remote6.c
+
+dns_sortip6.o: \
+compile dns_sortip6.c byte.h dns.h stralloc.h gen_alloc.h iopause.h \
+taia.h tai.h uint64.h taia.h
+	./compile dns_sortip6.c
+
+dns_nd6.o: \
+compile dns_nd6.c byte.h fmt.h dns.h stralloc.h gen_alloc.h iopause.h \
+taia.h tai.h uint64.h taia.h
+	./compile dns_nd6.c
+
+dns_ipq6.o: \
+compile dns_ipq6.c stralloc.h gen_alloc.h case.h byte.h str.h dns.h \
+stralloc.h iopause.h taia.h tai.h uint64.h taia.h ip6.h
+	./compile dns_ipq6.c
+
+dns_ip6.o: \
+compile dns_ip6.c stralloc.h gen_alloc.h uint16.h byte.h dns.h \
+stralloc.h iopause.h taia.h tai.h uint64.h taia.h
+	./compile dns_ip6.c
+
+fmt_xlong.o: \
+compile fmt_xlong.c scan.h
+	./compile fmt_xlong.c
+
+scan_xlong.o: \
+compile scan_xlong.c scan.h
+	./compile scan_xlong.c
+
+ip6_fmt.o: \
+compile ip6_fmt.c fmt.h ip6.h
+	./compile ip6_fmt.c
+
+scan_ip6.o: \
+compile scan_ip6.c scan.h ip6.h
+	./compile scan_ip6.c
+
+socket_tcp6.o: \
+compile socket_tcp6.c ndelay.h socket.h uint16.h haveip6.h uint32.h
+	./compile socket_tcp6.c
+
+socket_udp6.o: \
+compile socket_udp6.c ndelay.h socket.h uint16.h haveip6.h uint32.h
+	./compile socket_udp6.c
+
+haveip6.h: \
+tryip6.c choose compile haveip6.h1 haveip6.h2
+	./choose c tryip6 haveip6.h1 haveip6.h2 > haveip6.h
+
+socket_getifname.o: \
+compile socket_getifname.c socket.h uint16.h uint32.h
+	./compile socket_getifname.c
+
+socket_getifidx.o: \
+compile socket_getifidx.c socket.h uint16.h uint32.h
+	./compile socket_getifidx.c
+
+socket_ip4loopback.o: \
+compile socket_ip4loopback.c
+	./compile socket_ip4loopback.c
+
+socket_v4mappedprefix.o: \
+compile socket_v4mappedprefix.c
+	./compile socket_v4mappedprefix.c
+
+socket_v6any.o: \
+compile socket_v6any.c
+	./compile socket_v6any.c
+
+socket_v6loopback.o: \
+compile socket_v6loopback.c
+	./compile socket_v6loopback.c
+
+clean:
+	rm -f `cat TARGETS`
diff -uNr ucspi-tcp-0.88.orig/TARGETS ucspi-tcp-0.88/TARGETS
--- ucspi-tcp-0.88.orig/TARGETS	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/TARGETS	2009-08-04 17:45:59.000000000 -0500
@@ -169,3 +169,31 @@
 it
 setup
 check
+dns_ip6.o
+dns_ipq6.o
+dns_nd6.o
+dns_sortip6.o
+fmt_xlong.o
+ip6_fmt.o
+ip6_scan.o
+scan_0x.o
+socket_accept6.o
+socket_bind6.o
+socket_conn6.o
+socket_local6.o
+socket_recv6.o
+socket_remote6.o
+socket_send6.o
+socket_tcp6.o
+timeoutconn6.o
+haveip6.h
+remoteinfo6.o
+socket_getifidx.o
+socket_getifname.o
+scan_ip6.o
+scan_xlong.o
+socket_ip4loopback.o
+socket_udp6.o
+socket_v4mappedprefix.o
+socket_v6any.o
+socket_v6loopback.o
diff -uNr ucspi-tcp-0.88.orig/addcr.1 ucspi-tcp-0.88/addcr.1
--- ucspi-tcp-0.88.orig/addcr.1	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/addcr.1	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,22 @@
+.TH addcr 1
+.SH NAME
+addcr \- add a CR before each LF
+.SH SYNOPSIS
+.B addcr
+.SH DESCRIPTION
+.B addcr
+inserts CR at the end of each line of input.
+It does not insert CR at the end of a partial final line.
+.SH COMPATIBILITY
+Some vendors ship
+.B unix2dos
+or
+.B bsd2dos
+tools similar to
+.BR addcr .
+Those tools often blow up on long lines and nulls.
+.B addcr
+has no trouble with long lines and nulls.
+.SH "SEE ALSO"
+delcr(1),
+fixcr(1)
diff -uNr ucspi-tcp-0.88.orig/argv0.1 ucspi-tcp-0.88/argv0.1
--- ucspi-tcp-0.88.orig/argv0.1	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/argv0.1	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,47 @@
+.TH argv0 1
+.SH NAME
+argv0 \- run a program with a specified 0th argument
+.SH SYNOPSIS
+.B argv0
+.I realname
+.I zero
+[
+.I arg ...
+]
+.SH DESCRIPTION
+.B argv0
+runs
+the program stored as
+.I realname
+on disk,
+with the given
+arguments.
+It sets the 0th argument of
+the program to
+.IR zero .
+
+For example,
+
+.EX
+     argv0 /bin/csh -bin/csh
+.EE
+
+runs
+.B /bin/csh
+with a 0th argument of
+.BR -bin/csh .
+.B csh
+will think it is a login shell
+and behave accordingly.
+
+.B argv0
+can be used to run some
+.B inetd
+wrappers under
+.BR tcpserver .
+.SH "SEE ALSO"
+csh(1),
+tcpserver(1),
+execve(2),
+execvp(3),
+inetd(8)
diff -uNr ucspi-tcp-0.88.orig/date@.1 ucspi-tcp-0.88/date@.1
--- ucspi-tcp-0.88.orig/date@.1	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/date@.1	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,32 @@
+.TH date@ 1
+.SH NAME
+date@ \- print the date on a host
+.SH SYNTAX
+.B date@
+[
+.I host
+]
+.SH DESCRIPTION
+.B date@
+connects to TCP port 13 (Daytime) on
+.I host
+and prints any data it receives.
+It removes CR and converts unprintable characters to a visible format.
+
+If
+.I host
+is not supplied,
+.B date@
+connects to the local host.
+
+Some computers respond to port 13 with a human-readable date.
+For example, they may be running
+
+.EX
+     tcpserver 0 13 date &
+.EE
+.SH "SEE ALSO"
+cat(1),
+delcr(1),
+tcpclient(1),
+tcpserver(1)
diff -uNr ucspi-tcp-0.88.orig/delcr.1 ucspi-tcp-0.88/delcr.1
--- ucspi-tcp-0.88.orig/delcr.1	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/delcr.1	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,30 @@
+.TH delcr 1
+.SH NAME
+delcr \- remove a CR before each LF
+.SH SYNOPSIS
+.B delcr
+.SH DESCRIPTION
+.B delcr
+removes a CR at the end of each line of input,
+if a CR is present.
+It also removes a CR at the end of a partial final line.
+
+The pipeline
+
+.EX
+     addcr | delcr
+.EE
+
+prints an exact copy of its input.
+.SH COMPATIBILITY
+Some vendors ship
+.B dos2unix
+or
+.B dos2bsd
+tools similar to
+.BR delcr .
+Those tools often blow up on long lines and nulls.
+.B delcr
+has no trouble with long lines and nulls.
+.SH "SEE ALSO"
+addcr(1)
diff -uNr ucspi-tcp-0.88.orig/dns.h ucspi-tcp-0.88/dns.h
--- ucspi-tcp-0.88.orig/dns.h	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/dns.h	2009-08-04 17:45:59.000000000 -0500
@@ -34,51 +34,60 @@
   unsigned int curserver;
   struct taia deadline;
   unsigned int pos;
-  char *servers;
-  char localip[4];
+  const char *servers;
+  char localip[16];
+  unsigned int scope_id;
   char qtype[2];
 } ;
 
-extern void dns_random_init(char *);
+extern void dns_random_init(const char *);
 extern unsigned int dns_random(unsigned int);
 
 extern void dns_sortip(char *,unsigned int);
+extern void dns_sortip6(char *,unsigned int);
 
 extern void dns_domain_free(char **);
-extern int dns_domain_copy(char **,char *);
-extern unsigned int dns_domain_length(char *);
-extern int dns_domain_equal(char *,char *);
-extern char *dns_domain_suffix(char *,char *);
-extern int dns_domain_fromdot(char **,char *,unsigned int);
-extern int dns_domain_todot_cat(stralloc *,char *);
-
-extern unsigned int dns_packet_copy(char *,unsigned int,unsigned int,char *,unsigned int);
-extern unsigned int dns_packet_getname(char *,unsigned int,unsigned int,char **);
-extern unsigned int dns_packet_skipname(char *,unsigned int,unsigned int);
-extern int dns_packet_nameequal(char *,unsigned int,unsigned int,char *,unsigned int,unsigned int);
+extern int dns_domain_copy(char **,const char *);
+extern unsigned int dns_domain_length(const char *);
+extern int dns_domain_equal(const char *,const char *);
+extern int dns_domain_suffix(const char *,const char *);
+extern unsigned int dns_domain_suffixpos(const char *,const char *);
+extern int dns_domain_fromdot(char **,const char *,unsigned int);
+extern int dns_domain_todot_cat(stralloc *,const char *);
+
+extern unsigned int dns_packet_copy(const char *,unsigned int,unsigned int,char *,unsigned int);
+extern unsigned int dns_packet_getname(const char *,unsigned int,unsigned int,char **);
+extern unsigned int dns_packet_skipname(const char *,unsigned int,unsigned int);
 
-extern int dns_transmit_start(struct dns_transmit *,char *,int,char *,char *,char *);
+extern int dns_transmit_start(struct dns_transmit *,const char *,int,const char *,const char *,const char *);
 extern void dns_transmit_free(struct dns_transmit *);
 extern void dns_transmit_io(struct dns_transmit *,iopause_fd *,struct taia *);
-extern int dns_transmit_get(struct dns_transmit *,iopause_fd *,struct taia *);
+extern int dns_transmit_get(struct dns_transmit *,const iopause_fd *,const struct taia *);
 
 extern int dns_resolvconfip(char *);
-extern int dns_resolve(char *,char *);
+extern int dns_resolve(const char *,const char *);
 extern struct dns_transmit dns_resolve_tx;
 
-extern int dns_ip4_packet(stralloc *,char *,unsigned int);
-extern int dns_ip4(stralloc *,stralloc *);
-extern int dns_name_packet(stralloc *,char *,unsigned int);
-extern void dns_name4_domain(char *,char *);
+extern int dns_ip4_packet(stralloc *,const char *,unsigned int);
+extern int dns_ip4(stralloc *,const stralloc *);
+extern int dns_ip6_packet(stralloc *,const char *,unsigned int);
+extern int dns_ip6(stralloc *,stralloc *);
+extern int dns_name_packet(stralloc *,const char *,unsigned int);
+extern void dns_name4_domain(char *,const char *);
 #define DNS_NAME4_DOMAIN 31
-extern int dns_name4(stralloc *,char *);
-extern int dns_txt_packet(stralloc *,char *,unsigned int);
-extern int dns_txt(stralloc *,stralloc *);
-extern int dns_mx_packet(stralloc *,char *,unsigned int);
-extern int dns_mx(stralloc *,stralloc *);
+extern int dns_name4(stralloc *,const char *);
+extern int dns_txt_packet(stralloc *,const char *,unsigned int);
+extern int dns_txt(stralloc *,const stralloc *);
+extern int dns_mx_packet(stralloc *,const char *,unsigned int);
+extern int dns_mx(stralloc *,const stralloc *);
 
 extern int dns_resolvconfrewrite(stralloc *);
-extern int dns_ip4_qualify_rules(stralloc *,stralloc *,stralloc *,stralloc *);
-extern int dns_ip4_qualify(stralloc *,stralloc *,stralloc *);
+extern int dns_ip4_qualify_rules(stralloc *,stralloc *,const stralloc *,const stralloc *);
+extern int dns_ip4_qualify(stralloc *,stralloc *,const stralloc *);
+extern int dns_ip6_qualify_rules(stralloc *,stralloc *,const stralloc *,const stralloc *);
+extern int dns_ip6_qualify(stralloc *,stralloc *,const stralloc *);
+
+extern int dns_name6_domain(char *,char *);
+#define DNS_NAME6_DOMAIN (4*16+11)
 
 #endif
diff -uNr ucspi-tcp-0.88.orig/dns_dfd.c ucspi-tcp-0.88/dns_dfd.c
--- ucspi-tcp-0.88.orig/dns_dfd.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/dns_dfd.c	2009-08-04 17:45:59.000000000 -0500
@@ -1,9 +1,10 @@
-#include "error.h"
-#include "alloc.h"
+#include <stdlib.h>
+#include <errno.h>
 #include "byte.h"
 #include "dns.h"
+#include "error.h"
 
-int dns_domain_fromdot(char **out,char *buf,unsigned int n)
+int dns_domain_fromdot(char **out,const char *buf,unsigned int n)
 {
   char label[63];
   unsigned int labellen = 0; /* <= sizeof label */
@@ -59,11 +60,11 @@
   if (namelen + 1 > sizeof name) return 0;
   name[namelen++] = 0;
 
-  x = alloc(namelen);
+  x = malloc(namelen);
   if (!x) return 0;
   byte_copy(x,namelen,name);
 
-  if (*out) alloc_free(*out);
+  if (*out) free(*out);
   *out = x;
   return 1;
 }
diff -uNr ucspi-tcp-0.88.orig/dns_domain.c ucspi-tcp-0.88/dns_domain.c
--- ucspi-tcp-0.88.orig/dns_domain.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/dns_domain.c	2009-08-04 17:45:59.000000000 -0500
@@ -1,16 +1,15 @@
-#include "error.h"
-#include "alloc.h"
+#include <stdlib.h>
 #include "case.h"
 #include "byte.h"
 #include "dns.h"
 
-unsigned int dns_domain_length(char *dn)
+unsigned int dns_domain_length(const char *dn)
 {
-  char *x;
+  const char *x;
   unsigned char c;
 
   x = dn;
-  while (c = *x++)
+  while ((c = *x++))
     x += (unsigned int) c;
   return x - dn;
 }
@@ -18,26 +17,26 @@
 void dns_domain_free(char **out)
 {
   if (*out) {
-    alloc_free(*out);
+    free(*out);
     *out = 0;
   }
 }
 
-int dns_domain_copy(char **out,char *in)
+int dns_domain_copy(char **out,const char *in)
 {
   unsigned int len;
   char *x;
 
   len = dns_domain_length(in);
-  x = alloc(len);
+  x = malloc(len);
   if (!x) return 0;
   byte_copy(x,len,in);
-  if (*out) alloc_free(*out);
+  if (*out) free(*out);
   *out = x;
   return 1;
 }
 
-int dns_domain_equal(char *dn1,char *dn2)
+int dns_domain_equal(const char *dn1,const char *dn2)
 {
   unsigned int len;
 
@@ -48,12 +47,25 @@
   return 1;
 }
 
-char *dns_domain_suffix(char *big,char *little)
+int dns_domain_suffix(const char *big,const char *little)
+{
+  unsigned char c;
+
+  for (;;) {
+    if (dns_domain_equal(big,little)) return 1;
+    c = *big++;
+    if (!c) return 0;
+    big += c;
+  }
+}
+
+unsigned int dns_domain_suffixpos(const char *big,const char *little)
 {
+  const char *orig = big;
   unsigned char c;
 
   for (;;) {
-    if (dns_domain_equal(big,little)) return big;
+    if (dns_domain_equal(big,little)) return big - orig;
     c = *big++;
     if (!c) return 0;
     big += c;
diff -uNr ucspi-tcp-0.88.orig/dns_dtda.c ucspi-tcp-0.88/dns_dtda.c
--- ucspi-tcp-0.88.orig/dns_dtda.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/dns_dtda.c	2009-08-04 17:45:59.000000000 -0500
@@ -1,7 +1,7 @@
 #include "stralloc.h"
 #include "dns.h"
 
-int dns_domain_todot_cat(stralloc *out,char *d)
+int dns_domain_todot_cat(stralloc *out,const char *d)
 {
   char ch;
   char ch2;
diff -uNr ucspi-tcp-0.88.orig/dns_ip.c ucspi-tcp-0.88/dns_ip.c
--- ucspi-tcp-0.88.orig/dns_ip.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/dns_ip.c	2009-08-04 17:45:59.000000000 -0500
@@ -3,7 +3,7 @@
 #include "byte.h"
 #include "dns.h"
 
-int dns_ip4_packet(stralloc *out,char *buf,unsigned int len)
+int dns_ip4_packet(stralloc *out,const char *buf,unsigned int len)
 {
   unsigned int pos;
   char header[12];
@@ -36,7 +36,7 @@
 
 static char *q = 0;
 
-int dns_ip4(stralloc *out,stralloc *fqdn)
+int dns_ip4(stralloc *out,const stralloc *fqdn)
 {
   unsigned int i;
   char code;
diff -uNr ucspi-tcp-0.88.orig/dns_ip6.c ucspi-tcp-0.88/dns_ip6.c
--- ucspi-tcp-0.88.orig/dns_ip6.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/dns_ip6.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,103 @@
+#include "stralloc.h"
+#include "uint16.h"
+#include "byte.h"
+#include "dns.h"
+#include "ip4.h"
+#include "ip6.h"
+
+static int dns_ip6_packet_add(stralloc *out,const char *buf,unsigned int len)
+{
+  unsigned int pos;
+  char header[16];
+  uint16 numanswers;
+  uint16 datalen;
+
+  pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return -1;
+  uint16_unpack_big(header + 6,&numanswers);
+  pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1;
+  pos += 4;
+
+  while (numanswers--) {
+    pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1;
+    pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return -1;
+    uint16_unpack_big(header + 8,&datalen);
+    if (byte_equal(header,2,DNS_T_AAAA)) {
+      if (byte_equal(header + 2,2,DNS_C_IN))
+        if (datalen == 16) {
+	  if (!dns_packet_copy(buf,len,pos,header,16)) return -1;
+	  if (!stralloc_catb(out,header,16)) return -1;
+	}
+    } else if (byte_equal(header,2,DNS_T_A))
+      if (byte_equal(header + 2,2,DNS_C_IN))
+        if (datalen == 4) {
+	  byte_copy(header,12,V4mappedprefix);
+	  if (!dns_packet_copy(buf,len,pos,header+12,4)) return -1;
+	  if (!stralloc_catb(out,header,16)) return -1;
+	}
+    pos += datalen;
+  }
+
+  dns_sortip6(out->s,out->len);
+  return 0;
+}
+
+int dns_ip6_packet(stralloc *out,const char *buf,unsigned int len) {
+  if (!stralloc_copys(out,"")) return -1;
+  return dns_ip6_packet_add(out,buf,len);
+}
+
+static char *q = 0;
+
+int dns_ip6(stralloc *out,stralloc *fqdn)
+{
+  unsigned int i;
+  char code;
+  char ch;
+  char ip[16];
+
+  if (!stralloc_copys(out,"")) return -1;
+  if (!stralloc_readyplus(fqdn,1)) return -1;
+  fqdn->s[fqdn->len]=0;
+  if ((i=scan_ip6(fqdn->s,ip))) {
+    if (fqdn->s[i]) return -1;
+    stralloc_copyb(out,ip,16);
+    return 0;
+  }
+  code = 0;
+  for (i = 0;i <= fqdn->len;++i) {
+    if (i < fqdn->len)
+      ch = fqdn->s[i];
+    else
+      ch = '.';
+
+    if ((ch == '[') || (ch == ']')) continue;
+    if (ch == '.') {
+      if (!stralloc_append(out,&code)) return -1;
+      code = 0;
+      continue;
+    }
+    if ((ch >= '0') && (ch <= '9')) {
+      code *= 10;
+      code += ch - '0';
+      continue;
+    }
+
+    if (!dns_domain_fromdot(&q,fqdn->s,fqdn->len)) return -1;
+    if (!stralloc_copys(out,"")) return -1;
+    if (dns_resolve(q,DNS_T_AAAA) != -1)
+      if (dns_ip6_packet_add(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen) != -1) {
+	dns_transmit_free(&dns_resolve_tx);
+	dns_domain_free(&q);
+      }
+    if (!dns_domain_fromdot(&q,fqdn->s,fqdn->len)) return -1;
+    if (dns_resolve(q,DNS_T_A) != -1)
+      if (dns_ip6_packet_add(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen) != -1) {
+	dns_transmit_free(&dns_resolve_tx);
+	dns_domain_free(&q);
+      }
+    return out->a>0?0:-1;
+  }
+
+  out->len &= ~3;
+  return 0;
+}
diff -uNr ucspi-tcp-0.88.orig/dns_ipq.c ucspi-tcp-0.88/dns_ipq.c
--- ucspi-tcp-0.88.orig/dns_ipq.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/dns_ipq.c	2009-08-04 17:45:59.000000000 -0500
@@ -4,7 +4,7 @@
 #include "str.h"
 #include "dns.h"
 
-static int doit(stralloc *work,char *rule)
+static int doit(stralloc *work,const char *rule)
 {
   char ch;
   unsigned int colon;
@@ -30,7 +30,7 @@
   return stralloc_cats(work,rule + colon + 1);
 }
 
-int dns_ip4_qualify_rules(stralloc *out,stralloc *fqdn,stralloc *in,stralloc *rules)
+int dns_ip4_qualify_rules(stralloc *out,stralloc *fqdn,const stralloc *in,const stralloc *rules)
 {
   unsigned int i;
   unsigned int j;
@@ -63,7 +63,7 @@
   }
 }
 
-int dns_ip4_qualify(stralloc *out,stralloc *fqdn,stralloc *in)
+int dns_ip4_qualify(stralloc *out,stralloc *fqdn,const stralloc *in)
 {
   static stralloc rules;
   if (dns_resolvconfrewrite(&rules) == -1) return -1;
diff -uNr ucspi-tcp-0.88.orig/dns_ipq6.c ucspi-tcp-0.88/dns_ipq6.c
--- ucspi-tcp-0.88.orig/dns_ipq6.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/dns_ipq6.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,72 @@
+#include "stralloc.h"
+#include "case.h"
+#include "byte.h"
+#include "str.h"
+#include "dns.h"
+
+static int doit(stralloc *work,const char *rule)
+{
+  char ch;
+  unsigned int colon;
+  unsigned int prefixlen;
+
+  ch = *rule++;
+  if ((ch != '?') && (ch != '=') && (ch != '*') && (ch != '-')) return 1;
+  colon = str_chr(rule,':');
+  if (!rule[colon]) return 1;
+
+  if (work->len < colon) return 1;
+  prefixlen = work->len - colon;
+  if ((ch == '=') && prefixlen) return 1;
+  if (case_diffb(rule,colon,work->s + prefixlen)) return 1;
+  if (ch == '?') {
+    if (byte_chr(work->s,prefixlen,'.') < prefixlen) return 1;
+    if (byte_chr(work->s,prefixlen,':') < prefixlen) return 1;
+    if (byte_chr(work->s,prefixlen,'[') < prefixlen) return 1;
+    if (byte_chr(work->s,prefixlen,']') < prefixlen) return 1;
+  }
+
+  work->len = prefixlen;
+  if (ch == '-') work->len = 0;
+  return stralloc_cats(work,rule + colon + 1);
+}
+
+int dns_ip6_qualify_rules(stralloc *out,stralloc *fqdn,const stralloc *in,const stralloc *rules)
+{
+  unsigned int i;
+  unsigned int j;
+  unsigned int plus;
+  unsigned int fqdnlen;
+
+  if (!stralloc_copy(fqdn,in)) return -1;
+
+  for (j = i = 0;j < rules->len;++j)
+    if (!rules->s[j]) {
+      if (!doit(fqdn,rules->s + i)) return -1;
+      i = j + 1;
+    }
+
+  fqdnlen = fqdn->len;
+  plus = byte_chr(fqdn->s,fqdnlen,'+');
+  if (plus >= fqdnlen)
+    return dns_ip6(out,fqdn);
+
+  i = plus + 1;
+  for (;;) {
+    j = byte_chr(fqdn->s + i,fqdnlen - i,'+');
+    byte_copy(fqdn->s + plus,j,fqdn->s + i);
+    fqdn->len = plus + j;
+    if (dns_ip6(out,fqdn) == -1) return -1;
+    if (out->len) return 0;
+    i += j;
+    if (i >= fqdnlen) return 0;
+    ++i;
+  }
+}
+
+int dns_ip6_qualify(stralloc *out,stralloc *fqdn,const stralloc *in)
+{
+  static stralloc rules;
+  if (dns_resolvconfrewrite(&rules) == -1) return -1;
+  return dns_ip6_qualify_rules(out,fqdn,in,&rules);
+}
diff -uNr ucspi-tcp-0.88.orig/dns_name.c ucspi-tcp-0.88/dns_name.c
--- ucspi-tcp-0.88.orig/dns_name.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/dns_name.c	2009-08-04 17:45:59.000000000 -0500
@@ -2,10 +2,11 @@
 #include "uint16.h"
 #include "byte.h"
 #include "dns.h"
+#include "ip6.h"
 
 static char *q = 0;
 
-int dns_name_packet(stralloc *out,char *buf,unsigned int len)
+int dns_name_packet(stralloc *out,const char *buf,unsigned int len)
 {
   unsigned int pos;
   char header[12];
@@ -35,7 +36,7 @@
   return 0;
 }
 
-int dns_name4(stralloc *out,char ip[4])
+int dns_name4(stralloc *out,const char ip[4])
 {
   char name[DNS_NAME4_DOMAIN];
 
@@ -46,3 +47,17 @@
   dns_domain_free(&q);
   return 0;
 }
+
+int dns_name6(stralloc *out,char ip[16])
+{
+  char name[DNS_NAME6_DOMAIN];
+
+  if (ip6_isv4mapped(ip))
+    return dns_name4(out,ip+12);
+  dns_name6_domain(name,ip);
+  if (dns_resolve(name,DNS_T_PTR) == -1) return -1;
+  if (dns_name_packet(out,dns_resolve_tx.packet,dns_resolve_tx.packetlen) == -1) return -1;
+  dns_transmit_free(&dns_resolve_tx);
+  dns_domain_free(&q);
+  return 0;
+}
diff -uNr ucspi-tcp-0.88.orig/dns_nd.c ucspi-tcp-0.88/dns_nd.c
--- ucspi-tcp-0.88.orig/dns_nd.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/dns_nd.c	2009-08-04 17:45:59.000000000 -0500
@@ -2,7 +2,7 @@
 #include "fmt.h"
 #include "dns.h"
 
-void dns_name4_domain(char name[DNS_NAME4_DOMAIN],char ip[4])
+void dns_name4_domain(char name[DNS_NAME4_DOMAIN],const char ip[4])
 {
   unsigned int namelen;
   unsigned int i;
diff -uNr ucspi-tcp-0.88.orig/dns_nd6.c ucspi-tcp-0.88/dns_nd6.c
--- ucspi-tcp-0.88.orig/dns_nd6.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/dns_nd6.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,28 @@
+#include "byte.h"
+#include "fmt.h"
+#include "dns.h"
+
+/* RFC1886:
+ *   4321:0:1:2:3:4:567:89ab
+ * ->
+ *   b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.0.0.0.0.1.2.3.4.IP6.INT.
+ */
+
+static inline char tohex(char c) {
+  return c>=10?c-10+'a':c+'0';
+}
+
+int dns_name6_domain(char name[DNS_NAME6_DOMAIN],char ip[16])
+{
+  unsigned int j;
+
+  for (j=0; j<16; j++) {
+    name[j*4]=1;
+    name[j*4+1]=tohex(ip[15-j] & 15);
+    name[j*4+2]=1;
+    name[j*4+3]=tohex((unsigned char)ip[15-j] >> 4);
+  }
+  byte_copy(name + 4*16,10,"\3ip6\4arpa\0");
+  return 4*16+10;
+}
+
diff -uNr ucspi-tcp-0.88.orig/dns_packet.c ucspi-tcp-0.88/dns_packet.c
--- ucspi-tcp-0.88.orig/dns_packet.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/dns_packet.c	2009-08-04 17:45:59.000000000 -0500
@@ -2,10 +2,11 @@
 DNS should have used LZ77 instead of its own sophomoric compression algorithm.
 */
 
-#include "error.h"
+#include <errno.h>
 #include "dns.h"
+#include "error.h"
 
-unsigned int dns_packet_copy(char *buf,unsigned int len,unsigned int pos,char *out,unsigned int outlen)
+unsigned int dns_packet_copy(const char *buf,unsigned int len,unsigned int pos,char *out,unsigned int outlen)
 {
   while (outlen) {
     if (pos >= len) { errno = error_proto; return 0; }
@@ -15,7 +16,7 @@
   return pos;
 }
 
-unsigned int dns_packet_skipname(char *buf,unsigned int len,unsigned int pos)
+unsigned int dns_packet_skipname(const char *buf,unsigned int len,unsigned int pos)
 {
   unsigned char ch;
 
@@ -32,7 +33,7 @@
   return 0;
 }
 
-unsigned int dns_packet_getname(char *buf,unsigned int len,unsigned int pos,char **d)
+unsigned int dns_packet_getname(const char *buf,unsigned int len,unsigned int pos,char **d)
 {
   unsigned int loop = 0;
   unsigned int state = 0;
diff -uNr ucspi-tcp-0.88.orig/dns_random.c ucspi-tcp-0.88/dns_random.c
--- ucspi-tcp-0.88.orig/dns_random.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/dns_random.c	2009-08-04 17:45:59.000000000 -0500
@@ -1,3 +1,4 @@
+#include <unistd.h>
 #include "dns.h"
 #include "taia.h"
 #include "uint32.h"
@@ -29,7 +30,7 @@
   }
 }
 
-void dns_random_init(char data[128])
+void dns_random_init(const char data[128])
 {
   int i;
   struct taia t;
diff -uNr ucspi-tcp-0.88.orig/dns_rcip.c ucspi-tcp-0.88/dns_rcip.c
--- ucspi-tcp-0.88.orig/dns_rcip.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/dns_rcip.c	2009-08-04 17:45:59.000000000 -0500
@@ -2,12 +2,13 @@
 #include "openreadclose.h"
 #include "byte.h"
 #include "ip4.h"
-#include "env.h"
+#include "ip6.h"
 #include "dns.h"
+#include "env.h"
 
 static stralloc data = {0};
 
-static int init(char ip[64])
+static int init(char ip[256])
 {
   int i;
   int j;
@@ -16,15 +17,16 @@
 
   x = env_get("DNSCACHEIP");
   if (x)
-    while (iplen <= 60)
+    while (iplen <= 60) {
       if (*x == '.')
 	++x;
       else {
-        i = ip4_scan(x,ip + iplen);
+        i = scan_ip6(x,ip + iplen);
 	if (!i) break;
 	x += i;
-	iplen += 4;
+	iplen += 16;
       }
+    }
 
   if (!iplen) {
     i = openreadclose("/etc/resolv.conf",&data,64);
@@ -39,8 +41,9 @@
             while ((data.s[i] == ' ') || (data.s[i] == '\t'))
               ++i;
             if (iplen <= 60)
-              if (ip4_scan(data.s + i,ip + iplen))
-                iplen += 4;
+              if (scan_ip6(data.s + i,ip + iplen)) {
+                iplen += 16;
+	      }
           }
           i = j + 1;
         }
@@ -48,19 +51,19 @@
   }
 
   if (!iplen) {
-    byte_copy(ip,4,"\177\0\0\1");
-    iplen = 4;
+    byte_copy(ip,16,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1");
+    iplen = 16;
   }
-  byte_zero(ip + iplen,64 - iplen);
+  byte_zero(ip + iplen,256 - iplen);
   return 0;
 }
 
 static int ok = 0;
 static unsigned int uses;
 static struct taia deadline;
-static char ip[64]; /* defined if ok */
+static char ip[256]; /* defined if ok */
 
-int dns_resolvconfip(char s[64])
+int dns_resolvconfip(char s[256])
 {
   struct taia now;
 
@@ -77,6 +80,6 @@
   }
 
   --uses;
-  byte_copy(s,64,ip);
+  byte_copy(s,256,ip);
   return 0;
 }
diff -uNr ucspi-tcp-0.88.orig/dns_rcrw.c ucspi-tcp-0.88/dns_rcrw.c
--- ucspi-tcp-0.88.orig/dns_rcrw.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/dns_rcrw.c	2009-08-04 17:45:59.000000000 -0500
@@ -1,16 +1,17 @@
+#include <unistd.h>
 #include "taia.h"
-#include "env.h"
 #include "byte.h"
 #include "str.h"
 #include "openreadclose.h"
 #include "dns.h"
+#include "env.h"
 
 static stralloc data = {0};
 
 static int init(stralloc *rules)
 {
   char host[256];
-  char *x;
+  const char *x;
   int i;
   int j;
   int k;
diff -uNr ucspi-tcp-0.88.orig/dns_resolve.c ucspi-tcp-0.88/dns_resolve.c
--- ucspi-tcp-0.88.orig/dns_resolve.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/dns_resolve.c	2009-08-04 17:45:59.000000000 -0500
@@ -2,19 +2,20 @@
 #include "taia.h"
 #include "byte.h"
 #include "dns.h"
+#include "ip6.h"
 
 struct dns_transmit dns_resolve_tx = {0};
 
-int dns_resolve(char *q,char qtype[2])
+int dns_resolve(const char *q,const char qtype[2])
 {
   struct taia stamp;
   struct taia deadline;
-  char servers[64];
+  char servers[256];
   iopause_fd x[1];
   int r;
 
   if (dns_resolvconfip(servers) == -1) return -1;
-  if (dns_transmit_start(&dns_resolve_tx,servers,1,q,qtype,"\0\0\0\0") == -1) return -1;
+  if (dns_transmit_start(&dns_resolve_tx,servers,1,q,qtype,V6any) == -1) return -1;
 
   for (;;) {
     taia_now(&stamp);
diff -uNr ucspi-tcp-0.88.orig/dns_sortip6.c ucspi-tcp-0.88/dns_sortip6.c
--- ucspi-tcp-0.88.orig/dns_sortip6.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/dns_sortip6.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,20 @@
+#include "byte.h"
+#include "dns.h"
+
+/* XXX: sort servers by configurable notion of closeness? */
+/* XXX: pay attention to competence of each server? */
+
+void dns_sortip6(char *s,unsigned int n)
+{
+  unsigned int i;
+  char tmp[16];
+
+  n >>= 4;
+  while (n > 1) {
+    i = dns_random(n);
+    --n;
+    byte_copy(tmp,16,s + (i << 4));
+    byte_copy(s + (i << 4),16,s + (n << 4));
+    byte_copy(s + (n << 4),16,tmp);
+  }
+}
diff -uNr ucspi-tcp-0.88.orig/dns_transmit.c ucspi-tcp-0.88/dns_transmit.c
--- ucspi-tcp-0.88.orig/dns_transmit.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/dns_transmit.c	2009-08-04 17:45:59.000000000 -0500
@@ -1,12 +1,15 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <stdlib.h>
 #include "socket.h"
-#include "alloc.h"
-#include "error.h"
+#include <errno.h>
 #include "byte.h"
-#include "readwrite.h"
 #include "uint16.h"
 #include "dns.h"
+#include "ip6.h"
 
-static int serverwantstcp(char *buf,unsigned int len)
+static int serverwantstcp(const char *buf,unsigned int len)
 {
   char out[12];
 
@@ -15,7 +18,7 @@
   return 0;
 }
 
-static int serverfailed(char *buf,unsigned int len)
+static int serverfailed(const char *buf,unsigned int len)
 {
   char out[12];
   unsigned int rcode;
@@ -23,11 +26,11 @@
   if (!dns_packet_copy(buf,len,0,out,12)) return 1;
   rcode = out[3];
   rcode &= 15;
-  if (rcode && (rcode != 3)) { errno = error_again; return 1; }
+  if (rcode && (rcode != 3)) { errno = EAGAIN; return 1; }
   return 0;
 }
 
-static int irrelevant(struct dns_transmit *d,char *buf,unsigned int len)
+static int irrelevant(const struct dns_transmit *d,const char *buf,unsigned int len)
 {
   char out[12];
   char *dn;
@@ -40,8 +43,8 @@
 
   dn = 0;
   pos = dns_packet_getname(buf,len,pos,&dn); if (!pos) return 1;
-  if (!dns_domain_equal(dn,d->query + 14)) { alloc_free(dn); return 1; }
-  alloc_free(dn);
+  if (!dns_domain_equal(dn,d->query + 14)) { free(dn); return 1; }
+  free(dn);
 
   pos = dns_packet_copy(buf,len,pos,out,4); if (!pos) return 1;
   if (byte_diff(out,2,d->qtype)) return 1;
@@ -53,14 +56,14 @@
 static void packetfree(struct dns_transmit *d)
 {
   if (!d->packet) return;
-  alloc_free(d->packet);
+  free(d->packet);
   d->packet = 0;
 }
 
 static void queryfree(struct dns_transmit *d)
 {
   if (!d->query) return;
-  alloc_free(d->query);
+  free(d->query);
   d->query = 0;
 }
 
@@ -83,9 +86,9 @@
   int j;
 
   for (j = 0;j < 10;++j)
-    if (socket_bind4(d->s1 - 1,d->localip,1025 + dns_random(64510)) == 0)
+    if (socket_bind6(d->s1 - 1,d->localip,1025 + dns_random(64510),d->scope_id) == 0)
       return 0;
-  if (socket_bind4(d->s1 - 1,d->localip,0) == 0)
+  if (socket_bind6(d->s1 - 1,d->localip,0,d->scope_id) == 0)
     return 0;
   return -1;
 }
@@ -94,22 +97,22 @@
 
 static int thisudp(struct dns_transmit *d)
 {
-  char *ip;
+  const char *ip;
 
   socketfree(d);
 
   while (d->udploop < 4) {
     for (;d->curserver < 16;++d->curserver) {
-      ip = d->servers + 4 * d->curserver;
-      if (byte_diff(ip,4,"\0\0\0\0")) {
+      ip = d->servers + 16 * d->curserver;
+      if (byte_diff(ip,16,V6any)) {
 	d->query[2] = dns_random(256);
 	d->query[3] = dns_random(256);
   
-        d->s1 = 1 + socket_udp();
+        d->s1 = 1 + socket_udp6();
         if (!d->s1) { dns_transmit_free(d); return -1; }
 	if (randombind(d) == -1) { dns_transmit_free(d); return -1; }
 
-        if (socket_connect4(d->s1 - 1,ip,53) == 0)
+        if (socket_connect6(d->s1 - 1,ip,53,d->scope_id) == 0)
           if (send(d->s1 - 1,d->query + 2,d->querylen - 2,0) == d->querylen - 2) {
             struct taia now;
             taia_now(&now);
@@ -145,29 +148,29 @@
 static int thistcp(struct dns_transmit *d)
 {
   struct taia now;
-  char *ip;
+  const char *ip;
 
   socketfree(d);
   packetfree(d);
 
   for (;d->curserver < 16;++d->curserver) {
-    ip = d->servers + 4 * d->curserver;
-    if (byte_diff(ip,4,"\0\0\0\0")) {
+    ip = d->servers + 16 * d->curserver;
+    if (byte_diff(ip,16,V6any)) {
       d->query[2] = dns_random(256);
       d->query[3] = dns_random(256);
 
-      d->s1 = 1 + socket_tcp();
+      d->s1 = 1 + socket_tcp6();
       if (!d->s1) { dns_transmit_free(d); return -1; }
       if (randombind(d) == -1) { dns_transmit_free(d); return -1; }
   
       taia_now(&now);
       taia_uint(&d->deadline,10);
       taia_add(&d->deadline,&d->deadline,&now);
-      if (socket_connect4(d->s1 - 1,ip,53) == 0) {
+      if (socket_connect6(d->s1 - 1,ip,53,d->scope_id) == 0) {
         d->tcpstate = 2;
         return 0;
       }
-      if ((errno == error_inprogress) || (errno == error_wouldblock)) {
+      if ((errno == EINPROGRESS) || (errno == EWOULDBLOCK)) {
         d->tcpstate = 1;
         return 0;
       }
@@ -191,16 +194,16 @@
   return thistcp(d);
 }
 
-int dns_transmit_start(struct dns_transmit *d,char servers[64],int flagrecursive,char *q,char qtype[2],char localip[4])
+int dns_transmit_start(struct dns_transmit *d,const char servers[256],int flagrecursive,const char *q,const char qtype[2],const char localip[16])
 {
   unsigned int len;
 
   dns_transmit_free(d);
-  errno = error_io;
+  errno = EIO;
 
   len = dns_domain_length(q);
   d->querylen = len + 18;
-  d->query = alloc(d->querylen);
+  d->query = malloc(d->querylen);
   if (!d->query) return -1;
 
   uint16_pack_big(d->query,len + 16);
@@ -211,7 +214,7 @@
 
   byte_copy(d->qtype,2,qtype);
   d->servers = servers;
-  byte_copy(d->localip,4,localip);
+  byte_copy(d->localip,16,localip);
 
   d->udploop = flagrecursive ? 1 : 0;
 
@@ -236,19 +239,19 @@
     *deadline = d->deadline;
 }
 
-int dns_transmit_get(struct dns_transmit *d,iopause_fd *x,struct taia *when)
+int dns_transmit_get(struct dns_transmit *d,const iopause_fd *x,const struct taia *when)
 {
   char udpbuf[513];
   unsigned char ch;
   int r;
   int fd;
 
-  errno = error_io;
+  errno = EIO;
   fd = d->s1 - 1;
 
   if (!x->revents) {
     if (taia_less(when,&d->deadline)) return 0;
-    errno = error_timeout;
+    errno = ETIMEDOUT;
     if (d->tcpstate == 0) return nextudp(d);
     return nexttcp(d);
   }
@@ -260,7 +263,7 @@
 */
     r = recv(fd,udpbuf,sizeof udpbuf,0);
     if (r <= 0) {
-      if (d->udploop == 2) return 0;
+      if (errno == ECONNREFUSED) if (d->udploop == 2) return 0;
       return nextudp(d);
     }
     if (r + 1 > sizeof udpbuf) return 0;
@@ -274,7 +277,7 @@
     socketfree(d);
 
     d->packetlen = r;
-    d->packet = alloc(d->packetlen);
+    d->packet = malloc(d->packetlen);
     if (!d->packet) { dns_transmit_free(d); return -1; }
     byte_copy(d->packet,d->packetlen,udpbuf);
     queryfree(d);
@@ -334,7 +337,7 @@
     d->packetlen += ch;
     d->tcpstate = 5;
     d->pos = 0;
-    d->packet = alloc(d->packetlen);
+    d->packet = malloc(d->packetlen);
     if (!d->packet) { dns_transmit_free(d); return -1; }
     return 0;
   }
diff -uNr ucspi-tcp-0.88.orig/dns_txt.c ucspi-tcp-0.88/dns_txt.c
--- ucspi-tcp-0.88.orig/dns_txt.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/dns_txt.c	2009-08-04 17:45:59.000000000 -0500
@@ -3,7 +3,7 @@
 #include "byte.h"
 #include "dns.h"
 
-int dns_txt_packet(stralloc *out,char *buf,unsigned int len)
+int dns_txt_packet(stralloc *out,const char *buf,unsigned int len)
 {
   unsigned int pos;
   char header[12];
@@ -48,7 +48,7 @@
 
 static char *q = 0;
 
-int dns_txt(stralloc *out,stralloc *fqdn)
+int dns_txt(stralloc *out,const stralloc *fqdn)
 {
   if (!dns_domain_fromdot(&q,fqdn->s,fqdn->len)) return -1;
   if (dns_resolve(q,DNS_T_TXT) == -1) return -1;
diff -uNr ucspi-tcp-0.88.orig/error.h ucspi-tcp-0.88/error.h
--- ucspi-tcp-0.88.orig/error.h	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/error.h	2009-08-04 17:45:59.000000000 -0500
@@ -1,7 +1,7 @@
 #ifndef ERROR_H
 #define ERROR_H
 
-extern int errno;
+#include <errno.h>
 
 extern int error_intr;
 extern int error_nomem;
diff -uNr ucspi-tcp-0.88.orig/finger@.1 ucspi-tcp-0.88/finger@.1
--- ucspi-tcp-0.88.orig/finger@.1	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/finger@.1	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,45 @@
+.TH finger@ 1
+.SH NAME
+finger@ \- get user information from a host
+.SH SYNTAX
+.B finger@
+[
+.I host
+[
+.I user
+]
+]
+.SH DESCRIPTION
+.B finger@
+connects to TCP port 79 (Finger) on
+.IR host ,
+sends
+.I user
+(with an extra CR)
+to
+.IR host ,
+and prints any data it receives.
+It removes CR and converts unprintable characters to a visible format.
+Some computers respond to port 79 with information about
+.IR user .
+
+If
+.I user
+is not supplied,
+.B finger@
+sends a blank line to
+.IR host .
+Some computers respond with information about
+all the users who are logged in.
+
+If
+.I host
+is not supplied,
+.B finger@
+connects to the local host.
+.SH "SEE ALSO"
+addcr(1),
+cat(1),
+delcr(1),
+finger(1),
+tcpclient(1)
diff -uNr ucspi-tcp-0.88.orig/fixcr.1 ucspi-tcp-0.88/fixcr.1
--- ucspi-tcp-0.88.orig/fixcr.1	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/fixcr.1	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,11 @@
+.TH fixcr 1
+.SH NAME
+fixcr \- make sure that there is a CR before each LF
+.SH SYNOPSIS
+.B fixcr
+.SH DESCRIPTION
+.B fixcr
+inserts CR at the end of each line of input where a CR is not already present.
+It does not insert CR at the end of a partial final line.
+.SH "SEE ALSO"
+addcr(1)
diff -uNr ucspi-tcp-0.88.orig/fmt_xlong.c ucspi-tcp-0.88/fmt_xlong.c
--- ucspi-tcp-0.88.orig/fmt_xlong.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/fmt_xlong.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,22 @@
+#include "fmt.h"
+
+char tohex(char num) {
+  if (num<10)
+    return num+'0';
+  else if (num<16)
+    return num-10+'a';
+  else
+    return -1;
+}
+
+unsigned int fmt_xlong(register char *s,register unsigned long u)
+{
+  register unsigned int len; register unsigned long q;
+  len = 1; q = u;
+  while (q > 15) { ++len; q /= 16; }
+  if (s) {
+    s += len;
+    do { *--s = tohex(u % 16); u /= 16; } while(u); /* handles u == 0 */
+  }
+  return len;
+}
diff -uNr ucspi-tcp-0.88.orig/haveip6.h1 ucspi-tcp-0.88/haveip6.h1
--- ucspi-tcp-0.88.orig/haveip6.h1	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/haveip6.h1	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1 @@
+
diff -uNr ucspi-tcp-0.88.orig/haveip6.h2 ucspi-tcp-0.88/haveip6.h2
--- ucspi-tcp-0.88.orig/haveip6.h2	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/haveip6.h2	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1 @@
+#define LIBC_HAS_IP6 1
diff -uNr ucspi-tcp-0.88.orig/hier.c ucspi-tcp-0.88/hier.c
--- ucspi-tcp-0.88.orig/hier.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/hier.c	2009-08-04 17:45:59.000000000 -0500
@@ -4,6 +4,9 @@
 {
   h(auto_home,-1,-1,02755);
   d(auto_home,"bin",-1,-1,02755);
+  d(auto_home,"man",-1,-1,02755);
+  d(auto_home,"man/man1",-1,-1,02755);
+  d(auto_home,"man/man5",-1,-1,02755);
 
   c(auto_home,"bin","tcpserver",-1,-1,0755);
   c(auto_home,"bin","tcprules",-1,-1,0755);
@@ -22,4 +25,20 @@
   c(auto_home,"bin","delcr",-1,-1,0755);
   c(auto_home,"bin","fixcrio",-1,-1,0755);
   c(auto_home,"bin","rblsmtpd",-1,-1,0755);
+
+  c(auto_home,"man/man1","tcpclient.1",-1,-1,0644);
+  c(auto_home,"man/man1","tcpserver.1",-1,-1,0644);
+  c(auto_home,"man/man1","tcprules.1",-1,-1,0644);
+  c(auto_home,"man/man1","tcprulescheck.1",-1,-1,0644);
+  c(auto_home,"man/man1","fixcr.1",-1,-1,0644);
+  c(auto_home,"man/man1","addcr.1",-1,-1,0644);
+  c(auto_home,"man/man1","delcr.1",-1,-1,0644);
+  c(auto_home,"man/man1","who@.1",-1,-1,0644);
+  c(auto_home,"man/man1","date@.1",-1,-1,0644);
+  c(auto_home,"man/man1","finger@.1",-1,-1,0644);
+  c(auto_home,"man/man1","http@.1",-1,-1,0644);
+  c(auto_home,"man/man1","mconnect.1",-1,-1,0644);
+  c(auto_home,"man/man1","argv0.1",-1,-1,0644);
+  c(auto_home,"man/man1","recordio.1",-1,-1,0644);
+  c(auto_home,"man/man5","tcp-environ.5",-1,-1,0644);
 }
diff -uNr ucspi-tcp-0.88.orig/http@.1 ucspi-tcp-0.88/http@.1
--- ucspi-tcp-0.88.orig/http@.1	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/http@.1	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,52 @@
+.TH http@ 1
+.SH NAME
+http@ \- get a web page from a host through HTTP
+.SH SYNTAX
+.B http@
+[
+.I host
+[
+.I page
+[
+.I port
+]
+]
+]
+.SH DESCRIPTION
+.B http@
+connects to
+.I port
+on
+.IR host ,
+sends
+.B GET /\fIpage
+(with an extra CR)
+to
+.IR host ,
+and prints any data it receives,
+removing CR from the end of each line.
+
+If
+.I port
+is not supplied,
+.B http@
+uses port 80 (HTTP).
+
+If
+.I page
+is not supplied,
+.B http@
+sends
+.B GET /
+to
+.IR host .
+
+If
+.I host
+is not supplied,
+.B http@
+connects to the local host.
+.SH "SEE ALSO"
+addcr(1),
+delcr(1),
+tcpclient(1)
diff -uNr ucspi-tcp-0.88.orig/ip4.h ucspi-tcp-0.88/ip4.h
--- ucspi-tcp-0.88.orig/ip4.h	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/ip4.h	2009-08-04 17:45:59.000000000 -0500
@@ -6,4 +6,6 @@
 
 #define IP4_FMT 20
 
+extern const char ip4loopback[4]; /* = {127,0,0,1}; */
+
 #endif
diff -uNr ucspi-tcp-0.88.orig/ip6.h ucspi-tcp-0.88/ip6.h
--- ucspi-tcp-0.88.orig/ip6.h	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/ip6.h	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,28 @@
+#ifndef IP6_H
+#define IP6_H
+
+#include "byte.h"
+
+extern unsigned int scan_ip6(const char *src,char *ip);
+extern unsigned int fmt_ip6(char *dest,const char *ip);
+
+extern unsigned int scan_ip6_flat(const char *src,char *);
+extern unsigned int fmt_ip6_flat(char *dest,const char *);
+
+/*
+ ip6 address syntax: (h = hex digit), no leading '0' required
+   1. hhhh:hhhh:hhhh:hhhh:hhhh:hhhh:hhhh:hhhh
+   2. any number of 0000 may be abbreviated as "::", but only once
+ flat ip6 address syntax:
+   hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
+ */
+
+#define IP6_FMT 40
+
+extern const unsigned char V4mappedprefix[12]; /*={0,0,0,0,0,0,0,0,0,0,0xff,0xff}; */
+extern const unsigned char V6loopback[16]; /*={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; */
+extern const unsigned char V6any[16]; /*={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; */
+
+#define ip6_isv4mapped(ip) (byte_equal(ip,12,V4mappedprefix))
+
+#endif
diff -uNr ucspi-tcp-0.88.orig/ip6_fmt.c ucspi-tcp-0.88/ip6_fmt.c
--- ucspi-tcp-0.88.orig/ip6_fmt.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/ip6_fmt.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,64 @@
+#include "fmt.h"
+#include "byte.h"
+#include "ip4.h"
+#include "ip6.h"
+
+unsigned int ip6_fmt(char *s,char ip[16])
+{
+  unsigned long len,temp, k, pos0=0,len0=0, pos1=0, compr=0;
+
+  for (k=0; k<16; k+=2) {
+    if (ip[k]==0 && ip[k+1]==0) {
+      if (!compr) {
+        compr=1;
+        pos1=k;
+      }
+      if (k==14) { k=16; goto last; }
+    } else if (compr) {
+    last:
+      if ((temp=k-pos1) > len0) {
+        len0=temp;
+        pos0=pos1;
+      }
+      compr=0;
+    }
+  }
+
+  for (len=0,k=0; k<16; k+=2) {
+    if (k==12 && ip6_isv4mapped(ip)) {
+      len += ip4_fmt(s,ip+12);
+      break;
+    }
+    if (pos0==k && len0) {
+      if (k==0) { ++len; if (s) *s++ = ':'; }
+      ++len; if (s) *s++ = ':';
+      k += len0-2;
+      continue;
+    }
+    temp = ((unsigned long) (unsigned char) ip[k] << 8) +
+            (unsigned long) (unsigned char) ip[k+1];
+    temp = fmt_xlong(s,temp); len += temp; if (s) s += temp;
+    if (k<14) { ++len; if (s) *s++ = ':'; }
+  }
+
+  return len;
+}
+
+static char tohex(char num) {
+  if (num<10)
+    return num+'0';
+  else if (num<16)
+    return num-10+'a';
+  else
+    return -1;
+}
+
+unsigned int ip6_fmt_flat(char *s,char ip[16])
+{
+  int i;
+  for (i=0; i<16; i++) {
+    *s++=tohex((unsigned char)ip[i] >> 4);
+    *s++=tohex((unsigned char)ip[i] & 15);
+  }
+  return 32;
+}
diff -uNr ucspi-tcp-0.88.orig/mconnect.1 ucspi-tcp-0.88/mconnect.1
--- ucspi-tcp-0.88.orig/mconnect.1	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/mconnect.1	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,36 @@
+.TH mconnect 1
+.SH NAME
+mconnect \- connect to the SMTP server on a host
+.SH SYNTAX
+.B mconnect
+[
+.I host
+[
+.I port
+]
+]
+.SH DESCRIPTION
+.B mconnect
+connects to
+.I port
+on
+.IR host .
+It sends its input to
+.IR host ,
+adding a CR to each line.
+Meanwhile it prints anything it receives from
+.IR host .
+
+If
+.I port
+is not supplied,
+.B mconnect
+uses port 25 (SMTP).
+
+If
+.I host
+is not supplied,
+.B mconnect
+connects to the local host.
+.SH "SEE ALSO"
+tcpclient(1)
diff -uNr ucspi-tcp-0.88.orig/old-rules.c ucspi-tcp-0.88/old-rules.c
--- ucspi-tcp-0.88.orig/old-rules.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/old-rules.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,101 @@
+#include "alloc.h"
+#include "stralloc.h"
+#include "open.h"
+#include "cdb.h"
+#include "rules.h"
+
+stralloc rules_name = {0};
+
+static struct cdb c;
+
+static int dorule(void (*callback)(char *,unsigned int))
+{
+  char *data;
+  unsigned int datalen;
+
+  switch(cdb_find(&c,rules_name.s,rules_name.len)) {
+    case -1: return -1;
+    case 0: return 0;
+  }
+
+  datalen = cdb_datalen(&c);
+  data = alloc(datalen);
+  if (!data) return -1;
+  if (cdb_read(&c,data,datalen,cdb_datapos(&c)) == -1) {
+    alloc_free(data);
+    return -1;
+  }
+
+  callback(data,datalen);
+  alloc_free(data);
+  return 1;
+}
+
+static int doit(void (*callback)(char *,unsigned int),char *ip,char *host,char *info)
+{
+  int r;
+
+  if (info) {
+    if (!stralloc_copys(&rules_name,info)) return -1;
+    if (!stralloc_cats(&rules_name,"@")) return -1;
+    if (!stralloc_cats(&rules_name,ip)) return -1;
+    r = dorule(callback);
+    if (r) return r;
+
+    if (host) {
+      if (!stralloc_copys(&rules_name,info)) return -1;
+      if (!stralloc_cats(&rules_name,"@=")) return -1;
+      if (!stralloc_cats(&rules_name,host)) return -1;
+      r = dorule(callback);
+      if (r) return r;
+    }
+  }
+
+  if (!stralloc_copys(&rules_name,ip)) return -1;
+  r = dorule(callback);
+  if (r) return r;
+
+  if (host) {
+    if (!stralloc_copys(&rules_name,"=")) return -1;
+    if (!stralloc_cats(&rules_name,host)) return -1;
+    r = dorule(callback);
+    if (r) return r;
+  }
+
+  if (!stralloc_copys(&rules_name,ip)) return -1;
+  while (rules_name.len > 0) {
+    if (ip[rules_name.len - 1] == '.' ||
+	(ip[rules_name.len-1]==':' && rules_name.len>1)) {
+      r = dorule(callback);
+      if (r) return r;
+    }
+    --rules_name.len;
+  }
+
+  if (host) {
+    while (*host) {
+      if (*host == '.') {
+        if (!stralloc_copys(&rules_name,"=")) return -1;
+        if (!stralloc_cats(&rules_name,host)) return -1;
+	r = dorule(callback);
+	if (r) return r;
+      }
+      ++host;
+    }
+    if (!stralloc_copys(&rules_name,"=")) return -1;
+    r = dorule(callback);
+    if (r) return r;
+  }
+
+  rules_name.len = 0;
+  return dorule(callback);
+}
+
+int rules(void (*callback)(char *,unsigned int),int fd,char *ip,char *host,char *info)
+{
+  int r;
+  cdb_init(&c,fd);
+  r = doit(callback,ip,host,info);
+  cdb_free(&c);
+  return r;
+}
diff -uNr ucspi-tcp-0.88.orig/pathexec.h ucspi-tcp-0.88/pathexec.h
--- ucspi-tcp-0.88.orig/pathexec.h	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/pathexec.h	2009-08-04 17:45:59.000000000 -0500
@@ -2,7 +2,7 @@
 #define PATHEXEC_H
 
 extern void pathexec_run(char *,char **,char **);
-extern int pathexec_env(char *,char *);
+extern int pathexec_env(const char *,const char *);
 extern void pathexec(char **);
 
 #endif
diff -uNr ucspi-tcp-0.88.orig/pathexec_env.c ucspi-tcp-0.88/pathexec_env.c
--- ucspi-tcp-0.88.orig/pathexec_env.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/pathexec_env.c	2009-08-04 17:45:59.000000000 -0500
@@ -8,7 +8,7 @@
 static stralloc plus;
 static stralloc tmp;
 
-int pathexec_env(char *s,char *t)
+int pathexec_env(const char *s,const char *t)
 {
   if (!s) return 1;
   if (!stralloc_copys(&tmp,s)) return 0;
@@ -22,7 +22,6 @@
 
 void pathexec(char **argv)
 {
-  char *path;
   char **e;
   unsigned int elen;
   unsigned int i;
diff -uNr ucspi-tcp-0.88.orig/rblsmtpd.c ucspi-tcp-0.88/rblsmtpd.c
--- ucspi-tcp-0.88.orig/rblsmtpd.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/rblsmtpd.c	2009-08-04 17:51:17.000000000 -0500
@@ -25,26 +25,58 @@
   strerr_die1x(100,"rblsmtpd: usage: rblsmtpd [ -b ] [ -R ] [ -t timeout ] [ -r base ] [ -a base ] smtpd [ arg ... ]");
 }
 
+char *tcp_proto;
 char *ip_env;
 static stralloc ip_reverse;
 
+static inline char tohex(char c) {
+  return c>=10?c-10+'a':c+'0';
+}
+
 void ip_init(void)
 {
   unsigned int i;
   unsigned int j;
+  unsigned char remoteip[16];
+  char hexval;
 
+  tcp_proto = env_get("PROTO");
+  if (!tcp_proto) tcp_proto = "";
   ip_env = env_get("TCPREMOTEIP");
   if (!ip_env) ip_env = "";
 
   if (!stralloc_copys(&ip_reverse,"")) nomem();
 
   i = str_len(ip_env);
-  while (i) {
-    for (j = i;j > 0;--j) if (ip_env[j - 1] == '.') break;
-    if (!stralloc_catb(&ip_reverse,ip_env + j,i - j)) nomem();
-    if (!stralloc_cats(&ip_reverse,".")) nomem();
-    if (!j) break;
-    i = j - 1;
+  if (str_diff(tcp_proto, "TCP6") != 0)
+  {
+    // IPv4
+    while (i) {
+      for (j = i;j > 0;--j) if (ip_env[j - 1] == '.') break;
+      if (!stralloc_catb(&ip_reverse,ip_env + j,i - j)) nomem();
+      if (!stralloc_cats(&ip_reverse,".")) nomem();
+      if (!j) break;
+      i = j - 1;
+    }
+  }
+  else
+  {
+    // IPv6
+    if ((i=scan_ip6(ip_env, remoteip))==0)
+      return;
+
+    for (j=16; j>0; j--)
+    {
+      hexval=tohex(remoteip[j-1] & 15);
+      if(!stralloc_catb(&ip_reverse, &hexval, 1)) nomem();
+      if(!stralloc_cats(&ip_reverse, ".")) nomem();
+
+      hexval=tohex(remoteip[j-1] >> 4);
+      if(!stralloc_catb(&ip_reverse, &hexval, 1)) nomem();
+      if(!stralloc_cats(&ip_reverse, ".")) nomem();
+    }
+
+    if(!stralloc_cats(&ip_reverse, "ipv6.")) nomem();
   }
 }
 
@@ -190,7 +222,7 @@
   argv += optind;
   if (!*argv) usage();
 
-  if (flagwantdefaultrbl) rbl("rbl.maps.vix.com");
+  if (flagwantdefaultrbl) rbl("zen.spamhaus.org");
   if (decision >= 2) rblsmtpd();
 
   pathexec_run(*argv,argv,envp);
diff -uNr ucspi-tcp-0.88.orig/recordio.1 ucspi-tcp-0.88/recordio.1
--- ucspi-tcp-0.88.orig/recordio.1	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/recordio.1	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,75 @@
+.TH recordio 1
+.SH NAME
+recordio \- record the input and output of a program
+.SH SYNTAX
+.B recordio
+.I program
+[
+.I arg ...
+]
+.SH DESCRIPTION
+.B recordio
+runs
+.I program
+with the given arguments.
+It prints lines to stderr
+showing the input and output of
+.IR program .
+
+At the beginning of each line on stderr,
+.B recordio
+inserts the
+.I program
+process ID,
+along with
+.B <
+for input or
+.B >
+for output.
+At the end of each line it inserts a space, a plus sign, or [EOF];
+a space indicates that there was a newline in the input or output,
+and [EOF] indicates the end of input or output.
+
+.B recordio
+prints every packet of input and output immediately.
+It does not attempt to combine packets into coherent stderr lines.
+For example,
+
+.EX
+     recordio sh -c 'cat /dev/fd/8 2>&1' > /dev/null
+.EE
+
+could produce
+
+.EX
+     5135 > cat: /dev/fd/8: Bad file descriptor 
+.br
+     5135 > [EOF]
+.EE
+
+or
+
+.EX
+     5135 > cat: +
+.br
+     5135 > /dev/fd/8+
+.br
+     5135 > : +
+.br
+     5135 > Bad file descriptor 
+.br
+     5135 > [EOF]
+.EE
+
+.B recordio
+uses several lines for long packets
+to guarantee that each line is printed atomically to stderr.
+
+.B recordio
+runs as a child of
+.IR program .
+It exits when it sees the end of
+.IR program 's
+output.
+.SH "SEE ALSO"
+tcpserver(1)
diff -uNr ucspi-tcp-0.88.orig/remoteinfo.h ucspi-tcp-0.88/remoteinfo.h
--- ucspi-tcp-0.88.orig/remoteinfo.h	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/remoteinfo.h	2009-08-04 17:45:59.000000000 -0500
@@ -5,5 +5,6 @@
 #include "uint16.h"
 
 extern int remoteinfo(stralloc *,char *,uint16,char *,uint16,unsigned int);
+extern int remoteinfo6(stralloc *,char *,uint16,char *,uint16,unsigned int,uint32);
 
 #endif
diff -uNr ucspi-tcp-0.88.orig/remoteinfo6.c ucspi-tcp-0.88/remoteinfo6.c
--- ucspi-tcp-0.88.orig/remoteinfo6.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/remoteinfo6.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,98 @@
+#include "fmt.h"
+#include "buffer.h"
+#include "socket.h"
+#include "error.h"
+#include "iopause.h"
+#include "timeoutconn.h"
+#include "remoteinfo.h"
+
+static struct taia now;
+static struct taia deadline;
+
+static int mywrite(int fd,char *buf,int len)
+{
+  iopause_fd x;
+
+  x.fd = fd;
+  x.events = IOPAUSE_WRITE;
+  for (;;) {
+    taia_now(&now);
+    iopause(&x,1,&deadline,&now);
+    if (x.revents) break;
+    if (taia_less(&deadline,&now)) {
+      errno = error_timeout;
+      return -1;
+    }
+  }
+  return write(fd,buf,len);
+}
+
+static int myread(int fd,char *buf,int len)
+{
+  iopause_fd x;
+
+  x.fd = fd;
+  x.events = IOPAUSE_READ;
+  for (;;) {
+    taia_now(&now);
+    iopause(&x,1,&deadline,&now);
+    if (x.revents) break;
+    if (taia_less(&deadline,&now)) {
+      errno = error_timeout;
+      return -1;
+    }
+  }
+  return read(fd,buf,len);
+}
+
+static int doit(stralloc *out,int s,char ipremote[16],uint16 portremote,char iplocal[16],uint16 portlocal,unsigned int timeout,uint32 netif)
+{
+  buffer b;
+  char bspace[128];
+  char strnum[FMT_ULONG];
+  int numcolons;
+  char ch;
+
+  if (socket_bind6(s,iplocal,0,netif) == -1) return -1;
+  if (timeoutconn6(s,ipremote,113,timeout,netif) == -1) return -1;
+
+  buffer_init(&b,mywrite,s,bspace,sizeof bspace);
+  buffer_put(&b,strnum,fmt_ulong(strnum,portremote));
+  buffer_put(&b," , ",3);
+  buffer_put(&b,strnum,fmt_ulong(strnum,portlocal));
+  buffer_put(&b,"\r\n",2);
+  if (buffer_flush(&b) == -1) return -1;
+
+  buffer_init(&b,myread,s,bspace,sizeof bspace);
+  numcolons = 0;
+  for (;;) {
+    if (buffer_get(&b,&ch,1) != 1) return -1;
+    if ((ch == ' ') || (ch == '\t') || (ch == '\r')) continue;
+    if (ch == '\n') return 0;
+    if (numcolons < 3) {
+      if (ch == ':') ++numcolons;
+    }
+    else {
+      if (!stralloc_append(out,&ch)) return -1;
+      if (out->len > 256) return 0;
+    }
+  }
+}
+
+int remoteinfo6(stralloc *out,char ipremote[16],uint16 portremote,char iplocal[16],uint16 portlocal,unsigned int timeout,uint32 netif)
+{
+  int s;
+  int r;
+
+  if (!stralloc_copys(out,"")) return -1;
+
+  taia_now(&now);
+  taia_uint(&deadline,timeout);
+  taia_add(&deadline,&now,&deadline);
+
+  s = socket_tcp6();
+  if (s == -1) return -1;
+  r = doit(out,s,ipremote,portremote,iplocal,portlocal,timeout,netif);
+  close(s);
+  return r;
+}
diff -uNr ucspi-tcp-0.88.orig/rules.c ucspi-tcp-0.88/rules.c
--- ucspi-tcp-0.88.orig/rules.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/rules.c	2009-08-04 17:45:59.000000000 -0500
@@ -64,7 +64,7 @@
 
   if (!stralloc_copys(&rules_name,ip)) return -1;
   while (rules_name.len > 0) {
-    if (ip[rules_name.len - 1] == '.') {
+    if (ip[rules_name.len - 1] == '.' || ip[rules_name.len - 1] == ':') {
       r = dorule(callback);
       if (r) return r;
     }
diff -uNr ucspi-tcp-0.88.orig/scan_ip6.c ucspi-tcp-0.88/scan_ip6.c
--- ucspi-tcp-0.88.orig/scan_ip6.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/scan_ip6.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,87 @@
+#include "scan.h"
+#include "ip4.h"
+#include "ip6.h"
+
+/*
+ * IPv6 addresses are really ugly to parse.
+ * Syntax: (h = hex digit)
+ *   1. hhhh:hhhh:hhhh:hhhh:hhhh:hhhh:hhhh:hhhh
+ *   2. any number of 0000 may be abbreviated as "::", but only once
+ *   3. The last two words may be written as IPv4 address
+ */
+
+unsigned int scan_ip6(const char *s,char ip[16])
+{
+  unsigned int i;
+  unsigned int len=0;
+  unsigned long u;
+
+  char suffix[16];
+  int prefixlen=0;
+  int suffixlen=0;
+
+  if ((i=ip4_scan((char*)s,ip+12))) {
+    for (len=0; len<12; ++len) ip[len]=V4mappedprefix[len];
+    return i;
+  }
+  for (i=0; i<16; i++) ip[i]=0;
+  for (;;) {
+    if (*s == ':') {
+      len++;
+      if (s[1] == ':') {	/* Found "::", skip to part 2 */
+	s+=2;
+	len++;
+	break;
+      }
+      s++;
+    }
+    i = scan_xlong((char*)s,&u);
+    if (!i) return 0;
+    if (prefixlen==12 && s[i]=='.') {
+      /* the last 4 bytes may be written as IPv4 address */
+      i=ip4_scan((char*)s,ip+12);
+      if (i)
+	return i+len;
+      else
+	return 0;
+    }
+    ip[prefixlen++] = (u >> 8);
+    ip[prefixlen++] = (u & 255);
+    s += i; len += i;
+    if (prefixlen==16)
+      return len;
+  }
+
+/* part 2, after "::" */
+  for (;;) {
+    if (*s == ':') {
+      if (suffixlen==0)
+	break;
+      s++;
+      len++;
+    } else if (suffixlen!=0)
+      break;
+    i = scan_xlong((char*)s,&u);
+    if (!i) {
+      len--;
+      break;
+    }
+    if (suffixlen+prefixlen<=12 && s[i]=='.') {
+      int j=ip4_scan((char*)s,suffix+suffixlen);
+      if (j) {
+	suffixlen+=4;
+	len+=j;
+	break;
+      } else
+	prefixlen=12-suffixlen;	/* make end-of-loop test true */
+    }
+    suffix[suffixlen++] = (u >> 8);
+    suffix[suffixlen++] = (u & 255);
+    s += i; len += i;
+    if (prefixlen+suffixlen==16)
+      break;
+  }
+  for (i=0; i<suffixlen; i++)
+    ip[16-suffixlen+i] = suffix[i];
+  return len;
+}
diff -uNr ucspi-tcp-0.88.orig/scan_xlong.c ucspi-tcp-0.88/scan_xlong.c
--- ucspi-tcp-0.88.orig/scan_xlong.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/scan_xlong.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,23 @@
+#include "scan.h"
+
+static int fromhex(unsigned char c) {
+  if (c>='0' && c<='9')
+    return c-'0';
+  else if (c>='A' && c<='F')
+    return c-'A'+10;
+  else if (c>='a' && c<='f')
+    return c-'a'+10;
+  return -1;
+}
+
+unsigned int scan_xlong(char *src,unsigned long *dest) {
+  register const char *tmp=src;
+  register int l=0;
+  register unsigned char c;
+  while ((c=fromhex(*tmp))<16) {
+    l=(l<<4)+c;
+    ++tmp;
+  }
+  *dest=l;
+  return tmp-src;
+}
diff -uNr ucspi-tcp-0.88.orig/socket.h ucspi-tcp-0.88/socket.h
--- ucspi-tcp-0.88.orig/socket.h	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/socket.h	2009-08-04 17:45:59.000000000 -0500
@@ -2,21 +2,52 @@
 #define SOCKET_H
 
 #include "uint16.h"
+#include "uint32.h"
 
 extern int socket_tcp(void);
 extern int socket_udp(void);
+extern int socket_tcp6(void);
+extern int socket_udp6(void);
 
-extern int socket_connect4(int,char *,uint16);
+extern int socket_connect4(int,const char *,uint16);
+extern int socket_connect6(int s,const char *ip,uint16 port,uint32 scope_id);
 extern int socket_connected(int);
-extern int socket_bind4(int,char *,uint16);
-extern int socket_bind4_reuse(int,char *,uint16);
+extern int socket_bind4(int,const char *,uint16);
+extern int socket_bind4_reuse(int,const char *,uint16);
+extern int socket_bind6(int s,const char *ip,uint16 port,uint32 scope_id);
+extern int socket_bind6_reuse(int s,const char *ip,uint16 port,uint32 scope_id);
 extern int socket_listen(int,int);
 extern int socket_accept4(int,char *,uint16 *);
+extern int socket_accept6(int s,char *ip,uint16 *port,uint32 *scope_id);
 extern int socket_recv4(int,char *,int,char *,uint16 *);
-extern int socket_send4(int,char *,int,char *,uint16);
+extern int socket_send4(int,const char *,int,const char *,uint16);
+extern int socket_recv6(int s,char *buf,unsigned int len,char *ip,uint16 *port,uint32 *scope_id);
+extern int socket_send6(int s,const char *buf,unsigned int len,const char *ip,uint16 port,uint32 scope_id);
 extern int socket_local4(int,char *,uint16 *);
 extern int socket_remote4(int,char *,uint16 *);
+extern int socket_local6(int s,char *ip,uint16 *port,uint32 *scope_id);
+extern int socket_remote6(int s,char *ip,uint16 *port,uint32 *scope_id);
+
+/* enable sending udp packets to the broadcast address */
+extern int socket_broadcast(int);
+/* join a multicast group on the given interface */
+extern int socket_mcjoin4(int,char *,char *);
+extern int socket_mcjoin6(int,char *,int);
+/* leave a multicast group on the given interface */
+extern int socket_mcleave4(int,char *);
+extern int socket_mcleave6(int,char *);
+/* set multicast TTL/hop count for outgoing packets */
+extern int socket_mcttl4(int,char);
+extern int socket_mcttl6(int,char);
+/* enable multicast loopback */
+extern int socket_mcloop4(int,char);
+extern int socket_mcloop6(int,char);
+
+extern const char* socket_getifname(uint32 interface);
+extern uint32 socket_getifidx(const char *ifname);
 
 extern void socket_tryreservein(int,int);
 
+extern int noipv6;
+
 #endif
diff -uNr ucspi-tcp-0.88.orig/socket_accept6.c ucspi-tcp-0.88/socket_accept6.c
--- ucspi-tcp-0.88.orig/socket_accept6.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/socket_accept6.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,44 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "byte.h"
+#include "socket.h"
+#include "ip6.h"
+#include "haveip6.h"
+#include "error.h"
+
+int socket_accept6(int s,char ip[16],uint16 *port,uint32 *scope_id)
+{
+#ifdef LIBC_HAS_IP6
+  struct sockaddr_in6 sa;
+#else
+  struct sockaddr_in sa;
+#endif
+  unsigned int dummy = sizeof sa;
+  int fd;
+
+  fd = accept(s,(struct sockaddr *) &sa,&dummy);
+  if (fd == -1) return -1;
+
+#ifdef LIBC_HAS_IP6
+  if (sa.sin6_family==AF_INET) {
+    struct sockaddr_in *sa4=(struct sockaddr_in*)&sa;
+    byte_copy(ip,12,V4mappedprefix);
+    byte_copy(ip+12,4,(char *) &sa4->sin_addr);
+    uint16_unpack_big((char *) &sa4->sin_port,port);
+    return fd;
+  }
+  byte_copy(ip,16,(char *) &sa.sin6_addr);
+  uint16_unpack_big((char *) &sa.sin6_port,port);
+  if (scope_id) *scope_id=sa.sin6_scope_id;
+
+  return fd;
+#else
+  byte_copy(ip,12,V4mappedprefix);
+  byte_copy(ip+12,4,(char *) &sa.sin_addr);
+  uint16_unpack_big((char *) &sa.sin_port,port);
+  if (scope_id) *scope_id=0;
+  return fd;
+#endif
+}
diff -uNr ucspi-tcp-0.88.orig/socket_bind.c ucspi-tcp-0.88/socket_bind.c
--- ucspi-tcp-0.88.orig/socket_bind.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/socket_bind.c	2009-08-04 17:45:59.000000000 -0500
@@ -5,7 +5,7 @@
 #include "byte.h"
 #include "socket.h"
 
-int socket_bind4(int s,char ip[4],uint16 port)
+int socket_bind4(int s,const char ip[4],uint16 port)
 {
   struct sockaddr_in sa;
 
@@ -17,7 +17,7 @@
   return bind(s,(struct sockaddr *) &sa,sizeof sa);
 }
 
-int socket_bind4_reuse(int s,char ip[4],uint16 port)
+int socket_bind4_reuse(int s,const char ip[4],uint16 port)
 {
   int opt = 1;
   setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof opt);
diff -uNr ucspi-tcp-0.88.orig/socket_bind6.c ucspi-tcp-0.88/socket_bind6.c
--- ucspi-tcp-0.88.orig/socket_bind6.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/socket_bind6.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,45 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "byte.h"
+#include "socket.h"
+#include "ip6.h"
+#include "haveip6.h"
+#include "error.h"
+
+int socket_bind6(int s,const char ip[16],uint16 port,uint32 scope_id)
+{
+#ifdef LIBC_HAS_IP6
+  struct sockaddr_in6 sa;
+
+  if (noipv6) {
+#endif
+    int i;
+    for (i=0; i<16; i++)
+      if (ip[i]!=0) break;
+    if (i==16 || ip6_isv4mapped(ip))
+      return socket_bind4(s,ip+12,port);
+#ifdef LIBC_HAS_IP6
+  }
+  byte_zero(&sa,sizeof sa);
+  sa.sin6_family = AF_INET6;
+  uint16_pack_big((char *) &sa.sin6_port,port);
+/*  implicit: sa.sin6_flowinfo = 0; */
+  byte_copy((char *) &sa.sin6_addr,16,ip);
+  sa.sin6_scope_id=scope_id;
+
+  return bind(s,(struct sockaddr *) &sa,sizeof sa);
+#else
+  errno=error_proto;
+  return -1;
+#endif
+}
+
+int socket_bind6_reuse(int s,const char ip[16],uint16 port,uint32 scope_id)
+{
+  int opt = 1;
+  setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof opt);
+  return socket_bind6(s,ip,port,scope_id);
+}
+
diff -uNr ucspi-tcp-0.88.orig/socket_conn.c ucspi-tcp-0.88/socket_conn.c
--- ucspi-tcp-0.88.orig/socket_conn.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/socket_conn.c	2009-08-04 17:45:59.000000000 -0500
@@ -6,7 +6,7 @@
 #include "byte.h"
 #include "socket.h"
 
-int socket_connect4(int s,char ip[4],uint16 port)
+int socket_connect4(int s,const char ip[4],uint16 port)
 {
   struct sockaddr_in sa;
 
diff -uNr ucspi-tcp-0.88.orig/socket_conn6.c ucspi-tcp-0.88/socket_conn6.c
--- ucspi-tcp-0.88.orig/socket_conn6.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/socket_conn6.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,38 @@
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include "byte.h"
+#include "socket.h"
+#include "ip6.h"
+#include "haveip6.h"
+#include "uint32.h"
+#include "ip4.h"
+
+int socket_connect6(int s,const char ip[16],uint16 port,uint32 scope_id)
+{
+#ifdef LIBC_HAS_IP6
+  struct sockaddr_in6 sa;
+
+  if (noipv6) {
+#endif
+    if (ip6_isv4mapped(ip))
+      return socket_connect4(s,ip+12,port);
+    if (byte_equal(ip,16,V6loopback))
+      return socket_connect4(s,ip4loopback,port);
+#ifdef LIBC_HAS_IP6
+  }
+  byte_zero(&sa,sizeof sa);
+  sa.sin6_family = PF_INET6;
+  uint16_pack_big((char *) &sa.sin6_port,port);
+  sa.sin6_flowinfo = 0;
+  sa.sin6_scope_id = scope_id;
+  byte_copy((char *) &sa.sin6_addr,16,ip);
+
+  return connect(s,(struct sockaddr *) &sa,sizeof sa);
+#else
+  errno=EPROTONOSUPPORT;
+  return -1;
+#endif
+}
diff -uNr ucspi-tcp-0.88.orig/socket_getifidx.c ucspi-tcp-0.88/socket_getifidx.c
--- ucspi-tcp-0.88.orig/socket_getifidx.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/socket_getifidx.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,8 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include "socket.h"
+
+uint32 socket_getifidx(const char* ifname) {
+  return if_nametoindex(ifname);
+}
diff -uNr ucspi-tcp-0.88.orig/socket_getifname.c ucspi-tcp-0.88/socket_getifname.c
--- ucspi-tcp-0.88.orig/socket_getifname.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/socket_getifname.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,14 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include "socket.h"
+
+static char ifname[IFNAMSIZ];
+
+const char* socket_getifname(uint32 interface) {
+  char *tmp=if_indextoname(interface,ifname);
+  if (tmp)
+    return tmp;
+  else
+    return "[unknown]";
+}
diff -uNr ucspi-tcp-0.88.orig/socket_ip4loopback.c ucspi-tcp-0.88/socket_ip4loopback.c
--- ucspi-tcp-0.88.orig/socket_ip4loopback.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/socket_ip4loopback.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,2 @@
+
+const char ip4loopback[4] = {127,0,0,1};
diff -uNr ucspi-tcp-0.88.orig/socket_local6.c ucspi-tcp-0.88/socket_local6.c
--- ucspi-tcp-0.88.orig/socket_local6.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/socket_local6.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,39 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "byte.h"
+#include "socket.h"
+#include "ip6.h"
+#include "haveip6.h"
+#include "error.h"
+
+int socket_local6(int s,char ip[16],uint16 *port,uint32 *scope_id)
+{
+#ifdef LIBC_HAS_IP6
+  struct sockaddr_in6 sa;
+#else
+  struct sockaddr_in sa;
+#endif
+  unsigned int dummy = sizeof sa;
+
+  if (getsockname(s,(struct sockaddr *) &sa,&dummy) == -1) return -1;
+#ifdef LIBC_HAS_IP6
+  if (sa.sin6_family==AF_INET) {
+    struct sockaddr_in *sa4=(struct sockaddr_in*)&sa;
+    byte_copy(ip,12,V4mappedprefix);
+    byte_copy(ip+12,4,(char *) &sa4->sin_addr);
+    uint16_unpack_big((char *) &sa4->sin_port,port);
+    return 0;
+  }
+  byte_copy(ip,16,(char *) &sa.sin6_addr);
+  uint16_unpack_big((char *) &sa.sin6_port,port);
+  if (scope_id) *scope_id=sa.sin6_scope_id;
+#else
+  byte_copy(ip,12,V4mappedprefix);
+  byte_copy(ip+12,4,(char *) &sa.sin_addr);
+  uint16_unpack_big((char *) &sa.sin_port,port);
+  if (scope_id) *scope_id=0;
+#endif
+  return 0;
+}
diff -uNr ucspi-tcp-0.88.orig/socket_recv6.c ucspi-tcp-0.88/socket_recv6.c
--- ucspi-tcp-0.88.orig/socket_recv6.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/socket_recv6.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,44 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "byte.h"
+#include "socket.h"
+#include "ip6.h"
+#include "haveip6.h"
+#include "error.h"
+
+int socket_recv6(int s,char *buf,unsigned int len,char ip[16],uint16 *port,uint32 *scope_id)
+{
+#ifdef LIBC_HAS_IP6
+  struct sockaddr_in6 sa;
+#else
+  struct sockaddr_in sa;
+#endif
+  unsigned int dummy = sizeof sa;
+  int r;
+
+  byte_zero(&sa,dummy);
+  r = recvfrom(s,buf,len,0,(struct sockaddr *) &sa,&dummy);
+  if (r == -1) return -1;
+
+#ifdef LIBC_HAS_IP6
+  if (noipv6) {
+    struct sockaddr_in *sa4=(struct sockaddr_in *)&sa;
+    byte_copy(ip,12,V4mappedprefix);
+    byte_copy(ip+12,4,(char *) &sa4->sin_addr);
+    uint16_unpack_big((char *) &sa4->sin_port,port);
+    return r;
+  }
+  byte_copy(ip,16,(char *) &sa.sin6_addr);
+  uint16_unpack_big((char *) &sa.sin6_port,port);
+  if (scope_id) *scope_id=sa.sin6_scope_id;
+#else
+  byte_copy(ip,12,(char *)V4mappedprefix);
+  byte_copy(ip+12,4,(char *) &sa.sin_addr);
+  uint16_unpack_big((char *) &sa.sin_port,port);
+  if (scope_id) *scope_id=0;
+#endif
+
+  return r;
+}
diff -uNr ucspi-tcp-0.88.orig/socket_remote6.c ucspi-tcp-0.88/socket_remote6.c
--- ucspi-tcp-0.88.orig/socket_remote6.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/socket_remote6.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,39 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "byte.h"
+#include "socket.h"
+#include "ip6.h"
+#include "haveip6.h"
+#include "error.h"
+
+int socket_remote6(int s,char ip[16],uint16 *port,uint32 *scope_id)
+{
+#ifdef LIBC_HAS_IP6
+  struct sockaddr_in6 sa;
+#else
+  struct sockaddr_in sa;
+#endif
+  unsigned int dummy = sizeof sa;
+
+  if (getpeername(s,(struct sockaddr *) &sa,&dummy) == -1) return -1;
+#ifdef LIBC_HAS_IP6
+  if (sa.sin6_family==AF_INET) {
+    struct sockaddr_in *sa4=(struct sockaddr_in*)&sa;
+    byte_copy(ip,12,V4mappedprefix);
+    byte_copy(ip+12,4,(char *) &sa4->sin_addr);
+    uint16_unpack_big((char *) &sa4->sin_port,port);
+    return 0;
+  }
+  byte_copy(ip,16,(char *) &sa.sin6_addr);
+  uint16_unpack_big((char *) &sa.sin6_port,port);
+  if (scope_id) *scope_id=sa.sin6_scope_id;
+#else
+  byte_copy(ip,12,V4mappedprefix);
+  byte_copy(ip+12,4,(char *) &sa.sin_addr);
+  uint16_unpack_big((char *) &sa.sin_port,port);
+  if (scope_id) *scope_id=0;
+#endif
+  return 0;
+}
diff -uNr ucspi-tcp-0.88.orig/socket_send6.c ucspi-tcp-0.88/socket_send6.c
--- ucspi-tcp-0.88.orig/socket_send6.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/socket_send6.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,40 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include "byte.h"
+#include "socket.h"
+#include "ip4.h"
+#include "ip6.h"
+#include "haveip6.h"
+#include "error.h"
+
+int socket_send6(int s,const char *buf,unsigned int len,const char ip[16],uint16 port,uint32 scope_id)
+{
+#ifdef LIBC_HAS_IP6
+  struct sockaddr_in6 sa;
+#else
+  struct sockaddr_in sa;
+#endif
+
+  byte_zero(&sa,sizeof sa);
+#ifdef LIBC_HAS_IP6
+  if (noipv6) {
+#endif
+    if (ip6_isv4mapped(ip))
+      return socket_send4(s,buf,len,ip+12,port);
+    if (byte_equal(ip,16,V6loopback))
+      return socket_send4(s,buf,len,ip4loopback,port);
+#ifdef LIBC_HAS_IP6
+    errno=error_proto;
+    return -1;
+  }
+  sa.sin6_family = AF_INET6;
+  uint16_pack_big((char *) &sa.sin6_port,port);
+  byte_copy((char *) &sa.sin6_addr,16,ip);
+  return sendto(s,buf,len,0,(struct sockaddr *) &sa,sizeof sa);
+#else
+  errno=error_proto;
+  return -1;
+#endif
+}
diff -uNr ucspi-tcp-0.88.orig/socket_tcp6.c ucspi-tcp-0.88/socket_tcp6.c
--- ucspi-tcp-0.88.orig/socket_tcp6.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/socket_tcp6.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,44 @@
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include "ndelay.h"
+#include "socket.h"
+#include "haveip6.h"
+#include "error.h"
+
+#ifdef LIBC_HAS_IP6
+int noipv6=0;
+#else
+int noipv6=1;
+#endif
+
+int socket_tcp6(void)
+{
+#ifdef LIBC_HAS_IP6
+  int s;
+
+  if (noipv6) goto compat;
+  s = socket(PF_INET6,SOCK_STREAM,0);
+  if (s == -1) {
+    if (errno == EINVAL || errno == EAFNOSUPPORT) {
+compat:
+      s=socket(AF_INET,SOCK_STREAM,0);
+      noipv6=1;
+      if (s==-1) return -1;
+    } else
+    return -1;
+  }
+  if (ndelay_on(s) == -1) { close(s); return -1; }
+#ifdef IPV6_V6ONLY
+  {
+    int zero=0;
+    setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void*)&zero,sizeof(zero));
+  }
+#endif
+  return s;
+#else
+  return socket_tcp();
+#endif
+}
diff -uNr ucspi-tcp-0.88.orig/socket_udp6.c ucspi-tcp-0.88/socket_udp6.c
--- ucspi-tcp-0.88.orig/socket_udp6.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/socket_udp6.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,38 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include "haveip6.h"
+#include "socket.h"
+
+#ifndef EAFNOSUPPORT
+#define EAFNOSUPPORT EINVAL
+#endif
+
+int socket_udp6(void)
+{
+#ifdef LIBC_HAS_IP6
+  int s;
+
+  if (noipv6) goto compat;
+  s = socket(PF_INET6,SOCK_DGRAM,0);
+  if (s == -1) {
+    if (errno == EINVAL || errno == EAFNOSUPPORT) {
+compat:
+      s=socket(AF_INET,SOCK_DGRAM,0);
+      noipv6=1;
+      if (s==-1) return -1;
+    } else
+    return -1;
+  }
+#ifdef IPV6_V6ONLY
+  {
+    int zero=0;
+    setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void*)&zero,sizeof(zero));
+  }
+#endif
+  return s;
+#else
+  return socket_udp();
+#endif
+}
diff -uNr ucspi-tcp-0.88.orig/socket_v4mappedprefix.c ucspi-tcp-0.88/socket_v4mappedprefix.c
--- ucspi-tcp-0.88.orig/socket_v4mappedprefix.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/socket_v4mappedprefix.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,2 @@
+
+const unsigned char V4mappedprefix[12]={0,0,0,0,0,0,0,0,0,0,0xff,0xff};
diff -uNr ucspi-tcp-0.88.orig/socket_v6any.c ucspi-tcp-0.88/socket_v6any.c
--- ucspi-tcp-0.88.orig/socket_v6any.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/socket_v6any.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,2 @@
+
+const unsigned char V6any[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
diff -uNr ucspi-tcp-0.88.orig/socket_v6loopback.c ucspi-tcp-0.88/socket_v6loopback.c
--- ucspi-tcp-0.88.orig/socket_v6loopback.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/socket_v6loopback.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,2 @@
+
+const unsigned char V6loopback[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1};
diff -uNr ucspi-tcp-0.88.orig/str.h ucspi-tcp-0.88/str.h
--- ucspi-tcp-0.88.orig/str.h	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/str.h	2009-08-04 17:45:59.000000000 -0500
@@ -1,13 +1,13 @@
 #ifndef STR_H
 #define STR_H
 
-extern unsigned int str_copy(char *,char *);
-extern int str_diff(char *,char *);
-extern int str_diffn(char *,char *,unsigned int);
-extern unsigned int str_len(char *);
-extern unsigned int str_chr(char *,int);
-extern unsigned int str_rchr(char *,int);
-extern int str_start(char *,char *);
+extern unsigned int str_copy(char *,const char *);
+extern int str_diff(const char *,const char *);
+extern int str_diffn(const char *,const char *,unsigned int);
+extern unsigned int str_len(const char *);
+extern unsigned int str_chr(const char *,int);
+extern unsigned int str_rchr(const char *,int);
+extern int str_start(const char *,const char *);
 
 #define str_equal(s,t) (!str_diff((s),(t)))
 
diff -uNr ucspi-tcp-0.88.orig/str_chr.c ucspi-tcp-0.88/str_chr.c
--- ucspi-tcp-0.88.orig/str_chr.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/str_chr.c	2009-08-04 17:45:59.000000000 -0500
@@ -1,9 +1,9 @@
 #include "str.h"
 
-unsigned int str_chr(register char *s,int c)
+unsigned int str_chr(register const char *s,int c)
 {
   register char ch;
-  register char *t;
+  register const char *t;
 
   ch = c;
   t = s;
diff -uNr ucspi-tcp-0.88.orig/str_diff.c ucspi-tcp-0.88/str_diff.c
--- ucspi-tcp-0.88.orig/str_diff.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/str_diff.c	2009-08-04 17:45:59.000000000 -0500
@@ -1,6 +1,6 @@
 #include "str.h"
 
-int str_diff(register char *s,register char *t)
+int str_diff(register const char *s,register const char *t)
 {
   register char x;
 
diff -uNr ucspi-tcp-0.88.orig/str_len.c ucspi-tcp-0.88/str_len.c
--- ucspi-tcp-0.88.orig/str_len.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/str_len.c	2009-08-04 17:45:59.000000000 -0500
@@ -1,8 +1,8 @@
 #include "str.h"
 
-unsigned int str_len(char *s)
+unsigned int str_len(const char *s)
 {
-  register char *t;
+  register const char *t;
 
   t = s;
   for (;;) {
diff -uNr ucspi-tcp-0.88.orig/str_start.c ucspi-tcp-0.88/str_start.c
--- ucspi-tcp-0.88.orig/str_start.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/str_start.c	2009-08-04 17:45:59.000000000 -0500
@@ -1,6 +1,6 @@
 #include "str.h"
 
-int str_start(register char *s,register char *t)
+int str_start(register const char *s,register const char *t)
 {
   register char x;
 
diff -uNr ucspi-tcp-0.88.orig/stralloc.h ucspi-tcp-0.88/stralloc.h
--- ucspi-tcp-0.88.orig/stralloc.h	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/stralloc.h	2009-08-04 17:45:59.000000000 -0500
@@ -9,18 +9,20 @@
 extern int stralloc_readyplus(stralloc *,unsigned int);
 extern int stralloc_copy(stralloc *,stralloc *);
 extern int stralloc_cat(stralloc *,stralloc *);
-extern int stralloc_copys(stralloc *,char *);
-extern int stralloc_cats(stralloc *,char *);
-extern int stralloc_copyb(stralloc *,char *,unsigned int);
-extern int stralloc_catb(stralloc *,char *,unsigned int);
+extern int stralloc_copys(stralloc *,const char *);
+extern int stralloc_cats(stralloc *,const char *);
+extern int stralloc_copyb(stralloc *,const char *,unsigned int);
+extern int stralloc_catb(stralloc *,const char *,unsigned int);
 extern int stralloc_append(stralloc *,char *); /* beware: this takes a pointer to 1 char */
-extern int stralloc_starts(stralloc *,char *);
+extern int stralloc_starts(stralloc *,const char *);
 
 #define stralloc_0(sa) stralloc_append(sa,"")
 
 extern int stralloc_catulong0(stralloc *,unsigned long,unsigned int);
 extern int stralloc_catlong0(stralloc *,long,unsigned int);
 
+extern void stralloc_free(stralloc *);
+
 #define stralloc_catlong(sa,l) (stralloc_catlong0((sa),(l),0))
 #define stralloc_catuint0(sa,i,n) (stralloc_catulong0((sa),(i),(n)))
 #define stralloc_catint0(sa,i,n) (stralloc_catlong0((sa),(i),(n)))
diff -uNr ucspi-tcp-0.88.orig/stralloc_catb.c ucspi-tcp-0.88/stralloc_catb.c
--- ucspi-tcp-0.88.orig/stralloc_catb.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/stralloc_catb.c	2009-08-04 17:45:59.000000000 -0500
@@ -1,7 +1,7 @@
 #include "stralloc.h"
 #include "byte.h"
 
-int stralloc_catb(stralloc *sa,char *s,unsigned int n)
+int stralloc_catb(stralloc *sa,const char *s,unsigned int n)
 {
   if (!sa->s) return stralloc_copyb(sa,s,n);
   if (!stralloc_readyplus(sa,n + 1)) return 0;
diff -uNr ucspi-tcp-0.88.orig/stralloc_cats.c ucspi-tcp-0.88/stralloc_cats.c
--- ucspi-tcp-0.88.orig/stralloc_cats.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/stralloc_cats.c	2009-08-04 17:45:59.000000000 -0500
@@ -2,7 +2,7 @@
 #include "str.h"
 #include "stralloc.h"
 
-int stralloc_cats(stralloc *sa,char *s)
+int stralloc_cats(stralloc *sa,const char *s)
 {
   return stralloc_catb(sa,s,str_len(s));
 }
diff -uNr ucspi-tcp-0.88.orig/stralloc_opyb.c ucspi-tcp-0.88/stralloc_opyb.c
--- ucspi-tcp-0.88.orig/stralloc_opyb.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/stralloc_opyb.c	2009-08-04 17:45:59.000000000 -0500
@@ -1,7 +1,7 @@
 #include "stralloc.h"
 #include "byte.h"
 
-int stralloc_copyb(stralloc *sa,char *s,unsigned int n)
+int stralloc_copyb(stralloc *sa,const char *s,unsigned int n)
 {
   if (!stralloc_ready(sa,n + 1)) return 0;
   byte_copy(sa->s,n,s);
diff -uNr ucspi-tcp-0.88.orig/stralloc_opys.c ucspi-tcp-0.88/stralloc_opys.c
--- ucspi-tcp-0.88.orig/stralloc_opys.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/stralloc_opys.c	2009-08-04 17:45:59.000000000 -0500
@@ -2,7 +2,7 @@
 #include "str.h"
 #include "stralloc.h"
 
-int stralloc_copys(stralloc *sa,char *s)
+int stralloc_copys(stralloc *sa,const char *s)
 {
   return stralloc_copyb(sa,s,str_len(s));
 }
diff -uNr ucspi-tcp-0.88.orig/tcp-environ.5 ucspi-tcp-0.88/tcp-environ.5
--- ucspi-tcp-0.88.orig/tcp-environ.5	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/tcp-environ.5	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,66 @@
+.TH tcp-environ 5
+.SH NAME
+tcp-environ \- TCP-related environment variables
+.SH DESCRIPTION
+The following environment variables
+describe a TCP connection.
+They are set up by
+.BR tcp-env ,
+.BR tcpclient ,
+and
+.BR tcpserver .
+Note that
+.BR TCPLOCALHOST ,
+.BR TCPREMOTEHOST ,
+and
+.B TCPREMOTEINFO
+can contain arbitrary characters.
+.TP 5
+PROTO
+The string
+.BR TCP .
+.TP 5
+TCPLOCALHOST
+The domain name of the local host,
+with uppercase letters converted to lowercase.
+If there is no currently available domain name
+for the local IP address,
+.B TCPLOCALHOST
+is not set.
+.TP 5
+TCPLOCALIP
+The IP address of the local host, in dotted-decimal form.
+.TP 5
+TCPLOCALPORT
+The local TCP port number, in decimal.
+.TP 5
+TCPREMOTEHOST
+The domain name of the remote host,
+with uppercase letters converted to lowercase.
+If there is no currently available domain name
+for the remote IP address,
+.B TCPREMOTEHOST
+is not set.
+.TP 5
+TCPREMOTEINFO
+A connection-specific string, perhaps a username,
+supplied by the remote host
+via 931/1413/IDENT/TAP.
+If the remote host did not supply connection information,
+.B TCPREMOTEINFO
+is not set.
+.TP 5
+TCPREMOTEIP
+The IP address of the remote host.
+.TP 5
+TCPREMOTEPORT
+The remote TCP port number.
+.TP 5
+TCPINTERFACE
+The interface name ("eth0") for IPv6 connections using link-local
+addresses.
+.SH "SEE ALSO"
+tcpclient(1),
+tcpserver(1),
+tcp-env(1),
+tcp(4)
diff -uNr ucspi-tcp-0.88.orig/tcpcat.1 ucspi-tcp-0.88/tcpcat.1
--- ucspi-tcp-0.88.orig/tcpcat.1	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/tcpcat.1	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,20 @@
+.TH tcpcat 1
+.SH NAME
+tcpcat \- print data from a TCP port
+.SH SYNTAX
+.B tcpcat
+.I host
+.I port
+.SH DESCRIPTION
+.B tcpcat
+connects to
+.I port
+on
+.I host
+and prints any data it receives.
+
+.B tcpcat
+can be used to transfer binary data.
+It does no conversions.
+.SH "SEE ALSO"
+tcpclient(1)
diff -uNr ucspi-tcp-0.88.orig/tcpclient.1 ucspi-tcp-0.88/tcpclient.1
--- ucspi-tcp-0.88.orig/tcpclient.1	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/tcpclient.1	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,173 @@
+.TH tcpclient 1
+.SH NAME
+tcpclient \- create an outgoing TCP connection
+.SH SYNOPSIS
+.B tcpclient
+[
+.B \-46hHrRdDqQv
+]
+[
+.B \-i\fIlocalip
+]
+[
+.B \-p\fIlocalport
+]
+[
+.B \-T\fItimeoutconn
+]
+[
+.B \-l\fIlocalname
+]
+[
+.B \-t\fItimeoutinfo
+]
+[
+.B \-I\fIinterface
+]
+.I host
+.I port
+.I program
+[
+.I arg ...
+]
+.SH DESCRIPTION
+.B tcpclient
+attempts to connect to a TCP server.
+If it is successful, it runs
+.I program
+with the given arguments,
+with descriptor 6 reading from the network
+and descriptor 7 writing to the network.
+
+The server's address is given by
+.I host
+and
+.IR port .
+.I host
+may be 0, referring to the local machine,
+or a dotted-decimal IP address,
+or a host name;
+if a host has several IP addresses,
+.B tcpclient
+tries each in turn.
+.I port
+may be a numeric port number
+or a port name.
+
+.B tcpclient
+sets up several environment variables,
+as described in
+.B tcp-environ(5).
+.SH OPTIONS
+.TP
+.B \-i\fIlocalip
+Use
+.I localip
+as the IP address for the local side of the connection;
+quit if
+.I localip
+is not available.
+.TP
+.B \-p\fIlocalport
+Use
+.I localport
+as the port number for the local side of the connection;
+quit if
+.I localport
+is not available.
+.TP
+.B \-I\fIinterface
+Use
+.I interface
+as the local network interface.  This is only defined for IPv6 sockets
+and needed if you use link-local IPv6 addresses.
+.TP
+.B \-T\fItimeoutconn
+Give up on the
+connection attempt
+after
+.I timeoutconn
+seconds. Default: 60.
+This timeout applies to each IP address tried.
+.TP
+.B \-d
+(Default.)
+Delay sending data for a fraction of a second whenever the
+remote host is responding slowly,
+to make better use of the network.
+.TP
+.B \-D
+Never delay sending data;
+enable TCP_NODELAY.
+This is appropriate for interactive connections.
+.TP
+.B \-q
+Quiet.
+Do not print any messages.
+.TP
+.B \-Q
+(Default.)
+Print error messages.
+.TP
+.B \-v
+Verbose.
+Print all available messages.
+.SH "DATA-GATHERING OPTIONS"
+.TP
+.B \-h
+(Default.)
+Look up the remote host name for
+.BR TCPREMOTEHOST .
+.TP
+.B \-H
+Do not look up the remote host name;
+unset
+.BR TCPREMOTEHOST .
+.TP
+.B \-l\fIlocalname
+Do not look up the local host name;
+use
+.I localname
+for
+.BR TCPLOCALHOST .
+.TP
+.B \-r
+(Default.)
+Attempt to obtain
+.B TCPREMOTEINFO
+from the remote host.
+.TP
+.B \-R
+Do not attempt to obtain
+.B TCPREMOTEINFO
+from the remote host.
+.TP
+.B \-t\fItimeoutinfo
+Give up on the 
+.B TCPREMOTEINFO
+connection attempt
+after
+.I timeoutinfo
+seconds. Default: 26.
+.TP
+.B \-4
+Fall back to IPv4 sockets.  This is necessary for terminally broken
+systems like OpenBSD which will not let IPv6 sockets connect to
+V4-mapped IPv6 addresses.  Please note that this also applies to DNS
+lookups, so you will have to use an DNS resolver with an IPv6 address to
+connect to IPv6 systems.  Use \fBDNSCACHEIP\fR to set the DNS resolver
+IP dynamically.
+.TP
+.B \-6
+Force IPv6 mode in UCSPI environment variables, even for
+IPv4 connections.  This will set \fB$PROTO\fR to \fBTCP6\fR and put
+IPv4-mapped IPv6 addresses in \fBTCPLOCALIP\fR and \fBTCPREMOTEIP\fR.
+.SH "SEE ALSO"
+date@(1),
+finger@(1),
+http@(1),
+mconnect(1),
+tcpcat(1),
+tcpserver(1),
+who@(1),
+tcp-environ(5)
diff -uNr ucspi-tcp-0.88.orig/tcpclient.c ucspi-tcp-0.88/tcpclient.c
--- ucspi-tcp-0.88.orig/tcpclient.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/tcpclient.c	2009-08-04 17:45:59.000000000 -0500
@@ -9,6 +9,7 @@
 #include "scan.h"
 #include "str.h"
 #include "ip4.h"
+#include "ip6.h"
 #include "uint16.h"
 #include "socket.h"
 #include "fd.h"
@@ -20,6 +21,7 @@
 #include "timeoutconn.h"
 #include "remoteinfo.h"
 #include "dns.h"
+#include "byte.h"
 
 #define FATAL "tcpclient: fatal: "
 #define CONNECT "tcpclient: unable to connect to "
@@ -31,27 +33,30 @@
 void usage(void)
 {
   strerr_die1x(100,"tcpclient: usage: tcpclient \
-[ -hHrRdDqQv ] \
+[ -46hHrRdDqQv ] \
 [ -i localip ] \
 [ -p localport ] \
 [ -T timeoutconn ] \
 [ -l localname ] \
 [ -t timeoutinfo ] \
+[ -I interface ] \
 host port program");
 }
 
+int forcev6 = 0;
 int verbosity = 1;
 int flagdelay = 1;
 int flagremoteinfo = 1;
 int flagremotehost = 1;
 unsigned long itimeout = 26;
 unsigned long ctimeout[2] = { 2, 58 };
+uint32 netif = 0;
 
-char iplocal[4] = { 0,0,0,0 };
+char iplocal[16] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
 uint16 portlocal = 0;
 char *forcelocal = 0;
 
-char ipremote[4];
+char ipremote[16];
 uint16 portremote;
 
 char *hostname;
@@ -61,12 +66,13 @@
 static stralloc tmp;
 static stralloc fqdn;
 char strnum[FMT_ULONG];
-char ipstr[IP4_FMT];
+char ipstr[IP6_FMT];
 
 char seed[128];
 
 main(int argc,char **argv)
 {
+  int fakev4=0;
   unsigned long u;
   int opt;
   char *x;
@@ -80,8 +86,10 @@
   close(7);
   sig_ignore(sig_pipe);
  
-  while ((opt = getopt(argc,argv,"dDvqQhHrRi:p:t:T:l:")) != opteof)
+  while ((opt = getopt(argc,argv,"46dDvqQhHrRi:p:t:T:l:I:")) != opteof)
     switch(opt) {
+      case '4': noipv6 = 1; break;
+      case '6': forcev6 = 1; break;
       case 'd': flagdelay = 1; break;
       case 'D': flagdelay = 0; break;
       case 'v': verbosity = 2; break;
@@ -97,7 +105,8 @@
 		if (optarg[j] == '+') ++j;
 		scan_ulong(optarg + j,&ctimeout[1]);
 		break;
-      case 'i': if (!ip4_scan(optarg,iplocal)) usage(); break;
+      case 'i': if (!scan_ip6(optarg,iplocal)) usage(); break;
+      case 'I': netif=socket_getifidx(optarg); break;
       case 'p': scan_ulong(optarg,&u); portlocal = u; break;
       default: usage();
     }
@@ -108,8 +117,8 @@
 
   hostname = *argv;
   if (!hostname) usage();
-  if (str_equal(hostname,"")) hostname = "127.0.0.1";
-  if (str_equal(hostname,"0")) hostname = "127.0.0.1";
+  if (!hostname[0] || str_equal(hostname,"0"))
+    hostname = (noipv6?"127.0.0.1":"::1");
 
   x = *++argv;
   if (!x) usage();
@@ -127,33 +136,36 @@
   if (!*++argv) usage();
 
   if (!stralloc_copys(&tmp,hostname)) nomem();
-  if (dns_ip4_qualify(&addresses,&fqdn,&tmp) == -1)
+  if (dns_ip6_qualify(&addresses,&fqdn,&tmp) == -1)
     strerr_die4sys(111,FATAL,"temporarily unable to figure out IP address for ",hostname,": ");
-  if (addresses.len < 4)
+  if (addresses.len < 16)
     strerr_die3x(111,FATAL,"no IP address for ",hostname);
 
-  if (addresses.len == 4) {
+  if (addresses.len == 16) {
     ctimeout[0] += ctimeout[1];
     ctimeout[1] = 0;
   }
 
   for (cloop = 0;cloop < 2;++cloop) {
     if (!stralloc_copys(&moreaddresses,"")) nomem();
-    for (j = 0;j + 4 <= addresses.len;j += 4) {
-      s = socket_tcp();
+    for (j = 0;j + 16 <= addresses.len;j += 4) {
+      s = socket_tcp6();
       if (s == -1)
         strerr_die2sys(111,FATAL,"unable to create socket: ");
-      if (socket_bind4(s,iplocal,portlocal) == -1)
+      if (socket_bind6(s,iplocal,portlocal,netif) == -1)
         strerr_die2sys(111,FATAL,"unable to bind socket: ");
-      if (timeoutconn(s,addresses.s + j,portremote,ctimeout[cloop]) == 0)
+      if (timeoutconn6(s,addresses.s + j,portremote,ctimeout[cloop],netif) == 0)
         goto CONNECTED;
       close(s);
       if (!cloop && ctimeout[1] && (errno == error_timeout)) {
-	if (!stralloc_catb(&moreaddresses,addresses.s + j,4)) nomem();
+	if (!stralloc_catb(&moreaddresses,addresses.s + j,16)) nomem();
       }
       else {
         strnum[fmt_ulong(strnum,portremote)] = 0;
-        ipstr[ip4_fmt(ipstr,addresses.s + j)] = 0;
+	if (ip6_isv4mapped(addresses.s+j))
+	  ipstr[ip4_fmt(ipstr,addresses.s + j + 12)] = 0;
+	else
+	  ipstr[ip6_fmt(ipstr,addresses.s + j)] = 0;
         strerr_warn5(CONNECT,ipstr," port ",strnum,": ",&strerr_sys);
       }
     }
@@ -169,37 +181,46 @@
   if (!flagdelay)
     socket_tcpnodelay(s); /* if it fails, bummer */
 
-  if (!pathexec_env("PROTO","TCP")) nomem();
-
-  if (socket_local4(s,iplocal,&portlocal) == -1)
+  if (socket_local6(s,iplocal,&portlocal,&netif) == -1)
     strerr_die2sys(111,FATAL,"unable to get local address: ");
 
+  if (!forcev6 && (ip6_isv4mapped(iplocal) || byte_equal(iplocal,16,V6any)))
+    fakev4=1;
+
+  if (!pathexec_env("PROTO",fakev4?"TCP":"TCP6")) nomem();
+
   strnum[fmt_ulong(strnum,portlocal)] = 0;
   if (!pathexec_env("TCPLOCALPORT",strnum)) nomem();
-  ipstr[ip4_fmt(ipstr,iplocal)] = 0;
+  if (fakev4)
+    ipstr[ip4_fmt(ipstr,iplocal+12)] = 0;
+  else
+    ipstr[ip6_fmt(ipstr,iplocal)] = 0;
   if (!pathexec_env("TCPLOCALIP",ipstr)) nomem();
 
   x = forcelocal;
   if (!x)
-    if (dns_name4(&tmp,iplocal) == 0) {
+    if (dns_name6(&tmp,iplocal) == 0) {
       if (!stralloc_0(&tmp)) nomem();
       x = tmp.s;
     }
   if (!pathexec_env("TCPLOCALHOST",x)) nomem();
 
-  if (socket_remote4(s,ipremote,&portremote) == -1)
+  if (socket_remote6(s,ipremote,&portremote,&netif) == -1)
     strerr_die2sys(111,FATAL,"unable to get remote address: ");
 
   strnum[fmt_ulong(strnum,portremote)] = 0;
   if (!pathexec_env("TCPREMOTEPORT",strnum)) nomem();
-  ipstr[ip4_fmt(ipstr,ipremote)] = 0;
+  if (fakev4)
+    ipstr[ip4_fmt(ipstr,ipremote+12)] = 0;
+  else
+    ipstr[ip6_fmt(ipstr,ipremote)] = 0;
   if (!pathexec_env("TCPREMOTEIP",ipstr)) nomem();
   if (verbosity >= 2)
     strerr_warn4("tcpclient: connected to ",ipstr," port ",strnum,0);
 
   x = 0;
   if (flagremotehost)
-    if (dns_name4(&tmp,ipremote) == 0) {
+    if (dns_name6(&tmp,ipremote) == 0) {
       if (!stralloc_0(&tmp)) nomem();
       x = tmp.s;
     }
@@ -207,7 +228,7 @@
 
   x = 0;
   if (flagremoteinfo)
-    if (remoteinfo(&tmp,ipremote,portremote,iplocal,portlocal,itimeout) == 0) {
+    if (remoteinfo6(&tmp,ipremote,portremote,iplocal,portlocal,itimeout,netif) == 0) {
       if (!stralloc_0(&tmp)) nomem();
       x = tmp.s;
     }
diff -uNr ucspi-tcp-0.88.orig/tcprules.1 ucspi-tcp-0.88/tcprules.1
--- ucspi-tcp-0.88.orig/tcprules.1	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/tcprules.1	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,221 @@
+.TH tcprules 1
+.SH NAME
+tcprules \- compile rules for tcpserver
+.SH SYNOPSIS
+.B tcprules
+.I rules.cdb
+.I rules.tmp
+.SH OVERVIEW
+.B tcpserver
+optionally follows rules to decide whether a TCP connection is acceptable.
+For example, a rule of
+
+.EX
+   18.23.0.32:deny
+.EE
+
+prohibits connections from IP address 18.23.0.32.
+
+.B tcprules
+reads rules from its standard input
+and writes them into
+.I rules.cdb
+in a binary format suited
+for quick access by
+.BR tcpserver .
+
+.B tcprules
+can be used while
+.B tcpserver
+is running:
+it ensures that
+.I rules.cdb
+is updated atomically.
+It does this by first writing the rules to
+.I rules.tmp
+and then moving
+.I rules.tmp
+on top of
+.IR rules.cdb .
+If
+.I rules.tmp
+already exists, it is destroyed.
+The directories containing
+.I rules.cdb
+and
+.I rules.tmp
+must be writable to
+.BR tcprules ;
+they must also be on the same filesystem.
+
+If there is a problem with the input,
+.B tcprules
+complains and leaves
+.I rules.cdb
+alone.
+
+The binary
+.I rules.cdb
+format is portable across machines.
+.SH "RULE FORMAT"
+A rule takes up one line.
+A file containing rules
+may also contain comments: lines beginning with # are ignored.
+
+Each rule contains an
+.BR address ,
+a colon,
+and a list of
+.BR instructions ,
+with no extra spaces.
+When
+.B tcpserver
+receives a connection from that address,
+it follows the instructions.
+.SH "ADDRESSES"
+.B tcpserver
+starts by looking for a rule with address
+.IR TCPREMOTEINFO\fB@\fITCPREMOTEIP .
+If it doesn't find one, or if
+.I TCPREMOTEINFO
+is not set, it tries the address
+.IR TCPREMOTEIP .
+If that doesn't work, it tries shorter and shorter prefixes of
+.I TCPREMOTEIP
+ending with a dot.
+If none of them work, it tries the empty string.
+
+For example, here are some rules:
+
+.EX
+   joe@127.0.0.1:first
+.br
+   18.23.0.32:second
+.br
+   127.:third
+.br
+   :fourth
+.br
+   ::1:fifth
+.EE
+
+If
+.I TCPREMOTEIP
+is
+.BR 10.119.75.38 ,
+.B tcpserver
+will follow the
+.B fourth
+instructions.
+
+If
+.I TCPREMOTEIP
+is
+.BR ::1 ,
+.B tcpserver
+will follow the
+.B fifth
+instructions.  Note that you cannot detect IPv4 mapped addresses by
+matching "::ffff", as those addresses will be converted to IPv4 before
+looking at the rules.
+
+If
+.I TCPREMOTEIP
+is
+.BR 18.23.0.32 ,
+.B tcpserver
+will follow the
+.B second
+instructions.
+
+If
+.I TCPREMOTEINFO
+is
+.B bill
+and
+.I TCPREMOTEIP
+is
+.BR 127.0.0.1 ,
+.B tcpserver
+will follow the
+.B third
+instructions.
+
+If
+.I TCPREMOTEINFO
+is
+.B joe
+and
+.I TCPREMOTEIP
+is
+.BR 127.0.0.1 ,
+.B tcpserver
+will follow the
+.B first
+instructions.
+.SH "ADDRESS RANGES"
+.B tcprules
+treats
+.B 1.2.3.37-53:ins
+as an abbreviation
+for the rules
+.BR 1.2.3.37:ins ,
+.BR 1.2.3.38:ins ,
+and so on up through
+.BR 1.2.3.53:ins .
+Similarly,
+.BR 10.2-3.:ins
+is an abbreviation for
+.B 10.2.:ins
+and
+.BR 10.3.:ins .
+.SH "INSTRUCTIONS"
+The instructions in a rule must begin with either
+.B allow
+or
+.BR deny .
+.B deny
+tells
+.B tcpserver
+to drop the connection without running anything.
+For example, the rule
+
+.EX
+   :deny
+.EE
+
+tells
+.B tcpserver
+to drop all connections that aren't handled by more specific rules.
+
+The instructions may continue with some environment variables,
+in the format
+.IR ,VAR="VALUE" .
+.B tcpserver
+adds
+.I VAR=VALUE
+to the current environment.
+For example,
+
+.EX
+   10.0.:allow,RELAYCLIENT="@fix.me"
+.EE
+
+adds
+.B RELAYCLIENT=@fix.me
+to the environment.
+The quotes here may be replaced by any repeated character:
+
+.EX
+   10.0.:allow,RELAYCLIENT=/@fix.me/
+.EE
+
+Any number of variables may be listed:
+
+.EX
+   127.0.0.1:allow,RELAYCLIENT="",TCPLOCALHOST="movie.edu"
+.EE
+.SH "SEE ALSO"
+tcprulescheck(1),
+tcpserver(1),
+tcp-environ(5)
diff -uNr ucspi-tcp-0.88.orig/tcprules.c ucspi-tcp-0.88/tcprules.c
--- ucspi-tcp-0.88.orig/tcprules.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/tcprules.c	2009-08-04 17:45:59.000000000 -0500
@@ -123,8 +123,15 @@
     }
     line.len = len; /* for die_bad() */
 
-    colon = byte_chr(x,len,':');
-    if (colon == len) continue;
+    colon = 0;
+    for (;;) {
+      int tmp;
+      tmp = byte_chr(x + colon,len - colon,':');
+      colon += tmp;
+      if (colon == len) continue;
+      if (byte_equal(x+colon+1,4,"deny") || byte_equal(x+colon+1,5,"allow")) break;
+      ++colon;
+    }
 
     if (!stralloc_copyb(&address,x,colon)) nomem();
     if (!stralloc_copys(&data,"")) nomem();
diff -uNr ucspi-tcp-0.88.orig/tcprulescheck.1 ucspi-tcp-0.88/tcprulescheck.1
--- ucspi-tcp-0.88.orig/tcprulescheck.1	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/tcprulescheck.1	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,25 @@
+.TH tcprulescheck 1
+.SH NAME
+tcprulescheck \- try out rules for tcpserver
+.SH SYNTAX
+.B tcprulescheck
+.I rules.cdb
+.I tcpremoteip
+[
+.I tcpremoteinfo
+]
+.SH DESCRIPTION
+.B tcprulescheck
+says what
+.B tcpserver
+will do with a connection from
+IP address
+.IR tcpremoteip ,
+following the rules compiled into
+.I rules.cdb
+by
+.BR tcprules .
+.SH "SEE ALSO"
+tcprules(1),
+tcpserver(1),
+tcp-environ(5)
diff -uNr ucspi-tcp-0.88.orig/tcpserver.1 ucspi-tcp-0.88/tcpserver.1
--- ucspi-tcp-0.88.orig/tcpserver.1	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/tcpserver.1	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,266 @@
+.TH tcpserver 1
+.SH NAME
+tcpserver \- accept incoming TCP connections
+.SH SYNOPSIS
+.B tcpserver
+[
+.B \-146jpPhHrRoOdDqQv
+]
+[
+.B \-c\fIlimit
+]
+[
+.B \-x\fIrules.cdb
+]
+[
+.B \-B\fIbanner
+]
+[
+.B \-g\fIgid
+]
+[
+.B \-u\fIuid
+]
+[
+.B \-b\fIbacklog
+]
+[
+.B \-l\fIlocalname
+]
+[
+.B \-t\fItimeout
+]
+[
+.B \-I\fIinterface
+]
+.I host
+.I port
+.I program
+[
+.I arg ...
+]
+.SH DESCRIPTION
+.B tcpserver
+waits for connections from TCP clients.
+For each connection, it runs
+.I program
+with the given arguments,
+with descriptor 0 reading from the network
+and descriptor 1 writing to the network.
+
+The server's address is given by
+.I host
+and
+.IR port .
+.I host
+can be 0, allowing connections from any host;
+or a particular IP address,
+allowing connections only to that address;
+or a host name, allowing connections to the first IP address
+for that host.
+.I port
+may be a numeric port number
+or a port name.
+If
+.I port
+is 0,
+.B tcpserver
+will choose a free port.
+
+.B tcpserver
+sets up several environment variables,
+as described in
+.B tcp-environ(5).
+
+.B tcpserver
+exits when it receives SIGTERM.
+.SH "OPTIONS"
+.TP
+.B \-c\fIlimit
+Do not handle more than
+.I limit
+simultaneous connections.
+If there are
+.I limit
+simultaneous copies of
+.I program
+running, defer acceptance of a new connection
+until one copy finishes.
+.I limit
+must be a positive integer.
+Default: 40.
+.TP
+.B \-x\fIrules.cdb
+Follow the rules compiled into
+.I rules.cdb
+by
+.BR tcprules .
+These rules may specify setting environment variables
+or rejecting connections from bad sources.
+
+.B tcpserver
+does not read
+.I rules.cdb
+into memory;
+you can rerun
+.B tcprules
+to change
+.BR tcpserver 's
+behavior on the fly.
+.TP
+.B \-B\fIbanner
+Write
+.I banner
+to the network immediately after each connection is made.
+.B tcpserver
+writes
+.I banner
+before looking up
+.BR TCPREMOTEHOST ,
+before looking up
+.BR TCPREMOTEINFO ,
+and before checking
+.IR rules.cdb .
+
+This feature can be used to reduce latency in protocols
+where the client waits for a greeting from the server.
+.TP
+.B \-g\fIgid
+Switch group ID to
+.I gid
+after preparing to receive connections.
+.I gid
+must be a positive integer.
+.TP
+.B \-u\fIuid
+Switch user ID to 
+.I uid
+after preparing to receive connections.
+.I uid
+must be a positive integer.
+.TP
+.B \-1
+After preparing to receive connections,
+print the local port number to standard output.
+.TP
+.B \-4
+Fall back to IPv4 sockets.  This is necessary for terminally broken
+systems like OpenBSD which will not let IPv6 sockets connect to
+V4-mapped IPv6 addresses.  Please note that this also applies to DNS
+lookups, so you will have to use an DNS resolver with an IPv6 address to
+accept IPv6 connections.  Use \fBDNSCACHEIP\fR to set the DNS resolver
+IP dynamically.
+.TP
+.B \-6
+Force IPv6 mode in UCSPI environment variables, even for
+IPv4 connections.  This will set \fB$PROTO\fR to \fBTCP6\fR and put
+IPv4-mapped IPv6 addresses in \fBTCPLOCALIP\fR and \fBTCPREMOTEIP\fR.
+.TP
+.B \-I\fIinterface
+Bind to the network interface
+.I interface
+("eth0" on Linux, for example).  This is only defined and needed for
+IPv6 link-local addresses.
+.TP
+.B \-b\fIbacklog
+Allow up to
+.I backlog
+simultaneous SYN_RECEIVEDs.
+Default: 20.
+On some systems,
+.I backlog
+is silently limited to 5.
+See
+.BR listen (2)
+for more details.
+.TP
+.B \-o
+Leave IP options alone.
+If the client is sending packets along an IP source route,
+send packets back along the same route.
+.TP
+.B \-O
+(Default.)
+Kill IP options.
+A client can still use source routing to connect and to send data,
+but packets will be sent back along the default route.
+.TP
+.B \-d
+(Default.)
+Delay sending data for a fraction of a second whenever the
+remote host is responding slowly,
+to make better use of the network.
+.TP
+.B \-D
+Never delay sending data;
+enable TCP_NODELAY.
+This is appropriate for interactive connections.
+.TP
+.B \-q
+Quiet.
+Do not print any messages.
+.TP
+.B \-Q
+(Default.)
+Print error messages.
+.TP
+.B \-v
+Verbose.
+Print all available messages.
+.SH "DATA-GATHERING OPTIONS"
+.TP
+.B \-p
+Paranoid.
+After looking up the remote host name,
+look up the IP addresses for that name,
+and make sure one of them matches
+.BR TCPREMOTEIP .
+If none of them do,
+unset
+.BR TCPREMOTEHOST .
+.TP
+.B \-P
+(Default.)
+Not paranoid.
+.TP
+.B \-h
+(Default.)
+Look up the remote host name and set
+.BR TCPREMOTEHOST .
+.TP
+.B \-H
+Do not look up the remote host name.
+.TP
+.B \-l\fIlocalname
+Do not look up the local host name;
+use
+.I localname
+for
+.BR TCPLOCALHOST .
+.TP
+.B \-r
+(Default.)
+Attempt to obtain
+.B TCPREMOTEINFO
+from the remote host.
+.TP
+.B \-R
+Do not attempt to obtain
+.B TCPREMOTEINFO
+from the remote host.
+.TP
+.B \-t\fItimeout
+Give up on the 
+.B TCPREMOTEINFO
+connection attempt
+after
+.I timeout
+seconds. Default: 26.
+.SH "SEE ALSO"
+argv0(1),
+fixcr(1),
+recordio(1),
+tcpclient(1),
+tcprules(1),
+listen(2),
+tcp-environ(5)
diff -uNr ucspi-tcp-0.88.orig/tcpserver.c ucspi-tcp-0.88/tcpserver.c
--- ucspi-tcp-0.88.orig/tcpserver.c	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/tcpserver.c	2009-08-04 17:45:59.000000000 -0500
@@ -7,6 +7,7 @@
 #include "fmt.h"
 #include "scan.h"
 #include "ip4.h"
+#include "ip6.h"
 #include "fd.h"
 #include "exit.h"
 #include "env.h"
@@ -28,6 +29,7 @@
 #include "sig.h"
 #include "dns.h"
 
+int forcev6 = 0;
 int verbosity = 1;
 int flagkillopts = 1;
 int flagdelay = 1;
@@ -36,20 +38,21 @@
 int flagremotehost = 1;
 int flagparanoid = 0;
 unsigned long timeout = 26;
+uint32 netif = 0;
 
 static stralloc tcpremoteinfo;
 
 uint16 localport;
 char localportstr[FMT_ULONG];
-char localip[4];
-char localipstr[IP4_FMT];
+char localip[16];
+char localipstr[IP6_FMT];
 static stralloc localhostsa;
 char *localhost = 0;
 
 uint16 remoteport;
 char remoteportstr[FMT_ULONG];
-char remoteip[4];
-char remoteipstr[IP4_FMT];
+char remoteip[16];
+char remoteipstr[IP6_FMT];
 static stralloc remotehostsa;
 char *remotehost = 0;
 
@@ -96,12 +99,12 @@
     if (ch < 33) ch = '?';
     if (ch > 126) ch = '?';
     if (ch == '%') ch = '?'; /* logger stupidity */
-    if (ch == ':') ch = '?';
+/*    if (ch == ':') ch = '?'; */
     append(&ch);
   }
   cats("...");
 }
-void env(char *s,char *t)
+void env(const char *s,const char *t)
 {
   if (!pathexec_env(s,t)) drop_nomem();
 }
@@ -135,9 +138,16 @@
 
 void doit(int t)
 {
+  int fakev4=0;
   int j;
+  uint32 scope_id;
 
-  remoteipstr[ip4_fmt(remoteipstr,remoteip)] = 0;
+  if (!forcev6 && ip6_isv4mapped(remoteip))
+    fakev4=1;
+  if (fakev4)
+    remoteipstr[ip4_fmt(remoteipstr,remoteip+12)] = 0;
+  else
+    remoteipstr[ip6_fmt(remoteipstr,remoteip)] = 0;
 
   if (verbosity >= 2) {
     strnum[fmt_ulong(strnum,getpid())] = 0;
@@ -155,30 +165,40 @@
       strerr_die2sys(111,DROP,"unable to print banner: ");
   }
 
-  if (socket_local4(t,localip,&localport) == -1)
+  if (socket_local6(t,localip,&localport,&scope_id) == -1)
     strerr_die2sys(111,DROP,"unable to get local address: ");
 
-  localipstr[ip4_fmt(localipstr,localip)] = 0;
+  if (fakev4)
+    localipstr[ip4_fmt(localipstr,localip+12)] = 0;
+  else
+    localipstr[ip6_fmt(localipstr,localip)] = 0;
   remoteportstr[fmt_ulong(remoteportstr,remoteport)] = 0;
 
   if (!localhost)
-    if (dns_name4(&localhostsa,localip) == 0)
+    if (dns_name6(&localhostsa,localip) == 0)
       if (localhostsa.len) {
 	if (!stralloc_0(&localhostsa)) drop_nomem();
 	localhost = localhostsa.s;
       }
-  env("PROTO","TCP");
+  env("PROTO",fakev4?"TCP":"TCP6");
   env("TCPLOCALIP",localipstr);
+  localipstr[ip6_fmt(localipstr,localip)]=0;
+  env("TCP6LOCALIP",localipstr);
+
   env("TCPLOCALPORT",localportstr);
+  env("TCP6LOCALPORT",localportstr);
   env("TCPLOCALHOST",localhost);
+  env("TCP6LOCALHOST",localhost);
+  if (!fakev4 && scope_id)
+    env("TCP6INTERFACE",socket_getifname(scope_id));
 
   if (flagremotehost)
-    if (dns_name4(&remotehostsa,remoteip) == 0)
+    if (dns_name6(&remotehostsa,remoteip) == 0)
       if (remotehostsa.len) {
 	if (flagparanoid)
-	  if (dns_ip4(&tmp,&remotehostsa) == 0)
-	    for (j = 0;j + 4 <= tmp.len;j += 4)
-	      if (byte_equal(remoteip,4,tmp.s + j)) {
+	  if (dns_ip6(&tmp,&remotehostsa) == 0)
+	    for (j = 0;j + 16 <= tmp.len;j += 16)
+	      if (byte_equal(remoteip,16,tmp.s + j)) {
 		flagparanoid = 0;
 		break;
 	      }
@@ -188,15 +208,20 @@
 	}
       }
   env("TCPREMOTEIP",remoteipstr);
+  remoteipstr[ip6_fmt(remoteipstr,remoteip)]=0;
+  env("TCP6REMOTEIP",remoteipstr);
   env("TCPREMOTEPORT",remoteportstr);
+  env("TCP6REMOTEPORT",remoteportstr);
   env("TCPREMOTEHOST",remotehost);
+  env("TCP6REMOTEHOST",remotehost);
 
   if (flagremoteinfo) {
-    if (remoteinfo(&tcpremoteinfo,remoteip,remoteport,localip,localport,timeout) == -1)
+    if (remoteinfo6(&tcpremoteinfo,remoteip,remoteport,localip,localport,timeout,netif) == -1)
       flagremoteinfo = 0;
     if (!stralloc_0(&tcpremoteinfo)) drop_nomem();
   }
   env("TCPREMOTEINFO",flagremoteinfo ? tcpremoteinfo.s : 0);
+  env("TCP6REMOTEINFO",flagremoteinfo ? tcpremoteinfo.s : 0);
 
   if (fnrules) {
     int fdrules;
@@ -206,7 +231,15 @@
       if (!flagallownorules) drop_rules();
     }
     else {
-      if (rules(found,fdrules,remoteipstr,remotehost,flagremoteinfo ? tcpremoteinfo.s : 0) == -1) drop_rules();
+      int fakev4=0;
+      char* temp;
+      if (!forcev6 && ip6_isv4mapped(remoteip))
+	fakev4=1;
+      if (fakev4)
+	temp=remoteipstr+7;
+      else
+	temp=remoteipstr;
+      if (rules(found,fdrules,temp,remotehost,flagremoteinfo ? tcpremoteinfo.s : 0) == -1) drop_rules();
       close(fdrules);
     }
   }
@@ -240,7 +273,7 @@
 {
   strerr_warn1("\
 tcpserver: usage: tcpserver \
-[ -1UXpPhHrRoOdDqQv ] \
+[ -461UXpPhHrRoOdDqQv ] \
 [ -c limit ] \
 [ -x rules.cdb ] \
 [ -B banner ] \
@@ -249,6 +282,7 @@
 [ -b backlog ] \
 [ -l localname ] \
 [ -t timeout ] \
+[ -I interface ] \
 host port program",0);
   _exit(100);
 }
@@ -299,8 +333,8 @@
   unsigned long u;
   int s;
   int t;
- 
-  while ((opt = getopt(argc,argv,"dDvqQhHrR1UXx:t:u:g:l:b:B:c:pPoO")) != opteof)
+
+  while ((opt = getopt(argc,argv,"46dDvqQhHrR1UXx:t:u:g:l:b:B:c:I:pPoO")) != opteof)
     switch(opt) {
       case 'b': scan_ulong(optarg,&backlog); break;
       case 'c': scan_ulong(optarg,&limit); break;
@@ -325,7 +359,10 @@
 		x = env_get("GID"); if (x) scan_ulong(x,&gid); break;
       case 'u': scan_ulong(optarg,&uid); break;
       case 'g': scan_ulong(optarg,&gid); break;
+      case 'I': netif=socket_getifidx(optarg); break;
       case '1': flag1 = 1; break;
+      case '4': noipv6 = 1; break;
+      case '6': forcev6 = 1; break;
       case 'l': localhost = optarg; break;
       default: usage();
     }
@@ -337,8 +374,7 @@
  
   hostname = *argv++;
   if (!hostname) usage();
-  if (str_equal(hostname,"")) hostname = "0.0.0.0";
-  if (str_equal(hostname,"0")) hostname = "0.0.0.0";
+  if (str_equal(hostname,"")) hostname = "0";
 
   x = *argv++;
   if (!x) usage();
@@ -348,7 +384,7 @@
     se = getservbyname(x,"tcp");
     if (!se)
       strerr_die3x(111,FATAL,"unable to figure out port number for ",x);
-    localport = ntohs(se->s_port);
+    uint16_unpack_big((char*)&se->s_port,&localport);
   }
 
   if (!*argv) usage();
@@ -358,20 +394,26 @@
   sig_catch(sig_term,sigterm);
   sig_ignore(sig_pipe);
  
-  if (!stralloc_copys(&tmp,hostname))
-    strerr_die2x(111,FATAL,"out of memory");
-  if (dns_ip4_qualify(&addresses,&fqdn,&tmp) == -1)
-    strerr_die4sys(111,FATAL,"temporarily unable to figure out IP address for ",hostname,": ");
-  if (addresses.len < 4)
-    strerr_die3x(111,FATAL,"no IP address for ",hostname);
-  byte_copy(localip,4,addresses.s);
+  if (str_equal(hostname,"0")) {
+    byte_zero(localip,sizeof localip);
+  } else {
+    if (!stralloc_copys(&tmp,hostname))
+      strerr_die2x(111,FATAL,"out of memory");
+    if (dns_ip6_qualify(&addresses,&fqdn,&tmp) == -1)
+      strerr_die4sys(111,FATAL,"temporarily unable to figure out IP address for ",hostname,": ");
+    if (addresses.len < 16)
+      strerr_die3x(111,FATAL,"no IP address for ",hostname);
+    byte_copy(localip,16,addresses.s);
+    if (ip6_isv4mapped(localip))
+      noipv6=1;
+  }
 
-  s = socket_tcp();
+  s = socket_tcp6();
   if (s == -1)
     strerr_die2sys(111,FATAL,"unable to create socket: ");
-  if (socket_bind4_reuse(s,localip,localport) == -1)
+  if (socket_bind6_reuse(s,localip,localport,netif) == -1)
     strerr_die2sys(111,FATAL,"unable to bind: ");
-  if (socket_local4(s,localip,&localport) == -1)
+  if (socket_local6(s,localip,&localport,&netif) == -1)
     strerr_die2sys(111,FATAL,"unable to get local address: ");
   if (socket_listen(s,backlog) == -1)
     strerr_die2sys(111,FATAL,"unable to listen: ");
@@ -399,7 +441,7 @@
     while (numchildren >= limit) sig_pause();
 
     sig_unblock(sig_child);
-    t = socket_accept4(s,remoteip,&remoteport);
+    t = socket_accept6(s,remoteip,&remoteport,&netif);
     sig_block(sig_child);
 
     if (t == -1) continue;
diff -uNr ucspi-tcp-0.88.orig/timeoutconn.h ucspi-tcp-0.88/timeoutconn.h
--- ucspi-tcp-0.88.orig/timeoutconn.h	2009-08-04 15:19:16.000000000 -0500
+++ ucspi-tcp-0.88/timeoutconn.h	2009-08-04 17:45:59.000000000 -0500
@@ -2,7 +2,9 @@
 #define TIMEOUTCONN_H
 
 #include "uint16.h"
+#include "uint32.h"
 
 extern int timeoutconn(int,char *,uint16,unsigned int);
+extern int timeoutconn6(int,char *,uint16,unsigned int,uint32);
 
 #endif
diff -uNr ucspi-tcp-0.88.orig/timeoutconn6.c ucspi-tcp-0.88/timeoutconn6.c
--- ucspi-tcp-0.88.orig/timeoutconn6.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/timeoutconn6.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,34 @@
+#include "ndelay.h"
+#include "socket.h"
+#include "iopause.h"
+#include "error.h"
+#include "timeoutconn.h"
+
+int timeoutconn6(int s,char ip[16],uint16 port,unsigned int timeout,uint32 netif)
+{
+  struct taia now;
+  struct taia deadline;
+  iopause_fd x;
+
+  if (socket_connect6(s,ip,port,netif) == -1) {
+    if ((errno != error_wouldblock) && (errno != error_inprogress)) return -1;
+    x.fd = s;
+    x.events = IOPAUSE_WRITE;
+    taia_now(&now);
+    taia_uint(&deadline,timeout);
+    taia_add(&deadline,&now,&deadline);
+    for (;;) {
+      taia_now(&now);
+      iopause(&x,1,&deadline,&now);
+      if (x.revents) break;
+      if (taia_less(&deadline,&now)) {
+	errno = error_timeout; /* note that connect attempt is continuing */
+	return -1;
+      }
+    }
+    if (!socket_connected(s)) return -1;
+  }
+
+  if (ndelay_off(s) == -1) return -1;
+  return 0;
+}
diff -uNr ucspi-tcp-0.88.orig/tryip6.c ucspi-tcp-0.88/tryip6.c
--- ucspi-tcp-0.88.orig/tryip6.c	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/tryip6.c	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,8 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+main() {
+  struct sockaddr_in6 sa;
+  sa.sin6_family = PF_INET6;
+}
diff -uNr ucspi-tcp-0.88.orig/usr/local/man/man1/tcpclient.1 ucspi-tcp-0.88/usr/local/man/man1/tcpclient.1
--- ucspi-tcp-0.88.orig/usr/local/man/man1/tcpclient.1	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/usr/local/man/man1/tcpclient.1	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,173 @@
+.TH tcpclient 1
+.SH NAME
+tcpclient \- create an outgoing TCP connection
+.SH SYNOPSIS
+.B tcpclient
+[
+.B \-46hHrRdDqQv
+]
+[
+.B \-i\fIlocalip
+]
+[
+.B \-p\fIlocalport
+]
+[
+.B \-T\fItimeoutconn
+]
+[
+.B \-l\fIlocalname
+]
+[
+.B \-t\fItimeoutinfo
+]
+[
+.B \-I\fIinterface
+]
+.I host
+.I port
+.I program
+[
+.I arg ...
+]
+.SH DESCRIPTION
+.B tcpclient
+attempts to connect to a TCP server.
+If it is successful, it runs
+.I program
+with the given arguments,
+with descriptor 6 reading from the network
+and descriptor 7 writing to the network.
+
+The server's address is given by
+.I host
+and
+.IR port .
+.I host
+may be 0, referring to the local machine,
+or a dotted-decimal IP address,
+or a host name;
+if a host has several IP addresses,
+.B tcpclient
+tries each in turn.
+.I port
+may be a numeric port number
+or a port name.
+
+.B tcpclient
+sets up several environment variables,
+as described in
+.B tcp-environ(5).
+.SH OPTIONS
+.TP
+.B \-i\fIlocalip
+Use
+.I localip
+as the IP address for the local side of the connection;
+quit if
+.I localip
+is not available.
+.TP
+.B \-p\fIlocalport
+Use
+.I localport
+as the port number for the local side of the connection;
+quit if
+.I localport
+is not available.
+.TP
+.B \-I\fIinterface
+Use
+.I interface
+as the local network interface.  This is only defined for IPv6 sockets
+and needed if you use link-local IPv6 addresses.
+.TP
+.B \-T\fItimeoutconn
+Give up on the
+connection attempt
+after
+.I timeoutconn
+seconds. Default: 60.
+This timeout applies to each IP address tried.
+.TP
+.B \-d
+(Default.)
+Delay sending data for a fraction of a second whenever the
+remote host is responding slowly,
+to make better use of the network.
+.TP
+.B \-D
+Never delay sending data;
+enable TCP_NODELAY.
+This is appropriate for interactive connections.
+.TP
+.B \-q
+Quiet.
+Do not print any messages.
+.TP
+.B \-Q
+(Default.)
+Print error messages.
+.TP
+.B \-v
+Verbose.
+Print all available messages.
+.SH "DATA-GATHERING OPTIONS"
+.TP
+.B \-h
+(Default.)
+Look up the remote host name for
+.BR TCPREMOTEHOST .
+.TP
+.B \-H
+Do not look up the remote host name;
+unset
+.BR TCPREMOTEHOST .
+.TP
+.B \-l\fIlocalname
+Do not look up the local host name;
+use
+.I localname
+for
+.BR TCPLOCALHOST .
+.TP
+.B \-r
+(Default.)
+Attempt to obtain
+.B TCPREMOTEINFO
+from the remote host.
+.TP
+.B \-R
+Do not attempt to obtain
+.B TCPREMOTEINFO
+from the remote host.
+.TP
+.B \-t\fItimeoutinfo
+Give up on the 
+.B TCPREMOTEINFO
+connection attempt
+after
+.I timeoutinfo
+seconds. Default: 26.
+.TP
+.B \-4
+Fall back to IPv4 sockets.  This is necessary for terminally broken
+systems like OpenBSD which will not let IPv6 sockets connect to
+V4-mapped IPv6 addresses.  Please note that this also applies to DNS
+lookups, so you will have to use an DNS resolver with an IPv6 address to
+connect to IPv6 systems.  Use \fBDNSCACHEIP\fR to set the DNS resolver
+IP dynamically.
+.TP
+.B \-6
+Force IPv6 mode in UCSPI environment variables, even for
+IPv4 connections.  This will set \fB$PROTO\fR to \fBTCP6\fR and put
+IPv4-mapped IPv6 addresses in \fBTCPLOCALIP\fR and \fBTCPREMOTEIP\fR.
+.SH "SEE ALSO"
+date@(1),
+finger@(1),
+http@(1),
+mconnect(1),
+tcpcat(1),
+tcpserver(1),
+who@(1),
+tcp-environ(5)
diff -uNr ucspi-tcp-0.88.orig/usr/local/man/man1/tcpserver.1 ucspi-tcp-0.88/usr/local/man/man1/tcpserver.1
--- ucspi-tcp-0.88.orig/usr/local/man/man1/tcpserver.1	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/usr/local/man/man1/tcpserver.1	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,266 @@
+.TH tcpserver 1
+.SH NAME
+tcpserver \- accept incoming TCP connections
+.SH SYNOPSIS
+.B tcpserver
+[
+.B \-146jpPhHrRoOdDqQv
+]
+[
+.B \-c\fIlimit
+]
+[
+.B \-x\fIrules.cdb
+]
+[
+.B \-B\fIbanner
+]
+[
+.B \-g\fIgid
+]
+[
+.B \-u\fIuid
+]
+[
+.B \-b\fIbacklog
+]
+[
+.B \-l\fIlocalname
+]
+[
+.B \-t\fItimeout
+]
+[
+.B \-I\fIinterface
+]
+.I host
+.I port
+.I program
+[
+.I arg ...
+]
+.SH DESCRIPTION
+.B tcpserver
+waits for connections from TCP clients.
+For each connection, it runs
+.I program
+with the given arguments,
+with descriptor 0 reading from the network
+and descriptor 1 writing to the network.
+
+The server's address is given by
+.I host
+and
+.IR port .
+.I host
+can be 0, allowing connections from any host;
+or a particular IP address,
+allowing connections only to that address;
+or a host name, allowing connections to the first IP address
+for that host.
+.I port
+may be a numeric port number
+or a port name.
+If
+.I port
+is 0,
+.B tcpserver
+will choose a free port.
+
+.B tcpserver
+sets up several environment variables,
+as described in
+.B tcp-environ(5).
+
+.B tcpserver
+exits when it receives SIGTERM.
+.SH "OPTIONS"
+.TP
+.B \-c\fIlimit
+Do not handle more than
+.I limit
+simultaneous connections.
+If there are
+.I limit
+simultaneous copies of
+.I program
+running, defer acceptance of a new connection
+until one copy finishes.
+.I limit
+must be a positive integer.
+Default: 40.
+.TP
+.B \-x\fIrules.cdb
+Follow the rules compiled into
+.I rules.cdb
+by
+.BR tcprules .
+These rules may specify setting environment variables
+or rejecting connections from bad sources.
+
+.B tcpserver
+does not read
+.I rules.cdb
+into memory;
+you can rerun
+.B tcprules
+to change
+.BR tcpserver 's
+behavior on the fly.
+.TP
+.B \-B\fIbanner
+Write
+.I banner
+to the network immediately after each connection is made.
+.B tcpserver
+writes
+.I banner
+before looking up
+.BR TCPREMOTEHOST ,
+before looking up
+.BR TCPREMOTEINFO ,
+and before checking
+.IR rules.cdb .
+
+This feature can be used to reduce latency in protocols
+where the client waits for a greeting from the server.
+.TP
+.B \-g\fIgid
+Switch group ID to
+.I gid
+after preparing to receive connections.
+.I gid
+must be a positive integer.
+.TP
+.B \-u\fIuid
+Switch user ID to 
+.I uid
+after preparing to receive connections.
+.I uid
+must be a positive integer.
+.TP
+.B \-1
+After preparing to receive connections,
+print the local port number to standard output.
+.TP
+.B \-4
+Fall back to IPv4 sockets.  This is necessary for terminally broken
+systems like OpenBSD which will not let IPv6 sockets connect to
+V4-mapped IPv6 addresses.  Please note that this also applies to DNS
+lookups, so you will have to use an DNS resolver with an IPv6 address to
+accept IPv6 connections.  Use \fBDNSCACHEIP\fR to set the DNS resolver
+IP dynamically.
+.TP
+.B \-6
+Force IPv6 mode in UCSPI environment variables, even for
+IPv4 connections.  This will set \fB$PROTO\fR to \fBTCP6\fR and put
+IPv4-mapped IPv6 addresses in \fBTCPLOCALIP\fR and \fBTCPREMOTEIP\fR.
+.TP
+.B \-I\fIinterface
+Bind to the network interface
+.I interface
+("eth0" on Linux, for example).  This is only defined and needed for
+IPv6 link-local addresses.
+.TP
+.B \-b\fIbacklog
+Allow up to
+.I backlog
+simultaneous SYN_RECEIVEDs.
+Default: 20.
+On some systems,
+.I backlog
+is silently limited to 5.
+See
+.BR listen (2)
+for more details.
+.TP
+.B \-o
+Leave IP options alone.
+If the client is sending packets along an IP source route,
+send packets back along the same route.
+.TP
+.B \-O
+(Default.)
+Kill IP options.
+A client can still use source routing to connect and to send data,
+but packets will be sent back along the default route.
+.TP
+.B \-d
+(Default.)
+Delay sending data for a fraction of a second whenever the
+remote host is responding slowly,
+to make better use of the network.
+.TP
+.B \-D
+Never delay sending data;
+enable TCP_NODELAY.
+This is appropriate for interactive connections.
+.TP
+.B \-q
+Quiet.
+Do not print any messages.
+.TP
+.B \-Q
+(Default.)
+Print error messages.
+.TP
+.B \-v
+Verbose.
+Print all available messages.
+.SH "DATA-GATHERING OPTIONS"
+.TP
+.B \-p
+Paranoid.
+After looking up the remote host name,
+look up the IP addresses for that name,
+and make sure one of them matches
+.BR TCPREMOTEIP .
+If none of them do,
+unset
+.BR TCPREMOTEHOST .
+.TP
+.B \-P
+(Default.)
+Not paranoid.
+.TP
+.B \-h
+(Default.)
+Look up the remote host name and set
+.BR TCPREMOTEHOST .
+.TP
+.B \-H
+Do not look up the remote host name.
+.TP
+.B \-l\fIlocalname
+Do not look up the local host name;
+use
+.I localname
+for
+.BR TCPLOCALHOST .
+.TP
+.B \-r
+(Default.)
+Attempt to obtain
+.B TCPREMOTEINFO
+from the remote host.
+.TP
+.B \-R
+Do not attempt to obtain
+.B TCPREMOTEINFO
+from the remote host.
+.TP
+.B \-t\fItimeout
+Give up on the 
+.B TCPREMOTEINFO
+connection attempt
+after
+.I timeout
+seconds. Default: 26.
+.SH "SEE ALSO"
+argv0(1),
+fixcr(1),
+recordio(1),
+tcpclient(1),
+tcprules(1),
+listen(2),
+tcp-environ(5)
diff -uNr ucspi-tcp-0.88.orig/who@.1 ucspi-tcp-0.88/who@.1
--- ucspi-tcp-0.88.orig/who@.1	1969-12-31 18:00:00.000000000 -0600
+++ ucspi-tcp-0.88/who@.1	2009-08-04 17:45:59.000000000 -0500
@@ -0,0 +1,32 @@
+.TH who@ 1
+.SH NAME
+who@ \- print list of active users on a host
+.SH SYNTAX
+.B who@
+[
+.I host
+]
+.SH DESCRIPTION
+.B who@
+connects to TCP port 11 (Systat) on
+.I host
+and prints any data it receives.
+It removes CR and converts unprintable characters to a visible format.
+
+If
+.I host
+is not supplied,
+.B who@
+connects to the local host.
+
+Some computers respond to port 11 with a list of active users.
+For example, they may be running
+
+.EX
+     tcpserver 0 11 who &
+.EE
+.SH "SEE ALSO"
+cat(1),
+delcr(1),
+tcpclient(1),
+tcpserver(1)
