Index: FILES
===================================================================
RCS file: /usr/home/cvs-djbware/CVS/ucspi-tcp/FILES,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -p -r1.1.1.1 -r1.2
--- FILES	14 Jun 2002 08:47:30 -0000	1.1.1.1
+++ FILES	5 Apr 2004 15:34:57 -0000	1.2
@@ -216,3 +216,19 @@ wait_pid.c
 warn-auto.sh
 warn-shsgr
 x86cpuid.c
+addcr.1
+argv0.1
+date@.1
+delcr.1
+finger@.1
+fixcrio.1
+http@.1
+mconnect.1
+recordio.1
+tcpcat.1
+tcpclient.1
+tcprules.1
+tcprulescheck.1
+tcpserver.1
+who@.1
+tcp-environ.5
Index: Makefile
===================================================================
RCS file: /usr/home/cvs-djbware/CVS/ucspi-tcp/Makefile,v
retrieving revision 1.1.1.1
retrieving revision 1.3
diff -u -p -r1.1.1.1 -r1.3
--- Makefile	14 Jun 2002 08:47:30 -0000	1.1.1.1
+++ Makefile	5 Apr 2004 15:34:57 -0000	1.3
@@ -1,13 +1,25 @@
 # Don't edit Makefile! Use conf-* for configuration.
 
+DEFINES=-DWITH_SSL
+#add -DWITH_SSL to enable ssl support
+
+# LIBS for additional libraries and INCS for additional includes
+LIBS=-lcrypto -lssl
+#INCS=-I/usr/local/include
+OPENSSLBIN=openssl
+
 SHELL=/bin/sh
 
-default: it
+default: it man
 
 addcr: \
 load addcr.o unix.a byte.a
 	./load addcr unix.a byte.a 
 
+addcr.0: \
+addcr.1
+	nroff -man addcr.1 > addcr.0
+
 addcr.o: \
 compile addcr.c buffer.h exit.h
 	./compile addcr.c
@@ -24,6 +36,10 @@ argv0: \
 load argv0.o unix.a byte.a
 	./load argv0 unix.a byte.a 
 
+argv0.0: \
+argv0.1
+	nroff -man argv0.1 > argv0.0
+
 argv0.o: \
 compile argv0.c pathexec.h strerr.h
 	./compile argv0.c
@@ -151,6 +167,10 @@ warn-auto.sh choose.sh conf-home
 	> choose
 	chmod 755 choose
 
+clean: \
+TARGETS
+	rm -f `cat TARGETS`
+
 commands.o: \
 compile commands.c buffer.h stralloc.h gen_alloc.h str.h case.h \
 commands.h
@@ -170,10 +190,18 @@ warn-auto.sh date@.sh conf-home
 	> date@
 	chmod 755 date@
 
+date@.0: \
+date@.1
+	nroff -man date@.1 > date@.0
+
 delcr: \
 load delcr.o unix.a byte.a
 	./load delcr unix.a byte.a 
 
+delcr.0: \
+delcr.1
+	nroff -man delcr.1 > delcr.0
+
 delcr.o: \
 compile delcr.c buffer.h exit.h
 	./compile delcr.c
@@ -292,10 +320,18 @@ warn-auto.sh finger@.sh conf-home
 	> finger@
 	chmod 755 finger@
 
+finger@.0: \
+finger@.1
+	nroff -man finger@.1 > finger@.0
+
 fixcrio: \
 load fixcrio.o time.a unix.a byte.a
 	./load fixcrio time.a unix.a byte.a 
 
+fixcrio.0: \
+fixcrio.1
+	nroff -man fixcrio.1 > fixcrio.0
+
 fixcrio.o: \
 compile fixcrio.c sig.h buffer.h strerr.h byte.h readwrite.h exit.h \
 iopause.h taia.h tai.h uint64.h pathexec.h
@@ -346,6 +382,10 @@ warn-auto.sh http@.sh conf-home
 	> http@
 	chmod 755 http@
 
+http@.0: \
+http@.1
+	nroff -man http@.1 > http@.0
+
 install: \
 load install.o hier.o auto_home.o unix.a byte.a
 	./load install hier.o auto_home.o unix.a byte.a 
@@ -409,6 +449,11 @@ warn-auto.sh systype
 	) > makelib
 	chmod 755 makelib
 
+man: \
+tcpclient.0 tcpserver.0 tcprules.0 tcprulescheck.0 tcp-environ.0 \
+who@.0 date@.0 finger@.0 http@.0 tcpcat.0 mconnect.0 fixcrio.0 addcr.0 \
+delcr.0 argv0.0 recordio.0
+
 mconnect: \
 warn-auto.sh mconnect.sh conf-home
 	cat warn-auto.sh mconnect.sh \
@@ -425,6 +470,10 @@ compile mconnect-io.c sig.h wait.h fork.
 readwrite.h exit.h
 	./compile mconnect-io.c
 
+mconnect.0: \
+mconnect.1
+	nroff -man mconnect.1 > mconnect.0
+
 ndelay_off.o: \
 compile ndelay_off.c ndelay.h
 	./compile ndelay_off.c
