diff --git a/srcpkgs/ucspi-tcp/patches/0001-rblsmtpd.c-don-t-use-a-the-default-rbl.maps.vix.com.diff b/srcpkgs/ucspi-tcp/patches/0001-rblsmtpd.c-don-t-use-a-the-default-rbl.maps.vix.com.diff new file mode 100644 index 00000000000..d984f183849 --- /dev/null +++ b/srcpkgs/ucspi-tcp/patches/0001-rblsmtpd.c-don-t-use-a-the-default-rbl.maps.vix.com.diff @@ -0,0 +1,43 @@ +From d0327e3325837b4aa22198ac090530a3060952ef Mon Sep 17 00:00:00 2001 +From: Gerrit Pape +Date: Wed, 9 Jan 2008 07:16:42 +0000 +Subject: [PATCH] rblsmtpd.c: don't use a the default rbl.maps.vix.com if run without -r switch. + +rbl.maps.vix.com is obsolete, so don't use it as default rbl if run +without the -r switch; default to no rbl instead. +--- + rblsmtpd.c | 4 +--- + 1 files changed, 1 insertions(+), 3 deletions(-) + +diff --git a/rblsmtpd.c b/rblsmtpd.c +index cc8ba2e..9695d9e 100644 +--- a/rblsmtpd.c ++++ b/rblsmtpd.c +@@ -155,7 +155,6 @@ void rblsmtpd(void) + + main(int argc,char **argv,char **envp) + { +- int flagwantdefaultrbl = 1; + char *x; + int opt; + +@@ -182,7 +181,7 @@ main(int argc,char **argv,char **envp) + case 'c': flagfailclosed = 1; break; + case 'C': flagfailclosed = 0; break; + case 't': scan_ulong(optarg,&timeout); break; +- case 'r': rbl(optarg); flagwantdefaultrbl = 0; break; ++ case 'r': rbl(optarg); break; + case 'a': antirbl(optarg); break; + default: usage(); + } +@@ -190,7 +189,6 @@ main(int argc,char **argv,char **envp) + argv += optind; + if (!*argv) usage(); + +- if (flagwantdefaultrbl) rbl("rbl.maps.vix.com"); + if (decision >= 2) rblsmtpd(); + + pathexec_run(*argv,argv,envp); +-- +1.6.0.3 + diff --git a/srcpkgs/ucspi-tcp/patches/0002-Makefile-target-choose-do-not-depend-on-conf-home.diff b/srcpkgs/ucspi-tcp/patches/0002-Makefile-target-choose-do-not-depend-on-conf-home.diff new file mode 100644 index 00000000000..d9b4f224307 --- /dev/null +++ b/srcpkgs/ucspi-tcp/patches/0002-Makefile-target-choose-do-not-depend-on-conf-home.diff @@ -0,0 +1,27 @@ +From 3be7db918230dde2ce4937f7329d6461b0702d6d Mon Sep 17 00:00:00 2001 +From: Gerrit Pape +Date: Wed, 16 Apr 2008 20:56:00 +0000 +Subject: [PATCH] Makefile: target choose: do not depend on conf-home + +--- + Makefile | 3 +-- + 1 files changed, 1 insertions(+), 2 deletions(-) + +diff --git a/Makefile b/Makefile +index a67b0cb..bcf5bd5 100644 +--- a/Makefile ++++ b/Makefile +@@ -145,9 +145,8 @@ compile chkshsgr.c exit.h + ./compile chkshsgr.c + + choose: \ +-warn-auto.sh choose.sh conf-home ++warn-auto.sh choose.sh + cat warn-auto.sh choose.sh \ +- | sed s}HOME}"`head -1 conf-home`"}g \ + > choose + chmod 755 choose + +-- +1.6.0.3 + diff --git a/srcpkgs/ucspi-tcp/patches/0003-Apply-fefe-s-ucspi-tcp-0.88-ipv6.diff19-ipv6-patch.diff b/srcpkgs/ucspi-tcp/patches/0003-Apply-fefe-s-ucspi-tcp-0.88-ipv6.diff19-ipv6-patch.diff new file mode 100644 index 00000000000..7563bd7b718 --- /dev/null +++ b/srcpkgs/ucspi-tcp/patches/0003-Apply-fefe-s-ucspi-tcp-0.88-ipv6.diff19-ipv6-patch.diff @@ -0,0 +1,4778 @@ +From 79799b389703e8775f90e9728fa7e9d0dca26739 Mon Sep 17 00:00:00 2001 +From: Gerrit Pape +Date: Tue, 13 Oct 2009 22:38:01 +0000 +Subject: [PATCH] Apply fefe's ucspi-tcp-0.88-ipv6.diff19 ipv6 patch + +What is ucspi-tcp and why does it need IPv6? + +Please see http://cr.yp.to/ucspi-tcp.html for an explanation what +ucspi-tcp is. Basically, many people use it to replace inetd. It +consists of tcpserver and tcpclient. tcpserver can listen on sockets and +start programs in an inetd like fashion except that information like the +IP address and remote port are communicated via environment variables, +so writing a server is very easy and the servers are also more portable +since they don't have to use the socket API directly. + +What does your diff do? + +The current version adds simple IPv6 support for tcpserver and +tcpclient. + +Note that IPv6 has inherent IPv4 compatibility, so IPv4 connections are +still possible without having to start a second tcpserver or something +like that. But since some systems may have IPv6 support in their libc +but not in the kernel, my tcpserver will always try to create an IPv6 +listening socket but will fall back to transparent IPv4 support. Note +that this is done by the socket wrappers, not explicitly by tcpserver, +so to tcpserver the connections look like a normal IPv4 mapped IPv6 +connection. + +If a client connects via IPv4 (and you don't use the new -6 option), +tcpserver will make the environment variables look just like the +unpatched version. tcpclient also has a -6 option now with the same +effect. + +IPv6 connections use $PROTO=TCP6, so clients can use this to see how +they should try to parse the IP numbers given in the environment, should +they feel the need to do so. + +There is a new -4 option to tcpclient. I updated the resolver so that +you can request an IPv6 address for a machine that has only an A record. +If the lookup for an AAAA record fails, dns_ip6 will do an A lookup and +convert the address(es) to IP4-mapped IPv6 addresses. If you want +tcpclient to connect to the IPv4 address even if there is an AAAA +record, you can use the -4 option to tcpclient. + +What does not work yet? + +There are quite a few servers using tcpserver out there. If they try to +parse the IP addresses, they are not IPv6 ready and need to be touched. + +Amend: + rm -rf usr/ +--- + FILES | 37 +++++++ + Makefile | 184 ++++++++++++++++++++++++++++----- + TARGETS | 28 +++++ + addcr.1 | 22 ++++ + argv0.1 | 47 ++++++++ + date@.1 | 32 ++++++ + delcr.1 | 30 ++++++ + dns.h | 63 +++++++----- + dns_dfd.c | 11 +- + dns_domain.c | 36 ++++-- + dns_dtda.c | 2 +- + dns_ip.c | 4 +- + dns_ip6.c | 103 ++++++++++++++++++ + dns_ipq.c | 6 +- + dns_ipq6.c | 72 +++++++++++++ + dns_name.c | 19 +++- + dns_nd.c | 2 +- + dns_nd6.c | 28 +++++ + dns_packet.c | 9 +- + dns_random.c | 3 +- + dns_rcip.c | 29 +++--- + dns_rcrw.c | 5 +- + dns_resolve.c | 7 +- + dns_sortip6.c | 20 ++++ + dns_transmit.c | 71 +++++++------ + dns_txt.c | 4 +- + error.h | 2 +- + finger@.1 | 45 ++++++++ + fixcr.1 | 11 ++ + fmt_xlong.c | 22 ++++ + haveip6.h1 | 1 + + haveip6.h2 | 1 + + hier.c | 19 ++++ + http@.1 | 52 +++++++++ + ip4.h | 2 + + ip6.h | 28 +++++ + ip6_fmt.c | 64 +++++++++++ + mconnect.1 | 36 +++++++ + old-rules.c | 101 ++++++++++++++++++ + pathexec.h | 2 +- + pathexec_env.c | 3 +- + recordio.1 | 75 +++++++++++++ + remoteinfo.h | 1 + + remoteinfo6.c | 98 +++++++++++++++++ + rules.c | 2 +- + scan_ip6.c | 87 +++++++++++++++ + scan_xlong.c | 23 ++++ + socket.h | 39 ++++++- + socket_accept6.c | 44 ++++++++ + socket_bind.c | 4 +- + socket_bind6.c | 45 ++++++++ + socket_conn.c | 2 +- + socket_conn6.c | 38 +++++++ + socket_getifidx.c | 8 ++ + socket_getifname.c | 14 +++ + socket_ip4loopback.c | 2 + + socket_local6.c | 39 +++++++ + socket_recv6.c | 44 ++++++++ + socket_remote6.c | 39 +++++++ + socket_send6.c | 40 +++++++ + socket_tcp6.c | 44 ++++++++ + socket_udp6.c | 38 +++++++ + socket_v4mappedprefix.c | 2 + + socket_v6any.c | 2 + + socket_v6loopback.c | 2 + + str.h | 14 ++-- + str_chr.c | 4 +- + str_diff.c | 2 +- + str_len.c | 4 +- + str_start.c | 2 +- + stralloc.h | 12 ++- + stralloc_catb.c | 2 +- + stralloc_cats.c | 2 +- + stralloc_opyb.c | 2 +- + stralloc_opys.c | 2 +- + tcp-environ.5 | 66 ++++++++++++ + tcpcat.1 | 20 ++++ + tcpclient.1 | 173 ++++++++++++++++++++++++++++++ + tcpclient.c | 73 ++++++++----- + tcprules.1 | 221 +++++++++++++++++++++++++++++++++++++++ + tcprules.c | 11 ++- + tcprulescheck.1 | 25 +++++ + tcpserver.1 | 266 +++++++++++++++++++++++++++++++++++++++++++++++ + tcpserver.c | 112 ++++++++++++++------ + timeoutconn.h | 2 + + timeoutconn6.c | 34 ++++++ + tryip6.c | 8 ++ + who@.1 | 32 ++++++ + 88 files changed, 2849 insertions(+), 235 deletions(-) + create mode 100644 addcr.1 + create mode 100644 argv0.1 + create mode 100644 date@.1 + create mode 100644 delcr.1 + create mode 100644 dns_ip6.c + create mode 100644 dns_ipq6.c + create mode 100644 dns_nd6.c + create mode 100644 dns_sortip6.c + create mode 100644 finger@.1 + create mode 100644 fixcr.1 + create mode 100644 fmt_xlong.c + create mode 100644 haveip6.h1 + create mode 100644 haveip6.h2 + create mode 100644 http@.1 + create mode 100644 ip6.h + create mode 100644 ip6_fmt.c + create mode 100644 mconnect.1 + create mode 100644 old-rules.c + create mode 100644 recordio.1 + create mode 100644 remoteinfo6.c + create mode 100644 scan_ip6.c + create mode 100644 scan_xlong.c + create mode 100644 socket_accept6.c + create mode 100644 socket_bind6.c + create mode 100644 socket_conn6.c + create mode 100644 socket_getifidx.c + create mode 100644 socket_getifname.c + create mode 100644 socket_ip4loopback.c + create mode 100644 socket_local6.c + create mode 100644 socket_recv6.c + create mode 100644 socket_remote6.c + create mode 100644 socket_send6.c + create mode 100644 socket_tcp6.c + create mode 100644 socket_udp6.c + create mode 100644 socket_v4mappedprefix.c + create mode 100644 socket_v6any.c + create mode 100644 socket_v6loopback.c + create mode 100644 tcp-environ.5 + create mode 100644 tcpcat.1 + create mode 100644 tcpclient.1 + create mode 100644 tcprules.1 + create mode 100644 tcprulescheck.1 + create mode 100644 tcpserver.1 + create mode 100644 timeoutconn6.c + create mode 100644 tryip6.c + create mode 100644 who@.1 + +diff --git a/FILES b/FILES +index cfb38a5..142aed9 100644 +--- a/FILES ++++ b/FILES +@@ -216,3 +216,40 @@ wait_pid.c + 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 --git a/Makefile b/Makefile +index bcf5bd5..9dc6844 100644 +--- a/Makefile ++++ b/Makefile +@@ -76,12 +76,14 @@ byte.a: \ + 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 +@@ -180,11 +182,13 @@ compile delcr.c buffer.h exit.h + 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 \ +@@ -256,7 +260,7 @@ taia.h tai.h uint64.h taia.h + 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: \ +@@ -497,9 +501,15 @@ exit.h fmt.h iopause.h taia.h tai.h uint64.h pathexec.h + 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 \ +@@ -556,43 +566,43 @@ trylsock.c compile load + 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: \ +@@ -709,9 +719,9 @@ warn-auto.sh tcpcat.sh conf-home + 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: \ +@@ -719,7 +729,7 @@ compile tcpclient.c sig.h exit.h sgetopt.h subgetopt.h uint16.h fmt.h \ + 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: \ +@@ -741,9 +751,9 @@ stralloc.h gen_alloc.h + ./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: \ +@@ -752,7 +762,7 @@ exit.h env.h prot.h open.h wait.h readwrite.h stralloc.h gen_alloc.h \ + 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: \ +@@ -764,9 +774,14 @@ taia_less.o taia_now.o taia_pack.o taia_sub.o taia_uint.o + + 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 +@@ -805,7 +820,12 @@ socket_conn.o socket_delay.o socket_listen.o socket_local.o \ + 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 \ +@@ -818,7 +838,12 @@ strerr_die.o strerr_sys.o subgetopt.o wait_nohang.o wait_pid.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 ++ 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 +@@ -834,3 +859,110 @@ warn-auto.sh who@.sh conf-home + | 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 --git a/TARGETS b/TARGETS +index 4d1f2a0..0385f96 100644 +--- a/TARGETS ++++ b/TARGETS +@@ -169,3 +169,31 @@ instcheck + 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 --git a/addcr.1 b/addcr.1 +new file mode 100644 +index 0000000..3bae1f7 +--- /dev/null ++++ b/addcr.1 +@@ -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 --git a/argv0.1 b/argv0.1 +new file mode 100644 +index 0000000..ad9634d +--- /dev/null ++++ b/argv0.1 +@@ -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 --git a/date@.1 b/date@.1 +new file mode 100644 +index 0000000..fa0ba98 +--- /dev/null ++++ b/date@.1 +@@ -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 --git a/delcr.1 b/delcr.1 +new file mode 100644 +index 0000000..18ea736 +--- /dev/null ++++ b/delcr.1 +@@ -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 --git a/dns.h b/dns.h +index 0948b1a..f06c5a8 100644 +--- a/dns.h ++++ b/dns.h +@@ -34,51 +34,60 @@ struct dns_transmit { + 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 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(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 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 --git a/dns_dfd.c b/dns_dfd.c +index 14a29d8..c924718 100644 +--- a/dns_dfd.c ++++ b/dns_dfd.c +@@ -1,9 +1,10 @@ +-#include "error.h" +-#include "alloc.h" ++#include ++#include + #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 @@ int dns_domain_fromdot(char **out,char *buf,unsigned int n) + 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 --git a/dns_domain.c b/dns_domain.c +index f898485..80ac5ea 100644 +--- a/dns_domain.c ++++ b/dns_domain.c +@@ -1,16 +1,15 @@ +-#include "error.h" +-#include "alloc.h" ++#include + #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 @@ unsigned int dns_domain_length(char *dn) + 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 @@ int dns_domain_equal(char *dn1,char *dn2) + 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 --git a/dns_dtda.c b/dns_dtda.c +index 00b41a1..ba1db4f 100644 +--- a/dns_dtda.c ++++ b/dns_dtda.c +@@ -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 --git a/dns_ip.c b/dns_ip.c +index fb0526c..e7c3a9a 100644 +--- a/dns_ip.c ++++ b/dns_ip.c +@@ -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 @@ int dns_ip4_packet(stralloc *out,char *buf,unsigned int len) + + 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 --git a/dns_ip6.c b/dns_ip6.c +new file mode 100644 +index 0000000..1a2ce08 +--- /dev/null ++++ b/dns_ip6.c +@@ -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 --git a/dns_ipq.c b/dns_ipq.c +index 8181ab7..5b65e23 100644 +--- a/dns_ipq.c ++++ b/dns_ipq.c +@@ -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 @@ static int doit(stralloc *work,char *rule) + 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_rules(stralloc *out,stralloc *fqdn,stralloc *in,stralloc *ru + } + } + +-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 --git a/dns_ipq6.c b/dns_ipq6.c +new file mode 100644 +index 0000000..d5cea12 +--- /dev/null ++++ b/dns_ipq6.c +@@ -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 --git a/dns_name.c b/dns_name.c +index dcb10c7..1f03186 100644 +--- a/dns_name.c ++++ b/dns_name.c +@@ -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 @@ int dns_name_packet(stralloc *out,char *buf,unsigned int len) + 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 @@ int dns_name4(stralloc *out,char ip[4]) + 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 --git a/dns_nd.c b/dns_nd.c +index 279d74d..aa54e5d 100644 +--- a/dns_nd.c ++++ b/dns_nd.c +@@ -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 --git a/dns_nd6.c b/dns_nd6.c +new file mode 100644 +index 0000000..fb1da88 +--- /dev/null ++++ b/dns_nd6.c +@@ -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 --git a/dns_packet.c b/dns_packet.c +index 04a2cc8..72cfb35 100644 +--- a/dns_packet.c ++++ b/dns_packet.c +@@ -2,10 +2,11 @@ + DNS should have used LZ77 instead of its own sophomoric compression algorithm. + */ + +-#include "error.h" ++#include + #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 @@ unsigned int dns_packet_copy(char *buf,unsigned int len,unsigned int pos,char *o + 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 @@ unsigned int dns_packet_skipname(char *buf,unsigned int len,unsigned int pos) + 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 --git a/dns_random.c b/dns_random.c +index b9892b4..2158ed4 100644 +--- a/dns_random.c ++++ b/dns_random.c +@@ -1,3 +1,4 @@ ++#include + #include "dns.h" + #include "taia.h" + #include "uint32.h" +@@ -29,7 +30,7 @@ static void surf(void) + } + } + +-void dns_random_init(char data[128]) ++void dns_random_init(const char data[128]) + { + int i; + struct taia t; +diff --git a/dns_rcip.c b/dns_rcip.c +index 2356c8b..794f6be 100644 +--- a/dns_rcip.c ++++ b/dns_rcip.c +@@ -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 @@ static int init(char ip[64]) + + 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 @@ static int init(char ip[64]) + 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 @@ static int init(char ip[64]) + } + + 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 @@ int dns_resolvconfip(char s[64]) + } + + --uses; +- byte_copy(s,64,ip); ++ byte_copy(s,256,ip); + return 0; + } +diff --git a/dns_rcrw.c b/dns_rcrw.c +index 6f215ac..b0c8e6d 100644 +--- a/dns_rcrw.c ++++ b/dns_rcrw.c +@@ -1,16 +1,17 @@ ++#include + #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 --git a/dns_resolve.c b/dns_resolve.c +index 3365c00..82b5bbb 100644 +--- a/dns_resolve.c ++++ b/dns_resolve.c +@@ -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 --git a/dns_sortip6.c b/dns_sortip6.c +new file mode 100644 +index 0000000..7e752e9 +--- /dev/null ++++ b/dns_sortip6.c +@@ -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 --git a/dns_transmit.c b/dns_transmit.c +index df12826..9511511 100644 +--- a/dns_transmit.c ++++ b/dns_transmit.c +@@ -1,12 +1,15 @@ ++#include ++#include ++#include ++#include + #include "socket.h" +-#include "alloc.h" +-#include "error.h" ++#include + #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 @@ static int serverwantstcp(char *buf,unsigned int len) + 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 @@ static int serverfailed(char *buf,unsigned int len) + 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 @@ static int irrelevant(struct dns_transmit *d,char *buf,unsigned int len) + + 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 int irrelevant(struct dns_transmit *d,char *buf,unsigned int len) + 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 @@ static int randombind(struct dns_transmit *d) + 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 const int timeouts[4] = { 1, 3, 11, 45 }; + + 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 nextudp(struct dns_transmit *d) + 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 @@ static int nexttcp(struct dns_transmit *d) + 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 @@ int dns_transmit_start(struct dns_transmit *d,char servers[64],int flagrecursive + + 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 @@ void dns_transmit_io(struct dns_transmit *d,iopause_fd *x,struct taia *deadline) + *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 @@ have sent query to curserver on UDP socket s + */ + 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 @@ have sent query to curserver on UDP socket s + 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 @@ have received one byte of packet length into packetlen + 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 --git a/dns_txt.c b/dns_txt.c +index 263b641..44deafe 100644 +--- a/dns_txt.c ++++ b/dns_txt.c +@@ -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 @@ int dns_txt_packet(stralloc *out,char *buf,unsigned int len) + + 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 --git a/error.h b/error.h +index d5f3c7e..f660d93 100644 +--- a/error.h ++++ b/error.h +@@ -1,7 +1,7 @@ + #ifndef ERROR_H + #define ERROR_H + +-extern int errno; ++#include + + extern int error_intr; + extern int error_nomem; +diff --git a/finger@.1 b/finger@.1 +new file mode 100644 +index 0000000..93b6288 +--- /dev/null ++++ b/finger@.1 +@@ -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 --git a/fixcr.1 b/fixcr.1 +new file mode 100644 +index 0000000..ebb8b53 +--- /dev/null ++++ b/fixcr.1 +@@ -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 --git a/fmt_xlong.c b/fmt_xlong.c +new file mode 100644 +index 0000000..332fc9a +--- /dev/null ++++ b/fmt_xlong.c +@@ -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 --git a/haveip6.h1 b/haveip6.h1 +new file mode 100644 +index 0000000..8b13789 +--- /dev/null ++++ b/haveip6.h1 +@@ -0,0 +1 @@ ++ +diff --git a/haveip6.h2 b/haveip6.h2 +new file mode 100644 +index 0000000..5564de9 +--- /dev/null ++++ b/haveip6.h2 +@@ -0,0 +1 @@ ++#define LIBC_HAS_IP6 1 +diff --git a/hier.c b/hier.c +index 5663ada..546cc6d 100644 +--- a/hier.c ++++ b/hier.c +@@ -4,6 +4,9 @@ void hier() + { + 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 @@ void hier() + 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 --git a/http@.1 b/http@.1 +new file mode 100644 +index 0000000..4861b34 +--- /dev/null ++++ b/http@.1 +@@ -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 --git a/ip4.h b/ip4.h +index 64a7c1e..b906557 100644 +--- a/ip4.h ++++ b/ip4.h +@@ -6,4 +6,6 @@ extern unsigned int ip4_fmt(char *,char *); + + #define IP4_FMT 20 + ++extern const char ip4loopback[4]; /* = {127,0,0,1}; */ ++ + #endif +diff --git a/ip6.h b/ip6.h +new file mode 100644 +index 0000000..88ff120 +--- /dev/null ++++ b/ip6.h +@@ -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 --git a/ip6_fmt.c b/ip6_fmt.c +new file mode 100644 +index 0000000..d7c010a +--- /dev/null ++++ b/ip6_fmt.c +@@ -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 --git a/mconnect.1 b/mconnect.1 +new file mode 100644 +index 0000000..6648367 +--- /dev/null ++++ b/mconnect.1 +@@ -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 --git a/old-rules.c b/old-rules.c +new file mode 100644 +index 0000000..7225115 +--- /dev/null ++++ b/old-rules.c +@@ -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 --git a/pathexec.h b/pathexec.h +index 6fcbb89..bef93b4 100644 +--- a/pathexec.h ++++ b/pathexec.h +@@ -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 --git a/pathexec_env.c b/pathexec_env.c +index 48bba7e..157e71b 100644 +--- a/pathexec_env.c ++++ b/pathexec_env.c +@@ -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 @@ int pathexec_env(char *s,char *t) + + void pathexec(char **argv) + { +- char *path; + char **e; + unsigned int elen; + unsigned int i; +diff --git a/recordio.1 b/recordio.1 +new file mode 100644 +index 0000000..e056776 +--- /dev/null ++++ b/recordio.1 +@@ -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 --git a/remoteinfo.h b/remoteinfo.h +index 2ca779d..0884cc1 100644 +--- a/remoteinfo.h ++++ b/remoteinfo.h +@@ -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 --git a/remoteinfo6.c b/remoteinfo6.c +new file mode 100644 +index 0000000..cf3b7c1 +--- /dev/null ++++ b/remoteinfo6.c +@@ -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 --git a/rules.c b/rules.c +index 1840360..4fc2354 100644 +--- a/rules.c ++++ b/rules.c +@@ -64,7 +64,7 @@ static int doit(void (*callback)(char *,unsigned int),char *ip,char *host,char * + + 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 --git a/scan_ip6.c b/scan_ip6.c +new file mode 100644 +index 0000000..ee239fd +--- /dev/null ++++ b/scan_ip6.c +@@ -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='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 --git a/socket.h b/socket.h +index 80fb260..4fba762 100644 +--- a/socket.h ++++ b/socket.h +@@ -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 --git a/socket_accept6.c b/socket_accept6.c +new file mode 100644 +index 0000000..a8a9a07 +--- /dev/null ++++ b/socket_accept6.c +@@ -0,0 +1,44 @@ ++#include ++#include ++#include ++#include ++#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 --git a/socket_bind.c b/socket_bind.c +index 20830a4..067b4a8 100644 +--- a/socket_bind.c ++++ b/socket_bind.c +@@ -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 @@ int socket_bind4(int s,char ip[4],uint16 port) + 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 --git a/socket_bind6.c b/socket_bind6.c +new file mode 100644 +index 0000000..8a5a7cd +--- /dev/null ++++ b/socket_bind6.c +@@ -0,0 +1,45 @@ ++#include ++#include ++#include ++#include ++#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 --git a/socket_conn.c b/socket_conn.c +index 35adac4..dcc93ac 100644 +--- a/socket_conn.c ++++ b/socket_conn.c +@@ -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 --git a/socket_conn6.c b/socket_conn6.c +new file mode 100644 +index 0000000..0ad886d +--- /dev/null ++++ b/socket_conn6.c +@@ -0,0 +1,38 @@ ++#include ++#include ++#include ++#include ++#include ++#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 --git a/socket_getifidx.c b/socket_getifidx.c +new file mode 100644 +index 0000000..452d6d7 +--- /dev/null ++++ b/socket_getifidx.c +@@ -0,0 +1,8 @@ ++#include ++#include ++#include ++#include "socket.h" ++ ++uint32 socket_getifidx(const char* ifname) { ++ return if_nametoindex(ifname); ++} +diff --git a/socket_getifname.c b/socket_getifname.c +new file mode 100644 +index 0000000..77edff9 +--- /dev/null ++++ b/socket_getifname.c +@@ -0,0 +1,14 @@ ++#include ++#include ++#include ++#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 --git a/socket_ip4loopback.c b/socket_ip4loopback.c +new file mode 100644 +index 0000000..1bbbe95 +--- /dev/null ++++ b/socket_ip4loopback.c +@@ -0,0 +1,2 @@ ++ ++const char ip4loopback[4] = {127,0,0,1}; +diff --git a/socket_local6.c b/socket_local6.c +new file mode 100644 +index 0000000..23427c3 +--- /dev/null ++++ b/socket_local6.c +@@ -0,0 +1,39 @@ ++#include ++#include ++#include ++#include ++#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 --git a/socket_recv6.c b/socket_recv6.c +new file mode 100644 +index 0000000..a86ca96 +--- /dev/null ++++ b/socket_recv6.c +@@ -0,0 +1,44 @@ ++#include ++#include ++#include ++#include ++#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 --git a/socket_remote6.c b/socket_remote6.c +new file mode 100644 +index 0000000..e60a539 +--- /dev/null ++++ b/socket_remote6.c +@@ -0,0 +1,39 @@ ++#include ++#include ++#include ++#include ++#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 --git a/socket_send6.c b/socket_send6.c +new file mode 100644 +index 0000000..4b2d1e8 +--- /dev/null ++++ b/socket_send6.c +@@ -0,0 +1,40 @@ ++#include ++#include ++#include ++#include ++#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 --git a/socket_tcp6.c b/socket_tcp6.c +new file mode 100644 +index 0000000..74099e2 +--- /dev/null ++++ b/socket_tcp6.c +@@ -0,0 +1,44 @@ ++#include ++#include ++#include ++#include ++#include ++#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 --git a/socket_udp6.c b/socket_udp6.c +new file mode 100644 +index 0000000..3769b1d +--- /dev/null ++++ b/socket_udp6.c +@@ -0,0 +1,38 @@ ++#include ++#include ++#include ++#include ++#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 --git a/socket_v4mappedprefix.c b/socket_v4mappedprefix.c +new file mode 100644 +index 0000000..dbed824 +--- /dev/null ++++ b/socket_v4mappedprefix.c +@@ -0,0 +1,2 @@ ++ ++const unsigned char V4mappedprefix[12]={0,0,0,0,0,0,0,0,0,0,0xff,0xff}; +diff --git a/socket_v6any.c b/socket_v6any.c +new file mode 100644 +index 0000000..c6d0cbb +--- /dev/null ++++ b/socket_v6any.c +@@ -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 --git a/socket_v6loopback.c b/socket_v6loopback.c +new file mode 100644 +index 0000000..b81ee65 +--- /dev/null ++++ b/socket_v6loopback.c +@@ -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 --git a/str.h b/str.h +index ab4aedd..a2a4b75 100644 +--- a/str.h ++++ b/str.h +@@ -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 --git a/str_chr.c b/str_chr.c +index 886d6b6..042dfa2 100644 +--- a/str_chr.c ++++ b/str_chr.c +@@ -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 --git a/str_diff.c b/str_diff.c +index 037dcdf..071e7f5 100644 +--- a/str_diff.c ++++ b/str_diff.c +@@ -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 --git a/str_len.c b/str_len.c +index 5bd3f62..8411ebf 100644 +--- a/str_len.c ++++ b/str_len.c +@@ -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 --git a/str_start.c b/str_start.c +index 43430bb..757189d 100644 +--- a/str_start.c ++++ b/str_start.c +@@ -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 --git a/stralloc.h b/stralloc.h +index 7866812..cc17048 100644 +--- a/stralloc.h ++++ b/stralloc.h +@@ -9,18 +9,20 @@ extern int stralloc_ready(stralloc *,unsigned int); + 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 --git a/stralloc_catb.c b/stralloc_catb.c +index b739bed..b606e32 100644 +--- a/stralloc_catb.c ++++ b/stralloc_catb.c +@@ -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 --git a/stralloc_cats.c b/stralloc_cats.c +index 8b11e94..92cb66e 100644 +--- a/stralloc_cats.c ++++ b/stralloc_cats.c +@@ -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 --git a/stralloc_opyb.c b/stralloc_opyb.c +index 46b99fc..593029d 100644 +--- a/stralloc_opyb.c ++++ b/stralloc_opyb.c +@@ -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 --git a/stralloc_opys.c b/stralloc_opys.c +index 78594b0..860c7e0 100644 +--- a/stralloc_opys.c ++++ b/stralloc_opys.c +@@ -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 --git a/tcp-environ.5 b/tcp-environ.5 +new file mode 100644 +index 0000000..fecad70 +--- /dev/null ++++ b/tcp-environ.5 +@@ -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 --git a/tcpcat.1 b/tcpcat.1 +new file mode 100644 +index 0000000..4c51ed5 +--- /dev/null ++++ b/tcpcat.1 +@@ -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 --git a/tcpclient.1 b/tcpclient.1 +new file mode 100644 +index 0000000..f82f6b3 +--- /dev/null ++++ b/tcpclient.1 +@@ -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 --git a/tcpclient.c b/tcpclient.c +index 9f6d7f2..77b1ad5 100644 +--- a/tcpclient.c ++++ b/tcpclient.c +@@ -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 nomem(void) + 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 moreaddresses; + 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 @@ main(int argc,char **argv) + 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 @@ main(int argc,char **argv) + 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 @@ main(int argc,char **argv) + + 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 @@ main(int argc,char **argv) + 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 @@ main(int argc,char **argv) + 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 @@ main(int argc,char **argv) + + 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 --git a/tcprules.1 b/tcprules.1 +new file mode 100644 +index 0000000..084165b +--- /dev/null ++++ b/tcprules.1 +@@ -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 --git a/tcprules.c b/tcprules.c +index a684ac5..83519c8 100644 +--- a/tcprules.c ++++ b/tcprules.c +@@ -123,8 +123,15 @@ main(int argc,char **argv) + } + 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 --git a/tcprulescheck.1 b/tcprulescheck.1 +new file mode 100644 +index 0000000..3f0de24 +--- /dev/null ++++ b/tcprulescheck.1 +@@ -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 --git a/tcpserver.1 b/tcpserver.1 +new file mode 100644 +index 0000000..72c5ca0 +--- /dev/null ++++ b/tcpserver.1 +@@ -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 --git a/tcpserver.c b/tcpserver.c +index 979a0be..aab637f 100644 +--- a/tcpserver.c ++++ b/tcpserver.c +@@ -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 flagremoteinfo = 1; + 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 @@ void safecats(char *s) + 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 found(char *data,unsigned int datalen) + + 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 @@ void doit(int t) + 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 @@ void doit(int t) + } + } + 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 @@ void doit(int t) + 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 @@ void usage(void) + { + strerr_warn1("\ + tcpserver: usage: tcpserver \ +-[ -1UXpPhHrRoOdDqQv ] \ ++[ -461UXpPhHrRoOdDqQv ] \ + [ -c limit ] \ + [ -x rules.cdb ] \ + [ -B banner ] \ +@@ -249,6 +282,7 @@ tcpserver: usage: tcpserver \ + [ -b backlog ] \ + [ -l localname ] \ + [ -t timeout ] \ ++[ -I interface ] \ + host port program",0); + _exit(100); + } +@@ -299,8 +333,8 @@ main(int argc,char **argv) + 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 @@ main(int argc,char **argv) + 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 @@ main(int argc,char **argv) + + 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 @@ main(int argc,char **argv) + 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 @@ main(int argc,char **argv) + 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); +- +- s = socket_tcp(); ++ 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_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 @@ main(int argc,char **argv) + 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 --git a/timeoutconn.h b/timeoutconn.h +index 7f9dcc9..01e6a75 100644 +--- a/timeoutconn.h ++++ b/timeoutconn.h +@@ -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 --git a/timeoutconn6.c b/timeoutconn6.c +new file mode 100644 +index 0000000..75e9f5a +--- /dev/null ++++ b/timeoutconn6.c +@@ -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 --git a/tryip6.c b/tryip6.c +new file mode 100644 +index 0000000..e0d7cfb +--- /dev/null ++++ b/tryip6.c +@@ -0,0 +1,8 @@ ++#include ++#include ++#include ++ ++main() { ++ struct sockaddr_in6 sa; ++ sa.sin6_family = PF_INET6; ++} +diff --git a/who@.1 b/who@.1 +new file mode 100644 +index 0000000..0c13f84 +--- /dev/null ++++ b/who@.1 +@@ -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) +-- +1.6.0.3 + diff --git a/srcpkgs/ucspi-tcp/patches/0004-conf-home.diff b/srcpkgs/ucspi-tcp/patches/0004-conf-home.diff new file mode 100644 index 00000000000..c785535f5e0 --- /dev/null +++ b/srcpkgs/ucspi-tcp/patches/0004-conf-home.diff @@ -0,0 +1,9 @@ +diff --git a/conf-home b/conf-home +--- a/conf-home 2014-11-07 13:55:54.238896295 +0200 ++++ b/conf-home 2014-11-07 13:55:39.306591215 +0200 +@@ -1,4 +1,4 @@ +-/usr/local ++/usr + + This is the ucspi-tcp home directory. Programs will be installed in + .../bin. diff --git a/srcpkgs/ucspi-tcp/template b/srcpkgs/ucspi-tcp/template new file mode 100644 index 00000000000..56f57f83c81 --- /dev/null +++ b/srcpkgs/ucspi-tcp/template @@ -0,0 +1,63 @@ +# Template file for 'ucspi-tcp' +pkgname=ucspi-tcp +patch_args="-Np1" +version=0.88 +revision=1 +short_desc="command-line tools for building TCP client-server applications with IPv6 support" +maintainer="Nikolay Hristov " +license="public license" +build_style=gnu-makefile +homepage="http://cr.yp.to/ucspi-tcp.html" +distfiles="http://cr.yp.to/ucspi-tcp/ucspi-tcp-0.88.tar.gz" +#distfiles="http://ftp.de.debian.org/debian/pool/main/u/ucspi-tcp/ucspi-tcp_${version}.orig.tar.gz" +checksum=4a0615cab74886f5b4f7e8fd32933a07b955536a3476d74ea087a3ea66a23e9c + +do_build() { + +echo "$CC -D_GNU_SOURCE $CFLAGS -include /usr/include/errno.h" >conf-cc + echo "$CC $LDFLAGS -Wl,-z -Wl,noexecstack" >conf-ld + sed -i 's/"usr\/local"/"usr\/"/g' conf-home + + make ${makejobs} + +} + +do_install() { + + vbin addcr + vbin date@ + vbin finger@ + vbin http@ + vbin recordio + vbin tcpclient + vbin recordio + vbin tcprulescheck + vbin who@ + vbin argv0 + vbin delcr + vbin fixcrio + vbin mconnect + vbin mconnect-io + vbin tcpcat + vbin tcprules + vbin tcpserver + vbin rblsmtpd + + vman addcr.1 + vman date@.1 + vman finger@.1 + vman http@.1 + vman recordio.1 + vman tcpclient.1 + vman tcprulescheck.1 + vman who@.1 + vman argv0.1 + vman delcr.1 + vman fixcr.1 fixcrio.1 + vman mconnect.1 + vman tcpcat.1 + vman tcprules.1 + vman tcpserver.1 + + vdoc README +}