@@ -495,6 +544,10 @@ compile recordio.c sig.h buffer.h strerr
 exit.h fmt.h iopause.h taia.h tai.h uint64.h pathexec.h
 	./compile recordio.c
 
+recordio.0: \
+recordio.1
+	nroff -man recordio.1 > recordio.0
+
 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 \
@@ -702,6 +755,10 @@ taia_uint.o: \
 compile taia_uint.c taia.h tai.h uint64.h
 	./compile taia_uint.c
 
+tcp-environ.0: \
+tcp-environ.5
+	nroff -man tcp-environ.5 > tcp-environ.0
+
 tcpcat: \
 warn-auto.sh tcpcat.sh conf-home
 	cat warn-auto.sh tcpcat.sh \
@@ -709,12 +766,20 @@ warn-auto.sh tcpcat.sh conf-home
 	> tcpcat
 	chmod 755 tcpcat
 
+tcpcat.0: \
+tcpcat.1
+	nroff -man tcpcat.1 > tcpcat.0
+
 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 \
 	unix.a byte.a  `cat socket.lib`
 
+tcpclient.0: \
+tcpclient.1
+	nroff -man tcpclient.1 > tcpclient.0
+
 tcpclient.o: \
 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 \
@@ -727,6 +792,10 @@ tcprules: \
 load tcprules.o cdb.a unix.a byte.a
 	./load tcprules cdb.a unix.a byte.a 
 
+tcprules.0: \
+tcprules.1
+	nroff -man tcprules.1 > tcprules.0
+
 tcprules.o: \
 compile tcprules.c strerr.h stralloc.h gen_alloc.h getln.h buffer.h \
 stralloc.h buffer.h exit.h fmt.h byte.h cdb_make.h buffer.h uint32.h
@@ -736,6 +805,10 @@ tcprulescheck: \
 load tcprulescheck.o rules.o cdb.a unix.a byte.a
 	./load tcprulescheck rules.o cdb.a unix.a byte.a 
 
+tcprulescheck.0: \
+tcprulescheck.1
+	nroff -man tcprulescheck.1 > tcprulescheck.0
+
 tcprulescheck.o: \
 compile tcprulescheck.c byte.h buffer.h strerr.h env.h rules.h \
 stralloc.h gen_alloc.h
@@ -745,7 +818,11 @@ tcpserver: \
 load tcpserver.o rules.o remoteinfo.o timeoutconn.o cdb.a dns.a \
 time.a unix.a byte.a socket.lib
 	./load tcpserver rules.o remoteinfo.o timeoutconn.o cdb.a \
-	dns.a time.a unix.a byte.a  `cat socket.lib`
+	dns.a time.a unix.a byte.a  $(LIBS) `cat socket.lib`
+
+tcpserver.0: \
+tcpserver.1
+	nroff -man tcpserver.1 > tcpserver.0
 
 tcpserver.o: \
 compile tcpserver.c uint16.h str.h byte.h fmt.h scan.h ip4.h fd.h \
@@ -754,7 +831,7 @@ alloc.h buffer.h error.h strerr.h sgetop
 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
-	./compile tcpserver.c
+	./compile $(DEFINES) $(INCS) tcpserver.c
 
 time.a: \
 makelib iopause.o tai_pack.o taia_add.o taia_approx.o taia_frac.o \
@@ -835,3 +912,22 @@ warn-auto.sh who@.sh conf-home
 	| sed s}HOME}"`head -1 conf-home`"}g \
 	> who@
 	chmod 755 who@
+
+who@.0: \
+who@.1
+	nroff -man who@.1 > who@.0
+
+cert:
+	${OPENSSLBIN} req -new -x509 -nodes \
+	-out cert.pem -days 366 \
+	-keyout cert.pem
+
+cert-req:
+	${OPENSSLBIN} req -new -nodes \
+	-out req.pem \
+	-keyout cert.pem
+	@echo
+	@echo "Send req.pem to your CA to obtain signed_req.pem, and do:"
+	@echo "cat signed_req.pem >> `head -1 conf-qmail`/control/cert.pem"
+
+
Index: TARGETS
===================================================================
RCS file: /usr/home/cvs-djbware/CVS/ucspi-tcp/TARGETS,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -p -r1.1.1.1 -r1.2
--- TARGETS	14 Jun 2002 08:47:30 -0000	1.1.1.1
+++ TARGETS	5 Apr 2004 15:34:57 -0000	1.2
@@ -169,3 +169,19 @@ instcheck
 it
 setup
 check
+addcr.0
+argv0.0
+date@.0
+delcr.0
+finger@.0
+fixcrio.0
+http@.0
+mconnect.0
+recordio.0
+tcp-environ.0
+tcpcat.0
+tcpclient.0
+tcprules.0
+tcprulescheck.0
+tcpserver.0
+who@.0
Index: addcr.1
===================================================================
RCS file: addcr.1
diff -N addcr.1
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ addcr.1	5 Apr 2004 15:34:57 -0000	1.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),
+fixcrio(1)
Index: argv0.1
===================================================================
RCS file: argv0.1
diff -N argv0.1
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ argv0.1	5 Apr 2004 15:34:57 -0000	1.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)
Index: date@.1
===================================================================
RCS file: date@.1
diff -N date@.1
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ date@.1	5 Apr 2004 15:34:57 -0000	1.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)
Index: delcr.1
===================================================================
RCS file: delcr.1
diff -N delcr.1
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ delcr.1	5 Apr 2004 15:34:57 -0000	1.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)
Index: error.h
===================================================================
RCS file: /usr/home/cvs-djbware/CVS/ucspi-tcp/error.h,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -p -r1.1.1.1 -r1.2
--- error.h	14 Jun 2002 08:47:31 -0000	1.1.1.1
+++ error.h	6 Nov 2003 09:47:05 -0000	1.2
@@ -1,7 +1,7 @@
 #ifndef ERROR_H
 #define ERROR_H
 
-extern int errno;
+#include <errno.h>
 
 extern int error_intr;
 extern int error_nomem;
Index: finger@.1
===================================================================
RCS file: finger@.1
diff -N finger@.1
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ finger@.1	5 Apr 2004 15:34:57 -0000	1.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)
Index: fixcrio.1
===================================================================
RCS file: fixcrio.1
diff -N fixcrio.1
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ fixcrio.1	5 Apr 2004 15:34:57 -0000	1.1
@@ -0,0 +1,15 @@
+.TH fixcrio 1
+.SH NAME
+fixcrio \- make sure that there is a CR before each LF
+.SH SYNOPSIS
+.B fixcrio
+.I program
+[
+.I arg ...
+]
+.SH DESCRIPTION
+.B fixcrio
+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)
Index: hier.c
===================================================================
RCS file: /usr/home/cvs-djbware/CVS/ucspi-tcp/hier.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -p -r1.1.1.1 -r1.2
--- hier.c	14 Jun 2002 08:47:32 -0000	1.1.1.1
+++ hier.c	5 Apr 2004 15:34:57 -0000	1.2
@@ -2,8 +2,13 @@
 
 void hier()
 {
-  h(auto_home,-1,-1,02755);
-  d(auto_home,"bin",-1,-1,02755);
+  h(auto_home,-1,-1,0755);
+  d(auto_home,"bin",-1,-1,0755);
+  d(auto_home,"man",-1,-1,0755);
+  d(auto_home,"man/man1",-1,-1,0755);
+  d(auto_home,"man/man5",-1,-1,0755);
+  d(auto_home,"man/cat1",-1,-1,0755);
+  d(auto_home,"man/cat5",-1,-1,0755);
 
   c(auto_home,"bin","tcpserver",-1,-1,0755);
   c(auto_home,"bin","tcprules",-1,-1,0755);
@@ -22,4 +27,37 @@ 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","addcr.1",-1,-1,0644);
+  c(auto_home,"man/cat1","addcr.0",-1,-1,0644);
+  c(auto_home,"man/man1","argv0.1",-1,-1,0644);
+  c(auto_home,"man/cat1","argv0.0",-1,-1,0644);
+  c(auto_home,"man/man1","date@.1",-1,-1,0644);
+  c(auto_home,"man/cat1","date@.0",-1,-1,0644);
+  c(auto_home,"man/man1","delcr.1",-1,-1,0644);
+  c(auto_home,"man/cat1","delcr.0",-1,-1,0644);
+  c(auto_home,"man/man1","finger@.1",-1,-1,0644);
+  c(auto_home,"man/cat1","finger@.0",-1,-1,0644);
+  c(auto_home,"man/man1","fixcrio.1",-1,-1,0644);
+  c(auto_home,"man/cat1","fixcrio.0",-1,-1,0644);
+  c(auto_home,"man/man1","http@.1",-1,-1,0644);
+  c(auto_home,"man/cat1","http@.0",-1,-1,0644);
+  c(auto_home,"man/man1","mconnect.1",-1,-1,0644);
+  c(auto_home,"man/cat1","mconnect.0",-1,-1,0644);
+  c(auto_home,"man/man1","recordio.1",-1,-1,0644);
+  c(auto_home,"man/cat1","recordio.0",-1,-1,0644);
+  c(auto_home,"man/man1","tcpcat.1",-1,-1,0644);
+  c(auto_home,"man/cat1","tcpcat.0",-1,-1,0644);
+  c(auto_home,"man/man1","tcpclient.1",-1,-1,0644);
+  c(auto_home,"man/cat1","tcpclient.0",-1,-1,0644);
+  c(auto_home,"man/man1","tcprules.1",-1,-1,0644);
+  c(auto_home,"man/cat1","tcprules.0",-1,-1,0644);
+  c(auto_home,"man/man1","tcprulescheck.1",-1,-1,0644);
+  c(auto_home,"man/cat1","tcprulescheck.0",-1,-1,0644);
+  c(auto_home,"man/man1","tcpserver.1",-1,-1,0644);
+  c(auto_home,"man/cat1","tcpserver.0",-1,-1,0644);
+  c(auto_home,"man/man1","who@.1",-1,-1,0644);
+  c(auto_home,"man/cat1","who@.0",-1,-1,0644);
+  c(auto_home,"man/man5","tcp-environ.5",-1,-1,0644);
+  c(auto_home,"man/cat5","tcp-environ.0",-1,-1,0644);
 }
Index: http@.1
===================================================================
RCS file: http@.1
diff -N http@.1
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ http@.1	5 Apr 2004 15:34:57 -0000	1.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)
Index: mconnect.1
===================================================================
RCS file: mconnect.1
diff -N mconnect.1
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ mconnect.1	5 Apr 2004 15:34:57 -0000	1.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)
Index: recordio.1
===================================================================
RCS file: recordio.1
diff -N recordio.1
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ recordio.1	5 Apr 2004 15:34:57 -0000	1.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)
Index: tcp-environ.5
===================================================================
RCS file: tcp-environ.5
diff -N tcp-environ.5
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tcp-environ.5	5 Apr 2004 15:34:57 -0000	1.1
@@ -0,0 +1,62 @@
+.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.
+.SH "SEE ALSO"
+tcpclient(1),
+tcpserver(1),
+tcp-env(1),
+tcp(4)
Index: tcpcat.1
===================================================================
RCS file: tcpcat.1
diff -N tcpcat.1
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tcpcat.1	5 Apr 2004 15:34:57 -0000	1.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)
Index: tcpclient.1
===================================================================
RCS file: tcpclient.1
diff -N tcpclient.1
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tcpclient.1	5 Apr 2004 15:34:57 -0000	1.1
@@ -0,0 +1,151 @@
+.TH tcpclient 1
+.SH NAME
+tcpclient \- create an outgoing TCP connection
+.SH SYNOPSIS
+.B tcpclient
+[
+.B \-hHrRdDqQv
+]
+[
+.B \-i\fIlocalip
+]
+[
+.B \-p\fIlocalport
+]
+[
+.B \-T\fItimeoutconn
+]
+[
+.B \-l\fIlocalname
+]
+[
+.B \-t\fItimeoutinfo
+]
+.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 \-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.
+.SH "SEE ALSO"
+date@(1),
+finger@(1),
+http@(1),
+mconnect(1),
+tcpcat(1),
+tcpserver(1),
+who@(1),
+tcp-environ(5)
Index: tcprules.1
===================================================================
RCS file: tcprules.1
diff -N tcprules.1
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tcprules.1	5 Apr 2004 15:34:57 -0000	1.1
@@ -0,0 +1,208 @@
+.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
+.EE
+
+If
+.I TCPREMOTEIP
+is
+.BR 10.119.75.38 ,
+.B tcpserver
+will follow the
+.B fourth
+instructions.
+
+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)
Index: tcprules.c
===================================================================
RCS file: /usr/home/cvs-djbware/CVS/ucspi-tcp/tcprules.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -p -r1.1.1.1 -r1.2
--- tcprules.c	14 Jun 2002 08:47:30 -0000	1.1.1.1
+++ tcprules.c	16 Mar 2004 15:12:26 -0000	1.2
@@ -94,6 +94,7 @@ main(int argc,char **argv)
   int len;
   int fd;
   int i;
+  int e;
   char ch;
 
   fn = argv[1];
@@ -144,8 +145,16 @@ main(int argc,char **argv)
     while (len)
       switch(*x) {
         case ',':
+	  e = byte_chr(x + 1,len - 1,',');
           i = byte_chr(x,len,'=');
-          if (i == len) die_bad();
+          if (i > e) {
+	    if (e < 2 || x[1] != '!') die_bad();
+	    if (!stralloc_catb(&data,"-",1)) nomem();
+	    if (!stralloc_catb(&data,x + 2,e - 1)) nomem();
+	    if (!stralloc_0(&data)) nomem();
+	    x += e + 1; len -= e + 1;
+	    break;
+	  }
           if (!stralloc_catb(&data,"+",1)) nomem();
           if (!stralloc_catb(&data,x + 1,i)) nomem();
           x += i + 1; len -= i + 1;
Index: tcprulescheck.1
===================================================================
RCS file: tcprulescheck.1
diff -N tcprulescheck.1
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tcprulescheck.1	5 Apr 2004 15:34:57 -0000	1.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)
Index: tcprulescheck.c
===================================================================
RCS file: /usr/home/cvs-djbware/CVS/ucspi-tcp/tcprulescheck.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -p -r1.1.1.1 -r1.2
--- tcprulescheck.c	14 Jun 2002 08:47:30 -0000	1.1.1.1
+++ tcprulescheck.c	16 Mar 2004 15:12:27 -0000	1.2
@@ -22,6 +22,11 @@ void found(char *data,unsigned int datal
 	buffer_puts(buffer_1,data + 1);
 	buffer_puts(buffer_1,"\n");
 	break;
+      case '-':
+	buffer_puts(buffer_1,"unset environment variable ");
+	buffer_puts(buffer_1,data + 1);
+	buffer_puts(buffer_1,"\n");
+	break;
     }
     ++next0;
     data += next0; datalen -= next0;
Index: tcpserver.1
===================================================================
RCS file: tcpserver.1
diff -N tcpserver.1
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tcpserver.1	6 Apr 2004 11:49:45 -0000	1.2
@@ -0,0 +1,318 @@
+.TH tcpserver 1
+.SH NAME
+tcpserver \- accept incoming TCP connections
+.SH SYNOPSIS
+.B tcpserver
+[
+.B \-1UXpPhHrRoOdDqQsSv
+]
+[
+.B \-c\fIlimit
+]
+[
+.B \-C\fI[address[/len]:]limit
+]
+[
+.B \-e\fIname=[var]
+]
+[
+.B \-x\fIrules.cdb
+]
+[
+.B \-B\fIbanner
+]
+[
+.B \-g\fIgid
+]
+[
+.B \-u\fIuid
+]
+[
+.B \-b\fIbacklog
+]
+[
+.B \-l\fIlocalname
+]
+[
+.B \-t\fItimeout
+]
+[
+.B \-n\fIcertfile
+]
+.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 \-C\fI[address[/len]:]limit
+Do not handle more than
+.I limit
+connections from the specified address or prefix.
+If there is no address specified a generic per IP
+.I limit
+is enforced.
+It is possible to specify multiple
+.IR limits .
+These limits are evaluated in sequential order, from first to last.
+The first matching
+.I limit
+decides what action is taken.
+The only exception is the generic per IP
+.I limit
+which is always evaluated last.
+.TP
+.B \-e\fIname=[var]
+If a per address limit is hit instead of dropping the connection set the
+environment variable
+.I name
+with
+.I var
+as optional data.
+This may be handy with the qmail-ldap patched qmail-smtpd where setting
+the environment 421GREETING will result in a restricted SMTP session always
+returning a temporary error.
+.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 \-U
+Same as
+.B \-g\fI$GID
+.BR \-u\fI$UID .
+Typically
+.I $GID
+and
+.I $UID
+are set by envuidgid.
+.TP
+.B \-1
+After preparing to receive connections,
+print the local port number to standard output.
+.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 \-s
+Enable SSL/TLS mode. This modus needs a SSL enabled build and a certificat.
+.TP
+.B \-S
+(Default.)
+Don't enable SSL/TLS mode.
+.TP
+.B \-n\fIcertfile
+Instead of the default ./cert.pem certificate us the specified
+.IR certfile .
+.TP
+.B \-v
+Verbose.
+Print all available messages.
+.TP
+.B \-X
+With
+.BR -x\fIcdb ,
+allow connections even if
+.I cdb
+does not exist.
+Normally the connection gets dropped.
+.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 ENVIRONMENT
+.TP
+.B SSL_CIPHER
+Specifies the ciphers that should be used in SSL/TLS mode.
+See
+.I openssl(1)
+for more information.
+.SH "SEE ALSO"
+argv0(1),
+fixcr(1),
+recordio(1),
+tcpclient(1),
+tcprules(1),
+listen(2),
+tcp-environ(5),
+openssl(1)
Index: tcpserver.c
===================================================================
RCS file: /usr/home/cvs-djbware/CVS/ucspi-tcp/tcpserver.c,v
retrieving revision 1.1.1.1
retrieving revision 1.8
diff -u -p -r1.1.1.1 -r1.8
--- tcpserver.c	Sun Mar 19 00:18:42 2000
+++ tcpserver.c	Wed Aug 28 19:55:48 2013
@@ -1,6 +1,7 @@
 #include <sys/types.h>
 #include <sys/param.h>
 #include <netdb.h>
+#include <openssl/ssl.h>
 #include "uint16.h"
 #include "str.h"
 #include "byte.h"
@@ -36,6 +37,13 @@
 int flagremotehost = 1;
 int flagparanoid = 0;
 unsigned long timeout = 26;
+#ifdef WITH_SSL
+int flagssl = 0;
+struct stralloc certfile = {0};
+#define CERTFILE "./cert.pem"
+
+void translate(SSL*, int, int, unsigned int);
+#endif
 
 static stralloc tcpremoteinfo;
 
@@ -127,6 +135,9 @@
 	  env(data + 1,data + 1 + split + 1);
 	}
 	break;
+      case '-':
+	env(data + 1, (char *)0);
+	break;
     }
     ++next0;
     data += next0; datalen -= next0;
@@ -238,10 +249,13 @@
 
 void usage(void)
 {
+#ifndef WITH_SSL
   strerr_warn1("\
 tcpserver: usage: tcpserver \
 [ -1UXpPhHrRoOdDqQv ] \
 [ -c limit ] \
+[ -C [address[/len]:]limit ] \
+[ -e name=var ] \
 [ -x rules.cdb ] \
 [ -B banner ] \
 [ -g gid ] \
@@ -250,6 +264,23 @@
 [ -l localname ] \
 [ -t timeout ] \
 host port program",0);
+#else
+  strerr_warn1("\
+tcpserver: usage: tcpserver \
+[ -1UXpPhHrRoOdDqQsSv ] \
+[ -c limit ] \
+[ -C [address[/len]:]limit ] \
+[ -e name=var ] \
+[ -x rules.cdb ] \
+[ -B banner ] \
+[ -g gid ] \
+[ -u uid ] \
+[ -b backlog ] \
+[ -l localname ] \
+[ -t timeout ] \
+[ -n certfile ] \
+host port program",0);
+#endif
   _exit(100);
 }
 
@@ -274,12 +305,106 @@
   _exit(0);
 }
 
+struct conn {
+  int pid;
+  char remoteip[4];
+} *conns;
+
+struct ip_limelt {
+  char ip[4];
+  char mask[4];
+  unsigned long limit;
+  unsigned long count;
+};
+
+#include "gen_alloc.h"
+#include "gen_allocdefs.h"
+GEN_ALLOC_typedef(ip_limit,struct ip_limelt,l,len,a)
+GEN_ALLOC_readyplus(ip_limit, struct ip_limelt,l,len,a,i,n,x,10,
+  ip_limit_rp)
+
+ip_limit ipl = {0};
+unsigned long limit_ip = 0;
+
+void
+ip_limit_add(char *str)
+{
+  unsigned int n, len;
+  unsigned long ul = 0;
+  struct ip_limelt lim;
+
+  byte_zero(&lim, sizeof(lim));
+  n = str_chr(str, ':');
+  if (str[n] == ':') {
+    str[n] = 0;
+    scan_ulong(str + n + 1, &ul);
+    lim.limit = ul;
+    /* parse ip */
+    ul = 32;
+    n = str_chr(str, '/');
+    if (str[n] == '/')
+      scan_ulong(str + n + 1, &ul);
+    if (ul > 32)
+      strerr_die2x(111,FATAL,"ip prefix len > 32");
+    if (ip4_scan(str, lim.ip) == 0)
+      strerr_die2x(111,FATAL,"bad ip address");
+    for (n = 0; n < 4; n++)
+      if (ul > 8) {
+	lim.mask[n] = 0xff;
+	ul -= 8;
+      } else {
+	lim.mask[n] = 0xff << (8 - ul);
+	ul = 0;
+      }
+    if (!ip_limit_rp(&ipl,1))
+      strerr_die2x(111,FATAL,"out of memory");
+    ipl.l[ipl.len++] = lim;
+  } else {
+    scan_ulong(str, &ul);
+    limit_ip = ul;
+  }
+}
+
+int
+ip_limit_check(char ip[4], int d)
+{
+  unsigned long c;
+  unsigned int l;
+  int i;
+
+  /* global per ip limit */
+  for (l = 0, c= 0; l < limit; l++)
+    if (!byte_diff(conns[l].remoteip, sizeof(ip), ip))
+      c++;
+  if (limit_ip != 0 && c + d > limit_ip)
+    return 1;
+
+  for (l = 0; l < ipl.len; l++)
+    if ((ip[0] & ipl.l[l].mask[0]) == (ipl.l[l].ip[0] & ipl.l[l].mask[0]) &&
+        (ip[1] & ipl.l[l].mask[1]) == (ipl.l[l].ip[1] & ipl.l[l].mask[1]) &&
+        (ip[2] & ipl.l[l].mask[2]) == (ipl.l[l].ip[2] & ipl.l[l].mask[2]) &&
+        (ip[3] & ipl.l[l].mask[3]) == (ipl.l[l].ip[3] & ipl.l[l].mask[3])) {
+      if (ipl.l[l].count + d > ipl.l[l].limit)
+	return 1;
+      ipl.l[l].count += d;
+      return 0;
+    }
+
+  return 0;
+}
+
 void sigchld()
 {
   int wstat;
   int pid;
- 
+  unsigned int i;
+
   while ((pid = wait_nohang(&wstat)) > 0) {
+    for (i = 0; i < limit; i++)
+      if (conns[i].pid == pid) {
+	ip_limit_check(conns[i].remoteip, -1);
+	byte_zero(&conns[i], sizeof(struct conn));
+      }
     if (verbosity >= 2) {
       strnum[fmt_ulong(strnum,pid)] = 0;
       strnum2[fmt_ulong(strnum2,wstat)] = 0;
@@ -296,14 +421,32 @@
   int opt;
   struct servent *se;
   char *x;
+  char *iplimenv;
+  int flagiplim;
   unsigned long u;
   int s;
   int t;
- 
-  while ((opt = getopt(argc,argv,"dDvqQhHrR1UXx:t:u:g:l:b:B:c:pPoO")) != opteof)
+  unsigned int i;
+  int pid;
+#ifdef WITH_SSL
+  BIO *sbio;
+  SSL *ssl;
+  SSL_CTX *ctx;
+  int pi2c[2], pi4c[2];
+
+  ctx = NULL;
+  iplimenv = NULL;
+
+  if (!stralloc_copys(&certfile, CERTFILE) || !stralloc_0(&certfile) )
+    strerr_die2x(111,FATAL,"out of memory");
+  while ((opt = getopt(argc,argv,"dDvqQhHrRsS1UXx:t:u:g:l:b:B:c:C:e:n:pPoO")) != opteof)
+#else
+  while ((opt = getopt(argc,argv,"dDvqQhHrR1UXx:t:u:g:l:b:B:c:C:e:pPoO")) != opteof)
+#endif
     switch(opt) {
       case 'b': scan_ulong(optarg,&backlog); break;
       case 'c': scan_ulong(optarg,&limit); break;
+      case 'C': ip_limit_add(optarg); break;
       case 'X': flagallownorules = 1; break;
       case 'x': fnrules = optarg; break;
       case 'B': banner = optarg; break;
@@ -327,6 +470,18 @@
       case 'g': scan_ulong(optarg,&gid); break;
       case '1': flag1 = 1; break;
       case 'l': localhost = optarg; break;
+      case 'e': iplimenv = optarg;
+		if (iplimenv[str_chr(iplimenv, '=')] != '=')
+		  strerr_die2x(100,FATAL, "no '=' in ip limit env-var");
+		break;
+#ifdef WITH_SSL
+      case 's': flagssl = 1; break;
+      case 'S': flagssl = 0; break;
+      case 'n': if (!stralloc_copys(&certfile, optarg) ||
+		    !stralloc_0(&certfile) )
+		  strerr_die2x(111,FATAL,"out of memory");
+		break;
+#endif
       default: usage();
     }
   argc -= optind;
@@ -334,6 +489,15 @@
 
   if (!verbosity)
     buffer_2->fd = -1;
+
+  if (limit == 0)
+    strerr_die2x(100,FATAL,"limit may not be set to 0");
+  if (limit > 65000)
+    strerr_die2x(100,FATAL,"limit way to high");
+  conns = (struct conn *)alloc(limit * sizeof(struct conn));
+  if (!conns)
+    strerr_die2x(111,FATAL,"out of memory");
+  byte_zero(conns, limit * sizeof(struct conn));
  
   hostname = *argv++;
   if (!hostname) usage();
@@ -366,6 +530,25 @@
     strerr_die3x(111,FATAL,"no IP address for ",hostname);
   byte_copy(localip,4,addresses.s);
 
+#ifdef WITH_SSL
+  if (flagssl == 1) {
+    /* setup SSL context (load key and cert into ctx) */
+    SSL_library_init();
+    ctx=SSL_CTX_new(SSLv23_server_method());
+    if (!ctx) strerr_die2x(111,FATAL,"unable to create SSL context");
+
+    /* set prefered ciphers */
+    if (env_get("SSL_CIPHER"))
+      if (SSL_CTX_set_cipher_list(ctx, env_get("SSL_CIPHER")) == 0)
+	strerr_die2x(111,FATAL,"unable to set cipher list");
+
+    if(SSL_CTX_use_RSAPrivateKey_file(ctx, certfile.s, SSL_FILETYPE_PEM) != 1)
+      strerr_die2x(111,FATAL,"unable to load RSA private key");
+    if(SSL_CTX_use_certificate_chain_file(ctx, certfile.s) != 1)
+      strerr_die2x(111,FATAL,"unable to load certificate");
+  }
+#endif
+  
   s = socket_tcp();
   if (s == -1)
     strerr_die2sys(111,FATAL,"unable to create socket: ");
@@ -404,9 +587,31 @@
 
     if (t == -1) continue;
     ++numchildren; printstatus();
+
+    /* per ip handling */
+    flagiplim = 0;
+    if (ip_limit_check(remoteip, 1)) {
+      remoteipstr[ip4_fmt(remoteipstr,remoteip)] = 0;
+      if (iplimenv) {
+        strerr_warn4(DROP,"too many conections from ",remoteipstr,
+	    " only flagged",0);
+	flagiplim = 1;
+      } else {
+        strerr_warn3(DROP,"too many conections from ",remoteipstr,0);
+        --numchildren; printstatus();
+        close(t);
+        continue;
+      }
+    }
  
-    switch(fork()) {
+    switch(pid = fork()) {
       case 0:
+	if (flagiplim) {
+          int split;
+      	  split = str_chr(iplimenv,'=');
+	  iplimenv[split] = 0;
+	  env(iplimenv,iplimenv + split + 1);
+	}
         close(s);
         doit(t);
         if ((fd_move(0,t) == -1) || (fd_copy(1,0) == -1))
@@ -415,12 +620,159 @@
         sig_unblock(sig_child);
         sig_uncatch(sig_term);
         sig_uncatch(sig_pipe);
+#ifdef WITH_SSL
+	if (flagssl == 1) {
+	  if (pipe(pi2c) != 0)
+	    strerr_die2sys(111,DROP,"unable to create pipe: ");
+	  if (pipe(pi4c) != 0)
+	    strerr_die2sys(111,DROP,"unable to create pipe: ");
+	  switch(fork()) {
+	    case 0:
+	      close(0); close(1);
+	      close(pi2c[1]);
+	      close(pi4c[0]);
+	      if ((fd_move(0,pi2c[0]) == -1) || (fd_move(1,pi4c[1]) == -1))
+		strerr_die2sys(111,DROP,"unable to set up descriptors: ");
+	      /* signals are allready set in the parent */
+	      pathexec(argv);
+	      strerr_die4sys(111,DROP,"unable to run ",*argv,": ");
+	    case -1:
+	      strerr_die2sys(111,DROP,"unable to fork: ");
+	    default:
+	      ssl = SSL_new(ctx);
+	      if (!ssl)
+		strerr_die2x(111,DROP,"unable to set up SSL session");
+	      sbio = BIO_new_socket(0,BIO_NOCLOSE);
+	      if (!sbio)
+		strerr_die2x(111,DROP,"unable to set up BIO socket");
+	      SSL_set_bio(ssl,sbio,sbio);
+	      close(pi2c[0]);
+	      close(pi4c[1]);
+	      translate(ssl, pi2c[1], pi4c[0], 3600);
+	      _exit(0);
+	  }
+	}
+#endif
         pathexec(argv);
 	strerr_die4sys(111,DROP,"unable to run ",*argv,": ");
       case -1:
         strerr_warn2(DROP,"unable to fork: ",&strerr_sys);
         --numchildren; printstatus();
+	break;
+      default:
+	for (i = 0; i < limit; i++)
+	  if (conns[i].pid == 0) {
+	    conns[i].pid = pid;
+	    byte_copy(conns[i].remoteip, sizeof(remoteip), remoteip);
+	    break;
+	  }
     }
     close(t);
   }
 }
+
+#ifdef WITH_SSL
+static int allwrite(int fd, char *buf, int len)
+{
+  int w;
+
+  while (len) {
+    w = write(fd,buf,len);
+    if (w == -1) {
+      if (errno == error_intr) continue;
+      return -1; /* note that some data may have been written */
+    }
+    if (w == 0) ; /* luser's fault */
+    buf += w;
+    len -= w;
+  }
+  return 0;
+}
+
+static int allwritessl(SSL* ssl, char *buf, int len)
+{
+  int w;
+
+  while (len) {
+    w = SSL_write(ssl,buf,len);
+    if (w == -1) {
+      if (errno == error_intr) continue;
+      return -1; /* note that some data may have been written */
+    }
+    if (w == 0) ; /* luser's fault */
+    buf += w;
+    len -= w;
+  }
+  return 0;
+}
+
+char tbuf[2048];
+
+void translate(SSL* ssl, int clearout, int clearin, unsigned int iotimeout)
+{
+  struct taia now;
+  struct taia deadline;
+  iopause_fd iop[2];
+  int flagexitasap;
+  int iopl;
+  int sslout, sslin;
+  int n, r;
+
+  sslin = SSL_get_fd(ssl);
+  sslout = SSL_get_fd(ssl);
+  if (sslin == -1 || sslout == -1)
+    strerr_die2x(111,DROP,"unable to set up SSL connection");
+  
+  flagexitasap = 0;
+
+  if (SSL_accept(ssl)<=0)
+    strerr_die2x(111,DROP,"unable to accept SSL connection");
+
+  while (!flagexitasap) {
+    taia_now(&now);
+    taia_uint(&deadline,iotimeout);
+    taia_add(&deadline,&now,&deadline);
+
+    /* fill iopause struct */
+    iopl = 2;
+    iop[0].fd = sslin;
+    iop[0].events = IOPAUSE_READ;
+    iop[1].fd = clearin;
+    iop[1].events = IOPAUSE_READ;
+
+    /* do iopause read */
+    iopause(iop,iopl,&deadline,&now);
+    if (iop[0].revents) {
+      do {
+        /* data on sslin */
+        n = SSL_read(ssl, tbuf, sizeof(tbuf));
+        if ( n < 0 )
+	  strerr_die2sys(111,DROP,"unable to read form network: ");
+        if ( n == 0 )
+	  flagexitasap = 1;
+        r = allwrite(clearout, tbuf, n);
+        if ( r < 0 )
+	  strerr_die2sys(111,DROP,"unable to write to client: ");
+	/*
+	 * if the data payload was longer than sizeof(tbuf) then SSL will have
+	 * bytes processed and pending. We need to pick them up and write them
+	 * to clearout.
+	 */
+      } while (SSL_pending(ssl));
+    }
+    if (iop[1].revents) {
+      /* data on clearin */
+      n = read(clearin, tbuf, sizeof(tbuf));
+      if ( n < 0 )
+	strerr_die2sys(111,DROP,"unable to read form client: ");
+      if ( n == 0 )
+	flagexitasap = 1;
+      r = allwritessl(ssl, tbuf, n);
+      if ( r < 0 )
+	strerr_die2sys(111,DROP,"unable to write to network: ");
+    }
+    if (!iop[0].revents && !iop[1].revents)
+      strerr_die2x(0, DROP,"timeout reached without input");
+  }
+}
+#endif
Index: who@.1
===================================================================
RCS file: who@.1
diff -N who@.1
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ who@.1	5 Apr 2004 15:34:57 -0000	1.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)
