summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile351
-rw-r--r--src/TARGETS62
-rw-r--r--src/auto-str.c42
-rw-r--r--src/auto_cadir.h6
-rw-r--r--src/auto_cafile.h6
-rw-r--r--src/auto_ccafile.h6
-rw-r--r--src/auto_certchainfile.h6
-rw-r--r--src/auto_certfile.h6
-rw-r--r--src/auto_ciphers.h6
-rw-r--r--src/auto_dhfile.h6
-rw-r--r--src/auto_keyfile.h6
-rw-r--r--src/chkshsgr.c14
-rw-r--r--src/choose.sh18
-rw-r--r--src/coe.c9
-rw-r--r--src/coe.h8
-rw-r--r--src/exit.h6
-rw-r--r--src/exp.base325
-rw-r--r--src/exp.it0
-rw-r--r--src/exp.sslperl105
-rw-r--r--src/find-systype.sh151
-rw-r--r--src/fork.h19
-rw-r--r--src/fork.h29
-rw-r--r--src/hassgact.h13
-rw-r--r--src/hassgact.h24
-rw-r--r--src/hassgprm.h13
-rw-r--r--src/hassgprm.h24
-rw-r--r--src/hasshsgr.h13
-rw-r--r--src/hasshsgr.h24
-rw-r--r--src/haswaitp.h13
-rw-r--r--src/haswaitp.h24
-rw-r--r--src/https@.sh15
-rw-r--r--src/ip4_bit.c101
-rw-r--r--src/ip6_bit.c180
-rw-r--r--src/ip_bit.h15
-rw-r--r--src/it-base=d7
-rw-r--r--src/it-sslperl=d1
-rw-r--r--src/it-sys=d1
-rw-r--r--src/it=d1
-rw-r--r--src/print-ar.sh14
-rw-r--r--src/print-cc.sh62
-rw-r--r--src/print-ccperl.sh10
-rw-r--r--src/print-dl.sh14
-rw-r--r--src/print-ld.sh18
-rw-r--r--src/print-ldperl.sh10
-rw-r--r--src/print-perlembed.sh10
-rw-r--r--src/remoteinfo.c102
-rw-r--r--src/remoteinfo.h9
-rw-r--r--src/rts.base329
-rw-r--r--src/rts.it197
-rw-r--r--src/rts.sslperl157
-rw-r--r--src/rules.c141
-rw-r--r--src/rules.h9
-rw-r--r--src/select.h112
-rw-r--r--src/select.h213
-rw-r--r--src/ssl_ca.c11
-rw-r--r--src/ssl_cca.c18
-rw-r--r--src/ssl_certkey.c19
-rw-r--r--src/ssl_chainfile.c24
-rw-r--r--src/ssl_ciphers.c21
-rw-r--r--src/ssl_context.c34
-rw-r--r--src/ssl_env.c435
-rw-r--r--src/ssl_error.c12
-rw-r--r--src/ssl_io.c269
-rw-r--r--src/ssl_new.c16
-rw-r--r--src/ssl_params.c80
-rw-r--r--src/ssl_timeout.c125
-rw-r--r--src/ssl_verify.c63
-rw-r--r--src/sslcat.sh9
-rw-r--r--src/sslclient.c449
-rw-r--r--src/sslconnect.sh9
-rw-r--r--src/sslhandle.c887
-rw-r--r--src/sslperl.c105
-rw-r--r--src/sslprint.c411
-rw-r--r--src/sslserver.c991
-rw-r--r--src/trycpp.c9
-rw-r--r--src/trylsock.c4
-rw-r--r--src/trysgact.c12
-rw-r--r--src/trysgprm.c12
-rw-r--r--src/tryshsgr.c16
-rw-r--r--src/tryssl.c6
-rw-r--r--src/trysysel.c11
-rw-r--r--src/tryvfork.c4
-rw-r--r--src/ucspissl.c4
-rw-r--r--src/ucspissl.h70
-rw-r--r--src/warn-auto.sh2
-rw-r--r--src/warn-shsgr3
-rw-r--r--src/x86cpuid.c40
87 files changed, 6794 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..f1f124b
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,351 @@
+# Don't edit Makefile! Use conf-* for configuration.
+
+SHELL=/bin/sh
+
+default: it
+
+auto-str: \
+load auto-str.o
+ ./load auto-str
+
+auto-str.o: \
+compile auto-str.c
+ ./compile auto-str.c
+
+auto_cadir.c: \
+auto-str ../conf-cadir
+ ./auto-str auto_cadir "`head -1 ../conf-cadir`" > auto_cadir.c
+
+auto_cadir.o: \
+compile auto_cadir.c
+ ./compile auto_cadir.c
+
+auto_cafile.c: \
+auto-str ../conf-cafile
+ ./auto-str auto_cafile "`head -1 ../conf-cafile`" > auto_cafile.c
+
+auto_cafile.o: \
+compile auto_cafile.c
+ ./compile auto_cafile.c
+
+auto_ccafile.c: \
+auto-str ../conf-ccafile
+ ./auto-str auto_ccafile "`head -1 ../conf-ccafile`" > auto_ccafile.c
+
+auto_ccafile.o: \
+compile auto_ccafile.c
+ ./compile auto_ccafile.c
+
+auto_certchainfile.c: \
+auto-str ../conf-certchainfile
+ ./auto-str auto_certchainfile "`head -1 ../conf-certchainfile`" > auto_certchainfile.c
+
+auto_certchainfile.o: \
+compile auto_certchainfile.c
+ ./compile auto_certchainfile.c
+
+auto_certfile.c: \
+auto-str ../conf-certfile
+ ./auto-str auto_certfile "`head -1 ../conf-certfile`" > auto_certfile.c
+
+auto_certfile.o: \
+compile auto_certfile.c
+ ./compile auto_certfile.c
+
+auto_ciphers.c: \
+auto-str ../conf-ciphers
+ ./auto-str auto_ciphers "`head -1 ../conf-ciphers`" > auto_ciphers.c
+
+auto_ciphers.o: \
+compile auto_ciphers.c
+ ./compile auto_ciphers.c
+
+auto_dhfile.c: \
+auto-str ../conf-dhfile
+ ./auto-str auto_dhfile "`head -1 ../conf-dhfile`" > auto_dhfile.c
+
+auto_dhfile.o: \
+compile auto_dhfile.c
+ ./compile auto_dhfile.c
+
+auto_keyfile.c: \
+auto-str ../conf-keyfile
+ ./auto-str auto_keyfile "`head -1 ../conf-keyfile`" > auto_keyfile.c
+
+auto_keyfile.o: \
+compile auto_keyfile.c
+ ./compile auto_keyfile.c
+
+ccperl: \
+../conf-ccperl ../conf-perl print-ccperl.sh
+ rm -f ccperl
+ sh print-ccperl.sh > ccperl
+
+chkshsgr: \
+load chkshsgr.o
+ ./load chkshsgr
+
+chkshsgr.o: \
+compile chkshsgr.c
+ ./compile chkshsgr.c
+
+choose: \
+warn-auto.sh choose.sh
+ rm -f choose
+ cat warn-auto.sh choose.sh \
+ | sed s}HOME}"`head -1 ../conf-home`"}g \
+ > choose
+ chmod 755 choose
+
+coe.o: \
+compile coe.c coe.h
+ ./compile coe.c
+
+compile: \
+../conf-cc ../conf-ssl print-cc.sh systype warn-auto.sh
+ rm -f compile
+ sh print-cc.sh > compile
+ chmod 755 compile
+
+hassgact.h: \
+choose compile trysgact.c hassgact.h1 hassgact.h2
+ ./choose cl trysgact hassgact.h1 hassgact.h2 > hassgact.h
+
+hassgprm.h: \
+choose compile trysgprm.c hassgprm.h1 hassgprm.h2
+ ./choose cl trysgprm hassgprm.h1 hassgprm.h2 > hassgprm.h
+
+hasshsgr.h: \
+choose compile tryshsgr.c hasshsgr.h1 hasshsgr.h2 chkshsgr warn-shsgr
+ ./chkshsgr || ( cat warn-shsgr; exit 1 )
+ ./choose clr tryshsgr hasshsgr.h1 hasshsgr.h2 > hasshsgr.h
+
+https@: warn-auto.sh https@.sh
+ rm -f https@
+ cat warn-auto.sh https@.sh \
+ | sed s}HOME}"`head -1 ../conf-home`"}g \
+ > https@
+ chmod 755 https@
+
+it: it-sys it-base it-sslperl
+
+it-base: sslclient sslserver https@ sslcat sslconnect sslprint sslhandle
+
+it-sslperl: sslperl
+
+it-sys: sysdeps auto-str
+
+load: \
+../conf-ld print-ld.sh systype warn-auto.sh
+ rm -f load
+ sh print-ld.sh > load
+ chmod 755 load
+
+makelib: \
+print-ar.sh systype warn-auto.sh
+ rm -f makelib
+ sh print-ar.sh > makelib
+ chmod 755 makelib
+
+perlembed.lib: \
+../conf-perl ../conf-ldperl print-perlembed.sh
+ rm -f perlembed.lib
+ sh print-ldperl.sh > perlembed.lib
+
+remoteinfo.o: \
+compile remoteinfo.c remoteinfo.h
+ ./compile remoteinfo.c
+
+rules.o: \
+compile rules.c rules.h ip4_bit.c ip6_bit.c ip_bit.h
+ ./compile rules.c ip4_bit.c ip6_bit.c ip_bit.h
+
+socket.lib: \
+trylsock.c compile load
+ ( ( ./compile trylsock.c && \
+ ./load trylsock -lsocket -lnsl ) >/dev/null 2>&1 \
+ && echo -lsocket -lnsl || exit 0 ) > socket.lib
+ rm -f trylsock.o trylsock
+
+ssl.lib: \
+../conf-ssllib print-dl.sh
+ rm -f ssl.lib
+ sh print-dl.sh > ssl.lib
+ chmod 755 ssl.lib
+
+ssl_ca.o: \
+compile ssl_ca.c ucspissl.h
+ ./compile ssl_ca.c
+
+ssl_cca.o: \
+compile ssl_cca.c ucspissl.h
+ ./compile ssl_cca.c
+
+ssl_chainfile.o: \
+compile ssl_chainfile.c ucspissl.h
+ ./compile ssl_chainfile.c
+
+ssl_certkey.o: \
+compile ssl_certkey.c ucspissl.h
+ ./compile ssl_certkey.c
+
+ssl_ciphers.o: \
+compile ssl_ciphers.c ucspissl.h
+ ./compile ssl_ciphers.c
+
+ssl_context.o: \
+compile ssl_context.c ucspissl.h
+ ./compile ssl_context.c
+
+ssl_env.o: \
+compile ssl_env.c ucspissl.h
+ ./compile ssl_env.c
+
+ssl_error.o: \
+compile ssl_error.c ucspissl.h
+ ./compile ssl_error.c
+
+ssl_io.o: \
+compile ssl_io.c ucspissl.h
+ ./compile ssl_io.c
+
+ssl_new.o: \
+compile ssl_new.c ucspissl.h
+ ./compile ssl_new.c
+
+ssl_params.o: \
+compile ssl_params.c ucspissl.h
+ ./compile ssl_params.c
+
+ssl_timeout.o: \
+compile ssl_timeout.c ucspissl.h
+ ./compile ssl_timeout.c
+
+ssl_verify.o: \
+compile ssl_verify.c ucspissl.h
+ ./compile ssl_verify.c
+
+sslcat: \
+warn-auto.sh sslcat.sh
+ rm -f sslcat
+ cat warn-auto.sh sslcat.sh \
+ | sed s}HOME}"`head -1 ../conf-home`"}g \
+ > sslcat
+ chmod 755 sslcat
+
+sslclient: \
+load sslclient.o auto_cafile.o auto_cadir.o auto_ciphers.o \
+remoteinfo.o ucspissl.a socket.lib ssl.lib
+ ./load sslclient auto_cafile.o auto_cadir.o auto_ciphers.o \
+ remoteinfo.o ucspissl.a \
+ `cat socket.lib` `cat ssl.lib`
+
+sslclient.o: \
+compile sslclient.c auto_cadir.h auto_cafile.h auto_ciphers.h \
+remoteinfo.h ucspissl.h
+ ./compile sslclient.c
+
+sslconnect: \
+warn-auto.sh sslconnect.sh
+ rm -f sslconnect
+ cat warn-auto.sh sslconnect.sh \
+ | sed s}HOME}"`head -1 ../conf-home`"}g \
+ > sslconnect
+ chmod 755 sslconnect
+
+sslhandle: \
+load sslhandle.o auto_cafile.o auto_ccafile.o auto_cadir.o \
+auto_certchainfile.o auto_dhfile.o \
+auto_certfile.o auto_keyfile.o auto_ciphers.o \
+coe.o rules.o ip4_bit.o ip6_bit.o remoteinfo.o sslprint.o \
+ucspissl.a socket.lib ssl.lib
+ ./load sslhandle auto_cafile.o auto_ccafile.o auto_cadir.o \
+ auto_dhfile.o auto_ciphers.o \
+ auto_certchainfile.o auto_certfile.o auto_keyfile.o \
+ coe.o rules.o ip4_bit.o ip6_bit.o remoteinfo.o sslprint.o \
+ ucspissl.a `cat socket.lib` `cat ssl.lib`
+
+sslhandle.o: \
+compile sslhandle.c auto_cadir.h auto_cafile.h auto_ccafile.h \
+auto_certchainfile.h auto_certfile.h auto_ciphers.h \
+auto_dhfile.h auto_keyfile.h rules.h ip_bit.h ucspissl.h coe.h \
+remoteinfo.o rules.o ip4_bit.o ip6_bit.o ucspissl.a
+ ./compile sslhandle.c
+
+sslperl: \
+load sslperl.o ucspissl.a sslhandle.o \
+auto_cafile.o auto_ccafile.o auto_cadir.o \
+auto_dhfile.o auto_certfile.o auto_keyfile.o \
+auto_ciphers.o auto_certchainfile.o \
+coe.o rules.o remoteinfo.o ip4_bit.o ip6_bit.o \
+socket.lib ssl.lib perlembed.lib
+ ./load sslperl auto_cafile.o auto_ccafile.o auto_cadir.o \
+ auto_dhfile.o auto_certfile.o auto_keyfile.o \
+ auto_ciphers.o auto_certchainfile.o ucspissl.a sslhandle.o \
+ rules.o ip4_bit.o ip6_bit.o remoteinfo.o coe.o \
+ ucspissl.a `cat socket.lib` `cat ssl.lib` `cat perlembed.lib`
+
+sslperl.o: \
+compile ccperl sslperl.c sslperl.c ucspissl.h
+ ./compile `cat ccperl` sslperl.c
+
+sslprint: \
+load sslprint.o auto_cafile.o auto_ccafile.o auto_cadir.o \
+auto_dhfile.o auto_certfile.o auto_keyfile.o \
+auto_ciphers.o auto_certchainfile.o coe.o sslhandle.o \
+rules.o ip4_bit.o ip6_bit.o remoteinfo.o \
+ucspissl.a socket.lib ssl.lib
+ ./load sslprint auto_cafile.o auto_ccafile.o auto_cadir.o \
+ auto_dhfile.o auto_certfile.o auto_keyfile.o \
+ auto_ciphers.o auto_certchainfile.o \
+ rules.o ip4_bit.o ip6_bit.o remoteinfo.o coe.o sslhandle.o \
+ ucspissl.a `cat socket.lib` `cat ssl.lib`
+
+sslprint.o: \
+compile sslprint.c
+ ./compile sslprint.c
+
+sslserver: \
+load sslserver.o auto_cafile.o auto_ccafile.o auto_cadir.o \
+auto_certchainfile.o auto_dhfile.o \
+auto_certfile.o auto_keyfile.o auto_ciphers.o \
+rules.o ip4_bit.o ip6_bit.o remoteinfo.o \
+ucspissl.a socket.lib ssl.lib
+ ./load sslserver auto_cafile.o auto_ccafile.o auto_cadir.o \
+ auto_dhfile.o auto_ciphers.o \
+ auto_certchainfile.o auto_certfile.o auto_keyfile.o \
+ rules.o ip4_bit.o ip6_bit.o remoteinfo.o ucspissl.a \
+ `cat socket.lib` `cat ssl.lib`
+
+sslserver.o: \
+compile sslserver.c auto_cadir.h auto_cafile.h auto_ccafile.h \
+auto_certchainfile.h auto_certfile.h auto_ciphers.h \
+auto_dhfile.h auto_keyfile.h \
+remoteinfo.h rules.h ip_bit.h ucspissl.h
+ ./compile sslserver.c
+
+sysdeps: \
+systype compile load hassgact.h hassgprm.h
+ rm -f sysdeps
+ cat systype compile load >> sysdeps
+ grep sysdep hassgact.h >> sysdeps
+ grep sysdep hassgprm.h >> sysdeps
+
+systype: \
+find-systype.sh trycpp.c x86cpuid.c
+ sh find-systype.sh > systype
+
+ucspissl.a: \
+makelib ssl_ca.o ssl_cca.o ssl_certkey.o ssl_chainfile.o ssl_ciphers.o \
+ssl_context.o ssl_env.o ssl_error.o ssl_io.o ssl_new.o ssl_params.o \
+ssl_timeout.o ssl_verify.o ucspissl.o
+ ./makelib ucspissl.a ssl_ca.o ssl_cca.o ssl_certkey.o ssl_chainfile.o \
+ ssl_ciphers.o ssl_context.o ssl_env.o ssl_error.o ssl_io.o ssl_new.o \
+ ssl_params.o ssl_timeout.o ssl_verify.o ucspissl.o
+
+ucspissl.o: \
+compile ucspissl.c ucspissl.h
+ ./compile ucspissl.c
+
+clean:
+ rm -f `cat TARGETS`
diff --git a/src/TARGETS b/src/TARGETS
new file mode 100644
index 0000000..0c8ccf5
--- /dev/null
+++ b/src/TARGETS
@@ -0,0 +1,62 @@
+auto-str
+auto-str.o
+auto_cadir.c
+auto_cadir.o
+auto_cafile.c
+auto_cafile.o
+auto_ccafile.c
+auto_ccafile.o
+auto_certchainfile.c
+auto_certchainfile.o
+auto_certfile.c
+auto_certfile.o
+auto_ciphers.c
+auto_ciphers.o
+auto_dhfile.c
+auto_dhfile.o
+auto_keyfile.c
+auto_keyfile.o
+ccperl
+choose
+coe.o
+compile
+hasgact.h
+hasgprm.h
+https@
+load
+makelib
+perlembed.lib
+remoteinfo.o
+rules.o
+socket.lib
+ssl.lib
+ssl_ca.o
+ssl_cca.o
+ssl_certkey.o
+ssl_chainfile.o
+ssl_ciphers.o
+ssl_context.o
+ssl_env.o
+ssl_error.o
+ssl_io.o
+ssl_new.o
+ssl_params.o
+ssl_timeout.o
+ssl_verify.o
+sslcat
+sslclient
+sslclient.o
+sslconnect
+sslhandle.o
+sslperl
+sslperl.o
+sslprint
+sslprint.o
+sslserver
+sslserver.o
+sysdeps
+systype
+tryssl.o
+ucspissl.a
+ucspissl.o
+*.gch
diff --git a/src/auto-str.c b/src/auto-str.c
new file mode 100644
index 0000000..4086921
--- /dev/null
+++ b/src/auto-str.c
@@ -0,0 +1,42 @@
+/* what to do */
+#include "readwrite.h"
+#include "exit.h"
+#include "buffer.h"
+
+char bspace[BUFFER_SMALL];
+buffer b = BUFFER_INIT(buffer_unixwrite,1,bspace,sizeof(bspace));
+
+static void outs(const char *s)
+{
+ if (buffer_puts(&b,s) == -1) _exit(111);
+}
+
+int main(int argc,char **argv)
+{
+ char *name;
+ char *value;
+ unsigned char ch;
+ char octal[4];
+
+ name = argv[1];
+ if (!name) _exit(100);
+ value = argv[2];
+ if (!value) _exit(100);
+
+ outs("const char ");
+ outs(name);
+ outs("[] = \"\\\n");
+
+ while ((ch = *value++)) {
+ outs("\\");
+ octal[3] = 0;
+ octal[2] = '0' + (ch & 7); ch >>= 3;
+ octal[1] = '0' + (ch & 7); ch >>= 3;
+ octal[0] = '0' + (ch & 7);
+ outs(octal);
+ }
+
+ outs("\\\n\";\n");
+ if (buffer_flush(&b) == -1) _exit(111);
+ _exit(0);
+}
diff --git a/src/auto_cadir.h b/src/auto_cadir.h
new file mode 100644
index 0000000..9d9dfe2
--- /dev/null
+++ b/src/auto_cadir.h
@@ -0,0 +1,6 @@
+#ifndef AUTO_CADIR_H
+#define AUTO_CADIR_H
+
+extern const char auto_cadir[];
+
+#endif
diff --git a/src/auto_cafile.h b/src/auto_cafile.h
new file mode 100644
index 0000000..102ca55
--- /dev/null
+++ b/src/auto_cafile.h
@@ -0,0 +1,6 @@
+#ifndef AUTO_CAFILE_H
+#define AUTO_CAFILE_H
+
+extern const char auto_cafile[];
+
+#endif
diff --git a/src/auto_ccafile.h b/src/auto_ccafile.h
new file mode 100644
index 0000000..9d39c72
--- /dev/null
+++ b/src/auto_ccafile.h
@@ -0,0 +1,6 @@
+#ifndef AUTO_CCAFILE_H
+#define AUTO_CCAFILE_H
+
+extern const char auto_ccafile[];
+
+#endif
diff --git a/src/auto_certchainfile.h b/src/auto_certchainfile.h
new file mode 100644
index 0000000..31d4df8
--- /dev/null
+++ b/src/auto_certchainfile.h
@@ -0,0 +1,6 @@
+#ifndef AUTO_CERTCHAINFILE_H
+#define AUTO_CERTCHAINFILE_H
+
+extern const char auto_certchainfile[];
+
+#endif
diff --git a/src/auto_certfile.h b/src/auto_certfile.h
new file mode 100644
index 0000000..add5826
--- /dev/null
+++ b/src/auto_certfile.h
@@ -0,0 +1,6 @@
+#ifndef AUTO_CERTFILE_H
+#define AUTO_CERTFILE_H
+
+extern const char auto_certfile[];
+
+#endif
diff --git a/src/auto_ciphers.h b/src/auto_ciphers.h
new file mode 100644
index 0000000..2842fbd
--- /dev/null
+++ b/src/auto_ciphers.h
@@ -0,0 +1,6 @@
+#ifndef AUTO_CIPHERS_H
+#define AUTO_CIPHERS_H
+
+extern const char auto_ciphers[];
+
+#endif
diff --git a/src/auto_dhfile.h b/src/auto_dhfile.h
new file mode 100644
index 0000000..83afa2a
--- /dev/null
+++ b/src/auto_dhfile.h
@@ -0,0 +1,6 @@
+#ifndef AUTO_DHFILE_H
+#define AUTO_DHFILE_H
+
+extern const char auto_dhfile[];
+
+#endif
diff --git a/src/auto_keyfile.h b/src/auto_keyfile.h
new file mode 100644
index 0000000..feac74f
--- /dev/null
+++ b/src/auto_keyfile.h
@@ -0,0 +1,6 @@
+#ifndef AUTO_KEYFILE_H
+#define AUTO_KEYFILE_H
+
+extern const char auto_keyfile[];
+
+#endif
diff --git a/src/chkshsgr.c b/src/chkshsgr.c
new file mode 100644
index 0000000..12442ea
--- /dev/null
+++ b/src/chkshsgr.c
@@ -0,0 +1,14 @@
+/* Public domain. */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include "exit.h"
+
+int main()
+{
+ short x[4];
+
+ x[0] = x[1] = 0;
+ if (getgroups(1,x) == 0) if (setgroups(1,x) == -1) _exit(1);
+ _exit(0);
+}
diff --git a/src/choose.sh b/src/choose.sh
new file mode 100644
index 0000000..feff2da
--- /dev/null
+++ b/src/choose.sh
@@ -0,0 +1,18 @@
+
+result="$4"
+
+case "$1" in
+ *c*) ./compile $2.c >/dev/null 2>&1 || result="$3" ;;
+esac
+
+case "$1" in
+ *l*) ./load $2 >/dev/null 2>&1 || result="$3" ;;
+esac
+
+case "$1" in
+ *r*) ./$2 >/dev/null 2>&1 || result="$3" ;;
+esac
+
+rm -f $2.o $2
+
+exec cat "$result"
diff --git a/src/coe.c b/src/coe.c
new file mode 100644
index 0000000..50b2397
--- /dev/null
+++ b/src/coe.c
@@ -0,0 +1,9 @@
+/* Public domain. */
+
+#include <fcntl.h>
+#include "coe.h"
+
+int coe(int fd)
+{
+ return fcntl(fd,F_SETFD,1);
+}
diff --git a/src/coe.h b/src/coe.h
new file mode 100644
index 0000000..b17db54
--- /dev/null
+++ b/src/coe.h
@@ -0,0 +1,8 @@
+/* Public domain. */
+
+#ifndef COE_H
+#define COE_H
+
+extern int coe(int);
+
+#endif
diff --git a/src/exit.h b/src/exit.h
new file mode 100644
index 0000000..39011c8
--- /dev/null
+++ b/src/exit.h
@@ -0,0 +1,6 @@
+#ifndef EXIT_H
+#define EXIT_H
+
+extern void _exit();
+
+#endif
diff --git a/src/exp.base b/src/exp.base
new file mode 100644
index 0000000..c182da0
--- /dev/null
+++ b/src/exp.base
@@ -0,0 +1,325 @@
+---> test sslserver + sslclient: four instances of sslserver (ports 50013, 50014, 50015, 50016) are used
+---> sslserver @port 50015 requires client certs
+++++
+---> test sslclient/sslserver behavior with wrong parm (timeout 2 secs)
+++++
+--- sslclient prints usage message without enough arguments
+sslclient: usage: sslclient [ -463hHrRdDiqQveEsSnNxX ] [ -i localip ] [ -p localport ] [ -T timeoutconn ] [ -l localname ] [ -t timeoutinfo ] [ -I interface ] [ -a cafile ] [ -A cadir ] [ -c certfile ] [ -z ciphers ] [ -k keyfile ] [ -V verifydepth ] [ -w progtimeout ] host port program
+100
+--- sslclient prints error message with unknown port name
+sslclient: fatal: (111) unable to figure out port number for nonexistentport
+111
+--- sslclient prints error message when connection fails
+sslclient: drop: (110) unable to connect to: 127.0.0.1 port: 16
+110
+--- sslclient -q does not print error message when connection fails
+110
+--- sslclient prints error message with unknown host name
+sslclient: error: (111) No IP address for: nonexistent.local.
+111
+--- sslclient prints error message with unresolvable host name
+sslclient: error: (111) No IP address for: thislabelistoolongbecausednshasalimitof63charactersinasinglelabel.
+111
+--- sslserver prints usage message without enough arguments
+sslserver: usage: sslserver [ -1346UXpPhHrRoOdDqQvVIeEsSnNmzZ ] [ -c limit ] [ -x rules.cdb ] [ -B banner ] [ -g gid ] [ -u uid ] [ -b backlog ] [ -l localname ] [ -t timeout ] [ -I interface ] [ -T ssltimeout ] [ -w progtimeout ] host port program
+100
+--- sslserver prints error message with unknown port name
+sslserver: fatal: (111) unable to figure out port number for: nonexistentport
+111
+--- sslserver prints error message with unknown host name
+sslserver: fatal: (111) no IP address for: nonexistent.local.
+111
+--- sslserver prints error message with unresolvable host name
+sslserver: fatal: (111) temporarily unable to figure out IP address for: thislabelistoolongbecausednshasalimitof63charactersinasinglelabel.
+111
+--- sslserver prints error message with non-local host name
+sslserver: fatal: (111) unable to bind to: ...
+111
+---> test sslclient to connect to sslserver (on different port; note: cert verify will fail on localhost)
+++++
+--- sslclient sets basic environment variables
+sslclient: error: (110) missing credentials (CA) or unable to validate server certificate
+110
+--- sslserver -e also sets TCP environment variables
+sslclient: error: (110) missing credentials (CA) or unable to validate server certificate
+110
+--- sslclient recognizes -D, -z, -r, -h, -t (with elective cipher)
+sslclient: error: (110) missing credentials (CA) or unable to validate server certificate
+110
+--- sslclient sets basic environment variables
+sslclient: error: (110) missing credentials (CA) or unable to validate server certificate
+110
+--- sslclient -e sets TCP environment variables
+sslclient: error: (110) missing credentials (CA) or unable to validate server certificate
+110
+--- sslclient -s sets TLS environment variables
+sslclient: error: (110) missing credentials (CA) or unable to validate server certificate
+110
+--- sslclient looks up host names properly (localhost. -> ip6-loopback)
+sslclient: error: (110) missing credentials (CA) or unable to validate server certificate
+110
+--- sslclient -v works
+sslclient: error: (110) missing credentials (CA) or unable to validate server certificate
+110
+--- sslserver -N does not check certificates CN
+sslclient: tls connected to: ::1 port: 50014
+ok
+0
+--- sslserver and sslclient print errors for incompatible cipher lists for TLS < 1.3
+sslclient: error: (111) unable to set cipher list
+111
+--- sslclient -X ignores any server certificate
+sslclient: tls connected to: ::1 port: 50014
+ok
+0
+--- sslclient -n checks hostname with certificates SAN/CN
+sslclient: fatal: (111) unable to bind to: ::1 port: 50027
+111
+---> test sslclient to connect to sslserver requiring client cert
+++++
+--- sslserver prints error for no client certificate
+sslclient: error: (110) missing credentials (CA) or unable to validate server certificate
+110
+--- sslserver prints error for bad client certificate
+sslclient: error: (110) missing credentials (CA) or unable to validate server certificate
+110
+--- sslclient uses certificates
+sslclient: error: (110) missing credentials (CA) or unable to validate server certificate
+110
+---> test sslcat to connect to sslserver@5016
+++++
+--- sslcat works
+sslclient: error: (110) missing credentials (CA) or unable to validate server certificate
+110
+--- sslconnect works
+banner0
+--- https@ works
+0
+---> test sslconnect to connect to sslserver@5013
+++++
+--- sslclient and sslserver handle larger data
+sslclient: tls connected to: ::1 port: 50013
+0
+--- sslserver times out
+sslclient: tls connected to: ::1 port: 50013
+bannerhereur^M
+0
+sslclient: tls connected to: ::1 port: 50013
+banner0
+---> test sslprint@50021
+++++
+--- sslprint prints usage message without enough arguments
+sslprint: usage: sslprint[ -1346UXpPhHrRoOdDqQviIeEsS ] [ -c limit ] [ -x rules.cdb ] [ -B banner ] [ -g gid ] [ -u uid ] [ -b backlog ] [ -l localname ] [ -t timeout ] [ -T ssltimeout ] [ -w progtimeout ] [ -f lockfile ] [ -I interface ] host port program
+100
+--- sslprint prints error message with unknown port name
+sslprint: fatal: (111) unable to figure out port number for: nonexistentport
+111
+--- sslprint prints error message with unknown host name
+sslprint: fatal: (111) no IP address for: nonexistent.local.
+111
+--- sslprint prints error message with unresolvable host name
+sslprint: fatal: (111) temporarily unable to figure out IP address for: thislabelistoolongbecausednshasalimitof63charactersinasinglelabel.
+111
+--- sslprint prints error message with non-local host name
+sslprint: fatal: (111) unable to bind
+111
+--- sslprint prints error message with used port
+sslprint: fatal: (111) unable to bind
+111
+--- sslprint sets basic environment variables
+sslclient: error: (110) missing credentials (CA) or unable to validate server certificate
+110
+--- sslprint exits when environment changes
+sslclient: error: (110) missing credentials (CA) or unable to validate server certificate
+110
+--- sslprint does not lose descriptors
+110
+--- sslserver -1v prints proper messages
+::x1 : 50016
+sslserver::x ciphers x
+sslserver::x cafile x xxx/rootCA_cert.pem
+sslserver::x ccafile x
+sslserver::x cadir x xxx/etc
+sslserver::x certchainfile x
+sslserver::x cert x xxx/::1_cert.pem
+sslserver::x key x xxx/::1_key.pem
+sslserver::x dhparam x xxx
+sslserver::x status: 0/1/0
+sslserver::x status: 1/1/0
+sslserver::x pid x from ::1
+sslserver::x ok x Localserver:::1:x ip6-loopback:::1::x
+sslserver::x tls x accept TLSv1.3:TLS_AES_256_GCM_SHA384
+sslserver::x ended by x status 0
+sslserver::x status: 0/1/0
+sslserver::x status: 1/1/0
+sslserver::x pid x from ::1
+sslserver::x ok x Localserver:::1:x ip6-loopback:::1::x
+sslserver::x tls x accept TLSv1.3:TLS_AES_256_GCM_SHA384
+sslserver::x ended by x status 0
+sslserver::x status: 0/1/0
+sslserver::x status: 1/1/0
+sslserver::x pid x from ::1
+sslserver::x ok x Localserver:::1:x ip6-loopback:::1::x
+sslserver::x tls x accept TLSv1.3:TLS_CHACHA20_POLY1305_SHA256
+sslserver::x ended by x status 0
+sslserver::x status: 0/1/0
+sslserver::x status: 1/1/0
+sslserver::x pid x from ::1
+sslserver::x ok x Localserver:::1:x ip6-loopback:::1::x
+sslserver::x tls x accept TLSv1.3:TLS_AES_256_GCM_SHA384
+sslserver::x ended by x status 0
+sslserver::x status: 0/1/0
+sslserver::x status: 1/1/0
+sslserver::x pid x from ::1
+sslserver::x ok x Localserver:::1:x ip6-loopback:::1::x
+sslserver::x tls x accept TLSv1.3:TLS_AES_256_GCM_SHA384
+sslserver::x ended by x status 0
+sslserver::x status: 0/1/0
+sslserver::x status: 1/1/0
+sslserver::x pid x from ::1
+sslserver::x ok x Localserver:::1:x ip6-loopback:::1::x
+sslserver::x tls x accept TLSv1.3:TLS_AES_256_GCM_SHA384
+sslserver::x ended by x status 0
+sslserver::x status: 0/1/0
+sslserver::x status: 1/1/0
+sslserver::x pid x from ::1
+sslserver::x ok x Localserver:::1:x ip6-loopback:::1::x
+sslserver::x tls x accept TLSv1.3:TLS_AES_256_GCM_SHA384
+sslserver::x ended by x status 0
+sslserver::x status: 0/1/0
+sslserver::x status: 1/1/0
+sslserver::x pid x from ::1
+sslserver::x ok x Localserver:::1:x ip6-loopback:::1::x
+sslserver::x tls x accept TLSv1.3:TLS_AES_256_GCM_SHA384
+sslserver::x ended by x status 0
+sslserver::x status: 0/1/0
+::x1 : 50015
+sslserver::x ciphers x
+sslserver::x cafile x xxx/rootCA_cert.pem
+sslserver::x ccafile x xxx/rootCA_cert.pem
+sslserver::x cadir x xxx/etc
+sslserver::x certchainfile x
+sslserver::x cert x xxx/::1_cert.pem
+sslserver::x key x xxx/::1_key.pem
+sslserver::x dhparam x xxx
+sslserver::x status: 0/1/0
+sslserver::x status: 1/1/0
+sslserver::x pid x from ::1
+sslserver::x ok x Localserver:::1:x ip6-loopback:::1::x
+sslserver::x error: (111) unable to accept TLS for pid: x
+sslserver::x ended by x status 28416
+sslserver::x status: 0/1/0
+sslserver::x status: 1/1/0
+sslserver::x pid x from ::1
+sslserver::x ok x Localserver:::1:x ip6-loopback:::1::x
+sslserver::x error: (111) unable to accept TLS for pid: x
+sslserver::x ended by x status 28416
+sslserver::x status: 0/1/0
+sslserver::x status: 1/1/0
+sslserver::x pid x from ::1
+sslserver::x ok x Localserver:::1:x ip6-loopback:::1::x
+sslserver::x error: (111) unable to accept TLS for pid: x
+sslserver::x ended by x status 28416
+sslserver::x status: 0/1/0
+::x1 : 50014
+sslserver::x ciphers x
+sslserver::x cafile x xxx/rootCA_cert.pem
+sslserver::x ccafile x
+sslserver::x cadir x xxx/etc
+sslserver::x certchainfile x
+sslserver::x cert x xxx/::1_cert.pem
+sslserver::x key x xxx/::1_key.pem
+sslserver::x dhparam x xxx
+sslserver::x status: 0/1/0
+sslserver::x status: 1/1/0
+sslserver::x pid x from ::1
+sslserver::x ok x Localserver:::1:x ip6-loopback:::1::x
+sslserver::x tls x accept TLSv1.3:TLS_AES_256_GCM_SHA384
+sslserver::x ended by x status 0
+sslserver::x status: 0/1/0
+sslserver::x status: 1/1/0
+sslserver::x pid x from ::1
+sslserver::x ok x Localserver:::1:x ip6-loopback:::1::x
+sslserver::x error: (111) unable to accept TLS for pid: x
+sslserver::x ended by x status 28416
+sslserver::x status: 0/1/0
+sslserver::x status: 1/1/0
+sslserver::x pid x from ::1
+sslserver::x ok x Localserver:::1:x ip6-loopback:::1::x
+sslserver::x tls x accept TLSv1.3:TLS_AES_256_GCM_SHA384
+sslserver::x ended by x status 0
+sslserver::x status: 0/1/0
+::x1 : 50013
+sslserver::x ciphers x
+sslserver::x cafile x xxx/rootCA_cert.pem
+sslserver::x ccafile x
+sslserver::x cadir x xxx/etc
+sslserver::x certchainfile x
+sslserver::x cert x xxx/::1_cert.pem
+sslserver::x key x xxx/::1_key.pem
+sslserver::x dhparam x xxx
+sslserver::x status: 0/1/0
+sslserver::x status: 1/1/0
+sslserver::x pid x from ::1
+sslserver::x ok x Localserver:::1:x ip6-loopback:::1::x
+sslserver::x tls x accept TLSv1.3:TLS_AES_256_GCM_SHA384
+sslserver::x ended by x status 0
+sslserver::x status: 0/1/0
+sslserver::x status: 1/1/0
+sslserver::x pid x from ::1
+sslserver::x ok x Localserver:::1:x ip6-loopback:::1::x
+sslserver::x tls x accept TLSv1.3:TLS_AES_256_GCM_SHA384
+sslserver::x ended by x status 0
+sslserver::x status: 0/1/0
+sslserver::x status: 1/1/0
+sslserver::x pid x from ::1
+sslserver::x ok x Localserver:::1:x ip6-loopback:::1::x
+sslserver::x tls x accept TLSv1.3:TLS_AES_256_GCM_SHA384
+sslserver::x ended by x status 0
+sslserver::x status: 0/1/0
+sslserver::x status: 1/1/0
+sslserver::x pid x from ::1
+sslserver::x ok x Localserver:::1:x ip6-loopback:::1::x
+sslserver::x tls x accept TLSv1.3:TLS_AES_256_GCM_SHA384
+sslserver::x ended by x status 0
+sslserver::x status: 0/1/0
+sslserver::x status: 1/1/0
+sslserver::x pid x from ::1
+sslserver::x ok x Localserver:::1:x ip6-loopback:::1::x
+sslserver::x tls x accept TLSv1.3:TLS_AES_256_GCM_SHA384
+sslserver::x ended by x status 0
+sslserver::x status: 0/1/0
+sslserver::x status: 1/1/0
+sslserver::x pid x from ::1
+sslserver::x ok x Localserver:::1:x ip6-loopback:::1::x
+sslserver::x tls x accept TLSv1.3:TLS_AES_256_GCM_SHA384
+sslserver::x ended by x status 0
+sslserver::x status: 0/1/0
+::x1 : 50021
+sslprint::x ciphers x
+sslprint::x cafile x xxx/rootCA_cert.pem
+sslprint::x ccafile x
+sslprint::x cadir x xxx/etc
+sslprint::x certchainfile x
+sslprint::x cert x xxx/::1_cert.pem
+sslprint::x key x xxx/::1_key.pem
+sslprint::x dhparam x xxx
+sslprint::x status: 0/1
+sslprint::x status: 1/1
+sslprint::x pid x from ::
+sslprint::x ok x Localserver:::1:x ip6-localnet:::::x
+sslprint::x end x status 13
+sslprint::x status: 0/1
+sslprint::x status: 1/1
+sslprint::x pid x from ::
+sslprint::x ok x Localserver:::1:x ip6-localnet:::::x
+sslprint::x end x status 13
+sslprint::x status: 0/1
+sslprint::x status: 1/1
+sslprint::x pid x from ::
+sslprint::x ok x Localserver:::1:x ip6-localnet:::::x
+sslprint::x end x status 13
+sslprint::x status: 0/1
+sslprint::x status: 1/1
+sslprint::x end x status 15
+sslprint::x status: 0/1
diff --git a/src/exp.it b/src/exp.it
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/exp.it
diff --git a/src/exp.sslperl b/src/exp.sslperl
new file mode 100644
index 0000000..0a783dc
--- /dev/null
+++ b/src/exp.sslperl
@@ -0,0 +1,105 @@
+--- sslperl works
+sslperlHello, World! (1): here you are
+0
+sslperlHello, World! (2): here you are
+0
+sslperlHello, World! (1): here you are
+0
+sslperlHello, World! (2): here you are
+0
+--- sslperl prints usage message without enough arguments
+sslperl: usage: sslperl[ -1346UXpPhHrRoOdDqQviIeEsS ] [ -c limit ] [ -x rules.cdb ] [ -B banner ] [ -g gid ] [ -u uid ] [ -b backlog ] [ -l localname ] [ -t timeout ] [ -T ssltimeout ] [ -w progtimeout ] [ -f lockfile ] [ -I interface ] host port program
+100
+--- sslperl prints error message with unknown port name
+sslperl: fatal: (111) unable to figure out port number for: nonexistentport
+111
+--- sslperl prints error message with unknown host name
+sslperl: fatal: (111) temporarily unable to figure out IP address for: nonexistent.local.
+111
+--- sslperl prints error message with unresolvable host name
+sslperl: fatal: (111) no IP address for: thislabelistoolongbecausednshasalimitof63charactersinasinglelabel.
+111
+--- sslperl prints error message with non-local host name
+sslperl: fatal: (111) unable to bind
+111
+--- sslperl preserves environment
+sslperl changed environment
+0
+--- sslperl handles larger requests
+sslclient: tls connected to: 127.0.0.1 port: 50022
+0
+--- sslserver -1v prints proper messages
+127.0.0.1 : 50022
+sslperl: cafile x xxx/rootCA.pem
+sslperl: ccafile x
+sslperl: cadir x xxx/etc
+sslperl: certchainfile x
+sslperl: cert x xxx/::x
+sslperl: key x xxx/::x
+sslperl: dhparam x xxx
+sslperl: status: 0/1/0
+sslperl: status: 1/1/0
+sslperl: pid x from 127.0.0.1
+sslperl: ok x Localserver:127.0.0.1:50022 ip4-loopback:127.0.0.1::x
+sslperl: tls x accept
+log: Hello, World! (1): here you are
+sslperl: drop: (110) environ changed
+sslperl: done ...
+sslperl: pid x from 127.0.0.1
+sslperl: ok x Localserver:127.0.0.1:50022 ip4-loopback:127.0.0.1::x
+sslperl: tls x accept
+log: Hello, World! (2): here you are
+sslperl: end x status 0
+sslperl: status: 0/1/0
+sslperl: status: 1/1/0
+sslperl: pid x from 127.0.0.1
+sslperl: ok x Localserver:127.0.0.1:50022 ip4-loopback:127.0.0.1::x
+sslperl: tls x accept
+log: Hello, World! (1): here you are
+sslperl: drop: (110) environ changed
+sslperl: done ...
+sslperl: pid x from 127.0.0.1
+sslperl: ok x Localserver:127.0.0.1:50022 ip4-loopback:127.0.0.1::x
+sslperl: tls x accept
+log: Hello, World! (2): here you are
+sslperl: end x status 0
+sslperl: status: 0/1/0
+sslperl: status: 1/1/0
+sslperl: end x status 15
+sslperl: status: 0/1/0
+127.0.0.1 : 50022
+sslperl: cafile x xxx/rootCA.pem
+sslperl: ccafile x
+sslperl: cadir x xxx/etc
+sslperl: certchainfile x
+sslperl: cert x xxx/::x
+sslperl: key x xxx/::x
+sslperl: dhparam x xxx
+sslperl: status: 0/1/0
+sslperl: status: 1/1/0
+sslperl: pid x from 127.0.0.1
+sslperl: ok x Localserver:127.0.0.1:50022 ip4-loopback:127.0.0.1::x
+sslperl: tls x accept
+log: NOW=
+log: changed environment
+sslperl: drop: (110) environ changed
+sslperl: done ...
+sslperl: end x status 15
+sslperl: status: 0/1/0
+127.0.0.1 : 50022
+sslperl: cafile x xxx/rootCA.pem
+sslperl: ccafile x
+sslperl: cadir x xxx/etc
+sslperl: certchainfile x
+sslperl: cert x xxx/::x
+sslperl: key x xxx/::x
+sslperl: dhparam x xxx
+sslperl: status: 0/1/0
+sslperl: status: 1/1/0
+sslperl: pid x from 127.0.0.1
+sslperl: ok x Localserver:127.0.0.1:50022 ip4-loopback:127.0.0.1::x
+sslperl: tls x accept
+sslperl: drop: (110) environ changed
+sslperl: done ...
+sslperl: end x status 15
+sslperl: status: 0/1/0
diff --git a/src/find-systype.sh b/src/find-systype.sh
new file mode 100644
index 0000000..15322b4
--- /dev/null
+++ b/src/find-systype.sh
@@ -0,0 +1,151 @@
+# oper-:arch-:syst-:chip-:kern-
+# oper = operating system type; e.g., sunos-4.1.4
+# arch = machine language; e.g., sparc
+# syst = which binaries can run; e.g., sun4
+# chip = chip model; e.g., micro-2-80
+# kern = kernel version; e.g., sun4m
+# dependence: arch --- chip
+# \ \
+# oper --- syst --- kern
+# so, for example, syst is interpreted in light of oper, but chip is not.
+# anyway, no slashes, no extra colons, no uppercase letters.
+# the point of the extra -'s is to ease parsing: can add hierarchies later.
+# e.g., *:i386-*:*:pentium-*:* would handle pentium-100 as well as pentium,
+# and i386-486 (486s do have more instructions, you know) as well as i386.
+# the idea here is to include ALL useful available information.
+
+exec 2>/dev/null
+
+sys="`uname -s | tr '/:[:upper:]' '..[:lower:]'`"
+if [ x"$sys" != x ]
+then
+ unamer="`uname -r | tr /: ..`"
+ unamem="`uname -m | tr /: ..`"
+ unamev="`uname -v | tr /: ..`"
+
+ case "$sys" in
+ bsd.os|freebsd|netbsd|openbsd)
+ # in bsd 4.4, uname -v does not have useful info.
+ # in bsd 4.4, uname -m is arch, not chip.
+ oper="$sys-$unamer"
+ arch="$unamem"
+ syst=""
+ chip="`sysctl -n hw.model`" # hopefully
+ kern=""
+ ;;
+ linux)
+ # as in bsd 4.4, uname -v does not have useful info.
+ oper="$sys-$unamer"
+ syst=""
+ chip="$unamem"
+ kern=""
+ case "$chip" in
+ i386|i486|i586|i686)
+ arch="i386"
+ ;;
+ alpha)
+ arch="alpha"
+ ;;
+ esac
+ ;;
+ aix)
+ # naturally IBM has to get uname -r and uname -v backwards. dorks.
+ oper="$sys-$unamev-$unamer"
+ arch="`arch | tr /: ..`"
+ syst=""
+ chip="$unamem"
+ kern=""
+ ;;
+ sunos)
+ oper="$sys-$unamer-$unamev"
+ arch="`(uname -p || mach) | tr /: ..`"
+ syst="`arch | tr /: ..`"
+ chip="$unamem" # this is wrong; is there any way to get the real info?
+ kern="`arch -k | tr /: ..`"
+ ;;
+ unix_sv)
+ oper="$sys-$unamer-$unamev"
+ arch="`uname -m`"
+ syst=""
+ chip="$unamem"
+ kern=""
+ ;;
+ *)
+ oper="$sys-$unamer-$unamev"
+ arch="`arch | tr /: ..`"
+ syst=""
+ chip="$unamem"
+ kern=""
+ ;;
+ esac
+else
+ cc -c trycpp.c
+ cc -o trycpp trycpp.o
+ case `./trycpp` in
+ nextstep)
+ oper="nextstep-`hostinfo | sed -n 's/^[ ]*NeXT Mach \([^:]*\):.*$/\1/p'`"
+ arch="`hostinfo | sed -n 's/^Processor type: \(.*\) (.*)$/\1/p' | tr /: ..`"
+ syst=""
+ chip="`hostinfo | sed -n 's/^Processor type: .* (\(.*\))$/\1/p' | tr ' /:' '...'`"
+ kern=""
+ ;;
+ *)
+ oper="unknown"
+ arch=""
+ syst=""
+ chip=""
+ kern=""
+ ;;
+ esac
+ rm -f trycpp.o trycpp
+fi
+
+case "$chip" in
+80486)
+ # let's try to be consistent here. (BSD/OS)
+ chip=i486
+ ;;
+i486DX)
+ # respect the hyphen hierarchy. (FreeBSD)
+ chip=i486-dx
+ ;;
+i486.DX2)
+ # respect the hyphen hierarchy. (FreeBSD)
+ chip=i486-dx2
+ ;;
+Intel.586)
+ # no, you nitwits, there is no such chip. (NeXTStep)
+ chip=pentium
+ ;;
+i586)
+ # no, you nitwits, there is no such chip. (Linux)
+ chip=pentium
+ ;;
+i686)
+ # STOP SAYING THAT! (Linux)
+ chip=ppro
+ ;;
+arm)
+ # too many on the rood
+ chip=arm
+ ;;
+arm64)
+ # pi 3+
+ chip=arm64
+esac
+
+if cc -c x86cpuid.c
+then
+ if cc -o x86cpuid x86cpuid.o
+ then
+ x86cpuid="`./x86cpuid | tr /: ..`"
+ case "$x86cpuid" in
+ ?*)
+ chip="$x86cpuid"
+ ;;
+ esac
+ fi
+fi
+rm -f x86cpuid x86cpuid.o
+
+echo "$oper-:$arch-:$syst-:$chip-:$kern-" | tr ' [A-Z]' '.[a-z]'
diff --git a/src/fork.h1 b/src/fork.h1
new file mode 100644
index 0000000..ddd589d
--- /dev/null
+++ b/src/fork.h1
@@ -0,0 +1,9 @@
+#ifndef FORK_H
+#define FORK_H
+
+/* sysdep: -vfork */
+
+extern int fork();
+#define vfork fork
+
+#endif
diff --git a/src/fork.h2 b/src/fork.h2
new file mode 100644
index 0000000..7c1b0b9
--- /dev/null
+++ b/src/fork.h2
@@ -0,0 +1,9 @@
+#ifndef FORK_H
+#define FORK_H
+
+/* sysdep: +vfork */
+
+extern int fork();
+extern int vfork();
+
+#endif
diff --git a/src/hassgact.h1 b/src/hassgact.h1
new file mode 100644
index 0000000..7639d24
--- /dev/null
+++ b/src/hassgact.h1
@@ -0,0 +1,3 @@
+/* Public domain. */
+
+/* sysdep: -sigaction */
diff --git a/src/hassgact.h2 b/src/hassgact.h2
new file mode 100644
index 0000000..60ff776
--- /dev/null
+++ b/src/hassgact.h2
@@ -0,0 +1,4 @@
+/* Public domain. */
+
+/* sysdep: +sigaction */
+#define HASSIGACTION 1
diff --git a/src/hassgprm.h1 b/src/hassgprm.h1
new file mode 100644
index 0000000..ef3eee9
--- /dev/null
+++ b/src/hassgprm.h1
@@ -0,0 +1,3 @@
+/* Public domain. */
+
+/* sysdep: -sigprocmask */
diff --git a/src/hassgprm.h2 b/src/hassgprm.h2
new file mode 100644
index 0000000..be9d0d7
--- /dev/null
+++ b/src/hassgprm.h2
@@ -0,0 +1,4 @@
+/* Public domain. */
+
+/* sysdep: +sigprocmask */
+#define HASSIGPROCMASK 1
diff --git a/src/hasshsgr.h1 b/src/hasshsgr.h1
new file mode 100644
index 0000000..3806277
--- /dev/null
+++ b/src/hasshsgr.h1
@@ -0,0 +1,3 @@
+/* Public domain. */
+
+/* sysdep: -shortsetgroups */
diff --git a/src/hasshsgr.h2 b/src/hasshsgr.h2
new file mode 100644
index 0000000..5624ed0
--- /dev/null
+++ b/src/hasshsgr.h2
@@ -0,0 +1,4 @@
+/* Public domain. */
+
+/* sysdep: +shortsetgroups */
+#define HASSHORTSETGROUPS 1
diff --git a/src/haswaitp.h1 b/src/haswaitp.h1
new file mode 100644
index 0000000..0d6f82c
--- /dev/null
+++ b/src/haswaitp.h1
@@ -0,0 +1,3 @@
+/* Public domain. */
+
+/* sysdep: -waitpid */
diff --git a/src/haswaitp.h2 b/src/haswaitp.h2
new file mode 100644
index 0000000..015413f
--- /dev/null
+++ b/src/haswaitp.h2
@@ -0,0 +1,4 @@
+/* Public domain. */
+
+/* sysdep: +waitpid */
+#define HASWAITPID 1
diff --git a/src/https@.sh b/src/https@.sh
new file mode 100644
index 0000000..6a902b7
--- /dev/null
+++ b/src/https@.sh
@@ -0,0 +1,15 @@
+host=${1-0}
+path=${2-}
+port=${3-443}
+args=""
+if [ $# -gt 3 ]
+then
+ shift; shift; shift
+ args="$@"
+fi
+echo "GET /${path} HTTP/1.1
+Host: $host:$port
+" | HOME/bin/sslclient -XRHl0 $args -- "$host" "$port" sh -c '
+ addcr >&7
+ exec HOME/bin/delcr <&6
+' | awk '/^$/ { body=1; next } { if (body) print }'
diff --git a/src/ip4_bit.c b/src/ip4_bit.c
new file mode 100644
index 0000000..02dbf7a
--- /dev/null
+++ b/src/ip4_bit.c
@@ -0,0 +1,101 @@
+/***
+ @file ip4_bit.c
+ @author Jens Wehrenbrecht, feh
+ @funcs ip4_bitstring, bitstring_ip4
+*/
+#include "ip.h"
+#include "byte.h"
+#include "scan.h"
+#include "str.h"
+#include "fmt.h"
+#include "ip_bit.h"
+
+#define BITSUBSTITUTION
+
+static char strnum[FMT_ULONG];
+
+/***
+ /fn ip4_bitstring
+ /brief This function converts a IPv4 address into its binary representation with given prefix len
+ /param out: ip4string 0-terminated destination address.
+ /param in: ip4address The source address.
+ /param in: prefix The net prefix bits (maximum 32 bits for IPv4).
+ /return -1: lack of memory; 1: non valid IP address; 0: successful converted.
+*/
+
+int ip4_bitstring(stralloc *ip4string, char *ip, unsigned int prefix)
+{
+ int i, j;
+ char ip4[4];
+ int count = 0;
+ unsigned char number;
+#ifdef BITSUBSTITUTION
+ const char *letterarray = "abcdefghijklmnopqrstuvwxyz123456";
+#endif
+
+ if (!stralloc_copys(ip4string,"")) return -1;
+ if (!stralloc_readyplus(ip4string,32)) return -1;
+ ip4_scan(ip,ip4);
+
+ for (i = 0; i < 4; i++) {
+ number = (unsigned char) ip4[i];
+ for (j = 7; j >= 0; j--) {
+ if (number & (1<<j)) {
+#ifdef BITSUBSTITUTION
+ if (!stralloc_catb(ip4string,letterarray + count,1)) return -1;
+#else
+ if (!stralloc_cats(ip4string,"1")) return -1;
+#endif
+ } else {
+ if (!stralloc_cats(ip4string,"0")) return -1;
+ }
+ count++;
+ prefix--;
+ if (prefix == 0) return 0;
+ }
+ }
+
+ return 1;
+}
+
+/***
+ /fn bitstring_ip4
+ /brief This function takes an IPv4 bitstring and translates it to an IPv4 address + prefix
+ /param in: ip4string The source address (with '_' start token).
+ /param out: ip4addr 0-terminated estination IPv4 address + net prefix (eg. 127.0.0.0/16).
+ /return -1: lack of memory; 1: non valid IPv4 address; 0: successful converted.
+*/
+
+int bitstring_ip4(stralloc *ip4addr, stralloc *ip4string)
+{
+ int j;
+ int num = 0;
+ int value = 256;
+ int prefix;
+
+ if (!stralloc_copys(ip4addr,"")) return -1;
+ prefix = ip4string->len - 1;
+
+ if (prefix <= 0) return 1;
+ if (prefix <= 1 || prefix > 32) return 1;
+
+ for (j = 1; j <= prefix; j++) {
+ if (ip4string->s[j] != '0') {
+ num += (value/2);
+ value /= 2;
+ } else
+ value /= 2;
+ if (j % 8 == 0 || j == prefix) {
+ if (!stralloc_catb(ip4addr,strnum,fmt_ulong(strnum,num))) return -1;
+ if (j < 32) if (!stralloc_cats(ip4addr,".")) return -1;
+ num = 0;
+ value = 256;
+ }
+ }
+
+ if (!stralloc_cats(ip4addr,"/")) return -1;
+ if (!stralloc_catb(ip4addr,strnum,fmt_ulong(strnum,prefix))) return -1;
+ if (!stralloc_0(ip4addr)) return -1;
+
+ return 0;
+}
diff --git a/src/ip6_bit.c b/src/ip6_bit.c
new file mode 100644
index 0000000..528f0b0
--- /dev/null
+++ b/src/ip6_bit.c
@@ -0,0 +1,180 @@
+/**
+ @file ip6_bit.c
+ @author Li Minh Bui, feh
+ @funcs bytetohex, ip6_bitstring, bitstring_ip6, ip6_fmt_str
+*/
+#include "ip.h"
+#include "byte.h"
+#include "str.h"
+#include "fmt.h"
+#include "stralloc.h"
+#include "ip_bit.h"
+
+#define BITSUBSTITUTION
+
+/***
+ /fn bytetohex
+ /brief Convert a number of max 255 to hex.
+ /param decimal The decimal number.
+ /param hex The converted hex value.
+*/
+
+void bytetohex(unsigned char decimal, char hex[3])
+{
+ char* hexdigits = "0123456789ABCDEF";
+ int rest, number;
+ hex[0] = '0';
+ hex[1] = '0';
+ hex[2] = '\0';
+
+ number = decimal / 16;
+ rest = decimal % 16;
+
+ hex[0] = hexdigits[number];
+ hex[1] = hexdigits[rest];
+}
+
+static char strnum[FMT_ULONG];
+
+/***
+ /fn ip6_bitstring
+ /brief This function converts a IPv6 address into its binary representation.
+ /param out: ip6string The destination address.
+ /param in: ip6addr The source address.
+ /param in: prefix The net prefix bits (maximum 128 bits for IPv6).
+ /return -1: lack of memory; 1: non valid IPv6 address; 0: successful converted.
+*/
+
+int ip6_bitstring(stralloc *ip6string, char *ip6addr, unsigned int prefix)
+{
+ char ip6[16];
+ int bit, octettbitpos, number, shiftedvalue;
+ int i, slashpos, ip6len;
+
+#ifdef BITSUBSTITUTION
+ char subvalueforbitone[1];
+ subvalueforbitone[0] = 96; /* substitution starts from token '_' = 96 */
+#endif
+
+ ip6len = str_len(ip6addr);
+ slashpos = byte_chr(ip6addr,ip6len,'/');
+ if (!stralloc_copyb(ip6string,ip6addr,slashpos)) return -1;
+ ip6addr[slashpos] = '\0';
+
+ if (!ip6_scan(ip6addr,ip6)) return 1;
+ if (!stralloc_copys(ip6string,"")) return -1;
+
+ for (i = 0; i < 16; i++) {
+ number = (unsigned char) ip6[i];
+
+ for (octettbitpos = 7; octettbitpos >= 0; octettbitpos--) {
+ shiftedvalue = 1 << octettbitpos;
+ bit = number / shiftedvalue;
+ number = number - bit * (shiftedvalue);
+
+ if (bit) {
+#ifdef BITSUBSTITUTION
+ if (!stralloc_catb(ip6string,subvalueforbitone,1)) return -1;
+ subvalueforbitone[0]++;
+#else
+ if (!stralloc_cats(ip6string,"1")) return -1;
+#endif
+ } else
+ if (!stralloc_cats(ip6string,"0")) return -1;
+
+ prefix--;
+ if (prefix == 0) return 0;
+ }
+ }
+
+ return 1;
+}
+
+/***
+ /fn bitstring_ip6
+ /brief This function converts a bit string which is produced by ip6_bitstring()
+ into an IPv6 address. The string may start with a '^'.
+ /param in: ip6string Source string which need to be converted.
+ /param out ip6addr 0-terminated IPv6 destination address with net prefix.
+ /return -1: No memory could allocated,0: Failure,1: Success.
+*/
+
+int bitstring_ip6(stralloc *ip6addr ,stralloc *ip6string)
+{
+ int j = 0;
+ int i = 0;
+ int len, prefix, shiftedvalue;
+ int bitpos = 7;
+ int decimalnumber = 0;
+ char ip6[16] = {0};
+ char ip6compact[40] = {0};
+
+ if (!stralloc_copys(ip6addr,"")) return -1;
+ prefix = ip6string->len - 1;
+
+ if (prefix <= 0) return 1;
+ if (prefix <= 1 || prefix > 128) return 1;
+
+ if (ip6string->s[0] == '^') j = 1;
+
+ for (i = j, j = 0; i <= prefix; i++) {
+ if (ip6string->s[i] != '0') {
+ shiftedvalue = 1 << bitpos;
+ decimalnumber += shiftedvalue;
+ }
+ bitpos--;
+ if (bitpos == -1) { /* Put each converted byte into the array. */
+ if (j < 16) {
+ ip6[j] = (unsigned char) decimalnumber;
+ j++;
+ bitpos = 7;
+ decimalnumber = 0;
+ }
+ }
+ }
+
+ if (bitpos < 7) { /* Last bit was read,but the number was not converted. */
+ ip6[j] = (unsigned char) decimalnumber;
+ j++;
+ }
+
+ len = ip6_fmt(ip6compact,ip6);
+ if (!len) return 1;
+
+ if (!stralloc_copyb(ip6addr,ip6compact,len)) return -1;
+ if (!stralloc_cats(ip6addr,"/")) return -1;
+ if (!stralloc_catb(ip6addr,strnum,fmt_ulong(strnum,prefix))) return -1;
+ if (!stralloc_0(ip6addr)) return -1;
+
+ return 0;
+}
+/***
+ /fn ip6_fmt_str
+ /brief This function expands any valid IPv6 address into its full format of 16 bytes.
+ It returns the number of processed tokens on success.
+ /param src Source IPv6 address.
+ /param destination Expanded IPv6 address.
+ /return -1: No memory could allocated; 1: failure, 0: success
+*/
+
+unsigned int ip6_fmt_str(stralloc *dest, char *src)
+{
+ stralloc addr = {0};
+ char ip6[16];
+ char hexvalue[3] = {0, 0, 0};
+ int i;
+
+ if (!stralloc_copys(&addr,src)) return -1;
+ if (!stralloc_0(&addr)) return -1;
+
+ if (ip6_scan(addr.s,ip6) == 0) return 1;
+ if (!stralloc_copys(dest,"")) return -1;
+
+ for (i = 0; i < 16; i++) {
+ bytetohex((unsigned char)ip6[i],hexvalue);
+ stralloc_catb(dest,hexvalue,2);
+ if (!((i+1) % 2) && (i+1) < 16)
+ if (!stralloc_cats(dest,":")) return -1; /*Append ':' after every two bytes.*/
+ }
+ return 0;
+}
diff --git a/src/ip_bit.h b/src/ip_bit.h
new file mode 100644
index 0000000..49df160
--- /dev/null
+++ b/src/ip_bit.h
@@ -0,0 +1,15 @@
+#ifndef IP_BIT_H
+#define IP_BIT_H
+
+#include "stralloc.h"
+
+extern int bitstring_ip4(stralloc *,stralloc *);
+extern int ip4_bitstring(stralloc *,char *,unsigned int);
+extern unsigned int ip4_cscan(const char *,char [4]);
+extern void getnum(char *,int,unsigned long *);
+
+extern int bitstring_ip6(stralloc *,stralloc *);
+extern int ip6_bitstring(stralloc *,char *,unsigned int);
+extern unsigned int ip6_fmt_str(stralloc *,char *);
+
+#endif
diff --git a/src/it-base=d b/src/it-base=d
new file mode 100644
index 0000000..a1d0820
--- /dev/null
+++ b/src/it-base=d
@@ -0,0 +1,7 @@
+sslclient
+sslserver
+https@
+sslcat
+sslconnect
+sslprint
+sslhandle
diff --git a/src/it-sslperl=d b/src/it-sslperl=d
new file mode 100644
index 0000000..68065ca
--- /dev/null
+++ b/src/it-sslperl=d
@@ -0,0 +1 @@
+sslperl
diff --git a/src/it-sys=d b/src/it-sys=d
new file mode 100644
index 0000000..fa5d3e9
--- /dev/null
+++ b/src/it-sys=d
@@ -0,0 +1 @@
+sysdeps
diff --git a/src/it=d b/src/it=d
new file mode 100644
index 0000000..304fcac
--- /dev/null
+++ b/src/it=d
@@ -0,0 +1 @@
+it-base
diff --git a/src/print-ar.sh b/src/print-ar.sh
new file mode 100644
index 0000000..99bc116
--- /dev/null
+++ b/src/print-ar.sh
@@ -0,0 +1,14 @@
+cat warn-auto.sh
+echo 'main="$1"; shift'
+echo 'rm -f "$main"'
+echo 'ar cr "$main" ${1+"$@"}'
+case "`cat systype`" in
+ sunos-5.*) ;;
+ unix_sv*) ;;
+ irix64-*) ;;
+ irix-*) ;;
+ dgux-*) ;;
+ hp-ux-*) ;;
+ sco*) ;;
+ *) echo 'ranlib "$main"' ;;
+esac
diff --git a/src/print-cc.sh b/src/print-cc.sh
new file mode 100644
index 0000000..2a46533
--- /dev/null
+++ b/src/print-cc.sh
@@ -0,0 +1,62 @@
+cc="`head -1 ../conf-cc`"
+systype="`cat systype`"
+
+ccqlibs="`head -1 ../conf-qlibs`"
+[ -d "$ccqlibs"/include ] && ccqlibs="-I${ccqlibs}/include" \
+|| ccqlibs=""
+
+cc -c trycpp.c -malign-double >/dev/null 2>&1 \
+&& ccad="-malign-double"
+
+cc -c trycpp.c -march=ultrasparc >/dev/null 2>&1 \
+&& ccus="-march=ultrasparc"
+
+cc -c trycpp.c -march=powerpc >/dev/null 2>&1 \
+&& ccpp="-march=powerpc"
+
+cc -c trycpp.c -march=21164 >/dev/null 2>&1 \
+&& cc21="-march=21164"
+
+cc -c trycpp.c -march=native >/dev/null 2>&1 \
+&& ccarm="-march=native"
+
+rm -f trycpp.o
+
+ccssl="`head -1 ../conf-ssl`"
+eval cc -c tryssl.c ${ccssl} >/dev/null 2>&1 \
+|| ccssl=""
+
+ccbase="cc -fomit-frame-pointer -Wall"
+
+case "$cc:$systype" in
+ auto:*:i386-*:*)
+ cc="$ccbase -O1 $ccad"
+ ;;
+ auto:*:amd64-*:*)
+ cc="$ccbase -O2 $ccad"
+ ;;
+ auto:*:x86_64-*:*)
+ cc="$ccbase -O2 $ccad"
+ ;;
+ auto:*:sparc-*:*:*:*)
+ cc="$ccbase -O1 $ccus"
+ ;;
+ auto:*:ppc-*:*:*:*)
+ cc="$ccbase -O2 $ccpp"
+ ;;
+ auto:*:alpha-*:*:*:*)
+ cc="$ccbase -O2 $cc21"
+ ;;
+ auto:aix-*:-:-:*:-)
+ cc="$ccbase -O2 $ccpp"
+ ;;
+ auto:*:armv7l-:*)
+ cc="$ccbase -O2 $ccarm"
+ ;;
+ auto:*)
+ cc="$ccbase -O2"
+ ;;
+esac
+
+cat warn-auto.sh
+echo exec "$cc" ${ccqlibs} ${ccssl} '-c ${1+"$@"}'
diff --git a/src/print-ccperl.sh b/src/print-ccperl.sh
new file mode 100644
index 0000000..9cda68d
--- /dev/null
+++ b/src/print-ccperl.sh
@@ -0,0 +1,10 @@
+ccopts="`head -1 ../conf-ccperl`"
+runperl="`head -1 ../conf-perl`"
+
+case "$ccopts" in
+ auto)
+ ccopts="`$runperl -MExtUtils::Embed -e ccopts`"
+ ;;
+esac
+
+echo "$ccopts"
diff --git a/src/print-dl.sh b/src/print-dl.sh
new file mode 100644
index 0000000..faa491c
--- /dev/null
+++ b/src/print-dl.sh
@@ -0,0 +1,14 @@
+ssllib="`head -1 ../conf-ssllib`"
+
+dlflag=0
+
+rm -f trycpp.o
+
+dlflag=`cc -c tryssl.c -ldl 2>&1 | wc -l`
+if [ $dlflag -eq 0 ]; then
+ ssllib="$ssllib -ldl"
+fi
+
+rm -f trycpp.o
+
+echo $ssllib
diff --git a/src/print-ld.sh b/src/print-ld.sh
new file mode 100644
index 0000000..c13472c
--- /dev/null
+++ b/src/print-ld.sh
@@ -0,0 +1,18 @@
+ld="`head -1 ../conf-ld`"
+qlibs="`head -1 ../conf-qlibs`"
+systype="`cat systype`"
+
+flag=0
+
+rm -f trycpp.o
+
+flag=`cc -c tryssl.c -m64 2>&1 | wc -l`
+if [ $flag -eq 0 ]; then
+ ld="$ld -m64"
+fi
+
+rm -f trycpp.o
+
+cat warn-auto.sh
+echo 'main="$1"; shift'
+echo exec "$ld" -L"${qlibs}" '-o "$main" "$main".o ${1+"$@"} -ldnsresolv -lqlibs'
diff --git a/src/print-ldperl.sh b/src/print-ldperl.sh
new file mode 100644
index 0000000..02331f0
--- /dev/null
+++ b/src/print-ldperl.sh
@@ -0,0 +1,10 @@
+ldopts="`head -1 ../conf-ldperl`"
+runperl="`head -1 ../conf-perl`"
+
+case "$ldopts" in
+ auto)
+ ldopts="`$runperl -MExtUtils::Embed -e ldopts`"
+ ;;
+esac
+
+echo "$ldopts"
diff --git a/src/print-perlembed.sh b/src/print-perlembed.sh
new file mode 100644
index 0000000..337da8e
--- /dev/null
+++ b/src/print-perlembed.sh
@@ -0,0 +1,10 @@
+ldopts="`head -1 ../conf-ldperl`"
+runperl="`head -1 ../conf-perl`"
+
+case "$ldopts" in
+ auto)
+ ldopts="$runperl -MExtUtils::Embed -e ldopts"
+ ;;
+esac
+
+echo "$ldopts"
diff --git a/src/remoteinfo.c b/src/remoteinfo.c
new file mode 100644
index 0000000..665d2e5
--- /dev/null
+++ b/src/remoteinfo.c
@@ -0,0 +1,102 @@
+#include <unistd.h>
+#include "fmt.h"
+#include "buffer.h"
+#include "socket_if.h"
+#include "error.h"
+#include "iopause.h"
+#include "timeoutconn.h"
+#include "dnsresolv.h"
+#include "remoteinfo.h"
+
+static struct taia now;
+static struct taia deadline;
+
+static int mywrite(int fd,char *buf,int len)
+{
+ iopause_fd x;
+ int r;
+
+ x.fd = fd;
+ x.events = IOPAUSE_WRITE;
+ for (;;) {
+ taia_now(&now);
+ r = iopause(&x,1,&deadline,&now);
+ if (r > 0 && x.revents) break;
+ if (taia_less(&deadline,&now)) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ }
+ return write(fd,buf,len);
+}
+
+static int myread(int fd,char *buf,int len)
+{
+ iopause_fd x;
+ int r;
+
+ x.fd = fd;
+ x.events = IOPAUSE_READ;
+ for (;;) {
+ taia_now(&now);
+ r = iopause(&x,1,&deadline,&now);
+ if (r > 0 && x.revents) break;
+ if (taia_less(&deadline,&now)) {
+ errno = ETIMEDOUT;
+ 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_bind(s,iplocal,0,netif) == -1) return -1;
+ if (timeoutconn(s,ipremote,113,timeout,netif) == -1) return -1;
+
+ buffer_init(&b,(ssize_t (*)())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,(ssize_t (*)())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 remoteinfo(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_tcp();
+ if (s == -1) return -1;
+ r = doit(out,s,ipremote,portremote,iplocal,portlocal,timeout,netif);
+ close(s);
+ return r;
+}
diff --git a/src/remoteinfo.h b/src/remoteinfo.h
new file mode 100644
index 0000000..37092d0
--- /dev/null
+++ b/src/remoteinfo.h
@@ -0,0 +1,9 @@
+#ifndef REMOTEINFO_H
+#define REMOTEINFO_H
+
+#include "stralloc.h"
+#include "uint_t.h"
+
+extern int remoteinfo(stralloc *,char *,uint16,char *,uint16,unsigned int,uint32);
+
+#endif
diff --git a/src/rts.base b/src/rts.base
new file mode 100644
index 0000000..0096007
--- /dev/null
+++ b/src/rts.base
@@ -0,0 +1,329 @@
+#!/bin/sh
+# Assumptions:
+# ucspi-tcp
+# available TCP ports on ::1: 50013--50021
+# 127.0.0.1 is resolved as 'localhost'
+# ::1/128 is resolved as 'ip6-loopback'
+# 0.0.0.0 and ::/128 is resolved as 'localnet'
+#
+# $here is ucspi-ssl current directory
+#
+# Not tested:
+# setting UID or GID
+# rules
+# write timeout
+
+echo '---> test sslserver + sslclient: four instances of sslserver (ports 50013, 50014, 50015, 50016) are used'
+echo '---> sslserver @port 50015 requires client certs'
+echo '++++'
+
+sslserver -w 2 \
+-s -E -c 1 -Bbanner -Vo -D -1 -3 -Xx rules.cdb -Rt5 -h -l Localserver -b 2 \
+::1 50016 ./print 3< $CADIR/::1.pw > log.50016 2>&1 &
+pid_50016=$!
+
+sslserver -w 2 \
+-s -e -c 1 -Bbanner -Vo -D -1 -3 -Xx rules.cdb -Rt5 -h -l Localserver -b 2 -m \
+::1 50015 ./print 3< $CADIR/::1.pw > log.50015 2>&1 &
+pid_50015=$!
+
+CIPHERS='' sslserver -w 2 \
+-s -e -c 1 -Bbanner -Vo -D -1 -3 -Xx rules.cdb -Rt5 -h -l Localserver -b 2 \
+::1 50014 ./print >log.50014 3< $CADIR/::1.pw 2>&1 &
+pid_50014=$!
+sleep 1
+
+sslserver -w 2 \
+-s -e -c 1 -Bbanner -Vo -D -1 -3 -Xx rules.cdb -Rt5 -h -l Localserver -b 2 \
+::1 50013 cat - >log.50013 3< $CADIR/::1.pw 2>&1 &
+pid_50013=$!
+sleep 1
+
+echo '---> test sslclient/sslserver behavior with wrong parm (timeout 2 secs)'
+echo '++++'
+
+echo '--- sslclient prints usage message without enough arguments'
+sslclient -T2 0 0; echo $?
+
+echo '--- sslclient prints error message with unknown port name'
+sslclient -T2 0 nonexistentport echo wrong; echo $?
+
+echo '--- sslclient prints error message when connection fails'
+sslclient -T2 0 016 echo wrong; echo $?
+
+echo '--- sslclient -q does not print error message when connection fails'
+sslclient -T2 -q 0 016 echo wrong; echo $?
+
+echo '--- sslclient prints error message with unknown host name'
+sslclient nonexistent.local. 016 echo wrong; echo $?
+
+echo '--- sslclient prints error message with unresolvable host name'
+sslclient thislabelistoolongbecausednshasalimitof63charactersinasinglelabel. 50016 echo wrong; echo $?
+
+echo '--- sslserver prints usage message without enough arguments'
+sslserver 0 0; echo $?
+
+echo '--- sslserver prints error message with unknown port name'
+sslserver 0 nonexistentport echo wrong; echo $?
+
+echo '--- sslserver prints error message with unknown host name'
+sslserver nonexistent.local. 016 echo wrong; echo $?
+
+echo '--- sslserver prints error message with unresolvable host name'
+sslserver thislabelistoolongbecausednshasalimitof63charactersinasinglelabel. 50016 echo wrong; echo $?
+
+echo '--- sslserver prints error message with non-local host name'
+( sslserver 1.2.3.4 016 echo wrong 2>&1
+ echo $?
+) | sed -e 's/unable to bind to: .*/unable to bind to: .../'
+
+
+echo '---> test sslclient to connect to sslserver (on different port; note: cert verify will fail on localhost)'
+echo '++++'
+
+echo '--- sslclient sets basic environment variables'
+{
+ sslclient -p 50017 -R -N -H -T 10 -l Local -a "$CAFILE" ::1 50016 sh -c 'cat <&6'
+ echo $?
+} | sed -e 's/unable to bind to: .*/unable to bind to: .../'
+
+
+echo '--- sslserver -e also sets TCP environment variables'
+{
+ sslclient -p 50018 -e -S -R -N -H -T 10 -l Local -a "$CAFILE" ::1 50016 sh -c 'cat <&6'
+ echo $?
+} | sanitize
+
+echo '--- sslclient recognizes -D, -z, -r, -h, -t (with elective cipher)'
+{
+ sslclient -p 50019 -N -D -r -t1 -l Local -a "$CAFILE" \
+ -z 'TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256' \
+ ::1 50016 sh -c 'cat <&6'
+ echo $?
+}
+#} | sanitize
+
+echo '--- sslclient sets basic environment variables'
+{
+ sslclient -p 50020 -R -N -H -l Local -a "$CAFILE" ::1 50016 ./print
+ echo $?
+} | sanitize
+
+echo '--- sslclient -e sets TCP environment variables'
+{
+ sslclient -p 50021 -e -R -N -H -l Local -a "$CAFILE" ::1 50016 ./print
+ echo $?
+} | sanitize
+
+echo '--- sslclient -s sets TLS environment variables'
+{
+ sslclient -p 50022 -s -R -N -H -l Local -a "$CAFILE" ::1 50016 ./print
+ echo $?
+} | sanitize
+
+echo '--- sslclient looks up host names properly (localhost. -> ip6-loopback)'
+{
+ sslclient -p 50023 -R -N -a "$CAFILE" localhost. 50016 ./print
+ echo $?
+} | sanitize
+
+echo '--- sslclient -v works'
+sslclient -p 50024 -v -R -N -H -l Local -a "$CAFILE" ::1 50016 echo ok
+echo $?
+
+echo '--- sslserver -N does not check certificates CN'
+( exec 2>&1
+ sslclient -p 50025 -v -R -H -N -l ip6-localhost -a "$CAFILE" -X ::1 50014 sh -c 'sleep 1; echo ok'
+ echo $?
+) | sanitize
+
+echo '--- sslserver and sslclient print errors for incompatible cipher lists for TLS < 1.3'
+( exec 2>&1
+ sslclient -p 50026 -v -R -H -N -l ip6-localhost -z 'FOOBAR' -a "$CAFILE" ::1 50014 \
+ sh -c 'sleep 1; echo ok'
+ echo $?
+) | sanitize
+
+echo '--- sslclient -X ignores any server certificate'
+( exec 2>&1
+ sslclient -p 50027 -v -R -H -l ip6-localhost -X ::1 50014 \
+ sh -c 'sleep 1; echo ok'
+ echo $?
+) | sanitize
+
+echo '--- sslclient -n checks hostname with certificates SAN/CN'
+( exec 2>&1
+ sslclient -p 50027 -v -R -H -l ip6-localhost -a "$CAFILE" ::1 50014 \
+ sh -c 'sleep 1; echo ok'
+ echo $?
+) | sanitize
+
+echo '---> test sslclient to connect to sslserver requiring client cert'
+echo '++++'
+
+echo '--- sslserver prints error for no client certificate'
+( exec 2>&1
+ sslclient -p 50028 -v -R -N -h -l ip6-localhost -a "$CAFILE" ::1 50015 \
+ sh -c 'sleep 1; echo ok'
+ echo $?
+) | sanitize
+
+echo '--- sslserver prints error for bad client certificate'
+( exec 2>&1
+ exec 3< $CADIR/::1.pw
+ sslclient -p 50029 -v -R -h -l ip6-localhost -a "$CAFILE" -c "$CERTFILE" -k "$KEYFILE" -3 \
+ ::1 50015 sh -c 'sleep 1; echo ok'
+ echo $?
+) | sanitize
+
+echo '--- sslclient uses certificates'
+( exec 2>&1
+ exec 3< $CADIR/localhost.pw
+ sslclient -p 50030 -v -s -R -N -h -l ip6-localhost -a "$CAFILE" -c "$CCERTFILE" -k "$CKEYFILE" -3 \
+ ::1 50015 sh -c 'cat <&6; ./print'
+ echo $?
+) | sanitize
+
+echo '---> test sslcat to connect to sslserver@5016'
+echo '++++'
+
+echo '--- sslcat works'
+{
+ sslcat ::1 50013 -N -a "$CAFILE" -N
+ echo $?
+} | sanitize
+
+echo '--- sslconnect works'
+{
+ sslconnect ::1 50013 -N -a "$CAFILE" </dev/null
+ echo $?
+} | sanitize
+
+echo '--- https@ works'
+https@ ::1 somefile 50013 -X -a "$CAFILE"
+echo $?
+
+
+echo '---> test sslconnect to connect to sslserver@5013'
+echo '++++'
+
+
+echo '--- sslclient and sslserver handle larger data'
+( exec 2>&1
+ exec 3< $CADIR/localhost.pw
+ { for i in 0 1 2 3 4 5 6 7 8 9
+ do
+ for j in 0 1 2 3 4 5 6 7 8 9
+ do
+ for k in 0 1 2 3 4 5 6 7 8 9
+ do
+ echo "abcdefghijklmnopqrstuvwxyz"
+ echo "abcdefghijklmnopqrstuvwxyz"
+ echo "abcdefghijklmnopqrstuvwxyz"
+ echo "abcdefghijklmnopqrstuvwxyz"
+ done
+ done
+ done
+ } | sslconnect ::1 50013 -v -s -N \
+ -a "$CAFILE" -c "$CCERTFILE" -k "$CKEYFILE" -3 > /dev/null
+ echo $?
+) | sanitize
+
+echo '--- sslserver times out'
+( exec 2>&1
+ exec 3< $CADIR/localhost.pw
+ ( exec echo hereur ) | sslconnect ::1 50013 -v -s -N \
+ -a "$CAFILE" -c "$CCERTFILE" -k "$CKEYFILE" -3
+ echo $?
+) | sanitize
+
+( exec 2>&1
+ exec 3< $CADIR/localhost.pw
+ ( sleep 6; exec echo hereur; ) | sslconnect ::1 50013 -v -s -N \
+ -a "$CAFILE" -c "$CCERTFILE" -k "$CKEYFILE" -3
+ echo $?
+) | sanitize
+
+## Kill all sslserver processes
+
+kill -TERM $pid_50013
+kill -TERM $pid_50014
+kill -TERM $pid_50015
+kill -TERM $pid_50016
+wait $pid_50013
+wait $pid_50014
+wait $pid_50015
+wait $pid_50016
+
+echo '---> test sslprint@50021'
+echo '++++'
+
+
+sslprint \
+-s -c 1 -Bsslprint -vo -D -e -1 -3 -Xx rules.cdb -Rt5 -hp -l Localserver -b 2 \
+::1 50021 3< $CADIR/::1.pw > log.sslprint 2>&1 &
+pid_50021=$!
+sleep 2
+
+echo '--- sslprint prints usage message without enough arguments'
+sslprint 0; echo $?
+
+echo '--- sslprint prints error message with unknown port name'
+sslprint 0 nonexistentport; echo $?
+
+echo '--- sslprint prints error message with unknown host name'
+sslprint nonexistent.local. 016; echo $?
+
+echo '--- sslprint prints error message with unresolvable host name'
+sslprint thislabelistoolongbecausednshasalimitof63charactersinasinglelabel. 016; echo $?
+
+echo '--- sslprint prints error message with non-local host name'
+( sslprint 1.2.3.4 16 2>&1
+ echo $?
+) | sed -e 's/unable to bind to: .*/unable to bind to: .../'
+
+
+echo '--- sslprint prints error message with used port'
+sslprint -R -H -l Localserver ::1 50021 echo wrong
+echo $?
+
+echo '--- sslprint sets basic environment variables'
+{ sslclient -R -H -T 5 -l Local -a "$CAFILE" -N ::1 50021 sh -c 'cat <&6'
+ echo $?
+} | sanitize
+
+echo '--- sslprint exits when environment changes'
+{ sslclient -R -H -T 5 -l Local -a "$CAFILE" -N ::1 50021 sh -c 'cat <&6'
+ echo $?
+} | sanitize
+
+echo '--- sslprint does not lose descriptors'
+{ sslclient -R -H -T 5 -l Local -a "$CAFILE" -N ::1 50021 sh -c 'cat <&6' \
+ 0<&- 2<&-
+ echo $?
+} | sanitize
+
+sleep 1
+kill -TERM $pid_50021
+wait $pid_50021
+
+
+echo '--- sslserver -1v prints proper messages'
+cat log.50016 log.50015 log.50014 log.50013 log.sslprint | \
+sed -e 's/::*/::x/' \
+ -e 's} [0-9]* } x }g' \
+ -e 's} ip6-loopback:::1::[0-9]*} ip6-loopback:::1::x}' \
+ -e 's} :::1:[0-9]*} :::1:x}' \
+ -e 's} cafile x .*/\([^/]*\)} cafile x xxx/\1}' \
+ -e 's} ccafile x .*/\([^/]*\)} ccafile x xxx/\1}' \
+ -e 's} cadir x .*/\([^/]*\)} cadir x xxx/\1}' \
+ -e 's} cert x .*/\([^/]*\)} cert x xxx/\1}' \
+ -e 's} key x .*/\([^/]*\)} key x xxx/\1}' \
+ -e 's} dhparam x .*} dhparam x xxx}' \
+ -e 's} speak TLS: .*} speak TLS: ...}' \
+ -e 's} accept TLS: .*} accept TLS: ...}' \
+ -e 's} done [0-9]*$} done ...}' \
+ -e 's} Localserver:::1:[0-9]*} Localserver:::1:x}' \
+ -e 's} ip6-localnet:::::[0-9]*} ip6-localnet:::::x}' \
+ -e 's} valid client cert received for pid: .*} valid client cert received for pid: ...}'
+
diff --git a/src/rts.it b/src/rts.it
new file mode 100644
index 0000000..7c0fa0e
--- /dev/null
+++ b/src/rts.it
@@ -0,0 +1,197 @@
+# Umbrella script to provide *SSL environment
+# and helper functions
+
+PATH="`pwd`:$PATH"
+CADIR=`pwd | cut -d':' -f1 | sed s/compile/etc/`
+
+# include the ssl and crypto libs by means of LD_LIBRARY_PATH
+#export LD_LIBRARY_PATH="/home/ucspi/_SSL/libressl-3.7.2/ssl/.libs:/home/ucspi/_SSL/libressl-3.7.2/crypto/.libs"
+export LD_LIBRARY_PATH="/home/ucspi/_SSL/openssl-3.2.0-alpha2"
+
+rm -rf rts-tmp
+mkdir rts-tmp
+cd rts-tmp
+
+CAFILE="$CADIR/rootCA_cert.pem"
+if [ ! -f $CAFILE ]
+then
+ echo "$CAFILE does no exist!"
+ exit 1
+fi
+CERTFILE="$CADIR/::1_cert.pem"
+if [ ! -f $CERTFILE ]
+then
+ echo "$CERTFILE does no exist!"
+ exit 1
+fi
+CHAINFILE="$CADIR/chain6.pem"
+if [ ! -f $CHAINFILE ]
+then
+ echo "$CHAINFILE does no exist!"
+ exit 1
+fi
+KEYFILE="$CADIR/::1_key.pem"
+if [ ! -f $KEYFILE ]
+then
+ echo "$KEYFILE does no exist!"
+ exit 1
+fi
+CCAFILE="$CADIR/rootCA_cert.pem"
+if [ ! -f $CCAFILE ]
+then
+ echo "$CCAFILE does no exist!"
+ exit 1
+fi
+CCERTFILE="$CADIR/localhost_cert.pem"
+if [ ! -f $CCERTFILE ]
+then
+ echo "$CCERTFILE does no exist!"
+ exit 1
+fi
+CKEYFILE="$CADIR/localhost_key.pem"
+if [ ! -f $CKEYFILE ]
+then
+ echo "$CKEYFILE does no exist!"
+ exit 1
+fi
+DHFILE="$CADIR/dh2048.pem"
+if [ ! -f $DHFILE ]
+then
+ echo "$DHFILE does no exist!"
+ exit 1
+fi
+
+export CADIR CAFILE CCAFILE CERTFILE CHAINFILE KEYFILE CCERTFILE CKEYFILE DHFILE
+
+# Create ./print file
+
+echo '#!/bin/sh
+# trap "" 13
+ echo ""
+ echo PROTO="$PROTO"
+ echo SSLLOCALHOST="${SSLLOCALHOST-unset}"
+ echo SSLLOCALIP="${SSLLOCALIP-unset}"
+ echo SSLLOCALPORT="${SSLLOCALPORT-unset}"
+ echo SSLREMOTEHOST="${SSLREMOTEHOST-unset}"
+ echo SSLREMOTEIP="${SSLREMOTEIP-unset}"
+ echo SSLREMOTEPORT="${SSLREMOTEPORT-unset}"
+ echo SSLREMOTEINFO="${SSLREMOTEINFO-unset}"
+
+ echo TCPLOCALHOST="${TCPLOCALHOST-unset}"
+ echo TCPLOCALIP="${TCPLOCALIP-unset}"
+ echo TCPLOCALPORT="${TCPLOCALPORT-unset}"
+
+ echo TCPREMOTEHOST="${TCPREMOTEHOST-unset}"
+ echo TCPREMOTEIP="${TCPREMOTEIP-unset}"
+ echo TCPREMOTEPORT="${TCPREMOTEPORT-unset}"
+ echo TCPREMOTEINFO="${TCPREMOTEINFO-unset}"
+
+ echo TCP6REMOTEHOST="${TCP6REMOTEHOST-unset}"
+ echo TCP6REMOTEIP="${TCP6REMOTEIP-unset}"
+ echo TCP6REMOTEPORT="${TCP6REMOTEPORT-unset}"
+
+ echo SSL_PROTOCOL="${SSL_PROTOCOL-unset}"
+ echo SSL_SESSION_ID="${SSL_SESSION_ID-unset}"
+ echo SSL_CIPHER="${SSL_CIPHER-unset}"
+ echo SSL_CIPHER_EXPORT="${SSL_CIPHER_EXPORT-unset}"
+ echo SSL_CIPHER_USEKEYSIZE="${SSL_CIPHER_USEKEYSIZE-unset}"
+ echo SSL_CIPHER_ALGKEYSIZE="${SSL_CIPHER_ALGKEYSIZE-unset}"
+ echo SSL_VERSION_INTERFACE="${SSL_VERSION_INTERFACE-unset}"
+ echo SSL_VERSION_LIBRARY="${SSL_VERSION_LIBRARY-unset}"
+
+ echo SSL_SERVER_M_VERSION="${SSL_SERVER_M_VERSION-unset}"
+ echo SSL_SERVER_M_SERIAL="${SSL_SERVER_M_SERIAL-unset}"
+ echo SSL_SERVER_S_DN="${SSL_SERVER_S_DN-unset}"
+ echo SSL_SERVER_S_DN_C="${SSL_SERVER_S_DN_C-unset}"
+ echo SSL_SERVER_S_DN_ST="${SSL_SERVER_S_DN_ST-unset}"
+ echo SSL_SERVER_S_DN_L="${SSL_SERVER_S_DN_L-unset}"
+ echo SSL_SERVER_S_DN_O="${SSL_SERVER_S_DN_O-unset}"
+ echo SSL_SERVER_S_DN_OU="${SSL_SERVER_S_DN_OU-unset}"
+ echo SSL_SERVER_S_DN_CN="${SSL_SERVER_S_DN_CN-unset}"
+ echo SSL_SERVER_S_DN_T="${SSL_SERVER_S_DN_T-unset}"
+ echo SSL_SERVER_S_DN_I="${SSL_SERVER_S_DN_I-unset}"
+ echo SSL_SERVER_S_DN_G="${SSL_SERVER_S_DN_G-unset}"
+ echo SSL_SERVER_S_DN_S="${SSL_SERVER_S_DN_S-unset}"
+ echo SSL_SERVER_S_DN_D="${SSL_SERVER_S_DN_D-unset}"
+ echo SSL_SERVER_S_DN_UID="${SSL_SERVER_S_DN_UID-unset}"
+ echo SSL_SERVER_S_DN_Email="${SSL_SERVER_S_DN_Email-unset}"
+ echo SSL_SERVER_I_DN="${SSL_SERVER_I_DN-unset}"
+ echo SSL_SERVER_I_DN_C="${SSL_SERVER_I_DN_C-unset}"
+ echo SSL_SERVER_I_DN_ST="${SSL_SERVER_I_DN_ST-unset}"
+ echo SSL_SERVER_I_DN_L="${SSL_SERVER_I_DN_L-unset}"
+ echo SSL_SERVER_I_DN_O="${SSL_SERVER_I_DN_O-unset}"
+ echo SSL_SERVER_I_DN_OU="${SSL_SERVER_I_DN_OU-unset}"
+ echo SSL_SERVER_I_DN_CN="${SSL_SERVER_I_DN_CN-unset}"
+ echo SSL_SERVER_I_DN_T="${SSL_SERVER_I_DN_T-unset}"
+ echo SSL_SERVER_I_DN_I="${SSL_SERVER_I_DN_I-unset}"
+ echo SSL_SERVER_I_DN_G="${SSL_SERVER_I_DN_G-unset}"
+ echo SSL_SERVER_I_DN_S="${SSL_SERVER_I_DN_S-unset}"
+ echo SSL_SERVER_I_DN_D="${SSL_SERVER_I_DN_D-unset}"
+ echo SSL_SERVER_I_DN_UID="${SSL_SERVER_I_DN_UID-unset}"
+ echo SSL_SERVER_I_DN_Email="${SSL_SERVER_I_DN_Email-unset}"
+ echo SSL_SERVER_V_START="${SSL_SERVER_V_START-unset}"
+ echo SSL_SERVER_V_END="${SSL_SERVER_V_END-unset}"
+ echo SSL_SERVER_A_SIG="${SSL_SERVER_A_SIG-unset}"
+ echo SSL_SERVER_A_KEY="${SSL_SERVER_A_KEY-unset}"
+ echo SSL_SERVER_CERT="${SSL_SERVER_CERT-unset}"
+
+ echo SSL_CLIENT_M_VERSION="${SSL_CLIENT_M_VERSION-unset}"
+ echo SSL_CLIENT_M_SERIAL="${SSL_CLIENT_M_SERIAL-unset}"
+ echo SSL_CLIENT_S_DN="${SSL_CLIENT_S_DN-unset}"
+ echo SSL_CLIENT_S_DN_C="${SSL_CLIENT_S_DN_C-unset}"
+ echo SSL_CLIENT_S_DN_ST="${SSL_CLIENT_S_DN_ST-unset}"
+ echo SSL_CLIENT_S_DN_L="${SSL_CLIENT_S_DN_L-unset}"
+ echo SSL_CLIENT_S_DN_O="${SSL_CLIENT_S_DN_O-unset}"
+ echo SSL_CLIENT_S_DN_OU="${SSL_CLIENT_S_DN_OU-unset}"
+ echo SSL_CLIENT_S_DN_CN="${SSL_CLIENT_S_DN_CN-unset}"
+ echo SSL_CLIENT_S_DN_T="${SSL_CLIENT_S_DN_T-unset}"
+ echo SSL_CLIENT_S_DN_I="${SSL_CLIENT_S_DN_I-unset}"
+ echo SSL_CLIENT_S_DN_G="${SSL_CLIENT_S_DN_G-unset}"
+ echo SSL_CLIENT_S_DN_S="${SSL_CLIENT_S_DN_S-unset}"
+ echo SSL_CLIENT_S_DN_D="${SSL_CLIENT_S_DN_D-unset}"
+ echo SSL_CLIENT_S_DN_UID="${SSL_CLIENT_S_DN_UID-unset}"
+ echo SSL_CLIENT_S_DN_Email="${SSL_CLIENT_S_DN_Email-unset}"
+ echo SSL_CLIENT_I_DN="${SSL_CLIENT_I_DN-unset}"
+ echo SSL_CLIENT_I_DN_C="${SSL_CLIENT_I_DN_C-unset}"
+ echo SSL_CLIENT_I_DN_ST="${SSL_CLIENT_I_DN_ST-unset}"
+ echo SSL_CLIENT_I_DN_L="${SSL_CLIENT_I_DN_L-unset}"
+ echo SSL_CLIENT_I_DN_O="${SSL_CLIENT_I_DN_O-unset}"
+ echo SSL_CLIENT_I_DN_OU="${SSL_CLIENT_I_DN_OU-unset}"
+ echo SSL_CLIENT_I_DN_CN="${SSL_CLIENT_I_DN_CN-unset}"
+ echo SSL_CLIENT_I_DN_T="${SSL_CLIENT_I_DN_T-unset}"
+ echo SSL_CLIENT_I_DN_I="${SSL_CLIENT_I_DN_I-unset}"
+ echo SSL_CLIENT_I_DN_G="${SSL_CLIENT_I_DN_G-unset}"
+ echo SSL_CLIENT_I_DN_S="${SSL_CLIENT_I_DN_S-unset}"
+ echo SSL_CLIENT_I_DN_D="${SSL_CLIENT_I_DN_D-unset}"
+ echo SSL_CLIENT_I_DN_UID="${SSL_CLIENT_I_DN_UID-unset}"
+ echo SSL_CLIENT_I_DN_Email="${SSL_CLIENT_I_DN_Email-unset}"
+ echo SSL_CLIENT_V_START="${SSL_CLIENT_V_START-unset}"
+ echo SSL_CLIENT_V_END="${SSL_CLIENT_V_END-unset}"
+ echo SSL_CLIENT_A_SIG="${SSL_CLIENT_A_SIG-unset}"
+ echo SSL_CLIENT_A_KEY="${SSL_CLIENT_A_KEY-unset}"
+ echo SSL_CLIENT_CERT="${SSL_CLIENT_CERT-unset}"
+ echo SSL_CLIENT_CERT_CHAIN_0="${SSL_CLIENT_CERT_CHAIN_0-unset}"
+ echo SSL_CLIENT_CERT_CHAIN_1="${SSL_CLIENT_CERT_CHAIN_1-unset}"
+' > print
+chmod 755 print
+
+
+# Sanitze output
+
+sanitize() {
+ sed -e 's/^SSL_SESSION_ID=.*/SSL_SESSION_ID=.../' \
+ -e 's/^SSLREMOTEPORT=.*/SSLREMOTEPORT=.../' \
+ -e 's/^SSLLOCALPORT=.*/SSLLOCALPORT=.../' \
+ -e 's/^TCPREMOTEPORT=.*/TCPREMOTEPORT=.../' \
+ -e 's/^TCP6REMOTEPORT=.*/TCP6REMOTEPORT=.../' \
+ -e 's/^TCPLOCALPORT=.*/TCPLOCALPORT=.../' \
+ -e 's/^SSL_VERSION_LIBRARY=.*/SSL_VERSION_LIBRARY=.../' \
+ -e 's/^SSL_CIPHER_USEKEYSIZE=.*/SSL_CIPHER_USEKEYSIZE=.../' \
+ -e 's/^SSL_CIPHER_ALGKEYSIZE=.*/SSL_CIPHER_ALGKEYSIZE=.../' \
+ -e 's/^SSL_CIPHER=.*/SSL_CIPHER=.../' \
+ -e 's/^SSL_PROTOCOL=TLSv1.*/SSL_PROTOCOL=TLSv1.../' \
+ -e 's/Localserver:::1:[0-9]*/Localserver:::1:... /' \
+ -e 's/ip6-localnet:::::[0-9]*/ip6-localnet:::::.../'
+}
+
+# done
diff --git a/src/rts.sslperl b/src/rts.sslperl
new file mode 100644
index 0000000..3d1e560
--- /dev/null
+++ b/src/rts.sslperl
@@ -0,0 +1,157 @@
+# Assumptions:
+# available TCP ports on 127.0.0.1: 50022
+
+echo 'package Embedded::test;
+my $n = 0;
+$| = 1;
+sub server (@) {
+ ++$n;
+ print STDERR "log: Hello, World! ($n): @_\n";
+ print "Hello, World! ($n): @_\n";
+
+ $n > 1 and exit(0);
+}
+1;
+' > hello.pm
+
+sanitize() {
+ sed -e 's/^SSL_SESSION_ID=.*/SSL_SESSION_ID=.../' \
+ -e 's/^SSLREMOTEPORT=.*/SSLREMOTEPORT=.../' \
+ -e 's/^SSLLOCALPORT=.*/SSLLOCALPORT=.../' \
+ -e 's/^TCPREMOTEPORT=.*/TCPREMOTEPORT=.../' \
+ -e 's/^TCP6REMOTEPORT=.*/TCP6REMOTEPORT=.../' \
+ -e 's/^TCPLOCALPORT=.*/TCPLOCALPORT=.../' \
+ -e 's/^SSL_VERSION_LIBRARY=.*/SSL_VERSION_LIBRARY=.../' \
+ -e 's/^SSL_CIPHER_USEKEYSIZE=.*/SSL_CIPHER_USEKEYSIZE=.../' \
+ -e 's/^SSL_CIPHER_ALGKEYSIZE=.*/SSL_CIPHER_ALGKEYSIZE=.../' \
+ -e 's/^SSL_CIPHER=.*/SSL_CIPHER=.../' \
+ -e 's/^SSL_PROTOCOL=TLSv1.*/SSL_PROTOCOL=TLSv1.../'
+}
+
+sslperl -w 2 \
+-s -c 1 -Bsslperl -vo -D -1 -3 -Xx rules.cdb -Rt5 -hp -l Localserver -b 2 \
+-a -A \
+127.0.0.1 50022 hello.pm 'Embedded::test::server' here you are \
+3< $CADIR/127.0.0.1.pw >log.50022 2>&1 &
+pid_50022=$!
+sleep 2
+
+echo '--- sslperl works'
+{ sslclient -R -N -H -T 10 -l Local -a "$CAFILE" -4 0 50022 sh -c 'cat <&6'
+ echo $?
+} | sanitize
+{ sslclient -R -N -H -T 10 -l Local -a "$CAFILE" -4 0 50022 sh -c 'cat <&6'
+ echo $?
+} | sanitize
+{ sslclient -R -N -H -T 10 -l Local -a "$CAFILE" -4 0 50022 sh -c 'cat <&6'
+ echo $?
+} | sanitize
+{ sslclient -R -N -H -T 10 -l Local -X -4 0 50022 sh -c 'cat <&6'
+ echo $?
+} | sanitize
+
+echo '--- sslperl prints usage message without enough arguments'
+sslperl 0; echo $?
+
+echo '--- sslperl prints error message with unknown port name'
+sslperl 0 nonexistentport echo wrong; echo $?
+
+echo '--- sslperl prints error message with unknown host name'
+sslperl nonexistent.local. 016 echo wrong; echo $?
+
+echo '--- sslperl prints error message with unresolvable host name'
+sslperl thislabelistoolongbecausednshasalimitof63charactersinasinglelabel. 50022 echo wrong; echo $?
+
+echo '--- sslperl prints error message with non-local host name'
+( sslperl 1.2.3.4 016 echo wrong 2>&1
+ echo $?
+) | sed -e 's/unable to bind: .*$/unable to bind: .../'
+
+kill -TERM $pid_50022
+wait $pid_50022
+
+echo '--- sslperl preserves environment'
+echo 'package Embedded::test;
+my $n = 0;
+$| = 1;
+sub server () {
+ print STDERR "log: NOW=$ENV{NOW}\n";
+ print STDERR "log: changed environment\n";
+ print " changed environment\n";
+ $ENV{'HERE'} = 'NOW';
+}
+1;
+' > hello.pm
+
+sslperl -w 2 \
+-s -c 1 -Bsslperl -vo -D -1 -3 -Xx rules.cdb -Rt5 -hp -l Localserver -b 2 \
+-a -A \
+127.0.0.1 50022 hello.pm 'Embedded::test::server' here you are \
+3< $CADIR/127.0.0.1.pw >>log.50022 2>&1 &
+pid_50022=$!
+sleep 2
+
+{ sslclient -R -N -H -T 10 -l Local -a "$CAFILE" -4 0 50022 sh -c 'cat <&6'
+ echo $?
+} | sanitize
+
+kill -TERM $pid_50022
+wait $pid_50022
+
+echo '--- sslperl handles larger requests'
+echo 'package Embedded::test;
+my $n = 0;
+$| = 1;
+sub server (@) {
+ print @_;
+ while(<>) {
+ print $_;
+ }
+}
+1;
+' > echo.pm
+
+sslperl -w 2 \
+-s -c 1 -Bsslperl -vo -D -1 -3 -Xx rules.cdb -Rt5 -hp -l Localserver -b 2 \
+-a -A \
+127.0.0.1 50022 echo.pm 'Embedded::test::server' here you are \
+3< $CADIR/127.0.0.1.pw >>log.50022 2>&1 &
+pid_50022=$!
+sleep 2
+
+( exec 2>&1
+ exec 3< $CADIR/localhost.pw
+ { for i in 0 1 2 3 4 5 6 7 8 9
+ do
+ for j in 0 1 2 3 4 5 6 7 8 9
+ do
+ for k in 0 1 2 3 4 5 6 7 8 9
+ do
+ echo "abcdefghijklmnopqrstuvwxyz"
+ echo "abcdefghijklmnopqrstuvwxyz"
+ echo "abcdefghijklmnopqrstuvwxyz"
+ echo "abcdefghijklmnopqrstuvwxyz"
+ done
+ done
+ done
+ } | sslconnect 127.0.0.1 50022 -v -s \
+ -a "$CAFILE" -c "$CCERTFILE" -k "$CKEYFILE" -3 >/dev/null
+ echo $?
+) | sanitize
+
+kill -TERM $pid_50022
+wait $pid_50022
+
+echo '--- sslserver -1v prints proper messages'
+cat log.50022 | \
+sed -e 's/::.*/::x/' -e 's/ [0-9]* / x /' \
+ -e 's} cafile x .*/\([^/]*\)} cafile x xxx/\1}' \
+ -e 's} ccafile x .*/\([^/]*\)} ccafile x xxx/\1}' \
+ -e 's} cadir x .*/\([^/]*\)} cadir x xxx/\1}' \
+ -e 's} cert x .*/\([^/]*\)} cert x xxx/\1}' \
+ -e 's} key x .*/\([^/]*\)} key x xxx/\1}' \
+ -e 's/ dhparam x .*/ dhparam x xxx/' \
+ -e 's/ ecdhparam x .*/ ecdhparam x xxx/' \
+ -e 's/ speak TLS: .*/ speak TLS: .../' \
+ -e 's/ accept TLS: .*/ accept TLS: .../' \
+ -e 's/ done [0-9]*$/ done .../'
diff --git a/src/rules.c b/src/rules.c
new file mode 100644
index 0000000..279ffae
--- /dev/null
+++ b/src/rules.c
@@ -0,0 +1,141 @@
+#include "alloc.h"
+#include "stralloc.h"
+#include "open.h"
+#include "cdbread.h"
+#include "byte.h"
+#include "fmt.h"
+#include "getln.h"
+#include "ip.h"
+#include "str.h"
+#include "ip_bit.h"
+#include "rules.h"
+
+stralloc rules_name = {0};
+stralloc ipstring = {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 p;
+ int r;
+ int ipv6 = str_len(ip) - byte_chr(ip,str_len(ip),':');
+
+ if (info) { /* 1. info@ip */
+ if (!stralloc_copys(&rules_name,info)) return -1;
+ if (!stralloc_cats(&rules_name,"@")) return -1;
+ if (ipv6) {
+ if (!ip6_fmt_str(&ipstring,ip))
+ if (!stralloc_catb(&rules_name,ipstring.s,ipstring.len)) return -1;
+ }
+ else
+ if (!stralloc_cats(&rules_name,ip)) return -1;
+ r = dorule(callback);
+ if (r) return r;
+
+ if (host) { /* 2. info@=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 (ipv6) { /* 3. IPv6/IPv4 */
+ if (!ip6_fmt_str(&ipstring,ip)) {
+ if (!stralloc_copyb(&rules_name,ipstring.s,ipstring.len)) return -1;
+ r = dorule(callback);
+ if (r) return r;
+ }
+ } else {
+ if (!stralloc_copys(&rules_name,ip)) return -1;
+ r = dorule(callback);
+ if (r) return r;
+ }
+
+ if (host) { /* 4. =host */
+ if (!stralloc_copys(&rules_name,"=")) return -1;
+ if (!stralloc_cats(&rules_name,host)) return -1;
+ r = dorule(callback);
+ if (r) return r;
+ }
+
+ if (!ipv6) { /* 5. IPv4 class-based */
+ if (!stralloc_copys(&rules_name,ip)) return -1;
+ while (rules_name.len > 0) {
+ if (ip[rules_name.len - 1] == '.') {
+ r = dorule(callback);
+ if (r) return r;
+ }
+ --rules_name.len;
+ }
+ }
+
+ if (ipv6) { /* 6. IPv6/IPv4 CIDR */
+ if (!ip6_bitstring(&ipstring,ip,128)) {
+ for (p = 129; p > 1; p--) {
+ if (!stralloc_copys(&rules_name,"^")) return -1;
+ if (!stralloc_catb(&rules_name,ipstring.s,p)) return -1;
+ r = dorule(callback);
+ if (r) return r;
+ }
+ }
+ } else {
+ if (!ip4_bitstring(&ipstring,ip,32)) {
+ for (p = 33; p > 1; p--) {
+ if (!stralloc_copys(&rules_name,"_")) return -1;
+ if (!stralloc_catb(&rules_name,ipstring.s,p)) return -1;
+ r = dorule(callback);
+ if (r) return r;
+ }
+ }
+ }
+
+ if (host) { /* 7. =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; /* 8. = rule */
+ 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/src/rules.h b/src/rules.h
new file mode 100644
index 0000000..15d9b90
--- /dev/null
+++ b/src/rules.h
@@ -0,0 +1,9 @@
+#ifndef RULES_H
+#define RULES_H
+
+#include "stralloc.h"
+
+extern stralloc rules_name;
+extern int rules(void (*)(char *,unsigned int),int,char *,char *,char *);
+
+#endif
diff --git a/src/select.h1 b/src/select.h1
new file mode 100644
index 0000000..68e971f
--- /dev/null
+++ b/src/select.h1
@@ -0,0 +1,12 @@
+/* Public domain. */
+
+#ifndef SELECT_H
+#define SELECT_H
+
+/* sysdep: -sysselect */
+
+#include <sys/types.h>
+#include <sys/time.h>
+extern int select();
+
+#endif
diff --git a/src/select.h2 b/src/select.h2
new file mode 100644
index 0000000..4bd4fcf
--- /dev/null
+++ b/src/select.h2
@@ -0,0 +1,13 @@
+/* Public domain. */
+
+#ifndef SELECT_H
+#define SELECT_H
+
+/* sysdep: +sysselect */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/select.h>
+extern int select();
+
+#endif
diff --git a/src/ssl_ca.c b/src/ssl_ca.c
new file mode 100644
index 0000000..a6ab523
--- /dev/null
+++ b/src/ssl_ca.c
@@ -0,0 +1,11 @@
+#include "ucspissl.h"
+
+int ssl_ca(SSL_CTX *ctx,const char *certfile,const char *certdir,int d)
+{
+ if (!SSL_CTX_load_verify_locations(ctx,certfile,certdir)) return 0;
+
+ SSL_CTX_set_verify_depth(ctx,d);
+
+ return 1;
+}
+
diff --git a/src/ssl_cca.c b/src/ssl_cca.c
new file mode 100644
index 0000000..112a9f1
--- /dev/null
+++ b/src/ssl_cca.c
@@ -0,0 +1,18 @@
+#include "ucspissl.h"
+
+int ssl_cca(SSL_CTX *ctx,const char *certfile)
+{
+ STACK_OF(X509_NAME) *x;
+
+ if (!certfile) return 1;
+
+ x = SSL_load_client_CA_file(certfile);
+ if (!x) return 0;
+
+ SSL_CTX_set_client_CA_list(ctx,x);
+
+ SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,0);
+
+ return 1;
+}
+
diff --git a/src/ssl_certkey.c b/src/ssl_certkey.c
new file mode 100644
index 0000000..8292cbe
--- /dev/null
+++ b/src/ssl_certkey.c
@@ -0,0 +1,19 @@
+#include "ucspissl.h"
+
+int ssl_certkey(SSL_CTX *ctx,const char *certfile,const char *keyfile,pem_password_cb *passwd_cb)
+{
+ if (!certfile) return 0;
+
+ if (SSL_CTX_use_certificate_chain_file(ctx,certfile) != 1)
+ return -1;
+
+ if (!keyfile) keyfile = certfile;
+ SSL_CTX_set_default_passwd_cb(ctx,passwd_cb);
+ if (SSL_CTX_use_PrivateKey_file(ctx,keyfile,SSL_FILETYPE_PEM) != 1)
+ return -2;
+
+ if (SSL_CTX_check_private_key(ctx) != 1)
+ return -3;
+
+ return 0;
+}
diff --git a/src/ssl_chainfile.c b/src/ssl_chainfile.c
new file mode 100644
index 0000000..9e353c2
--- /dev/null
+++ b/src/ssl_chainfile.c
@@ -0,0 +1,24 @@
+/**
+ @file ssl_certchainfile.c
+ @author feh
+ @brief Enables Certificate chain file presentation for sslserver
+*/
+#include "ucspissl.h"
+
+int ssl_chainfile(SSL_CTX *ctx,const char *certchainfile,const char *keyfile,pem_password_cb *passwd_cb)
+{
+ if (!certchainfile) return 0;
+ if (!keyfile) return 0;
+
+ if (SSL_CTX_use_certificate_chain_file(ctx,certchainfile) <= 0)
+ return -1;
+
+ SSL_CTX_set_default_passwd_cb(ctx,passwd_cb);
+ if (SSL_CTX_use_PrivateKey_file(ctx,keyfile,SSL_FILETYPE_PEM) != 1)
+ return -2;
+
+ if (SSL_CTX_check_private_key(ctx) != 1)
+ return -3;
+
+ return 0;
+}
diff --git a/src/ssl_ciphers.c b/src/ssl_ciphers.c
new file mode 100644
index 0000000..168c2bb
--- /dev/null
+++ b/src/ssl_ciphers.c
@@ -0,0 +1,21 @@
+#include "ucspissl.h"
+
+int ssl_ciphers(SSL_CTX *ctx,const char *ciphers) {
+ int r = 0; // no cipher selected
+
+ if (!ciphers) return -1;
+
+/* TLS <= 1.2 SSL_CTX_set_cipher_list()
+ TLS = 1.3 SSL_CTX_set_ciphersuites() [only OpenSSL here]
+
+ see: https://community.openvpn.net/openvpn/ticket/1159
+*/
+
+#if (OPENSSL_VERSION_NUMBER > 0x10101000L && !LIBRESSL_VERSION_NUMBER) // 0xmnnffppsL
+ if ((r = SSL_CTX_set_ciphersuites(ctx,ciphers)) == 0)
+#endif
+ r = SSL_CTX_set_cipher_list(ctx,ciphers); // TLS < 1.3 and fallback
+
+ return r;
+}
+
diff --git a/src/ssl_context.c b/src/ssl_context.c
new file mode 100644
index 0000000..03ce58a
--- /dev/null
+++ b/src/ssl_context.c
@@ -0,0 +1,34 @@
+#include "ucspissl.h"
+
+SSL_CTX *ssl_context(const SSL_METHOD *m)
+{
+ SSL_CTX *ctx;
+
+ SSL_library_init();
+ ctx = SSL_CTX_new(m);
+#ifdef SSL_TWEAKING
+ SSL_CTX_set_options(ctx,SSL_OP_SINGLE_DH_USE|SSL_OP_NO_COMPRESSION|SSL_OP_CIPHER_SERVER_PREFERENCE);
+#else
+ SSL_CTX_set_options(ctx,SSL_OP_SINGLE_DH_USE);
+#endif
+#ifdef SSLv2_DISABLE
+ SSL_CTX_set_options(ctx,SSL_OP_NO_SSLv2);
+#endif
+#ifdef SSLv3_DISABLE
+ SSL_CTX_set_options(ctx,SSL_OP_NO_SSLv3);
+#endif
+#ifdef TLSv1_DISABLE
+ SSL_CTX_set_options(ctx,SSL_OP_NO_TLSv1);
+#endif
+#ifdef TLSv1_1_DISABLE
+ SSL_CTX_set_options(ctx,SSL_OP_NO_TLSv1_1);
+#endif
+#ifdef TLSv1_2_DISABLE
+ SSL_CTX_set_options(ctx,SSL_OP_NO_TLSv1_2);
+#endif
+#ifdef TLSv1_3_DISABLE
+ SSL_CTX_set_options(ctx,SSL_OP_NO_TLSv1_3);
+#endif
+ return ctx;
+}
+
diff --git a/src/ssl_env.c b/src/ssl_env.c
new file mode 100644
index 0000000..4ddff35
--- /dev/null
+++ b/src/ssl_env.c
@@ -0,0 +1,435 @@
+#include <unistd.h>
+#include <string.h>
+#include "fmt.h"
+#include "pathexec.h"
+#include "ucspissl.h"
+#include "stralloc.h"
+#include "str.h"
+
+static char strnum[FMT_ULONG];
+static stralloc ctemp = {0};
+static stralloc *envsa = 0;
+static stralloc btemp = {0};
+static stralloc etemp = {0};
+
+#define set_env_id(n,e,v) \
+if (!set_env_name_entry((n),(e),(v))) return 0
+
+static int env_val(const char *env,const void *val,int len) {
+ const char *v = val;
+ if (envsa) {
+ if (!stralloc_cats(envsa,env)) return 0;
+ if (!stralloc_catb(envsa,"=",1)) return 0;
+ if (!stralloc_catb(envsa,v,len)) return 0;
+ if (!stralloc_0(envsa)) return 0;
+ return 1;
+ }
+ if (!stralloc_copyb(&etemp,v,len)) return 0;
+ if (!stralloc_0(&etemp)) return 0;
+ return pathexec_env(env,etemp.s);
+}
+
+static int env_str(const char *env,const char *val) {
+ if (envsa) {
+ return env_val(env,val,str_len(val));
+ if (!stralloc_cats(envsa,env)) return 0;
+ if (!stralloc_catb(envsa,"=",1)) return 0;
+ if (!stralloc_catb(envsa,val,str_len(val) + 1)) return 0;
+ return 1;
+ }
+ return pathexec_env(env,val);
+}
+
+static int set_env_name_entry(X509_NAME *xname,const char *env,int nid) {
+ X509_NAME_ENTRY *xne;
+ int m;
+ int n;
+
+ if (!env) return 1;
+#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L))
+ for (m = 0; m < sk_X509_NAME_ENTRY_num(xname->entries); m++) {
+ xne = sk_X509_NAME_ENTRY_value(xname->entries,m);
+ n = OBJ_obj2nid(xne->object);
+ if (n == nid)
+ if (!env_val(env,xne->value->data,xne->value->length)) return 0;
+#else
+ for (m = 0; m < X509_NAME_entry_count(xname); m++) {
+ xne = X509_NAME_get_entry(xname,m);
+ n = OBJ_obj2nid(X509_NAME_ENTRY_get_object(xne));
+ if (n == nid)
+ if (!env_val(env,X509_NAME_ENTRY_get_data(xne)->data,X509_NAME_ENTRY_get_data(xne)->length)) return 0;
+#endif
+ }
+
+ return 1;
+}
+
+int ssl_session_vars(SSL *ssl) {
+ unsigned const char *x;
+ SSL_SESSION *session;
+ unsigned int n = 0;
+ int m;
+ const SSL_CIPHER *cipher;
+ unsigned char u;
+ unsigned char c;
+
+ if (!env_str("SSL_PROTOCOL",SSL_get_version(ssl)))
+ return 0;
+
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L) // 0xmnnffppsL
+ session = SSL_get_session(ssl);
+ x = session->session_id;
+ n = session->session_id_length;
+#else
+ session = SSL_get1_session(ssl);
+ x = SSL_SESSION_get_id(session,&n);
+#endif
+
+ if (!stralloc_ready(&btemp,2 * n)) return 0;
+ btemp.len = 2 * n;
+ while (n--) {
+ u = x[n];
+ c = '0' + (u & 15);
+ if (c > '0' + 9) c += 'a' - '0' - 10;
+ btemp.s[2 * n + 1] = c;
+ u >>= 4;
+ c = '0' + (u & 15);
+ if (c > '0' + 9) c += 'a' - '0' - 10;
+ btemp.s[2 * n] = c;
+ }
+ if (!env_val("SSL_SESSION_ID",btemp.s,btemp.len)) return 0;
+
+ if (!env_str("SSL_CIPHER",SSL_get_cipher_name(ssl))) return 0;
+
+ cipher = SSL_get_current_cipher(ssl);
+ if (!cipher) return 0;
+ n = SSL_CIPHER_get_bits(cipher,&m);
+ if (!env_str("SSL_CIPHER_EXPORT",n < 56 ? "true" : "false")) return 0;
+ if (!env_val("SSL_CIPHER_USEKEYSIZE",strnum,fmt_ulong(strnum,n))) return 0;
+ if (!env_val("SSL_CIPHER_ALGKEYSIZE",strnum,fmt_ulong(strnum,m))) return 0;
+
+ if (!env_str("SSL_VERSION_INTERFACE","ucspi-ssl")) return 0;
+ if (!env_str("SSL_VERSION_LIBRARY",OPENSSL_VERSION_TEXT)) return 0;
+
+ return 1;
+}
+
+static int ssl_client_bio_vars(X509 *cert,STACK_OF(X509) *chain,BIO *bio) {
+ ASN1_STRING *astring;
+ int n;
+ int m;
+
+ astring = X509_get_notBefore(cert);
+ if (!ASN1_UTCTIME_print(bio,astring)) return 0;
+ n = BIO_pending(bio);
+ if (!stralloc_ready(&btemp,n)) return 0;
+ btemp.len = n;
+ n = BIO_read(bio,btemp.s,n);
+ if (n != btemp.len) return 0;
+ if (!env_val("SSL_CLIENT_V_START",btemp.s,btemp.len)) return 0;
+
+ astring = X509_get_notAfter(cert);
+ if (!ASN1_UTCTIME_print(bio,astring)) return 0;
+ n = BIO_pending(bio);
+ if (!stralloc_ready(&btemp,n)) return 0;
+ btemp.len = n;
+ n = BIO_read(bio,btemp.s,n);
+ if (n != btemp.len) return 0;
+ if (!env_val("SSL_CLIENT_V_END",btemp.s,btemp.len)) return 0;
+
+ if (!PEM_write_bio_X509(bio,cert)) return 0;
+ n = BIO_pending(bio);
+ if (!stralloc_ready(&btemp,n)) return 0;
+ btemp.len = n;
+ n = BIO_read(bio,btemp.s,n);
+ if (n != btemp.len) return 0;
+ if (!env_val("SSL_CLIENT_CERT",btemp.s,btemp.len)) return 0;
+
+ if (chain) {
+ for (m = 0; m < sk_X509_num(chain); m++) {
+ if (!stralloc_copys(&ctemp,"SSL_CLIENT_CERT_CHAIN_")) return 0;
+ if (!stralloc_catb(&ctemp,strnum,fmt_ulong(strnum,m))) return 0;
+ if (!stralloc_0(&ctemp)) return 0;
+
+ if (m < sk_X509_num(chain)) {
+ if (!PEM_write_bio_X509(bio,sk_X509_value(chain,m))) return 0;
+ n = BIO_pending(bio);
+ if (!stralloc_ready(&btemp,n)) return 0;
+ btemp.len = n;
+ n = BIO_read(bio,btemp.s,n);
+ if (n != btemp.len) return 0;
+ if (!env_val(ctemp.s,btemp.s,btemp.len)) return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static int ssl_client_vars(X509 *cert,STACK_OF(X509) *chain) {
+ X509_NAME *xname;
+ X509_PUBKEY *pubkey;
+ const X509_ALGOR *sigalg;
+ const ASN1_OBJECT *calgoid;
+ ASN1_OBJECT *algoid;
+ BIGNUM *bn;
+ BIO *bio;
+ char *x = 0;
+ int n;
+
+ if (!cert) return 1;
+
+ if (!env_val("SSL_CLIENT_M_VERSION",strnum,fmt_ulong(strnum,X509_get_version(cert) + 1)))
+ return 0;
+
+ bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(cert), 0);
+ x = BN_bn2dec(bn);
+ BN_free(bn);
+ if (!env_val("SSL_CLIENT_M_SERIAL",x,strlen(x)))
+ return 0;
+ OPENSSL_free(x);
+
+ xname = X509_get_subject_name(cert);
+ x = X509_NAME_oneline(xname,0,0);
+ n = env_str("SSL_CLIENT_S_DN",x);
+ free(x);
+ if (!n) return 0;
+
+ set_env_id(xname,"SSL_CLIENT_S_DN_C",NID_countryName);
+ set_env_id(xname,"SSL_CLIENT_S_DN_ST",NID_stateOrProvinceName);
+ set_env_id(xname,"SSL_CLIENT_S_DN_L",NID_localityName);
+ set_env_id(xname,"SSL_CLIENT_S_DN_O",NID_organizationName);
+ set_env_id(xname,"SSL_CLIENT_S_DN_OU",NID_organizationalUnitName);
+ set_env_id(xname,"SSL_CLIENT_S_DN_CN",NID_commonName);
+ set_env_id(xname,"SSL_CLIENT_S_DN_T",NID_title);
+ set_env_id(xname,"SSL_CLIENT_S_DN_I",NID_initials);
+ set_env_id(xname,"SSL_CLIENT_S_DN_G",NID_givenName);
+ set_env_id(xname,"SSL_CLIENT_S_DN_S",NID_surname);
+ set_env_id(xname,"SSL_CLIENT_S_DN_D",NID_description);
+ set_env_id(xname,"SSL_CLIENT_S_DN_UID",NID_x500UniqueIdentifier);
+ set_env_id(xname,"SSL_CLIENT_S_DN_Email",NID_pkcs9_emailAddress);
+
+ xname = X509_get_issuer_name(cert);
+ x = X509_NAME_oneline(xname,0,0);
+ n = env_str("SSL_CLIENT_I_DN",x);
+ free(x);
+ if (!n) return 0;
+
+ set_env_id(xname,"SSL_CLIENT_I_DN_C",NID_countryName);
+ set_env_id(xname,"SSL_CLIENT_I_DN_ST",NID_stateOrProvinceName);
+ set_env_id(xname,"SSL_CLIENT_I_DN_L",NID_localityName);
+ set_env_id(xname,"SSL_CLIENT_I_DN_O",NID_organizationName);
+ set_env_id(xname,"SSL_CLIENT_I_DN_OU",NID_organizationalUnitName);
+ set_env_id(xname,"SSL_CLIENT_I_DN_CN",NID_commonName);
+ set_env_id(xname,"SSL_CLIENT_I_DN_T",NID_title);
+ set_env_id(xname,"SSL_CLIENT_I_DN_I",NID_initials);
+ set_env_id(xname,"SSL_CLIENT_I_DN_G",NID_givenName);
+ set_env_id(xname,"SSL_CLIENT_I_DN_S",NID_surname);
+ set_env_id(xname,"SSL_CLIENT_I_DN_D",NID_description);
+ set_env_id(xname,"SSL_CLIENT_I_DN_UID",NID_x500UniqueIdentifier);
+ set_env_id(xname,"SSL_CLIENT_I_DN_Email",NID_pkcs9_emailAddress);
+
+/* Signature Algorithm for PubKey */
+#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L))
+ n = OBJ_obj2nid(cert->cert_info->signature->algorithm);
+#else
+ sigalg = X509_get0_tbs_sigalg(cert);
+ X509_ALGOR_get0(&calgoid,0,0,sigalg);
+ n = OBJ_obj2nid(calgoid);
+#endif
+ if (!env_str("SSL_CLIENT_A_SIG",(n == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(n)))
+ return 0;
+
+/* Algorithm for PubKey */
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L) // 0xmnnffppsL
+ n = OBJ_obj2nid(cert->cert_info->key->algor->algorithm);
+#else
+ pubkey = X509_get_X509_PUBKEY(cert);
+ X509_PUBKEY_get0_param(&algoid,0,0,0,pubkey);
+ n = OBJ_obj2nid(algoid);
+#endif
+ if (!env_str("SSL_CLIENT_A_KEY",(n == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(n)))
+ return 0;
+
+ bio = BIO_new(BIO_s_mem());
+ if (!bio) return 0;
+ n = ssl_client_bio_vars(cert,chain,bio);
+ BIO_free(bio);
+ if (!n) return 0;
+
+ return 1;
+}
+
+static int ssl_server_bio_vars(X509 *cert,STACK_OF(X509) *chain,BIO *bio) {
+ ASN1_STRING *astring;
+ int n;
+ int m;
+
+ astring = X509_get_notBefore(cert);
+ if (!ASN1_UTCTIME_print(bio,astring)) return 0;
+ n = BIO_pending(bio);
+ if (!stralloc_ready(&btemp,n)) return 0;
+ btemp.len = n;
+ n = BIO_read(bio,btemp.s,n);
+ if (n != btemp.len) return 0;
+ if (!env_val("SSL_SERVER_V_START",btemp.s,btemp.len)) return 0;
+
+ astring = X509_get_notAfter(cert);
+ if (!ASN1_UTCTIME_print(bio,astring)) return 0;
+ n = BIO_pending(bio);
+ if (!stralloc_ready(&btemp,n)) return 0;
+ btemp.len = n;
+ n = BIO_read(bio,btemp.s,n);
+ if (n != btemp.len) return 0;
+ if (!env_val("SSL_SERVER_V_END",btemp.s,btemp.len)) return 0;
+
+ if (!PEM_write_bio_X509(bio,cert)) return 0;
+ n = BIO_pending(bio);
+ if (!stralloc_ready(&btemp,n)) return 0;
+ btemp.len = n;
+ n = BIO_read(bio,btemp.s,n);
+ if (n != btemp.len) return 0;
+ if (!env_val("SSL_SERVER_CERT",btemp.s,btemp.len)) return 0;
+
+ if (chain) {
+ for (m = 0; m < sk_X509_num(chain); m++) {
+ if (!stralloc_copys(&ctemp,"SSL_SERVER_CERT_CHAIN_")) return 0;
+ if (!stralloc_catb(&ctemp,strnum,fmt_ulong(strnum,m))) return 0;
+ if (!stralloc_0(&ctemp)) return 0;
+
+ if (m < sk_X509_num(chain)) {
+ if (!PEM_write_bio_X509(bio,sk_X509_value(chain,m))) return 0;
+ n = BIO_pending(bio);
+ if (!stralloc_ready(&btemp,n)) return 0;
+ btemp.len = n;
+ n = BIO_read(bio,btemp.s,n);
+ if (n != btemp.len) return 0;
+ if (!env_val(ctemp.s,btemp.s,btemp.len)) return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static int ssl_server_vars(X509 *cert,STACK_OF(X509) *chain) {
+ X509_NAME *xname;
+ X509_PUBKEY *pubkey;
+ const X509_ALGOR *sigalg;
+ const ASN1_OBJECT *calgoid;
+ ASN1_OBJECT *algoid;
+ BIGNUM *bn;
+ BIO *bio;
+ char *x = 0;
+ int n;
+
+ if (!cert) return 1;
+
+ if (!env_val("SSL_SERVER_M_VERSION",strnum,fmt_ulong(strnum,X509_get_version(cert) + 1)))
+ return 0;
+
+ bn = ASN1_INTEGER_to_BN(X509_get_serialNumber(cert), 0);
+ x = BN_bn2dec(bn);
+ BN_free(bn);
+ if (!env_val("SSL_SERVER_M_SERIAL",x,strlen(x))) return 0;
+ OPENSSL_free(x);
+
+ xname = X509_get_subject_name(cert);
+ x = X509_NAME_oneline(xname,0,0);
+ n = env_str("SSL_SERVER_S_DN",x);
+ free(x);
+ if (!n) return 0;
+
+ set_env_id(xname,"SSL_SERVER_S_DN_C",NID_countryName);
+ set_env_id(xname,"SSL_SERVER_S_DN_ST",NID_stateOrProvinceName);
+ set_env_id(xname,"SSL_SERVER_S_DN_L",NID_localityName);
+ set_env_id(xname,"SSL_SERVER_S_DN_O",NID_organizationName);
+ set_env_id(xname,"SSL_SERVER_S_DN_OU",NID_organizationalUnitName);
+ set_env_id(xname,"SSL_SERVER_S_DN_CN",NID_commonName);
+ set_env_id(xname,"SSL_SERVER_S_DN_T",NID_title);
+ set_env_id(xname,"SSL_SERVER_S_DN_I",NID_initials);
+ set_env_id(xname,"SSL_SERVER_S_DN_G",NID_givenName);
+ set_env_id(xname,"SSL_SERVER_S_DN_S",NID_surname);
+ set_env_id(xname,"SSL_SERVER_S_DN_D",NID_description);
+ set_env_id(xname,"SSL_SERVER_S_DN_UID",NID_x500UniqueIdentifier);
+ set_env_id(xname,"SSL_SERVER_S_DN_Email",NID_pkcs9_emailAddress);
+
+ xname = X509_get_issuer_name(cert);
+ x = X509_NAME_oneline(xname,0,0);
+ n = env_str("SSL_SERVER_I_DN",x);
+ free(x);
+ if (!n) return 0;
+
+ set_env_id(xname,"SSL_SERVER_I_DN_C",NID_countryName);
+ set_env_id(xname,"SSL_SERVER_I_DN_ST",NID_stateOrProvinceName);
+ set_env_id(xname,"SSL_SERVER_I_DN_L",NID_localityName);
+ set_env_id(xname,"SSL_SERVER_I_DN_O",NID_organizationName);
+ set_env_id(xname,"SSL_SERVER_I_DN_OU",NID_organizationalUnitName);
+ set_env_id(xname,"SSL_SERVER_I_DN_CN",NID_commonName);
+ set_env_id(xname,"SSL_SERVER_I_DN_T",NID_title);
+ set_env_id(xname,"SSL_SERVER_I_DN_I",NID_initials);
+ set_env_id(xname,"SSL_SERVER_I_DN_G",NID_givenName);
+ set_env_id(xname,"SSL_SERVER_I_DN_S",NID_surname);
+ set_env_id(xname,"SSL_SERVER_I_DN_D",NID_description);
+ set_env_id(xname,"SSL_SERVER_I_DN_UID",NID_x500UniqueIdentifier);
+ set_env_id(xname,"SSL_SERVER_I_DN_Email",NID_pkcs9_emailAddress);
+
+/* Signature Algorithm of PubKey */
+#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L))
+ n = OBJ_obj2nid(cert->cert_info->signature->algorithm);
+#else
+ sigalg = X509_get0_tbs_sigalg(cert);
+ X509_ALGOR_get0(&calgoid,0,0,sigalg);
+ n = OBJ_obj2nid(calgoid);
+#endif
+ if (!env_str("SSL_SERVER_A_SIG",(n == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(n)))
+ return 0;
+
+/* Algorithm of PubKey */
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L) // 0xmnnffppsL
+ n = OBJ_obj2nid(cert->cert_info->key->algor->algorithm);
+#else
+ pubkey = X509_get_X509_PUBKEY(cert);
+ X509_PUBKEY_get0_param(&algoid,0,0,0,pubkey);
+ n = OBJ_obj2nid(algoid);
+#endif
+ if (!env_str("SSL_SERVER_A_KEY",(n == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(n)))
+ return 0;
+
+ bio = BIO_new(BIO_s_mem());
+ if (!bio) return 0;
+ n = ssl_server_bio_vars(cert,chain,bio);
+ BIO_free(bio);
+
+ if (!n) return 0;
+
+ return 1;
+}
+
+int ssl_client_env(SSL *ssl,stralloc *sa) {
+ envsa = sa;
+ if (!ssl_session_vars(ssl)) return 0;
+ if (!ssl_client_vars(SSL_get_certificate(ssl),0))
+ return 0;
+#if (OPENSSL_VERSION_NUMBER > 0x30000000L) // 0xmnnffppsL
+ if (!ssl_server_vars(0,SSL_get_peer_cert_chain(ssl)))
+#else
+ if (!ssl_server_vars(SSL_get_peer_certificate(ssl),SSL_get_peer_cert_chain(ssl)))
+#endif
+ return 0;
+ return 1;
+}
+
+int ssl_server_env(SSL *ssl,stralloc *sa) {
+ envsa = sa;
+ if (!ssl_session_vars(ssl)) return 0;
+ if (!ssl_server_vars(SSL_get_certificate(ssl),0))
+ return 0;
+#if (OPENSSL_VERSION_NUMBER > 0x30000000L) // 0xmnnffppsL
+ if (!ssl_server_vars(0,SSL_get_peer_cert_chain(ssl)))
+#else
+ if (!ssl_client_vars(SSL_get_peer_certificate(ssl),SSL_get_peer_cert_chain(ssl)))
+#endif
+ return 0;
+ return 1;
+}
diff --git a/src/ssl_error.c b/src/ssl_error.c
new file mode 100644
index 0000000..88a01a1
--- /dev/null
+++ b/src/ssl_error.c
@@ -0,0 +1,12 @@
+#include "ucspissl.h"
+
+int ssl_error(int (*op)(const char *)) {
+ unsigned long e;
+ int r;
+
+ e = ERR_get_error();
+ if (!e) return 0;
+ r = op(ERR_error_string(e,0));
+ if (r) return r;
+ return ssl_error(op);
+}
diff --git a/src/ssl_io.c b/src/ssl_io.c
new file mode 100644
index 0000000..883380f
--- /dev/null
+++ b/src/ssl_io.c
@@ -0,0 +1,269 @@
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include "iopause.h"
+#include "buffer.h"
+#include "taia.h"
+#include "ucspissl.h"
+#include "error.h"
+
+static int leftstatus = 0;
+static char leftbuf[16 * 1024];
+static int leftlen;
+static int leftpos;
+
+static int rightstatus = 0;
+static char rightbuf[16 * 1024];
+static int rightlen;
+static int rightpos;
+
+int ssl_io(SSL *ssl,int fdleft,int fdright,unsigned int timeout) {
+ struct taia now;
+ struct taia deadline;
+ iopause_fd x[4];
+ int xlen;
+ iopause_fd *io0;
+ iopause_fd *ioleft;
+ iopause_fd *io1;
+ iopause_fd *ioright;
+ int r;
+ int rc = 0;
+ int rfd;
+ int wfd;
+
+ rfd = SSL_get_fd(ssl); /* XXX */
+ if (rfd == -1) {
+ close(fdleft); close(fdright);
+ return -1;
+ }
+ wfd = SSL_get_fd(ssl); /* XXX */
+ if (wfd == -1) {
+ close(fdleft); close(fdright);
+ return -1;
+ }
+
+ for (;;) {
+ xlen = 0;
+
+ if (leftstatus == -1 && rightstatus == -1)
+ goto DONE;
+
+ io0 = 0;
+ if (leftstatus == 0 && rightstatus != 1) {
+ io0 = &x[xlen++];
+ io0->fd = rfd;
+ io0->events = IOPAUSE_READ;
+ }
+
+ ioleft = 0;
+ if (leftstatus == 1) {
+ ioleft = &x[xlen++];
+ ioleft->fd = fdleft;
+ ioleft->events = IOPAUSE_WRITE;
+ }
+
+ ioright = 0;
+ if (rightstatus == 0) {
+ ioright = &x[xlen++];
+ ioright->fd = fdright;
+ ioright->events = IOPAUSE_READ;
+ }
+
+ io1 = 0;
+ if (rightstatus == 1) {
+ io1 = &x[xlen++];
+ io1->fd = wfd;
+ io1->events = IOPAUSE_WRITE;
+ }
+
+ if (taia_now(&now) == -1) {
+ errno = ETIMEDOUT;
+ rc = -1;
+ goto BOMB;
+ }
+ taia_uint(&deadline,timeout);
+ taia_add(&deadline,&now,&deadline);
+ iopause(x,xlen,&deadline,&now);
+
+ for (r = 0; r < xlen; ++r)
+ if (x[r].revents) goto EVENTS;
+
+ if (io0 && !ssl_pending(ssl)) {
+ close(fdleft);
+ leftstatus = -1;
+ continue;
+ }
+ errno = ETIMEDOUT;
+ rc = -1;
+ goto BOMB;
+
+
+EVENTS:
+ if (io0 && io0->revents) {
+ r = SSL_read(ssl,leftbuf,sizeof(leftbuf));
+ ssl_errno = SSL_get_error(ssl,r);
+ switch (ssl_errno) {
+ case SSL_ERROR_NONE:
+ leftstatus = 1;
+ leftpos = 0;
+ leftlen = r;
+ break;
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ if (rightstatus == -1) goto DONE;
+ close(fdleft);
+ leftstatus = -1;
+ break;
+ case SSL_ERROR_SYSCALL:
+ if (errno == EAGAIN || errno == EINTR) break;
+ close(fdleft);
+ leftstatus = -1;
+ if (!errno) break;
+ /* premature close */
+ if (errno == ECONNRESET && rightstatus == -1) goto DONE;
+ goto BOMB;
+ case SSL_ERROR_SSL:
+/* Continuing after a received SSL error given the socket is still
+ * potentially active might be a noble cause, but is impracticle.
+ * We consider an SSL_ERROR_SSL as application failure; not TLS
+ * and close the connection gracefully.
+ * if (errno == EAGAIN || errno == EINTR) break;
+ * if (!errno) break;
+ */
+ goto DONE;
+ default:
+ close(fdleft);
+ leftstatus = -1;
+ if (rightstatus == 1) break;
+ if (ssl_shutdown_pending(ssl)) goto DONE;
+ goto BOMB;
+ }
+ }
+
+ if (ioleft && ioleft->revents) {
+ r = buffer_unixwrite(fdleft,leftbuf + leftpos,leftlen - leftpos);
+ if (r == -1) {
+ if (errno == EINTR || errno == EWOULDBLOCK) {
+ /* retry */
+ }
+ else if (errno == EPIPE || errno == EAGAIN) {
+ if (rightstatus == -1) goto DONE;
+ close(fdleft);
+ leftstatus = -1;
+ } else {
+ rc = -1;
+ goto BOMB;
+ }
+ }
+ else {
+ leftpos += r;
+ if (leftpos == leftlen) {
+ leftstatus = 0;
+ if ((r = ssl_pending(ssl))) {
+ if (r > sizeof(leftbuf)) r = sizeof(leftbuf);
+ r = SSL_read(ssl,leftbuf,r);
+ ssl_errno = SSL_get_error(ssl,r);
+ switch (ssl_errno) {
+ case SSL_ERROR_NONE:
+ leftstatus = 1;
+ leftpos = 0;
+ leftlen = r;
+ break;
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ if (rightstatus == -1) goto DONE;
+ close(fdleft);
+ leftstatus = -1;
+ break;
+ default:
+ rc = -1;
+ goto BOMB;
+ }
+ }
+ }
+ }
+ }
+
+ if (ioright && ioright->revents) {
+ r = buffer_unixread(fdright,rightbuf,sizeof(rightbuf));
+ if (r == -1) {
+ if (errno == EINTR || errno == EWOULDBLOCK) {
+ /* retry */
+ } else {
+ rc = -1;
+ goto BOMB; /* errno == EAGAIN => unrecoverable */
+ }
+ }
+ else if (r == 0) {
+ close(fdright);
+ rightstatus = -1;
+ if (ssl_shutdown(ssl)) goto DONE;
+ if (leftstatus == -1) goto DONE;
+ }
+ else {
+ rightstatus = 1;
+ rightpos = 0;
+ rightlen = r;
+ }
+ }
+
+ if (io1 && io1->revents) {
+ r = SSL_write(ssl,rightbuf + rightpos,rightlen - rightpos);
+ ssl_errno = SSL_get_error(ssl,r);
+ switch (ssl_errno) {
+ case SSL_ERROR_NONE:
+ rightpos += r;
+ if (rightpos == rightlen) rightstatus = 0;
+ break;
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+ close(fdright);
+ rightstatus = -1;
+ if (leftstatus == -1) goto DONE;
+ if (ssl_shutdown(ssl)) goto DONE;
+ break;
+ case SSL_ERROR_SYSCALL:
+ if (errno == EAGAIN || errno == EINTR) break;
+ if (errno == EPIPE) {
+ close(fdright);
+ rightstatus = -1;
+ if (leftstatus == -1) goto DONE;
+ if (ssl_shutdown(ssl)) goto DONE;
+ break;
+ }
+ default:
+ rc = -1;
+ goto BOMB;
+ }
+ }
+ }
+
+
+BOMB:
+ r = errno;
+ if (leftstatus != -1) close(fdleft);
+ if (rightstatus != -1) close(fdright);
+ if (!ssl_shutdown_sent(ssl)) ssl_shutdown(ssl);
+ if (!ssl_shutdown_pending(ssl)) ssl_shutdown(ssl);
+ shutdown(wfd,2);
+ errno = r;
+ return rc;
+
+
+DONE:
+ if (!ssl_shutdown_sent(ssl)) ssl_shutdown(ssl);
+ if (!ssl_shutdown_pending(ssl)) ssl_shutdown(ssl);
+ shutdown(wfd,2);
+ if (leftstatus != -1) close(fdleft);
+ if (rightstatus != -1) close(fdright);
+ return 0;
+}
diff --git a/src/ssl_new.c b/src/ssl_new.c
new file mode 100644
index 0000000..4833778
--- /dev/null
+++ b/src/ssl_new.c
@@ -0,0 +1,16 @@
+#include "ucspissl.h"
+#include "ndelay.h"
+
+SSL *ssl_new(SSL_CTX *ctx,int s)
+{
+ BIO *sbio;
+ SSL *ssl;
+
+ ssl = SSL_new(ctx);
+ if (!ssl) return 0;
+ sbio = BIO_new_socket(s,BIO_NOCLOSE);
+ if (!sbio) return 0;
+ SSL_set_bio(ssl,sbio,sbio);
+ return ssl;
+}
+
diff --git a/src/ssl_params.c b/src/ssl_params.c
new file mode 100644
index 0000000..d3a49d0
--- /dev/null
+++ b/src/ssl_params.c
@@ -0,0 +1,80 @@
+/**
+ @file ssl_params.c
+ @author web, bergmann
+ @brief setup RSA, DH, ECDH
+*/
+#include "ucspissl.h"
+
+int ssl_params_rsa(SSL_CTX *ctx,int len)
+{
+ RSA *rsa;
+ long res;
+ BIGNUM *e;
+
+ /* check if ephemeral RSA key is actually needed */
+ if (!SSL_CTX_need_tmp_RSA(ctx)) return 1;
+
+ if (len) {
+ e = BN_new();
+ rsa = RSA_new();
+ BN_set_word(e,RSA_F4);
+
+ res = (long) RSA_generate_key_ex(rsa,len,e,NULL);
+ BN_free(e);
+
+ if (res == -1) return 0;
+ if (!rsa) return 0;
+
+ /* seldom "needed": maybe deal with an export cipher */
+ res = SSL_CTX_set_tmp_rsa(ctx,rsa);
+ RSA_free(rsa);
+ if (!res) return 0;
+ }
+
+ return 1;
+}
+
+int ssl_params_dh(SSL_CTX *ctx,const char *dhfile)
+{
+ DH *dh;
+ BIO *bio;
+
+ if (dhfile) {
+ dh = 0;
+ bio = BIO_new_file(dhfile,"r");
+ if (!bio) return 0;
+ dh = PEM_read_bio_DHparams(bio,0,0,0);
+ BIO_free(bio);
+ if (!dh) return 0;
+ if (!SSL_CTX_set_tmp_dh(ctx,dh)) return 0;
+ }
+
+ return 1;
+}
+
+int ssl_params_ecdh(SSL_CTX *ctx,const char *ecdhfile)
+{
+ EC_KEY *ecdh;
+
+ SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE);
+#ifdef SSL_CTRL_SET_ECDH_AUTO
+ SSL_CTX_set_ecdh_auto(ctx,1);
+#else
+ /* insecure and compatible curves, see http://safecurves.cr.yp.to/ */
+ ecdh = EC_KEY_new_by_curve_name(NID_secp521r1);
+ if (ecdh == NULL) {
+ /* NIST P-384 / AES-256 */
+ ecdh = EC_KEY_new_by_curve_name(NID_secp384r1);
+ }
+ if (ecdh == NULL) {
+ /* NIST P-256 / AES-128 */
+ ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+ }
+ if (ecdh != NULL) {
+ SSL_CTX_set_tmp_ecdh(ctx,ecdh);
+ EC_KEY_free(ecdh);
+ return 1;
+ }
+#endif
+ return 0;
+}
diff --git a/src/ssl_timeout.c b/src/ssl_timeout.c
new file mode 100644
index 0000000..737f72f
--- /dev/null
+++ b/src/ssl_timeout.c
@@ -0,0 +1,125 @@
+#include "ucspissl.h"
+#include "iopause.h"
+#include "logmsg.h"
+
+#define WHO "ssl_timeout"
+
+int ssl_timeoutaccept(SSL *ssl,unsigned int timeout)
+{
+ struct taia now;
+ struct taia deadline;
+ iopause_fd x;
+ int r;
+ int rfd;
+ int wfd;
+
+ if (taia_now(&now) == -1) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ taia_uint(&deadline,timeout);
+ taia_add(&deadline,&now,&deadline);
+
+ rfd = SSL_get_fd(ssl); /* XXX */
+ wfd = SSL_get_fd(ssl); /* XXX */
+
+ SSL_set_accept_state(ssl);
+
+ for (;;) {
+ r = SSL_accept(ssl);
+ if (r == 1) return 0;
+ ssl_errno = SSL_get_error(ssl,r);
+ errno = EPROTO;
+ if ((ssl_errno != SSL_ERROR_WANT_READ) && (ssl_errno != SSL_ERROR_WANT_WRITE))
+ return -1;
+ if (ssl_errno == SSL_ERROR_WANT_READ) {
+ x.events = IOPAUSE_READ;
+ x.fd = rfd;
+ if (x.fd == -1) return -1;
+ }
+ else {
+ x.events = IOPAUSE_WRITE;
+ x.fd = wfd;
+ if (x.fd == -1) return -1;
+ }
+ for (;;) {
+ if (taia_now(&now) == -1) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ iopause(&x,1,&deadline,&now);
+ if (x.revents) break;
+ if (taia_less(&deadline,&now)) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ }
+ }
+}
+
+int ssl_timeoutconn(SSL *ssl,unsigned int timeout)
+{
+ struct taia now;
+ struct taia deadline;
+ iopause_fd x;
+ int r;
+ int rfd;
+ int wfd;
+
+ taia_now(&now);
+ taia_uint(&deadline,timeout);
+ taia_add(&deadline,&now,&deadline);
+
+ rfd = SSL_get_fd(ssl); /* XXX */
+ wfd = SSL_get_fd(ssl); /* XXX */
+
+ SSL_set_connect_state(ssl);
+
+ for (;;) {
+ r = SSL_connect(ssl);
+ errno = EPROTO;
+ if (r == 1) return 0;
+ ssl_errno = SSL_get_error(ssl,r);
+ if ((ssl_errno != SSL_ERROR_WANT_READ) && (ssl_errno != SSL_ERROR_WANT_WRITE))
+ return -1;
+ if (ssl_errno == SSL_ERROR_WANT_READ) {
+ x.events = IOPAUSE_READ;
+ x.fd = rfd;
+ if (x.fd == -1) return -1;
+ }
+ else {
+ x.events = IOPAUSE_WRITE;
+ x.fd = wfd;
+ if (x.fd == -1) return -1;
+ }
+ for (;;) {
+ if (taia_now(&now) == -1) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ iopause(&x,1,&deadline,&now);
+ if (x.revents) break;
+ if (taia_less(&deadline,&now)) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ }
+ }
+}
+
+stralloc sslerror = {0};
+
+int ssl_verberror(void)
+{
+ char buf[256];
+ unsigned long err;
+
+ if (!stralloc_copys(&sslerror,"")) return -1;
+
+ while ((err = ERR_get_error()) != 0) {
+ ERR_error_string_n(err,buf,sizeof(buf));
+ if (!stralloc_cats(&sslerror,buf)) return -1;
+ if (!stralloc_cats(&sslerror," ")) return -1;
+ }
+ return err;
+}
diff --git a/src/ssl_verify.c b/src/ssl_verify.c
new file mode 100644
index 0000000..474c45b
--- /dev/null
+++ b/src/ssl_verify.c
@@ -0,0 +1,63 @@
+/**
+ @file ssl_verify.c
+ @author web, feh -- parts of code borrowed from Pavel Shramov; tx Peter Conrad
+ @brief Compares 'hostname' against SubAltName DNS:hostname + DN: /CN=hostname
+*/
+#include "ucspissl.h"
+#include "case.h"
+#include "str.h"
+
+int ssl_verify(SSL *ssl,const char *hostname,stralloc *dnsout)
+{
+ X509 *cert;
+ STACK_OF(GENERAL_NAME) *extensions;
+ const GENERAL_NAME *ext;
+ char buf[SSL_NAME_LEN];
+ char *dnsname = 0;
+ int i;
+ int num;
+ int len;
+ int dname = 0;
+
+#if (OPENSSL_VERSION_NUMBER > 0x30100000L)
+ cert = SSL_get1_peer_certificate(ssl);
+#else
+ cert = SSL_get_peer_certificate(ssl);
+#endif
+ if (!cert) return -1;
+
+ if (SSL_get_verify_result(ssl) != X509_V_OK) return -2;
+
+ if (hostname) {
+ if (!stralloc_copys(dnsout,"")) return 1;
+ extensions = X509_get_ext_d2i(cert,NID_subject_alt_name,0,0);
+ num = sk_GENERAL_NAME_num(extensions); /* num = 0, if no SAN extensions */
+
+ for (i = 0; i < num; ++i) {
+ ext = sk_GENERAL_NAME_value(extensions,i);
+ if (ext->type == GEN_DNS) {
+ if (ASN1_STRING_type(ext->d.dNSName) != V_ASN1_IA5STRING) continue;
+#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L))
+ dnsname = (char *)ASN1_STRING_data(ext->d.dNSName);
+#else
+ dnsname = (char *)ASN1_STRING_get0_data(ext->d.dNSName);
+#endif
+ len = ASN1_STRING_length(ext->d.dNSName);
+ if (len != str_len(dnsname)) continue;
+ if (!stralloc_copyb(dnsout,dnsname,len)) return 1;
+ if (case_diffs((char *)hostname,dnsname) == 0) return 0;
+ dname = 1;
+ }
+ }
+
+ if (!dname) {
+ X509_NAME_get_text_by_NID(X509_get_subject_name(cert),NID_commonName,buf,sizeof(buf));
+ buf[SSL_NAME_LEN - 1] = 0;
+ if (!stralloc_copyb(dnsout,buf,str_len(buf))) return 1;
+ if (case_diffs((char *)hostname,buf) == 0) return 0;
+ }
+
+ return -3;
+ }
+ return 0;
+}
diff --git a/src/sslcat.sh b/src/sslcat.sh
new file mode 100644
index 0000000..f923935
--- /dev/null
+++ b/src/sslcat.sh
@@ -0,0 +1,9 @@
+host=${1-0}
+port=${2-443}
+args=""
+if [ $# -gt 2 ]
+then
+ shift; shift
+ args="$@"
+fi
+exec sslclient -RHl0 $args -- "$host" "$port" sh -c 'exec cat <&6'
diff --git a/src/sslclient.c b/src/sslclient.c
new file mode 100644
index 0000000..1d4ce57
--- /dev/null
+++ b/src/sslclient.c
@@ -0,0 +1,449 @@
+/**
+ @file sslclient.c
+ @author web, fefe, feh
+ @brief IPv6 enabled sslclient
+*/
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include "ucspissl.h"
+#include "sig.h"
+#include "exit.h"
+#include "getoptb.h"
+#include "uint_t.h"
+#include "fmt.h"
+#include "scan.h"
+#include "str.h"
+#include "ip.h"
+#include "socket_if.h"
+#include "fd.h"
+#include "stralloc.h"
+#include "buffer.h"
+#include "getln.h"
+#include "logmsg.h"
+#include "pathexec.h"
+#include "timeoutconn.h"
+#include "remoteinfo.h"
+#include "dnsresolv.h"
+#include "byte.h"
+#include "ndelay.h"
+#include "wait.h"
+#include "auto_cafile.h"
+#include "auto_cadir.h"
+#include "auto_ciphers.h"
+
+#define WHO "sslclient"
+
+void nomem(void) {
+ logmsg(WHO,111,FATAL,"out of memory");
+}
+void env(const char *s,const char *t) {
+ if (!pathexec_env(s,t)) nomem();
+}
+
+void usage(void) {
+ logmsg(WHO,100,USAGE,"sslclient \
+[ -463hHrRdDiqQveEsSnNxX ] \
+[ -i localip ] \
+[ -p localport ] \
+[ -T timeoutconn ] \
+[ -l localname ] \
+[ -t timeoutinfo ] \
+[ -I interface ] \
+[ -a cafile ] \
+[ -A cadir ] \
+[ -c certfile ] \
+[ -z ciphers ] \
+[ -k keyfile ] \
+[ -V verifydepth ] \
+[ -w progtimeout ] \
+host port program");
+}
+
+int verbosity = 1;
+int flagdelay = 0;
+int flagremoteinfo = 0;
+int flagremotehost = 1;
+int flag3 = 0;
+int flagsslenv = 0;
+int flagtcpenv = 0;
+int flagsni = 0;
+unsigned long itimeout = 26;
+unsigned long ctimeout[2] = { 2, 58 };
+unsigned int progtimeout = 3600;
+uint32 netif = 0;
+
+const char *loopback = "127.0.0.1";
+char iplocal[16] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
+uint16 portlocal = 0;
+const char *forcelocal = 0;
+
+char ipremote[16];
+uint16 portremote;
+
+const char *hostname;
+int flagname = 1;
+int flagservercert = 1;
+static stralloc addresses;
+static stralloc certname;
+static stralloc moreaddresses;
+
+static stralloc tmp;
+static stralloc fqdn;
+static char strnum[FMT_ULONG];
+static char ipstr[IP6_FMT];
+
+char seed[128];
+
+char bspace[16];
+buffer b;
+
+SSL_CTX *ctx;
+const char *certfile = 0;
+const char *keyfile = 0;
+const char *cafile = auto_cafile;
+const char *cadir = auto_cadir;
+const char *ciphers = auto_ciphers;
+stralloc password = {0};
+int match = 0;
+int verifydepth = 1;
+
+int pi[2];
+int po[2];
+int pt[2];
+
+void read_passwd() {
+ if (!password.len) {
+ buffer_init(&b,buffer_unixread,3,bspace,sizeof(bspace));
+ if (getln(&b,&password,&match,'\0') == -1)
+ logmsg(WHO,111,ERROR,"unable to read password");
+ close(3);
+ if (match) --password.len;
+ }
+}
+
+int passwd_cb(char *buf,int size,int rwflag,void *userdata) {
+ if (size < password.len)
+ logmsg(WHO,111,ERROR,"password too long");
+
+ byte_copy(buf,password.len,password.s);
+ return password.len;
+}
+
+int main(int argc,char * const *argv) {
+ unsigned long u;
+ int opt;
+ const char *x;
+ int j;
+ int s;
+ int r;
+ int cloop;
+ SSL *ssl;
+ int wstat;
+ int ipflag = 0;
+
+ dns_random_init(seed);
+
+ close(6);
+ close(7);
+ sig_ignore(sig_pipe);
+
+ while ((opt = getopt(argc,argv,"dDvqQhHrRimM:p:t:T:l:a:A:c:z:k:V:346eEsSnN0xXw:")) != opteof)
+ switch(opt) {
+ case '4': ipflag = 1; break;
+ case '6': ipflag = 0; break;
+ case 'd': flagdelay = 1; break;
+ case 'D': flagdelay = 0; break;
+ case 'm': flagsni = 1; break;
+ case 'M': flagsni = 0; break;
+ case 'v': verbosity = 2; break;
+ case 'q': verbosity = 0; break;
+ case 'Q': verbosity = 1; break;
+ case 'l': forcelocal = optarg; break;
+ case 'H': flagremotehost = 0; break;
+ case 'h': flagremotehost = 1; break;
+ case 'R': flagremoteinfo = 0; break;
+ case 'r': flagremoteinfo = 1; break;
+ case 't': scan_ulong(optarg,&itimeout); break;
+ case 'T': j = scan_ulong(optarg,&ctimeout[0]);
+ if (optarg[j] == '+') ++j;
+ scan_ulong(optarg + j,&ctimeout[1]);
+ break;
+ case 'w': scan_uint(optarg,&progtimeout); break;
+ case 'i': if (!ip6_scan(optarg,iplocal)) usage(); break;
+ case 'I': netif = socket_getifidx(optarg); break;
+ case 'p': scan_ulong(optarg,&u); portlocal = u; break;
+ case 'a': cafile = optarg; break;
+ case 'A': cadir = optarg; break;
+ case 'c': certfile = optarg; break;
+ case 'z': ciphers = optarg; break;
+ case 'k': keyfile = optarg; break;
+ case 'V': scan_ulong(optarg,&u); verifydepth = u; break;
+ case '3': flag3 = 1; break;
+ case 'S': flagsslenv = 0; break;
+ case 's': flagsslenv = 1; break;
+ case 'E': flagtcpenv = 0; break;
+ case 'e': flagtcpenv = 1; break;
+ case 'N': flagname = 0; break;
+ case 'n': flagname = 1; break;
+ case 'x': flagservercert = 1; break;
+ case 'X': flagservercert = 0; break;
+ default: usage();
+ }
+ argv += optind;
+
+ if (!verbosity)
+ buffer_2->fd = -1;
+
+ hostname = *argv;
+ if (!hostname || str_equal((char *)hostname,"")) usage();
+ if (str_equal((char *)hostname,"0")) hostname = loopback;
+
+ x = *++argv;
+ if (!x) usage();
+ if (!x[scan_ulong(x,&u)])
+ portremote = u;
+ else {
+ struct servent *se;
+ se = getservbyname(x,"tcp");
+ if (!se)
+ logmsg(WHO,111,FATAL,B("unable to figure out port number for ",x));
+ uint16_unpack_big((char*)&se->s_port,&portremote);
+ }
+
+ if (flag3) read_passwd();
+
+ if (cafile && str_equal(cafile,"")) cafile = 0;
+ if (cadir && str_equal(cadir,"")) cadir= 0;
+ if (ciphers && str_equal(ciphers,"")) ciphers= 0;
+
+ if (certfile && str_equal(certfile,"")) certfile = 0;
+ if (keyfile && str_equal(keyfile,"")) keyfile = 0;
+
+ if (!*++argv) usage();
+
+ if (!stralloc_copys(&tmp,hostname)) nomem();
+ dns_ip_qualify(&addresses,&fqdn,&tmp);
+ if (addresses.len < 16)
+ logmsg(WHO,111,ERROR,B("No IP address for: ",hostname));
+
+ 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 + 16 <= addresses.len; j += 16) {
+ if (ipflag == 1 || ip6_isv4mapped(addresses.s + j)) {
+ s = socket_tcp4();
+ if (s == -1) logmsg(WHO,111,FATAL,"unable to create socket");
+ r = socket_bind4(s,iplocal,portlocal);
+ } else {
+ s = socket_tcp6();
+ if (s == -1) logmsg(WHO,111,FATAL,"unable to create socket");
+ r = socket_bind6(s,iplocal,portlocal,netif);
+ }
+ if (r == -1) {
+ strnum[fmt_ulong(strnum,portlocal)] = 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;
+
+ logmsg(WHO,111,FATAL,B("unable to bind to: ",ipstr," port: ",strnum));
+ }
+ if (timeoutconn(s,addresses.s + j,portremote,ctimeout[cloop],netif) == 0)
+ goto CONNECTED;
+ close(s);
+ if (!cloop && ctimeout[1] && (errno == ETIMEDOUT)) {
+ if (!stralloc_catb(&moreaddresses,addresses.s + j,16)) nomem();
+ }
+ else {
+ strnum[fmt_ulong(strnum,portremote)] = 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;
+ }
+ }
+ if (!stralloc_copy(&addresses,&moreaddresses)) nomem();
+ }
+ logmsg(WHO,110,DROP,B("unable to connect to: ",ipstr," port: ",strnum));
+
+ _exit(111);
+
+
+ CONNECTED:
+
+ /* Local */
+
+ if (socket_local(s,iplocal,&portlocal,&netif) == -1)
+ logmsg(WHO,111,FATAL,"unable to get local address");
+
+ if (ip6_isv4mapped(iplocal)) {
+ env("PROTO","TCP6");
+ ipstr[ip4_fmt(ipstr,iplocal + 12)] = 0;
+ } else {
+ env("PROTO","TCP6");
+ if (flagtcpenv && netif) env("TCP6INTERFACE",socket_getifname(netif));
+ ipstr[ip6_fmt(ipstr,iplocal)] = 0;
+ }
+
+ env("SSLLOCALIP",ipstr);
+ if (flagtcpenv) env("TCPLOCALIP",ipstr);
+
+ strnum[fmt_ulong(strnum,portlocal)] = 0;
+ env("SSLLOCALPORT",strnum);
+ if (flagtcpenv) env("TCPLOCALPORT",strnum);
+
+ x = forcelocal;
+ if (!x)
+ if (dns_name(&tmp,iplocal) >= 0) {
+ if (!stralloc_0(&tmp)) nomem();
+ x = tmp.s;
+ }
+ env("SSLLOCALHOST",x);
+ if (flagtcpenv) env("TCPLOCALHOST",x);
+
+ /* Remote */
+
+ if (socket_remote(s,ipremote,&portremote,&netif) == -1)
+ logmsg(WHO,111,FATAL,"unable to get remote address");
+
+ if (ip6_isv4mapped(ipremote))
+ ipstr[ip4_fmt(ipstr,ipremote + 12)] = 0;
+ else
+ ipstr[ip6_fmt(ipstr,ipremote)] = 0;
+
+ env("SSLREMOTEIP",ipstr);
+ if (flagtcpenv) env("TCPREMOTEIP",ipstr);
+
+ strnum[fmt_ulong(strnum,portremote)] = 0;
+ env("SSLREMOTEPORT",strnum);
+ if (flagtcpenv) env("TCPREMOTEPORT",strnum);
+
+ x = 0;
+ if (flagremotehost)
+ if (dns_name(&tmp,ipremote) >= 0) {
+ if (!stralloc_0(&tmp)) nomem();
+ x = tmp.s;
+ }
+
+ env("SSLREMOTEHOST",x);
+ if (flagtcpenv) env("TCPREMOTEHOST",x);
+
+ x = 0;
+ if (flagremoteinfo)
+ if (remoteinfo(&tmp,ipremote,portremote,iplocal,portlocal,itimeout,netif) == 0) {
+ if (!stralloc_0(&tmp)) nomem();
+ x = tmp.s;
+ }
+ env("SSLREMOTEINFO",x);
+ if (flagtcpenv) env("TCPREMOTEINFO",x);
+
+ /* Context */
+
+ ctx = ssl_client();
+ ssl_errstr();
+ if (!ctx)
+ logmsg(WHO,111,FATAL,"unable to create TLS context");
+
+ switch (ssl_certkey(ctx,certfile,keyfile,passwd_cb)) {
+ case -1: logmsg(WHO,111,ERROR,"unable to load certificate");
+ case -2: logmsg(WHO,111,ERROR,"unable to load key pair");
+ case -3: logmsg(WHO,111,ERROR,"key does not match certificate");
+ default: break;
+ }
+
+ if (flagservercert && !ssl_ca(ctx,cafile,cadir,verifydepth))
+ logmsg(WHO,111,ERROR,"unable to load CA list");
+
+ if (!ssl_ciphers(ctx,ciphers))
+ logmsg(WHO,111,ERROR,"unable to set cipher list");
+
+ ssl = ssl_new(ctx,s);
+ if (!ssl) logmsg(WHO,111,FATAL,"unable to create TLS instance");
+
+ if (flagsni)
+ if (!SSL_set_tlsext_host_name(ssl,hostname))
+ logmsg(WHO,111,FATAL,B("unable to set TLS SNI extensions for hostname: ",(char *)hostname));
+
+ for (cloop = 0; cloop < 2; ++cloop) {
+ if (!ssl_timeoutconn(ssl,ctimeout[cloop])) goto SSLCONNECTED;
+ if (!cloop && ctimeout[1]) continue;
+ logmsg(WHO,111,FATAL,"unable to TLS connect");
+ }
+
+ _exit(111);
+
+ SSLCONNECTED:
+
+ ndelay_off(s);
+
+ if (flagservercert)
+ switch(ssl_verify(ssl,hostname,&certname)) {
+ case -1:
+ logmsg(WHO,110,ERROR,"no server certificate");
+ case -2:
+ logmsg(WHO,110,ERROR,"missing credentials (CA) or unable to validate server certificate");
+ case -3:
+ if (!stralloc_0(&certname)) nomem();
+ if (flagname)
+ logmsg(WHO,110,ERROR,B("server hostname does not match certificate: ",(char *)hostname," <=> ",certname.s));
+ default: break;
+ }
+
+ if (verbosity >= 2)
+ log_who(WHO,B("tls connected to: ",ipstr," port: ",strnum));
+
+ if (!flagdelay)
+ socket_tcpnodelay(s); /* if it fails, bummer */
+
+ if (pipe(pi) == -1) logmsg(WHO,111,FATAL,"unable to create pipe");
+ if (pipe(po) == -1) logmsg(WHO,111,FATAL,"unable to create pipe");
+ if (pi[0] == 7) {
+ if (pipe(pt) == -1) logmsg(WHO,111,FATAL,"unable to create pipe");
+ close(pi[0]); close(pi[1]);
+ pi[0] = pt[0]; pi[1] = pt[1];
+ }
+ if (po[1] == 6) {
+ if (pipe(pt) == -1) logmsg(WHO,111,FATAL,"unable to create pipe");
+ close(po[0]); close(po[1]);
+ po[0] = pt[0]; po[1] = pt[1];
+ }
+
+ switch (opt = fork()) {
+ case -1:
+ logmsg(WHO,111,FATAL,"unable to fork");
+ case 0:
+ break;
+ default:
+ close(pi[0]); close(po[1]);
+ if (ssl_io(ssl,pi[1],po[0],progtimeout)) {
+ logmsg(WHO,110,DROP,"unable to speak TLS");
+ ssl_close(ssl);
+ wait_pid(&wstat,opt);
+ _exit(111);
+ }
+ ssl_close(ssl);
+ if (wait_pid(&wstat,opt) > 0)
+ _exit(wait_exitcode(wstat));
+ _exit(0);
+ }
+ ssl_close(ssl); close(pi[1]); close(po[0]);
+
+ if (flagsslenv && !ssl_client_env(ssl,0)) nomem();
+
+ if (fd_move(6,pi[0]) == -1)
+ logmsg(WHO,111,FATAL,"unable to set up descriptor 6");
+ if (fd_move(7,po[1]) == -1)
+ logmsg(WHO,111,FATAL,"unable to set up descriptor 7");
+ sig_uncatch(sig_pipe);
+
+ pathexec(argv);
+ logmsg(WHO,111,FATAL,B("unable to run: ",*argv));
+ return 0; /* never happens, but avoids compile warning */
+}
diff --git a/src/sslconnect.sh b/src/sslconnect.sh
new file mode 100644
index 0000000..3462540
--- /dev/null
+++ b/src/sslconnect.sh
@@ -0,0 +1,9 @@
+host=${1-0}
+port=${2-465}
+args=""
+if [ $# -gt 2 ]
+then
+ shift; shift
+ args="$@"
+fi
+exec sslclient -XRHl0 $args -- "$host" "$port" mconnect-io
diff --git a/src/sslhandle.c b/src/sslhandle.c
new file mode 100644
index 0000000..f31cee9
--- /dev/null
+++ b/src/sslhandle.c
@@ -0,0 +1,887 @@
+/**
+ @file sslhandle.c
+ @author web, feh
+ @brief IPv6 enabled TLS framework for a preforking server
+*/
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <netdb.h>
+#include <signal.h>
+#include <arpa/inet.h>
+#include "ucspissl.h"
+#include "uint_t.h"
+#include "str.h"
+#include "byte.h"
+#include "fmt.h"
+#include "scan.h"
+#include "ip.h"
+#include "fd.h"
+#include "exit.h"
+#include "env.h"
+#include "prot.h"
+#include "open.h"
+#include "wait.h"
+#include "stralloc.h"
+#include "alloc.h"
+#include "buffer.h"
+#include "getln.h"
+#include "logmsg.h"
+#include "getoptb.h"
+#include "socket_if.h"
+#include "ndelay.h"
+#include "remoteinfo.h"
+#include "rules.h"
+#include "sig.h"
+#include "iopause.h"
+#include "dnsresolv.h"
+#include "auto_cafile.h"
+#include "auto_cadir.h"
+#include "auto_ccafile.h"
+#include "auto_dhfile.h"
+#include "auto_certchainfile.h"
+#include "auto_certfile.h"
+#include "auto_keyfile.h"
+#include "auto_ciphers.h"
+#include "iopause.h"
+#include "coe.h"
+#include "lock.h"
+
+
+extern void server(int argcs,char * const *argvs);
+char *who;
+
+int verbosity = 1;
+int flagkillopts = 1;
+int flagafter = 0;
+int flagdelay = 0;
+const char *banner = "";
+int flagremoteinfo = 1;
+int flagremotehost = 1;
+int flagparanoid = 0;
+int flagclientcert = 0;
+int flagsslenv = 0;
+int flagtcpenv = 0;
+unsigned long timeout = 26;
+unsigned long ssltimeout = 26;
+unsigned int progtimeout = 3600;
+uint32 netif = 0;
+int selfpipe[2];
+int flagexit = 0;
+int flagdualstack = 0;
+
+static stralloc tcpremoteinfo = {0};
+
+uint16 localport;
+char localportstr[FMT_ULONG];
+char localip[16] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
+char localipstr[IP6_FMT];
+static stralloc localhostsa;
+const char *localhost = 0;
+const char *lockfile = 0;
+int fdlock;
+
+uint16 remoteport;
+char remoteportstr[FMT_ULONG];
+char remoteip[16];
+char remoteipstr[IP6_FMT];
+static stralloc remotehostsa;
+char *remotehost = 0;
+
+const char *hostname;
+const char *loopback = "127.0.0.1";
+
+static char strnum[FMT_ULONG];
+static char strnum2[FMT_ULONG];
+
+static stralloc tmp;
+static stralloc fqdn;
+static stralloc addresses;
+static stralloc certname;
+stralloc envplus = {0};
+stralloc envtmp = {0};
+
+char bspace[16];
+buffer b;
+
+SSL_CTX *ctx;
+const char *certchainfile = auto_certchainfile;
+const char *certfile = auto_certfile;
+const char *keyfile = auto_keyfile;
+stralloc password = {0};
+int match = 0;
+const char *cafile = auto_cafile;
+const char *ccafile = auto_ccafile;
+const char *cadir = auto_cadir;
+const char *ciphers = auto_ciphers;
+int verifydepth = 1;
+const char *dhfile = auto_dhfile;
+int rsalen = SSL_RSA_LEN;
+
+int pi[2];
+int po[2];
+
+X509 *cert;
+char buf[SSL_NAME_LEN];
+
+char **e;
+char **e1;
+
+/* ---------------------------- child */
+
+
+int flagdeny = 0;
+int flagallownorules = 0;
+const char *fnrules = 0;
+
+void drop_nomem(void) {
+ logmsg(who,111,FATAL,"out of memory");
+}
+void drop_notemp(void) {
+ logmsg(who,111,FATAL,"out of timestamps");
+}
+void cats(const char *s) {
+ if (!stralloc_cats(&tmp,s)) drop_nomem();
+}
+void append(const char *ch) {
+ if (!stralloc_append(&tmp,ch)) drop_nomem();
+}
+void safecats(const char *s) {
+ char ch;
+ int i;
+
+ for (i = 0;i < 100;++i) {
+ ch = s[i];
+ if (!ch) return;
+ if (ch < 33) ch = '?';
+ if (ch > 126) ch = '?';
+ if (ch == '%') ch = '?'; /* logger stupidity */
+ append(&ch);
+ }
+ cats("...");
+}
+void env(const char *s,const char *t) {
+ if (!s) return;
+ if (!stralloc_copys(&envtmp,s)) drop_nomem();
+ if (t) {
+ if (!stralloc_cats(&envtmp,"=")) drop_nomem();
+ if (!stralloc_cats(&envtmp,t)) drop_nomem();
+ }
+ if (!stralloc_0(&envtmp)) drop_nomem();
+ if (!stralloc_cat(&envplus,&envtmp)) drop_nomem();
+}
+static void env_def() {
+ unsigned int elen;
+ unsigned int i;
+ unsigned int j;
+ unsigned int split;
+ unsigned int t;
+
+ if (!stralloc_cats(&envplus,"")) return;
+
+ elen = 0;
+ for (i = 0; environ[i]; ++i)
+ ++elen;
+ for (i = 0; i < envplus.len; ++i)
+ if (!envplus.s[i])
+ ++elen;
+
+ e = (char **) alloc((elen + 1) * sizeof(char *));
+ if (!e) return;
+
+ elen = 0;
+ for (i = 0; environ[i]; ++i)
+ e[elen++] = environ[i];
+
+ j = 0;
+ for (i = 0; i < envplus.len; ++i)
+ if (!envplus.s[i]) {
+ split = str_chr(envplus.s + j,'=');
+ for (t = 0;t < elen;++t)
+ if (byte_equal(envplus.s + j,split,e[t]))
+ if (e[t][split] == '=') {
+ --elen;
+ e[t] = e[elen];
+ break;
+ }
+ if (envplus.s[j + split])
+ e[elen++] = envplus.s + j;
+ j = i + 1;
+ }
+ e[elen] = 0;
+
+ e1 = environ;
+ environ = e;
+}
+void env_reset(void) {
+ if (e) {
+ if (e != environ) {
+ alloc_free((char *)e);
+ logmsg(who,111,FATAL,"environ changed");
+ }
+ }
+
+ environ = e1;
+ envplus.len = 0;
+}
+int error_warn(const char *x) {
+ if (!x) return 0;
+ log_who(who,"x");
+ return 0;
+}
+void drop_rules(void) {
+ logmsg(who,111,FATAL,B("unable to read: ",(char *)fnrules));
+}
+
+void found(char *data,unsigned int datalen) {
+ unsigned int next0;
+ unsigned int split;
+
+ while ((next0 = byte_chr(data,datalen,0)) < datalen) {
+ switch(data[0]) {
+ case 'D':
+ flagdeny = 1;
+ break;
+ case '+':
+ split = str_chr(data + 1,'=');
+ if (data[1 + split] == '=') {
+ data[1 + split] = 0;
+ env(data + 1,data + 1 + split + 1);
+ }
+ break;
+ }
+ ++next0;
+ data += next0; datalen -= next0;
+ }
+}
+
+int doit(int t) {
+ int j;
+ SSL *ssl;
+ uint32 netif;
+
+ if (ip6_isv4mapped(remoteip)) {
+ remoteipstr[ip4_fmt(remoteipstr,remoteip+12)] = 0;
+ localipstr[ip4_fmt(localipstr,localip + 12)] = 0;
+ } else {
+ remoteipstr[ip6_fmt(remoteipstr,remoteip)] = 0;
+ localipstr[ip6_fmt(localipstr,localip)] = 0;
+ }
+
+ if (verbosity >= 2) {
+ strnum[fmt_ulong(strnum,getpid())] = 0;
+ log_who(who,B("pid ",strnum," from ",remoteipstr));
+ }
+
+ if (socket_local(t,localip,&localport,&netif) == -1)
+ logmsg(who,111,FATAL,"unable to get local address");
+
+ remoteportstr[fmt_ulong(remoteportstr,remoteport)] = 0;
+
+ if (!localhost)
+ if (dns_name(&localhostsa,localip) >= 0)
+ if (localhostsa.len) {
+ if (!stralloc_0(&localhostsa)) drop_nomem();
+ localhost = localhostsa.s;
+ }
+
+ env("PROTO","TLS");
+ env("SSLLOCALIP",localipstr);
+ env("SSLLOCALPORT",localportstr);
+ env("SSLLOCALHOST",localhost);
+ env("SSLREMOTEIP",remoteipstr);
+ env("SSLREMOTEPORT",remoteportstr);
+ env("SSLREMOTEHOST",remotehost);
+
+ if (flagtcpenv) {
+ env("TCPLOCALIP",localipstr);
+ env("TCPLOCALPORT",localportstr);
+ env("TCPLOCALHOST",localhost);
+ env("TCPREMOTEIP",remoteipstr);
+ env("TCPREMOTEPORT",remoteportstr);
+ env("TCPREMOTEHOST",remotehost);
+ if (!ip6_isv4mapped(localip)) {
+ env("PROTO","TCP6");
+ env("TCP6LOCALIP",localipstr);
+ env("TCP6LOCALHOST",localhost);
+ env("TCP6LOCALPORT",localportstr);
+ env("TCP6REMOTEIP",remoteipstr);
+ env("TCP6REMOTEPORT",remoteportstr);
+ env("TCP6REMOTEHOST",remotehost);
+ if (netif)
+ env("TCP6INTERFACE",socket_getifname(netif));
+ } else
+ env("PROTO","TCP");
+ }
+
+ if (flagremotehost)
+ if (dns_name(&remotehostsa,remoteip) >= 0)
+ if (remotehostsa.len) {
+ if (flagparanoid) {
+ 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;
+ }
+ if (dns_ip4(&tmp,&remotehostsa) >= 0)
+ for (j = 0; j + 4 <= tmp.len; j += 4)
+ if (byte_equal(remoteip,4,tmp.s + j)) {
+ flagparanoid = 0;
+ break;
+ }
+ }
+ if (!flagparanoid) {
+ if (!stralloc_0(&remotehostsa)) drop_nomem();
+ remotehost = remotehostsa.s;
+ }
+ }
+
+ if (flagremoteinfo) {
+ if (remoteinfo(&tcpremoteinfo,remoteip,remoteport,localip,localport,timeout,netif) == -1)
+ flagremoteinfo = 0;
+ if (!stralloc_0(&tcpremoteinfo)) drop_nomem();
+ }
+ env("SSLREMOTEINFO",flagremoteinfo ? tcpremoteinfo.s : 0);
+ if (flagtcpenv)
+ env("TCPREMOTEINFO",flagremoteinfo ? tcpremoteinfo.s : 0);
+
+ if (fnrules) {
+ int fdrules;
+ flagdeny = 0;
+ fdrules = open_read(fnrules);
+ if (fdrules == -1) {
+ if (errno != ENOENT) drop_rules();
+ if (!flagallownorules) drop_rules();
+ } else {
+ if (rules(found,fdrules,remoteipstr,remotehost,flagremoteinfo ? tcpremoteinfo.s : 0) == -1)
+ drop_rules();
+ close(fdrules);
+ }
+ }
+
+ if (verbosity >= 2) {
+ strnum[fmt_ulong(strnum,getpid())] = 0;
+ if (!stralloc_copys(&tmp,who)) drop_nomem();
+ if (!stralloc_cats(&tmp,": ")) drop_nomem();
+ safecats(flagdeny ? "deny" : "ok");
+ cats(" "); safecats(strnum);
+ cats(" "); if (localhost) safecats(localhost);
+ cats(":"); safecats(localipstr);
+ cats(":"); safecats(localportstr);
+ cats(" "); if (remotehost) safecats(remotehost);
+ cats(":"); safecats(remoteipstr);
+ cats(":"); if (flagremoteinfo) safecats(tcpremoteinfo.s);
+ cats(":"); safecats(remoteportstr);
+ cats("\n");
+ buffer_putflush(buffer_2,tmp.s,tmp.len);
+ }
+
+ if (flagdeny) {
+ close(t);
+ return(0);
+ }
+
+ if (pipe(pi) == -1) logmsg(who,111,FATAL,"unable to create pipe");
+ if (pipe(po) == -1) logmsg(who,111,FATAL,"unable to create pipe");
+
+ ssl = ssl_new(ctx,t);
+ if (!ssl) logmsg(who,111,FATAL,"unable to create SSL instance");
+ if (ndelay_on(t) == -1)
+ logmsg(who,111,FATAL,"unable to set socket options");
+ if (ssl_timeoutaccept(ssl,ssltimeout) == -1) {
+ strnum[fmt_ulong(strnum,getpid())] = 0;
+ logmsg(who,110,DROP,B("unable to TLS accept for pid:",strnum));
+ ssl_error(error_warn);
+ }
+
+ if (verbosity >= 2) {
+ strnum[fmt_ulong(strnum,getpid())] = 0;
+ log_who(who,B("tls ",strnum," accept "));
+ }
+
+ if (flagclientcert) {
+ switch(ssl_verify(ssl,remotehost,&certname)) {
+ case -1:
+ logmsg(who,110,ERROR,"no client certificate");
+ case -2:
+ logmsg(who,110,ERROR,"missing credentials (CA) or unable to validate client certificate");
+ case -3:
+ if (!stralloc_0(&certname)) drop_nomem();
+ logmsg(who,110,ERROR,B("client hostname name does not match certificate: ",remotehost," <=> ",certname.s));
+ default:
+ break;
+ }
+ }
+
+ switch(fork()) {
+ case -1:
+ logmsg(who,111,FATAL,"unable to fork ");
+ case 0:
+ close(pi[0]); close(po[1]);
+ sig_uncatch(sig_child);
+ sig_unblock(sig_child);
+ if (ssl_io(ssl,pi[1],po[0],progtimeout) == -1) {
+ strnum[fmt_ulong(strnum,getpid())] = 0;
+ logmsg(who,-99,WARN,B("unable to speak TLS for pid: ",strnum));
+ ssl_error(error_warn);
+ _exit(111);
+ }
+ _exit(0);
+ }
+ close(pi[1]); close(po[0]);
+
+ if (flagsslenv && !ssl_server_env(ssl,&envplus)) drop_nomem();
+ env_def();
+
+ if (fd_move(0,pi[0]) == -1)
+ logmsg(who,111,FATAL,"unable to set up descriptor 0");
+ if (fd_move(1,po[1]) == -1)
+ logmsg(who,111,FATAL,"unable to set up descriptor 1");
+
+ if (flagkillopts) {
+ socket_ipoptionskill(t);
+ }
+ if (!flagdelay)
+ socket_tcpnodelay(t);
+
+ if (*banner) {
+ buffer_init(&b,buffer_unixwrite,1,bspace,sizeof(bspace));
+ if (buffer_putsflush(&b,banner) == -1)
+ logmsg(who,111,FATAL,"unable to print banner");
+ }
+
+ ssl_free(ssl);
+ return 1;
+}
+
+void done(void) {
+ if (verbosity >= 2) {
+ strnum[fmt_ulong(strnum,getpid())] = 0;
+ if (!stralloc_copys(&tmp,who)) drop_nomem();
+ if (!stralloc_cats(&tmp,": ")) drop_nomem();
+ cats("done "); safecats(strnum); cats("\n");
+ buffer_putflush(buffer_2,tmp.s,tmp.len);
+ }
+}
+
+
+/* ---------------------------- parent */
+
+void usage(void)
+{
+ logmsg(who,100,USAGE,B(who,"\
+[ -1346UXpPhHrRoOdDqQviIeEsS ] \
+[ -c limit ] \
+[ -x rules.cdb ] \
+[ -B banner ] \
+[ -g gid ] \
+[ -u uid ] \
+[ -b backlog ] \
+[ -l localname ] \
+[ -t timeout ] \
+[ -T ssltimeout ] \
+[ -w progtimeout ] \
+[ -f lockfile ] \
+[ -I interface ] \
+host port program"));
+}
+
+unsigned long limit = 40;
+unsigned long numchildren = 0;
+
+int flag1 = 0;
+int flag3 = 0;
+unsigned long backlog = 20;
+unsigned long uid = 0;
+unsigned long gid = 0;
+
+void printstatus(void) {
+ if (verbosity < 2) return;
+ strnum[fmt_ulong(strnum,numchildren)] = 0;
+ strnum2[fmt_ulong(strnum2,limit)] = 0;
+ log_who(who,B("status: ",strnum,"/",strnum2));
+}
+
+void trigger(void) {
+ buffer_unixwrite(selfpipe[1],"",1);
+}
+
+void sigterm(void) {
+ int pid;
+
+ flagexit = 1;
+ pid = getpid();
+ if (pid < 0) logmsg(who,111,FATAL,"cannot get pid");
+ kill(-pid,SIGTERM);
+ trigger();
+}
+
+void sigchld(void) {
+ int wstat;
+ int pid;
+
+ while ((pid = wait_nohang(&wstat)) > 0) {
+ if (verbosity >= 2) {
+ strnum[fmt_ulong(strnum,pid)] = 0;
+ strnum2[fmt_ulong(strnum2,wstat)] = 0;
+ log_who(who,B("end ",strnum," status ",strnum2));
+ }
+ if (numchildren) --numchildren; printstatus();
+ if (flagexit && !numchildren) _exit(0);
+ }
+ trigger();
+}
+
+void read_passwd(void) {
+ if (!password.len) {
+ buffer_init(&b,buffer_unixread,3,bspace,sizeof(bspace));
+ if (getln(&b,&password,&match,'\0') == -1)
+ logmsg(who,111,ERROR,"unable to read password");
+ close(3);
+ if (match) --password.len;
+ }
+}
+
+int passwd_cb(char *buff,int size,int rwflag,void *userdata) {
+ if (size < password.len)
+ logmsg(who,111,ERROR,"password too long");
+
+ byte_copy(buff,password.len,password.s);
+ return password.len;
+}
+
+void spawn(int s,int argc,char * const *argv) {
+ int t;
+
+ while (numchildren >= limit) sig_pause();
+ while (numchildren < limit) {
+ ++numchildren; printstatus();
+
+ switch(fork()) {
+ case 0:
+ sig_uncatch(sig_child);
+ sig_unblock(sig_child);
+ sig_uncatch(sig_term);
+ sig_uncatch(sig_pipe);
+ for (;;) {
+ if (lockfile) {
+ if (lock_ex(fdlock) == -1)
+ logmsg(who,111,FATAL,B("unable to lock: ",(char *)lockfile));
+ if (flagdualstack)
+ t = socket_accept6(s,remoteip,&remoteport,&netif);
+ else
+ t = socket_accept4(s,remoteip,&remoteport);
+ lock_un(fdlock);
+ } else {
+ if (flagdualstack)
+ t = socket_accept6(s,remoteip,&remoteport,&netif);
+ else
+ t = socket_accept4(s,remoteip,&remoteport);
+ }
+
+ if (t == -1) continue;
+ if (!doit(t)) continue;
+ server(argc,argv);
+ close(0); close(1);
+ env_reset();
+ done();
+ }
+ break;
+ case -1:
+ logmsg(who,111,FATAL,"unable to fork");
+ --numchildren; printstatus();
+ }
+ }
+}
+
+int main(int argc,char * const *argv) {
+ int opt;
+ struct servent *se;
+ char *x;
+ int j;
+ int s;
+ int ipflag = 0;
+ iopause_fd io[2];
+ char ch;
+ struct taia deadline;
+ struct taia stamp;
+ unsigned long u;
+
+ who = argv[0];
+ while ((opt = getopt(argc,argv,"dDvqQhHrRUXx:t:T:u:g:l:b:B:c:pPoO1346I:EeSsaAf:w:zZ")) != opteof)
+ switch(opt) {
+ case 'b': scan_ulong(optarg,&backlog); break;
+ case 'c': scan_ulong(optarg,&limit); break;
+ case 'X': flagallownorules = 1; break;
+ case 'x': fnrules = optarg; break;
+ case 'B': banner = optarg; break;
+ case 'd': flagdelay = 1; break;
+ case 'D': flagdelay = 0; break;
+ case 'v': verbosity = 2; break;
+ case 'q': verbosity = 0; break;
+ case 'Q': verbosity = 1; break;
+ case 'P': flagparanoid = 0; break;
+ case 'p': flagparanoid = 1; break;
+ case 'O': flagkillopts = 1; break;
+ case 'o': flagkillopts = 0; break;
+ case 'H': flagremotehost = 0; break;
+ case 'h': flagremotehost = 1; break;
+ case 'R': flagremoteinfo = 0; break;
+ case 'r': flagremoteinfo = 1; break;
+ case 't': scan_ulong(optarg,&timeout); break;
+ case 'T': scan_ulong(optarg,&ssltimeout); break;
+ case 'U': x = env_get("UID"); if (x) scan_ulong(x,&uid);
+ 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 'l': localhost = optarg; break;
+ case 'I': netif = socket_getifidx(optarg); break;
+ case '1': flag1 = 1; break;
+ case '3': flag3 = 1; break;
+ case '4': ipflag = 1; break;
+ case '6': ipflag = 2; break;
+ case 'Z': flagclientcert = 0; break;
+ case 'z': flagclientcert = 1; break;
+ case 'S': flagsslenv = 0; break;
+ case 's': flagsslenv = 1; break;
+ case 'E': flagtcpenv = 0; break;
+ case 'e': flagtcpenv = 1; break;
+ case 'A': flagafter = 0; break;
+ case 'a': flagafter = 1; break;
+ case 'f': lockfile = optarg; break;
+ case 'w': scan_uint(optarg,&progtimeout); break;
+ default: usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!verbosity) buffer_2->fd = -1;
+
+ hostname = *argv++;
+ if (!hostname || str_equal((char *)hostname,"")) usage();
+ if (str_equal((char *)hostname,"0")) hostname = loopback;
+ else if (str_equal((char *)hostname,":0")) {
+ flagdualstack = 1;
+ hostname = "::";
+ }
+
+ x = *argv++; --argc;
+ if (!x) usage();
+ if (!x[scan_ulong(x,&u)])
+ localport = u;
+ else {
+ se = getservbyname(x,"tcp");
+ if (!se)
+ logmsg(who,111,FATAL,B("unable to figure out port number for: ",x));
+ uint16_unpack_big((char*)&se->s_port,&localport);
+ }
+
+ if ((x = env_get("VERIFYDEPTH"))) {
+ scan_ulong(x,&u);
+ verifydepth = u;
+ }
+
+ if ((x = env_get("CAFILE"))) cafile = x;
+ if (cafile && str_equal((char *)cafile,"")) cafile = 0;
+
+ if ((x = env_get("CCAFILE"))) ccafile = x;
+ if (ccafile && str_equal((char *)ccafile,"")) ccafile = 0;
+ if (!flagclientcert) ccafile = 0;
+
+ if ((x = env_get("CADIR"))) cadir = x;
+ if (cadir && str_equal((char *)cadir,"")) cadir= 0;
+
+ if ((x = env_get("CERTCHAINFILE"))) certchainfile = x;
+ if (certchainfile && str_equal((char *)certchainfile,"")) certchainfile = 0;
+
+ if ((x = env_get("CERTFILE"))) certfile = x;
+ if (certfile && str_equal((char *)certfile,"")) certfile = 0;
+
+ if ((x = env_get("KEYFILE"))) keyfile = x;
+ if (keyfile && str_equal((char *)keyfile,"")) keyfile = 0;
+
+ if ((x = env_get("DHFILE"))) dhfile = x;
+ if (dhfile && str_equal((char *)dhfile,"")) dhfile = 0;
+
+ if ((x = env_get("CIPHERS"))) ciphers = x;
+ if (ciphers && str_equal((char *)ciphers,"")) ciphers = 0;
+
+ if (setsid() == -1)
+ if (getpgrp() != getpid())
+ logmsg(who,111,FATAL,"unable to create process group");
+
+ if (lockfile) {
+ fdlock = open_append(lockfile);
+ if (fdlock == -1)
+ logmsg(who,111,FATAL,B("unable to open: ",(char *)lockfile));
+ }
+
+ if (pipe(selfpipe) == -1)
+ logmsg(who,111,FATAL,"unable to create pipe");
+
+ coe(selfpipe[0]);
+ coe(selfpipe[1]);
+ ndelay_on(selfpipe[0]);
+ ndelay_on(selfpipe[1]);
+
+ sig_block(sig_child);
+ sig_catch(sig_child,sigchld);
+ sig_catch(sig_term,sigterm);
+ sig_ignore(sig_pipe);
+
+ /* IP address only */
+
+ if (ip4_scan(hostname,localip)) {
+ if (!stralloc_copyb(&addresses,(char *)V4mappedprefix,12)) drop_nomem();
+ if (!stralloc_catb(&addresses,localip,4)) drop_nomem();
+ byte_copy(localip,16,addresses.s);
+ } else if (ip6_scan(hostname,localip)) {
+ if (!stralloc_copyb(&addresses,localip,16)) drop_nomem();
+ byte_copy(localip,16,addresses.s);
+ }
+
+ /* Asynchronous DNS IPv4/IPv6 Name qualification */
+
+ if (!addresses.len) {
+ if (!stralloc_copys(&tmp,hostname)) drop_nomem();
+ if (dns_ip_qualify(&addresses,&fqdn,&tmp) < 0)
+ logmsg(who,111,FATAL,B("temporarily unable to figure out IP address for: ",(char *)hostname));
+
+ byte_copy(localip,16,addresses.s);
+
+ for (j = 0; j < addresses.len; j += 16) { // Select best matching IP address
+ if (ipflag == 1 && !ip6_isv4mapped(addresses.s + j)) continue;
+ if (ipflag == 2 && !ip6_isv4mapped(addresses.s + j)) continue;
+ byte_copy(localip,16,addresses.s + j);
+ }
+
+ }
+ if (addresses.len < 16)
+ logmsg(who,111,FATAL,B("no IP address for: ",(char *)hostname));
+
+ if (ip6_isv4mapped(localip))
+ s = socket_tcp4();
+ else
+ s = socket_tcp6();
+ if (s == -1)
+ logmsg(who,111,FATAL,"unable to create socket");
+
+ if (flagdualstack)
+ socket_dualstack(s);
+ if (socket_bind_reuse(s,localip,localport,netif) == -1)
+ logmsg(who,111,FATAL,"unable to bind");
+ if (socket_local(s,localip,&localport,&netif) == -1)
+ logmsg(who,111,FATAL,"unable to get local address");
+ if (socket_listen(s,backlog) == -1)
+ logmsg(who,111,FATAL,"unable to listen");
+ ndelay_off(s);
+
+ if (!flagafter) {
+ if (gid) if (prot_gid(gid) == -1)
+ logmsg(who,111,FATAL,"unable to set gid");
+ if (uid) if (prot_uid(uid) == -1)
+ logmsg(who,111,FATAL,"unable to set uid");
+ }
+
+ if (ip6_isv4mapped(localip))
+ localipstr[ip4_fmt(localipstr,localip + 12)] = 0;
+ else
+ localipstr[ip6_fmt(localipstr,localip)] = 0;
+
+ localportstr[fmt_ulong(localportstr,localport)] = 0;
+
+ if (flag1) {
+ buffer_init(&b,buffer_unixwrite,1,bspace,sizeof(bspace));
+ buffer_puts(&b,localipstr);
+ buffer_puts(&b," : ");
+ buffer_puts(&b,localportstr);
+ buffer_puts(&b,"\n");
+ buffer_flush(&b);
+ }
+
+ if (flag3) read_passwd();
+
+ ctx = ssl_server();
+ ssl_errstr();
+ if (!ctx) logmsg(who,111,FATAL,"unable to create TLS context");
+
+ if (certchainfile) {
+ switch (ssl_chainfile(ctx,certchainfile,keyfile,passwd_cb)) {
+ case -1: logmsg(who,111,ERROR,"unable to load certificate chain file");
+ case -2: logmsg(who,111,ERROR,"unable to load key");
+ case -3: logmsg(who,111,ERROR,"key does not match certificate");
+ default: break;
+ }
+ } else {
+ switch (ssl_certkey(ctx,certfile,keyfile,passwd_cb)) {
+ case -1: logmsg(who,111,ERROR,"unable to load certificate");
+ case -2: logmsg(who,111,ERROR,"unable to load key");
+ case -3: logmsg(who,111,ERROR,"key does not match certificate");
+ default: break;
+ }
+ }
+
+ if (flagclientcert && !ssl_ca(ctx,cafile,cadir,verifydepth))
+ logmsg(who,111,ERROR,"unable to load CA list");
+
+ if (!ssl_cca(ctx,ccafile))
+ logmsg(who,111,ERROR,"unable to load client CA list");
+
+ if (!ssl_params_rsa(ctx,rsalen))
+ logmsg(who,111,ERROR,"unable to set RSA parameters");
+ if (!ssl_params_dh(ctx,dhfile))
+ logmsg(who,111,ERROR,"unable to set DH parameters");
+
+ if (flagafter) {
+ if (gid) if (prot_gid(gid) == -1)
+ logmsg(who,111,FATAL,"unable to set gid");
+ if (uid) if (prot_uid(uid) == -1)
+ logmsg(who,111,FATAL,"unable to set uid");
+ }
+
+ if (!ssl_ciphers(ctx,ciphers))
+ logmsg(who,111,ERROR,"unable to set cipher list");
+
+ if (verbosity >= 2) {
+ strnum[fmt_ulong(strnum,getpid())] = 0;
+ strnum2[fmt_ulong(strnum2,rsalen)] = 0;
+ log_who(who,B("ciphers ",strnum," ",(char *)ciphers));
+ log_who(who,B("cafile ",strnum," ",(char *)cafile));
+ log_who(who,B("ccafile ",strnum," ",(char *)ccafile));
+ log_who(who,B("cadir ",strnum," ",(char *)cadir));
+ log_who(who,B("certchainfile ",strnum," ",(char *)certchainfile));
+ log_who(who,B("cert ",strnum," ",(char *)certfile));
+ log_who(who,B("key ",strnum," ",(char *)keyfile));
+ /* XXX */
+ log_who(who,B("dhparam ",strnum," ",(char *)dhfile," ",strnum2));
+ }
+
+ close(0);
+ close(1);
+ printstatus();
+
+ for (;;) {
+ int pause_ret, read_ret;
+ if (!flagexit) spawn(s,argc,argv);
+
+ sig_unblock(sig_child);
+ io[0].fd = selfpipe[0];
+ io[0].events = IOPAUSE_READ;
+ taia_now(&stamp);
+ taia_uint(&deadline,3600);
+ taia_add(&deadline,&stamp,&deadline);
+ pause_ret = iopause(io,1,&deadline,&stamp);
+ sig_block(sig_child);
+
+ if (flagexit && !numchildren) _exit(0);
+ while ((read_ret = buffer_unixread(selfpipe[0],&ch,1)) == 1)
+ ;
+ if ((pause_ret > 0) && (read_ret == 0)) {
+ flagexit = 1;
+ --numchildren;
+ }
+ if (flagexit && !numchildren) _exit(0);
+ }
+}
diff --git a/src/sslperl.c b/src/sslperl.c
new file mode 100644
index 0000000..1d01da1
--- /dev/null
+++ b/src/sslperl.c
@@ -0,0 +1,105 @@
+#include <EXTERN.h>
+#include <perl.h>
+#include "exit.h"
+#include "logmsg.h"
+#include "stralloc.h"
+#include "str.h"
+#include "ucspissl.h"
+
+#ifndef eval_pv
+#define eval_pv perl_eval_pv
+#endif
+
+#ifndef call_argv
+#define call_argv perl_call_argv
+#endif
+
+extern char *Who = "PERL!";
+
+//extern const char *Who;
+
+/* ActiveState Perl requires this be called my_perl */
+static PerlInterpreter *my_perl = 0;
+
+static void usage(void) {
+ logmsg(Who,100,USAGE,"sslargs file sub args");
+}
+
+static stralloc newenv = {0};
+static char *trivenv[] = { 0 };
+static char **perlenv = trivenv;
+static char **origenv = 0;
+
+void env_append(const char *c) {
+ if (!stralloc_append(&newenv,c))
+ logmsg(Who,111,FATAL,"out of memory");
+}
+
+#define EXTERN_C extern
+
+EXTERN_C void xs_init() {
+}
+
+void server(int argc,char **argv) {
+ char *prog[] = { "", *argv };
+ int i;
+ int j;
+ int split;
+ const char *x;
+
+ ++argv; --argc;
+ if (!argv) usage();
+ if (!*argv) usage();
+
+ origenv = environ;
+ environ = perlenv;
+
+ if (!my_perl) {
+ my_perl = perl_alloc();
+ if (!my_perl) logmsg(Who,111,FATAL,"out of memory");
+ perl_construct(my_perl);
+ if (perl_parse(my_perl,xs_init,2,prog,trivenv))
+ logmsg(Who,111,FATAL,"perl_parse failed");
+
+ if (perl_run(my_perl))
+ logmsg(Who,111,FATAL,"perl_run failed");
+ }
+
+ if (!stralloc_copys(&newenv,"%ENV=("))
+ logmsg(Who,111,FATAL,"out of memory");
+
+ for (i = 0; origenv[i]; ++i) {
+ x = origenv[i];
+ if (!x) continue;
+ split = str_chr(x,'=');
+ env_append("'");
+ for (j = 0; j < split; ++j) {
+ if (*x == '\'' || *x == '\\') env_append("\\");
+ env_append(x++);
+ }
+ env_append("'");
+ env_append(",");
+ env_append("'");
+ if (*x == '=') ++x;
+ while (*x) {
+ if (*x == '\'' || *x == '\\') env_append("\\");
+ env_append(x++);
+ }
+ env_append("'");
+ env_append(",");
+ }
+ env_append(")");
+ env_append("\0");
+
+ ENTER;
+ SAVETMPS;
+ eval_pv(newenv.s,TRUE);
+ FREETMPS;
+ LEAVE;
+
+ if (call_argv(*argv,G_VOID|G_DISCARD,argv + 1))
+ logmsg(Who,111,FATAL,"interpreter failed");
+
+ perlenv = environ;
+ environ = origenv;
+}
diff --git a/src/sslprint.c b/src/sslprint.c
new file mode 100644
index 0000000..0033107
--- /dev/null
+++ b/src/sslprint.c
@@ -0,0 +1,411 @@
+#include "buffer.h"
+#include "env.h"
+
+static char *e[] = {0};
+static int n = 0;
+
+void server(int argc,const char * const *argv) {
+ char *x;
+
+ buffer_puts(buffer_1,"\nPROTO=");
+ x = env_get("PROTO");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSLLOCALHOST=");
+ x = env_get("SSLLOCALHOST");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSLLOCALIP=");
+ x = env_get("SSLLOCALIP");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSLLOCALPORT=");
+ x = env_get("SSLLOCALPORT");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSLREMOTEHOST=");
+ x = env_get("SSLREMOTEHOST");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSLREMOTEIP=");
+ x = env_get("SSLREMOTEIP");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSLREMOTEPORT=");
+ x = env_get("SSLREMOTEPORT");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSLREMOTEINFO=");
+ x = env_get("SSLREMOTEINFO");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nTCPLOCALHOST=");
+ x = env_get("TCPLOCALHOST");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nTCPLOCALIP=");
+ x = env_get("TCPLOCALIP");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nTCPLOCALPORT=");
+ x = env_get("TCPLOCALPORT");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nTCPREMOTEHOST=");
+ x = env_get("TCPREMOTEHOST");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nTCPREMOTEIP=");
+ x = env_get("TCPREMOTEIP");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nTCPREMOTEPORT=");
+ x = env_get("TCPREMOTEPORT");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nTCPREMOTEINFO=");
+ x = env_get("TCPREMOTEINFO");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+
+ buffer_puts(buffer_1,"\nTCP6REMOTEHOST=");
+ x = env_get("TCP6REMOTEHOST");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nTCP6REMOTEIP=");
+ x = env_get("TCP6REMOTEIP");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nTCP6REMOTEPORT=");
+ x = env_get("TCP6REMOTEPORT");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+
+ buffer_puts(buffer_1,"\nSSL_PROTOCOL=");
+ x = env_get("SSL_PROTOCOL");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SESSION_ID=");
+ x = env_get("SSL_SESSION_ID");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CIPHER=");
+ x = env_get("SSL_CIPHER");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CIPHER_EXPORT=");
+ x = env_get("SSL_CIPHER_EXPORT");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CIPHER_USEKEYSIZE=");
+ x = env_get("SSL_CIPHER_USEKEYSIZE");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CIPHER_ALGKEYSIZE=");
+ x = env_get("SSL_CIPHER_ALGKEYSIZE");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_VERSION_INTERFACE=");
+ x = env_get("SSL_VERSION_INTERFACE");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_VERSION_LIBRARY=");
+ x = env_get("SSL_VERSION_LIBRARY");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_M_VERSION=");
+ x = env_get("SSL_SERVER_M_VERSION");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_M_SERIAL=");
+ x = env_get("SSL_SERVER_M_SERIAL");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_S_DN=");
+ x = env_get("SSL_SERVER_S_DN");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_S_DN_C=");
+ x = env_get("SSL_SERVER_S_DN_C");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_S_DN_ST=");
+ x = env_get("SSL_SERVER_S_DN_ST");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_S_DN_L=");
+ x = env_get("SSL_SERVER_S_DN_L");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_S_DN_O=");
+ x = env_get("SSL_SERVER_S_DN_O");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_S_DN_OU=");
+ x = env_get("SSL_SERVER_S_DN_OU");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_S_DN_CN=");
+ x = env_get("SSL_SERVER_S_DN_CN");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_S_DN_T=");
+ x = env_get("SSL_SERVER_S_DN_T");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_S_DN_I=");
+ x = env_get("SSL_SERVER_S_DN_I");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_S_DN_G=");
+ x = env_get("SSL_SERVER_S_DN_G");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_S_DN_S=");
+ x = env_get("SSL_SERVER_S_DN_S");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_S_DN_D=");
+ x = env_get("SSL_SERVER_S_DN_D");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_S_DN_UID=");
+ x = env_get("SSL_SERVER_S_DN_UID");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_S_DN_Email=");
+ x = env_get("SSL_SERVER_S_DN_Email");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_I_DN=");
+ x = env_get("SSL_SERVER_I_DN");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_I_DN_C=");
+ x = env_get("SSL_SERVER_I_DN_C");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_I_DN_ST=");
+ x = env_get("SSL_SERVER_I_DN_ST");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_I_DN_L=");
+ x = env_get("SSL_SERVER_I_DN_L");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_I_DN_O=");
+ x = env_get("SSL_SERVER_I_DN_O");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_I_DN_OU=");
+ x = env_get("SSL_SERVER_I_DN_OU");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_I_DN_CN=");
+ x = env_get("SSL_SERVER_I_DN_CN");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_I_DN_T=");
+ x = env_get("SSL_SERVER_I_DN_T");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_I_DN_I=");
+ x = env_get("SSL_SERVER_I_DN_I");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_I_DN_G=");
+ x = env_get("SSL_SERVER_I_DN_G");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_I_DN_S=");
+ x = env_get("SSL_SERVER_I_DN_S");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_I_DN_D=");
+ x = env_get("SSL_SERVER_I_DN_D");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_I_DN_UID=");
+ x = env_get("SSL_SERVER_I_DN_UID");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_I_DN_Email=");
+ x = env_get("SSL_SERVER_I_DN_Email");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_V_START=");
+ x = env_get("SSL_SERVER_V_START");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_V_END=");
+ x = env_get("SSL_SERVER_V_END");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_A_SIG=");
+ x = env_get("SSL_SERVER_A_SIG");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_A_KEY=");
+ x = env_get("SSL_SERVER_A_KEY");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_SERVER_CERT=");
+ x = env_get("SSL_SERVER_CERT");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_M_VERSION=");
+ x = env_get("SSL_CLIENT_M_VERSION");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_M_SERIAL=");
+ x = env_get("SSL_CLIENT_M_SERIAL");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_S_DN=");
+ x = env_get("SSL_CLIENT_S_DN");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_S_DN_C=");
+ x = env_get("SSL_CLIENT_S_DN_C");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_S_DN_ST=");
+ x = env_get("SSL_CLIENT_S_DN_ST");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_S_DN_L=");
+ x = env_get("SSL_CLIENT_S_DN_L");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_S_DN_O=");
+ x = env_get("SSL_CLIENT_S_DN_O");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_S_DN_OU=");
+ x = env_get("SSL_CLIENT_S_DN_OU");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_S_DN_CN=");
+ x = env_get("SSL_CLIENT_S_DN_CN");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_S_DN_T=");
+ x = env_get("SSL_CLIENT_S_DN_T");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_S_DN_I=");
+ x = env_get("SSL_CLIENT_S_DN_I");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_S_DN_G=");
+ x = env_get("SSL_CLIENT_S_DN_G");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_S_DN_S=");
+ x = env_get("SSL_CLIENT_S_DN_S");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_S_DN_D=");
+ x = env_get("SSL_CLIENT_S_DN_D");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_S_DN_UID=");
+ x = env_get("SSL_CLIENT_S_DN_UID");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_S_DN_Email=");
+ x = env_get("SSL_CLIENT_S_DN_Email");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_I_DN=");
+ x = env_get("SSL_CLIENT_I_DN");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_I_DN_C=");
+ x = env_get("SSL_CLIENT_I_DN_C");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_I_DN_ST=");
+ x = env_get("SSL_CLIENT_I_DN_ST");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_I_DN_L=");
+ x = env_get("SSL_CLIENT_I_DN_L");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_I_DN_O=");
+ x = env_get("SSL_CLIENT_I_DN_O");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_I_DN_OU=");
+ x = env_get("SSL_CLIENT_I_DN_OU");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_I_DN_CN=");
+ x = env_get("SSL_CLIENT_I_DN_CN");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_I_DN_T=");
+ x = env_get("SSL_CLIENT_I_DN_T");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_I_DN_I=");
+ x = env_get("SSL_CLIENT_I_DN_I");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_I_DN_G=");
+ x = env_get("SSL_CLIENT_I_DN_G");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_I_DN_S=");
+ x = env_get("SSL_CLIENT_I_DN_S");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_I_DN_D=");
+ x = env_get("SSL_CLIENT_I_DN_D");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_I_DN_UID=");
+ x = env_get("SSL_CLIENT_I_DN_UID");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_I_DN_Email=");
+ x = env_get("SSL_CLIENT_I_DN_Email");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_V_START=");
+ x = env_get("SSL_CLIENT_V_START");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_V_END=");
+ x = env_get("SSL_CLIENT_V_END");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_A_SIG=");
+ x = env_get("SSL_CLIENT_A_SIG");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_A_KEY=");
+ x = env_get("SSL_CLIENT_A_KEY");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_CERT=");
+ x = env_get("SSL_CLIENT_CERT");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_CERT_CHAIN_0=");
+ x = env_get("SSL_CLIENT_CERT_CHAIN_0");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_puts(buffer_1,"\nSSL_CLIENT_CERT_CHAIN_1=");
+ x = env_get("SSL_CLIENT_CERT_CHAIN_1");
+ buffer_puts(buffer_1,x ? x : "unset");
+
+ buffer_putsflush(buffer_1,"\n");
+
+ if (++n > 1) {
+ environ = e;
+ }
+}
diff --git a/src/sslserver.c b/src/sslserver.c
new file mode 100644
index 0000000..b342430
--- /dev/null
+++ b/src/sslserver.c
@@ -0,0 +1,991 @@
+/**
+ @file sslserver.c
+ @author web, fefe, feh
+ @brief IPv6 enabled dualstack sslserver
+*/
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <arpa/inet.h>
+#include <signal.h>
+#include "ucspissl.h"
+#include "uint_t.h"
+#include "str.h"
+#include "byte.h"
+#include "fmt.h"
+#include "scan.h"
+#include "ip.h"
+#include "fd.h"
+#include "exit.h"
+#include "env.h"
+#include "prot.h"
+#include "open.h"
+#include "wait.h"
+#include "stralloc.h"
+#include "genalloc.h"
+#include "alloc.h"
+#include "buffer.h"
+#include "getln.h"
+#include "error.h"
+#include "logmsg.h"
+#include "getoptb.h"
+#include "pathexec.h"
+#include "socket_if.h"
+#include "ndelay.h"
+#include "remoteinfo.h"
+#include "rules.h"
+#include "sig.h"
+#include "iopause.h"
+#include "dnsresolv.h"
+#include "auto_cafile.h"
+#include "auto_cadir.h"
+#include "auto_ccafile.h"
+#include "auto_dhfile.h"
+#include "auto_certfile.h"
+#include "auto_certchainfile.h"
+#include "auto_keyfile.h"
+#include "auto_ciphers.h"
+
+#define WHO "sslserver"
+
+int verbosity = 1;
+int flagkillopts = 1;
+int flagdelay = 0;
+const char *banner = "";
+int flagremoteinfo = 0;
+int flagremotehost = 1;
+int flagparanoid = 0;
+int flagclientcert = 0;
+int flagsslenv = 0;
+int flagtcpenv = 1;
+int flagsslwait = 0;
+unsigned long timeout = 26;
+unsigned long ssltimeout = 26;
+unsigned int progtimeout = 3600;
+uint32 netif = 0;
+
+static stralloc tcpremoteinfo;
+
+uint16 localport;
+char iplocal[16] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
+char localportstr[FMT_ULONG];
+char localip[16];
+char localipstr[IP6_FMT];
+static stralloc localhostsa;
+const char *localhost = 0;
+
+uint16 remoteport;
+char remoteportstr[FMT_ULONG];
+char remoteip[16];
+char remoteipstr[IP6_FMT];
+static stralloc remotehostsa;
+char *remotehost = 0;
+char *verifyhost = 0;
+
+const char *hostname;
+const char *thishost = "0.0.0.0";
+
+unsigned long uid = 0;
+unsigned long gid = 0;
+
+static char strnum[FMT_ULONG];
+static char strnum2[FMT_ULONG];
+static char strnum3[FMT_ULONG];
+
+static stralloc tmp;
+static stralloc fqdn;
+static stralloc addresses;
+
+unsigned long limit = 40;
+unsigned long numchildren = 0;
+unsigned long ipchildren = 0;
+unsigned long maxconip = 0;
+
+char bspace[16];
+buffer bo;
+
+void drop_nomem(void)
+{
+ logmsg(WHO,111,FATAL,"out of memory");
+}
+
+/* ---------------------------- per ip limit */
+
+struct child {
+ char ipaddr[16];
+ uint32 num;
+};
+
+GEN_ALLOC_typedef(child_alloc,struct child,c,len,a)
+GEN_ALLOC_readyplus(child_alloc,struct child,c,len,a,i,n,x,24,child_readyplus)
+GEN_ALLOC_append(child_alloc,struct child,c,len,a,i,n,x,24,child_readyplus,child_append)
+
+child_alloc children = {0};
+
+void ipchild_append(char ip[16],unsigned long n)
+{
+ struct child *ipchild = 0;
+ int i;
+
+ for (i = 0; i <= n; ++i) {
+ ipchild = &children.c[i];
+ if (byte_equal(ipchild->ipaddr,16,ip)) {
+ ++ipchild->num;
+ break;
+ } else {
+ byte_copy(ipchild->ipaddr,16,ip);
+ ++ipchild->num;
+ break;
+ }
+ }
+}
+
+void ipchild_clear(char ip[16])
+{
+ struct child *ipchild = 0;
+ int i;
+
+ for (i = 0; i <= children.len; ++i) {
+ ipchild = &children.c[i];
+ if (byte_equal(ipchild->ipaddr,16,ip)) {
+ if (ipchild->num) --ipchild->num;
+ break;
+ }
+ }
+}
+
+int ipchild_limit(char ip[16],unsigned long n)
+{
+ int i;
+
+ for (i = 0; i <= n; ++i)
+ if (byte_equal(children.c[i].ipaddr,16,ip))
+ return children.c[i].num;
+
+ return 0;
+}
+
+SSL_CTX *ctx;
+const char *certchainfile = auto_certchainfile;
+const char *certfile = auto_certfile;
+const char *keyfile = auto_keyfile;
+stralloc password = {0};
+stralloc certname = {0};
+int match = 0;
+const char *cafile = auto_cafile;
+const char *ccafile = auto_ccafile;
+const char *cadir = auto_cadir;
+const char *ciphers = auto_ciphers;
+int verifydepth = 1;
+const char *dhfile = auto_dhfile;
+int rsalen = SSL_RSA_LEN;
+
+char * const *prog;
+
+int pi[2];
+int po[2];
+int pt[2];
+
+stralloc envsa = {0};
+
+X509 *cert;
+char buf[SSL_NAME_LEN];
+
+/* ---------------------------- child */
+
+int flagdualstack = 0;
+int flagdeny = 0;
+int flagallownorules = 0;
+const char *fnrules = 0;
+const char *fniprules = 0;
+
+void cats(const char *s)
+{
+ if (!stralloc_cats(&tmp,s)) drop_nomem();
+}
+void append(const char *ch)
+{
+ if (!stralloc_append(&tmp,ch)) drop_nomem();
+}
+void safecats(const char *s) {
+ char ch;
+ int i;
+
+ for (i = 0;i < 100;++i) {
+ ch = s[i];
+ if (!ch) return;
+ if (ch < 33) ch = '?';
+ if (ch > 126) ch = '?';
+ if (ch == '%') ch = '?'; /* logger stupidity */
+ append(&ch);
+ }
+ cats("...");
+}
+
+void env(const char *s,const char *t) {
+ if (!pathexec_env(s,t)) drop_nomem();
+}
+
+void drop_rules(const char *fnbase) {
+ logmsg(WHO,110,FATAL,B("unable to read: ",fnbase));
+}
+
+void found(char *data,unsigned int datalen) {
+ unsigned int next0;
+ unsigned int split;
+
+ while ((next0 = byte_chr(data,datalen,0)) < datalen) {
+ switch(data[0]) {
+ case 'D':
+ flagdeny = 1;
+ break;
+ case '+':
+ split = str_chr(data + 1,'=');
+ if (data[1 + split] == '=') {
+ data[1 + split] = 0;
+ env(data + 1,data + 1 + split + 1);
+ if (!str_diff(data + 1,"MAXCONIP")) {
+ scan_ulong(data + 1 + split + 1,&maxconip);
+ if (limit && maxconip > limit) maxconip = limit;
+ if (ipchildren >= maxconip) flagdeny = 2;
+ }
+ }
+ break;
+ }
+ ++next0;
+ data += next0; datalen -= next0;
+ }
+}
+
+void doit(int t) {
+ int j;
+ SSL *ssl = 0;
+ int wstat;
+ int sslctl[2];
+ char *s;
+ unsigned long tmp_long;
+ char ssl_cmd;
+ stralloc ssl_env = {0};
+ int bytesleft;
+ char envbuf[8192];
+ int childpid;
+ uint32 netif = 0;
+ stralloc tlsinfo = {0};
+
+ if (pipe(pi) == -1) logmsg(WHO,111,FATAL,"unable to create pipe");
+ if (pipe(po) == -1) logmsg(WHO,111,FATAL,"unable to create pipe");
+ if (socketpair(AF_UNIX,SOCK_STREAM,0,sslctl) == -1)
+ logmsg(WHO,111,FATAL,"unable to create socketpair");
+
+/* Get remote IP and FQDN to validate X.509 cert */
+
+ if (ip6_isv4mapped(remoteip)) {
+ localipstr[ip4_fmt(localipstr,localip + 12)] = 0;
+ remoteipstr[ip4_fmt(remoteipstr,remoteip + 12)] = 0;
+ } else {
+ remoteipstr[ip6_fmt(remoteipstr,remoteip)] = 0;
+ localipstr[ip6_fmt(localipstr,localip)] = 0;
+ }
+
+/* Early evaluation of IP rules (only) */
+
+ if (fniprules) {
+ int fdrules;
+ fdrules = open_read(fniprules);
+ if (fdrules == -1) {
+ if (errno != ENOENT) drop_rules(fniprules);
+ if (!flagallownorules) drop_rules(fniprules);
+ } else {
+ if (rules(found,fdrules,remoteipstr,0,0) == -1)
+ drop_rules(fniprules);
+ close(fdrules);
+ }
+ }
+
+ if (flagdeny) goto FINISH;
+
+/* Early lookup of remote information (before child invoked) */
+
+ if (flagremotehost)
+ if (dns_name(&remotehostsa,remoteip) >= 0)
+ if (remotehostsa.len) {
+ if (flagparanoid) {
+ verifyhost = remoteipstr;
+ 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;
+ }
+ if (dns_ip4(&tmp,&remotehostsa) >= 0)
+ for (j = 0; j + 4 <= tmp.len; j += 4)
+ if (byte_equal(remoteip + 12,4,tmp.s + j)) {
+ flagparanoid = 0;
+ break;
+ }
+ }
+ if (!flagparanoid) {
+ if (!stralloc_0(&remotehostsa)) drop_nomem();
+ remotehost = remotehostsa.s;
+ verifyhost = remotehostsa.s;
+ }
+ }
+
+ if (flagremoteinfo) {
+ if (remoteinfo(&tcpremoteinfo,remoteip,remoteport,localip,localport,timeout,netif) == -1)
+ flagremoteinfo = 0;
+ if (!stralloc_0(&tcpremoteinfo)) drop_nomem();
+ }
+
+ if (fnrules) {
+ int fdrules;
+ fdrules = open_read(fnrules);
+ if (fdrules == -1) {
+ if (errno != ENOENT) drop_rules(fnrules);
+ if (!flagallownorules) drop_rules(fnrules);
+ } else {
+ if (rules(found,fdrules,remoteipstr,remotehost,flagremoteinfo ? tcpremoteinfo.s : 0) == -1)
+ drop_rules(fnrules);
+ close(fdrules);
+ }
+ }
+
+ if (flagdeny) goto FINISH;
+
+/* Prepare the child process */
+
+ switch (childpid = fork()) {
+ case -1:
+ logmsg(WHO,111,FATAL,"unable to fork");
+ case 0:
+ /* Child */
+ close(sslctl[0]);
+ break;
+ default:
+ /* Parent */
+
+ close(pi[0]); close(po[1]); close(sslctl[1]);
+
+ if ((s = env_get("SSL_CHROOT")))
+ if (chroot(s) == -1) {
+ kill(childpid,SIGTERM);
+ logmsg(WHO,111,FATAL,"unable to chroot");
+ }
+
+ if ((s = env_get("SSL_GID"))) {
+ scan_ulong(s,&tmp_long);
+ gid = tmp_long;
+ }
+ if (gid) if (prot_gid(gid) == -1) {
+ kill(childpid,SIGTERM);
+ logmsg(WHO,111,FATAL,"unable to set gid");
+ }
+
+ if ((s = env_get("SSL_UID"))) {
+ scan_ulong(s,&tmp_long);
+ uid = tmp_long;
+ }
+ if (uid)
+ if (prot_uid(uid) == -1) {
+ kill(childpid,SIGTERM);
+ logmsg(WHO,111,FATAL,"unable to set uid");
+ }
+
+/* Get remote IP info to report in logmsg */
+
+ if (ip6_isv4mapped(remoteip))
+ remoteipstr[ip4_fmt(remoteipstr,remoteip + 12)] = 0;
+ else
+ remoteipstr[ip6_fmt(remoteipstr,remoteip)] = 0;
+
+/* Read the TLS command socket. This will block until/unless TLS is requested. */
+
+ if (read(sslctl[0],&ssl_cmd,1) == 1) {
+ ssl = ssl_new(ctx,t);
+ if (!ssl) {
+ kill(childpid,SIGTERM);
+ logmsg(WHO,111,FATAL,"unable to create TLS instance");
+ }
+ if (ndelay_on(t) == -1) {
+ kill(childpid,SIGTERM);
+ logmsg(WHO,111,FATAL,"unable to set socket options");
+ }
+ if (ssl_timeoutaccept(ssl,ssltimeout) == -1) {
+ strnum[fmt_ulong(strnum,childpid)] = 0;
+ kill(childpid,SIGTERM);
+ logmsg(WHO,111,FATAL,B("unable to accept TLS from: ",remoteipstr," for pid: ",strnum," ",ERR_reason_error_string(ssl_errno)));
+ }
+ } else if (errno == EAGAIN) {
+ strnum[fmt_ulong(strnum,childpid)] = 0;
+ kill(childpid,SIGTERM);
+ _exit(100);
+ }
+
+/* Check the remote client cert during TLS handshake; if requested */
+
+ if (flagclientcert) {
+ if (flagclientcert == 2) verifyhost = 0;
+ switch (ssl_verify(ssl,verifyhost,&certname)) {
+ case -1:
+ kill(childpid,SIGTERM);
+ logmsg(WHO,110,ERROR,B("no client certificate from: ",remoteipstr," for pid: ",strnum));
+ case -2:
+ kill(childpid,SIGTERM);
+ logmsg(WHO,110,ERROR,B("missing credentials (CA) or unable to validate client certificate from: ",remoteipstr," for pid: ",strnum));
+ case -3:
+ kill(childpid,SIGTERM);
+ if (!stralloc_0(&certname)) drop_nomem();
+ logmsg(WHO,110,ERROR,B("client hostname does not match certificate for pid: ",strnum," ",verifyhost," <=> ",certname.s));
+ default:
+ if (verbosity >= 2) {
+ strnum[fmt_ulong(strnum,childpid)] = 0;
+ logmsg(WHO,0,INFO,B("valid client cert received from: ",remoteipstr," for pid: ",strnum));
+ }
+ break;
+ }
+ }
+
+/* Request TLS communication pipe from/to the child process (the application called) */
+
+ if (ssl_cmd == 'Y') {
+ ssl_server_env(ssl,&ssl_env);
+ if (!stralloc_0(&ssl_env)) drop_nomem(); /* Add another NUL */
+ env("SSLCTL",ssl_env.s);
+
+ for (bytesleft = ssl_env.len; bytesleft > 0; bytesleft -= j)
+ if ((j = write(sslctl[0],ssl_env.s,bytesleft)) < 0) {
+ kill(childpid,SIGTERM);
+ logmsg(WHO,111,FATAL,"unable to write TLS environment");
+ }
+ }
+
+ if (verbosity >= 2) {
+ strnum[fmt_ulong(strnum,childpid)] = 0;
+ if (verbosity >= 3 && ssl_env.len > 1) {
+ s = ssl_env.s;
+ if ((j = str_chr(s,'=')))
+ if (!stralloc_copys(&tlsinfo,s + j + 1)) drop_nomem();
+ if (!stralloc_cats(&tlsinfo,":")) drop_nomem();
+ s = s + str_len(s) + 1;
+ s = s + str_len(s) + 1;
+ if ((j = str_chr(s,'=')))
+ if (!stralloc_cats(&tlsinfo,s + j + 1)) drop_nomem();
+ if (!stralloc_0(&tlsinfo)) drop_nomem();
+ log_who(WHO,B("tls ",strnum," accept ",tlsinfo.s));
+ } else
+ log_who(WHO,B("tls ",strnum," accept"));
+ }
+
+ if (ssl_cmd == 'Y' || ssl_cmd == 'y') {
+ if (ssl_io(ssl,pi[1],po[0],progtimeout) != 0) {
+ strnum[fmt_ulong(strnum,childpid)] = 0;
+ kill(childpid,SIGTERM);
+ logmsg(WHO,111,ERROR,B("unable to speak TLS with: ",remoteipstr," for pid: ",strnum," ",ERR_reason_error_string(ssl_errno)));
+ }
+ if (wait_nohang(&wstat) > 0)
+ _exit(wait_exitcode(wstat));
+ ssl_close(ssl);
+ }
+ kill(childpid,SIGTERM);
+ _exit(0);
+ }
+
+/* Child-only below this point */
+
+ if (ip6_isv4mapped(remoteip))
+ localipstr[ip4_fmt(localipstr,localip + 12)] = 0;
+ else
+ localipstr[ip6_fmt(localipstr,localip)] = 0;
+ localportstr[fmt_ulong(localportstr,localport)] = 0;
+
+ if (socket_local(t,localip,&localport,&netif) == -1)
+ logmsg(WHO,111,FATAL,B("unable to set local address/port: ",localipstr,"/",localportstr));
+
+ if (verbosity >= 2) {
+ strnum[fmt_ulong(strnum,getpid())] = 0;
+ log_who(WHO,B("pid ",strnum," from ",remoteipstr));
+ }
+
+ if (!localhost)
+ if (dns_name(&localhostsa,localip) >= 0)
+ if (localhostsa.len) {
+ if (!stralloc_0(&localhostsa)) drop_nomem();
+ localhost = localhostsa.s;
+ }
+
+ remoteportstr[fmt_ulong(remoteportstr,remoteport)] = 0;
+
+/* Setup environment variables */
+
+ env("PROTO","TLS");
+ env("SSLLOCALIP",localipstr);
+ env("SSLLOCALPORT",localportstr);
+ env("SSLLOCALHOST",localhost);
+ env("SSLREMOTEIP",remoteipstr);
+ env("SSLREMOTEPORT",remoteportstr);
+ env("SSLREMOTEHOST",remotehost);
+ env("SSLREMOTEINFO",flagremoteinfo ? tcpremoteinfo.s : 0);
+
+ if (flagtcpenv) {
+ env("TCPLOCALIP",localipstr);
+ env("TCPLOCALPORT",localportstr);
+ env("TCPLOCALHOST",localhost);
+ env("TCPREMOTEIP",remoteipstr);
+ env("TCPREMOTEPORT",remoteportstr);
+ env("TCPREMOTEHOST",remotehost);
+ if (!ip6_isv4mapped(remoteip)) {
+ env("PROTO","TCP6");
+ env("TCP6LOCALIP",localipstr);
+ env("TCP6LOCALHOST",localhost);
+ env("TCP6LOCALPORT",localportstr);
+ env("TCP6REMOTEIP",remoteipstr);
+ env("TCP6REMOTEPORT",remoteportstr);
+ env("TCP6REMOTEHOST",remotehost);
+ if (netif)
+ env("TCP6INTERFACE",socket_getifname(netif));
+ } else
+ env("PROTO","TCP");
+ env("TCPREMOTEINFO",flagremoteinfo ? tcpremoteinfo.s : 0);
+ }
+
+ FINISH:
+
+ if (verbosity >= 2) {
+ strnum[fmt_ulong(strnum,getpid())] = 0;
+ strnum2[fmt_ulong(strnum2,maxconip)] = 0;
+ if (!stralloc_copys(&tmp,"sslserver: ")) drop_nomem();
+ safecats(flagdeny ? "deny" : "ok");
+ cats(" "); safecats(strnum);
+ cats(" "); if (localhost) safecats(localhost);
+ cats(":"); safecats(localipstr);
+ cats(":"); safecats(localportstr);
+ cats(" "); if (remotehost) safecats(remotehost);
+ cats(":"); safecats(remoteipstr);
+ cats(":"); if (flagremoteinfo) safecats(tcpremoteinfo.s);
+ cats(":"); safecats(remoteportstr);
+ if (flagdeny == 2) { cats(" ip connection limit:"); cats(strnum2); cats(" exceeded"); }
+ cats("\n");
+ buffer_putflush(buffer_2,tmp.s,tmp.len);
+ }
+
+ if (flagdeny) _exit(100);
+
+ if (gid) if (prot_gid(gid) == -1)
+ logmsg(WHO,111,FATAL,"unable to set gid");
+ if (uid) if (prot_uid(uid) == -1)
+ logmsg(WHO,111,FATAL,"unable to set uid");
+
+ close(pi[1]); close(po[0]); close(sslctl[0]);
+
+ sig_uncatch(sig_child);
+ sig_unblock(sig_child);
+ sig_uncatch(sig_term);
+ sig_uncatch(sig_pipe);
+
+ if (fcntl(sslctl[1],F_SETFD,0) == -1)
+ logmsg(WHO,111,FATAL,"unable to clear close-on-exec flag");
+ strnum[fmt_ulong(strnum,sslctl[1])] = 0;
+ env("SSLCTLFD",strnum);
+
+ if (fcntl(pi[0],F_SETFD,0) == -1)
+ logmsg(WHO,111,FATAL,"unable to clear close-on-exec flag");
+ strnum[fmt_ulong(strnum,pi[0])] = 0;
+ env("SSLREADFD",strnum);
+
+ if (fcntl(po[1],F_SETFD,0) == -1)
+ logmsg(WHO,111,FATAL,"unable to clear close-on-exec flag");
+ strnum[fmt_ulong(strnum,po[1])] = 0;
+ env("SSLWRITEFD",strnum);
+
+ if (flagsslwait) {
+ if (fd_copy(0,t) == -1)
+ logmsg(WHO,111,FATAL,"unable to set up descriptor 0");
+ if (fd_copy(1,t) == -1)
+ logmsg(WHO,111,FATAL,"unable to set up descriptor 1");
+ } else {
+ if (fd_move(0,pi[0]) == -1)
+ logmsg(WHO,111,FATAL,"unable to set up descriptor 0");
+ if (fd_move(1,po[1]) == -1)
+ logmsg(WHO,111,FATAL,"unable to set up descriptor 1");
+ }
+
+ if (flagkillopts)
+ socket_ipoptionskill(t);
+
+ if (!flagdelay)
+ socket_tcpnodelay(t);
+
+ if (*banner) {
+ buffer_init(&bo,buffer_unixwrite,1,bspace,sizeof(bspace));
+ if (buffer_putsflush(&bo,banner) == -1)
+ logmsg(WHO,111,FATAL,"unable to print banner");
+ }
+
+ if (!flagsslwait) {
+ ssl_cmd = flagsslenv ? 'Y' : 'y';
+ if (write(sslctl[1],&ssl_cmd,1) < 1)
+ logmsg(WHO,111,FATAL,"unable to start TLS");
+ if (flagsslenv) {
+ while ((j = read(sslctl[1],envbuf,8192)) > 0) {
+ stralloc_catb(&ssl_env,envbuf,j);
+ if (ssl_env.len >= 2 && ssl_env.s[ssl_env.len - 2] == 0 && ssl_env.s[ssl_env.len - 1] == 0)
+ break;
+ }
+ if (j < 0)
+ logmsg(WHO,111,FATAL,"unable to read TLS environment");
+ pathexec_multienv(&ssl_env);
+ }
+ }
+
+ pathexec(prog);
+ logmsg(WHO,111,FATAL,B("unable to run: ",*prog));
+}
+
+/* ---------------------------- parent */
+
+void usage(void)
+{
+ logmsg(WHO,100,USAGE,"sslserver \
+[ -1346UXpPhHrRoOdDqQvVIeEsSnNmzZ ] \
+[ -c limit ] \
+[ -y iprules.cdb ] \
+[ -x rules.cdb ] \
+[ -B banner ] \
+[ -g gid ] \
+[ -u uid ] \
+[ -b backlog ] \
+[ -l localname ] \
+[ -t timeout ] \
+[ -I interface ] \
+[ -T ssltimeout ] \
+[ -w progtimeout ] \
+host port program");
+}
+
+int flag1 = 0;
+int flag3 = 0;
+unsigned long backlog = 20;
+
+void printstatus(void)
+{
+ if (verbosity < 2) return;
+ strnum[fmt_ulong(strnum,numchildren)] = 0;
+ strnum2[fmt_ulong(strnum2,limit)] = 0;
+ strnum3[fmt_ulong(strnum3,maxconip)] = 0;
+ log_who(WHO,B("status: ",strnum,"/",strnum2,"/",strnum3));
+}
+
+void sigterm(void)
+{
+ _exit(0);
+}
+
+void sigchld(void)
+{
+ int wstat;
+ int pid;
+
+ while ((pid = wait_nohang(&wstat)) > 0) {
+ if (verbosity >= 2) {
+ strnum[fmt_ulong(strnum,pid + 1)] = 0;
+ strnum2[fmt_ulong(strnum2,wstat)] = 0;
+ log_who(WHO,B("ended by ",strnum," status ",strnum2));
+ }
+ if (maxconip) ipchild_clear(remoteip);
+ if (numchildren) --numchildren;
+ printstatus();
+ }
+}
+
+void read_passwd(void) {
+ if (!password.len) {
+ buffer_init(&bo,buffer_unixread,3,bspace,sizeof(bspace));
+ if (getln(&bo,&password,&match,'\0') == -1)
+ logmsg(WHO,111,ERROR,"unable to read password");
+ close(3);
+ if (match) --password.len;
+ }
+}
+
+int passwd_cb(char *buff,int size,int rwflag,void *userdata) {
+ if (size < password.len)
+ logmsg(WHO,111,ERROR,"password too long");
+
+ byte_copy(buff,password.len,password.s);
+ return password.len;
+}
+
+int main(int argc,char * const *argv) {
+ int opt;
+ struct servent *se;
+ char *x;
+ unsigned long u = 0;
+ int j;
+ int s;
+ int t;
+ int ipflag = 0;
+
+ while ((opt = getopt(argc,argv,"1346dDvVqQhHrRUXx:y:t:T:u:g:l:b:B:c:pPoOIEeSsw:nNzZm")) != opteof)
+ switch(opt) {
+ case 'b': scan_ulong(optarg,&backlog); break;
+ case 'c': scan_ulong(optarg,&limit); break;
+ case 'X': flagallownorules = 1; break;
+ case 'x': fnrules = optarg; break;
+ case 'y': fniprules = optarg; break;
+ case 'B': banner = optarg; break;
+ case 'd': flagdelay = 1; break;
+ case 'D': flagdelay = 0; break;
+ case 'v': verbosity = 2; break;
+ case 'V': verbosity = 3; break;
+ case 'q': verbosity = 0; break;
+ case 'Q': verbosity = 1; break;
+ case 'P': flagparanoid = 0; break;
+ case 'p': flagparanoid = 1; break;
+ case 'O': flagkillopts = 1; break;
+ case 'o': flagkillopts = 0; break;
+ case 'H': flagremotehost = 0; break;
+ case 'h': flagremotehost = 1; break;
+ case 'R': flagremoteinfo = 0; break;
+ case 'r': flagremoteinfo = 1; break;
+ case 't': scan_ulong(optarg,&timeout); break;
+ case 'T': scan_ulong(optarg,&ssltimeout); break;
+ case 'w': scan_uint(optarg,&progtimeout); break;
+ case 'U': x = env_get("UID"); if (x) scan_ulong(x,&uid);
+ 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 'l': localhost = optarg; break;
+ case '1': flag1 = 1; break;
+ case '3': flag3 = 1; break;
+ case '4': ipflag = 1; break;
+ case '6': ipflag = 2; break;
+ case 'Z': flagclientcert = 0; break;
+ case 'z': flagclientcert = 1; break;
+ case 'm': flagclientcert = 2; break;
+ case 'S': flagsslenv = 0; break;
+ case 's': flagsslenv = 1; break;
+ case 'E': flagtcpenv = 0; break;
+ case 'e': flagtcpenv = 1; break;
+ case 'n': flagsslwait = 1; break;
+ case 'N': flagsslwait = 0; break;
+ default: usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!verbosity) buffer_2->fd = -1;
+
+ hostname = *argv++;
+ if (!hostname || (str_equal((char *)hostname,""))) usage();
+ if (str_equal((char *)hostname,"0")) hostname = thishost;
+ else if (str_equal((char *)hostname,":0")) {
+ flagdualstack = 1;
+ hostname = "::";
+ }
+
+ x = *argv++;
+ if (!x) usage();
+ prog = argv;
+ if (!*argv) usage();
+ if (!x[scan_ulong(x,&u)])
+ localport = u;
+ else {
+ se = getservbyname(x,"tcp");
+ if (!se)
+ logmsg(WHO,111,FATAL,B("unable to figure out port number for: ",x));
+ uint16_unpack_big((char *)&se->s_port,&localport);
+ }
+
+ if ((x = env_get("MAXCONIP"))) { scan_ulong(x,&u); maxconip = u; }
+ if (limit && maxconip > limit) maxconip = limit;
+ if (!child_readyplus(&children,limit)) drop_nomem();
+
+ if ((x = env_get("VERIFYDEPTH"))) { scan_ulong(x,&u); verifydepth = u; }
+
+ if ((x = env_get("CAFILE"))) cafile = x;
+ if (cafile && str_equal((char *)cafile,"")) cafile = 0;
+
+ if ((x = env_get("CCAFILE"))) ccafile = x;
+ if (ccafile && str_equal((char *)ccafile,"")) ccafile = 0;
+ if (ccafile && str_equal((char*)ccafile,"-")) flagclientcert = 0;
+ if (!flagclientcert) ccafile = 0;
+
+ if ((x = env_get("CADIR"))) cadir = x;
+ if (cadir && str_equal((char *)cadir,"")) cadir= 0;
+
+ if ((x = env_get("CERTCHAINFILE"))) certchainfile = x;
+ if (certchainfile && str_equal((char *)certchainfile,"")) certchainfile = 0;
+
+ if ((x = env_get("CERTFILE"))) certfile = x;
+ if (certfile && str_equal((char *)certfile,"")) certfile = 0;
+
+ if ((x = env_get("KEYFILE"))) keyfile = x;
+ if (keyfile && str_equal((char *)keyfile,"")) keyfile = 0;
+
+ if ((x = env_get("DHFILE"))) dhfile = x;
+ if (dhfile && str_equal((char *)dhfile,"")) dhfile = 0;
+
+ if ((x = env_get("CIPHERS"))) ciphers = x;
+ if (ciphers && str_equal((char *)ciphers,"")) ciphers = 0;
+
+ sig_block(sig_child);
+ sig_catch(sig_child,sigchld);
+ sig_catch(sig_term,sigterm);
+ sig_ignore(sig_pipe);
+
+ /* IP address only */
+
+ if (ip4_scan(hostname,localip)) {
+ if (!stralloc_copyb(&addresses,(char *)V4mappedprefix,12)) drop_nomem();
+ if (!stralloc_catb(&addresses,localip,4)) drop_nomem();
+ byte_copy(localip,16,addresses.s);
+ } else if (ip6_scan(hostname,localip)) {
+ if (!stralloc_copyb(&addresses,localip,16)) drop_nomem();
+ byte_copy(localip,16,addresses.s);
+ }
+
+ /* Asynchronous DNS IPv4/IPv6 Name qualification */
+
+ if (!addresses.len) {
+ if (!stralloc_copys(&tmp,hostname)) drop_nomem();
+ if (dns_ip_qualify(&addresses,&fqdn,&tmp) < 0)
+ logmsg(WHO,111,FATAL,B("temporarily unable to figure out IP address for: ",(char *)hostname));
+
+ byte_copy(localip,16,addresses.s);
+
+ for (j = 0; j < addresses.len; j += 16) { // Select best matching IP address
+ if (ipflag == 1 && !ip6_isv4mapped(addresses.s + j)) continue;
+ if (ipflag == 2 && !ip6_isv4mapped(addresses.s + j)) continue;
+ byte_copy(localip,16,addresses.s + j);
+ }
+
+ }
+ if (addresses.len < 16)
+ logmsg(WHO,111,FATAL,B("no IP address for: ",(char *)hostname));
+
+ if (ip6_isv4mapped(localip))
+ s = socket_tcp4();
+ else
+ s = socket_tcp6();
+ if (s == -1)
+ logmsg(WHO,111,FATAL,"unable to create socket");
+
+ if (ip6_isv4mapped(localip))
+ localipstr[ip4_fmt(localipstr,localip + 12)] = 0;
+ else
+ localipstr[ip6_fmt(localipstr,localip)] = 0;
+ localportstr[fmt_ulong(localportstr,localport)] = 0;
+
+ if (flagdualstack)
+ socket_dualstack(s);
+ if (socket_bind_reuse(s,localip,localport,netif) == -1)
+ logmsg(WHO,111,FATAL,B("unable to bind to: ",localipstr," port: ",localportstr));
+ if (socket_local(s,localip,&localport,&netif) == -1)
+ logmsg(WHO,111,FATAL,B("unable to set local address/port: ",localipstr,"/",localportstr));
+ if (socket_listen(s,backlog) == -1)
+ logmsg(WHO,111,FATAL,"unable to listen");
+ ndelay_off(s);
+
+ if (flag1) {
+ buffer_init(&bo,write,1,bspace,sizeof(bspace));
+ buffer_puts(&bo,localipstr);
+ buffer_puts(&bo," : ");
+ buffer_puts(&bo,localportstr);
+ buffer_puts(&bo,"\n");
+ buffer_flush(&bo);
+ }
+
+ if (flag3) read_passwd();
+
+ ctx = ssl_server();
+ ssl_errstr();
+ if (!ctx) logmsg(WHO,111,FATAL,"unable to create TLS context");
+
+ if (certchainfile) {
+ switch (ssl_chainfile(ctx,certchainfile,keyfile,passwd_cb)) {
+ case -1: logmsg(WHO,111,ERROR,"unable to load certificate chain file");
+ case -2: logmsg(WHO,111,ERROR,"unable to load key");
+ case -3: logmsg(WHO,111,ERROR,"key does not match certificate");
+ default: break;
+ }
+ } else {
+ switch (ssl_certkey(ctx,certfile,keyfile,passwd_cb)) {
+ case -1: logmsg(WHO,111,ERROR,"unable to load certificate");
+ case -2: logmsg(WHO,111,ERROR,"unable to load key");
+ case -3: logmsg(WHO,111,ERROR,"key does not match certificate");
+ default: break;
+ }
+ }
+
+ if (flagclientcert && !ssl_ca(ctx,cafile,cadir,verifydepth))
+ logmsg(WHO,111,ERROR,"unable to load CA list");
+ if (!ssl_cca(ctx,ccafile))
+ logmsg(WHO,111,ERROR,"unable to load client CA list");
+ if (!ssl_params_rsa(ctx,rsalen))
+ logmsg(WHO,111,ERROR,"unable to set RSA parameters");
+ if (!ssl_params_dh(ctx,dhfile))
+ logmsg(WHO,111,ERROR,"unable to set DH parameters");
+ if (!ssl_ciphers(ctx,ciphers))
+ logmsg(WHO,111,ERROR,"unable to set cipher list");
+
+ if (verbosity >= 2) {
+ strnum[fmt_ulong(strnum,getpid())] = 0;
+ strnum2[fmt_ulong(strnum2,rsalen)] = 0;
+ log_who(WHO,B("ciphers ",strnum," ",(char *)ciphers));
+ log_who(WHO,B("cafile ",strnum," ",(char *)cafile));
+ log_who(WHO,B("ccafile ",strnum," ",(char *)ccafile));
+ log_who(WHO,B("cadir ",strnum," ",(char *)cadir));
+ log_who(WHO,B("certchainfile ",strnum," ",(char *)certchainfile));
+ log_who(WHO,B("cert ",strnum," ",(char *)certfile));
+ log_who(WHO,B("key ",strnum," ",(char *)keyfile));
+ log_who(WHO,B("dhparam ",strnum," ",(char *)dhfile," ",strnum2));
+ }
+
+ close(0); open_read("/dev/null");
+ close(1); open_append("/dev/null");
+
+ printstatus();
+
+ for (;;) {
+ while (numchildren >= limit) sig_pause();
+ strnum[fmt_ulong(x,numchildren)] = 0;
+
+ sig_unblock(sig_child);
+ t = socket_accept(s,remoteip,&remoteport,&netif);
+ sig_block(sig_child);
+ if (t == -1) continue;
+
+ if (maxconip) {
+ ipchildren = ipchild_limit(remoteip,numchildren);
+ if (ipchildren >= maxconip) {
+ if (ip6_isv4mapped(remoteip))
+ remoteipstr[ip4_fmt(remoteipstr,remoteip + 12)] = 0;
+ else
+ remoteipstr[ip6_fmt(remoteipstr,remoteip)] = 0;
+
+ strnum[fmt_ulong(strnum,maxconip)] = 0;
+ logmsg(WHO,100,WARN,B("ip connection limit of ",strnum," exceeded for: ",remoteipstr));
+ close(t);
+ continue;
+ }
+ ipchild_append(remoteip,numchildren); // needs to happen in parent
+ }
+ ++numchildren;
+ printstatus();
+
+ switch (fork()) {
+ case 0:
+ close(s);
+ doit(t);
+ logmsg(WHO,111,FATAL,B("unable to run: ",*argv));
+ case -1:
+ if (maxconip) ipchild_clear(remoteip); --numchildren;
+ logmsg(WHO,111,FATAL,B("unable to fork: ",strnum));
+ }
+ close(t);
+ }
+}
diff --git a/src/trycpp.c b/src/trycpp.c
new file mode 100644
index 0000000..e4503d4
--- /dev/null
+++ b/src/trycpp.c
@@ -0,0 +1,9 @@
+/* Public domain. */
+
+int main()
+{
+#ifdef NeXT
+ printf("nextstep\n"); exit(0);
+#endif
+ printf("unknown\n"); exit(0);
+}
diff --git a/src/trylsock.c b/src/trylsock.c
new file mode 100644
index 0000000..c32bd40
--- /dev/null
+++ b/src/trylsock.c
@@ -0,0 +1,4 @@
+int main()
+{
+ ;
+}
diff --git a/src/trysgact.c b/src/trysgact.c
new file mode 100644
index 0000000..e264ef2
--- /dev/null
+++ b/src/trysgact.c
@@ -0,0 +1,12 @@
+/* Public domain. */
+
+#include <signal.h>
+
+main()
+{
+ struct sigaction sa;
+ sa.sa_handler = 0;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaction(0,&sa,(struct sigaction *) 0);
+}
diff --git a/src/trysgprm.c b/src/trysgprm.c
new file mode 100644
index 0000000..a46c82c
--- /dev/null
+++ b/src/trysgprm.c
@@ -0,0 +1,12 @@
+/* Public domain. */
+
+#include <signal.h>
+
+main()
+{
+ sigset_t ss;
+
+ sigemptyset(&ss);
+ sigaddset(&ss,SIGCHLD);
+ sigprocmask(SIG_SETMASK,&ss,(sigset_t *) 0);
+}
diff --git a/src/tryshsgr.c b/src/tryshsgr.c
new file mode 100644
index 0000000..c5ed6d6
--- /dev/null
+++ b/src/tryshsgr.c
@@ -0,0 +1,16 @@
+/* Public domain. */
+
+int main()
+{
+ short x[4];
+
+ x[0] = x[1] = 1;
+ if (getgroups(1,x) == 0) if (setgroups(1,x) == -1) _exit(1);
+
+ if (getgroups(1,x) == -1) _exit(1);
+ if (x[1] != 1) _exit(1);
+ x[1] = 2;
+ if (getgroups(1,x) == -1) _exit(1);
+ if (x[1] != 2) _exit(1);
+ _exit(0);
+}
diff --git a/src/tryssl.c b/src/tryssl.c
new file mode 100644
index 0000000..81dc4d1
--- /dev/null
+++ b/src/tryssl.c
@@ -0,0 +1,6 @@
+#include <openssl/ssl.h>
+
+int main()
+{
+ ;
+}
diff --git a/src/trysysel.c b/src/trysysel.c
new file mode 100644
index 0000000..5be862d
--- /dev/null
+++ b/src/trysysel.c
@@ -0,0 +1,11 @@
+/* Public domain. */
+
+#include <sys/types.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/select.h> /* SVR4 silliness */
+
+void foo()
+{
+ ;
+}
diff --git a/src/tryvfork.c b/src/tryvfork.c
new file mode 100644
index 0000000..cc39699
--- /dev/null
+++ b/src/tryvfork.c
@@ -0,0 +1,4 @@
+main()
+{
+ vfork();
+}
diff --git a/src/ucspissl.c b/src/ucspissl.c
new file mode 100644
index 0000000..3cbd8b4
--- /dev/null
+++ b/src/ucspissl.c
@@ -0,0 +1,4 @@
+#include "ucspissl.h"
+
+int ssl_errno = 0;
+
diff --git a/src/ucspissl.h b/src/ucspissl.h
new file mode 100644
index 0000000..5243689
--- /dev/null
+++ b/src/ucspissl.h
@@ -0,0 +1,70 @@
+/**
+ @file ucspissl.h
+ @author web, feh
+ @brief Header file to be used with sqmail; previously called ssl.h. (name clash)
+
+ All OpenSSL/LibreSSL header files are called from here
+*/
+#ifndef UCSPISSL_H
+#define UCSPISSL_H
+
+#include "openssl/asn1.h"
+#include "openssl/ec.h"
+#include "openssl/err.h"
+#include "openssl/evp.h"
+#include "openssl/ssl.h"
+#include "openssl/pem.h"
+#include "openssl/rsa.h"
+#include "openssl/opensslv.h"
+#include "openssl/safestack.h"
+#include "openssl/x509.h"
+#include "openssl/x509v3.h"
+#include "stralloc.h"
+
+#define SSLv2_DISABLE
+#define SSLv3_DISABLE
+// #define TLSv1_DISABLE
+// #define TLSv1_1_DISABLE
+// #define TLSv1_2_DISABLE
+// #define TLSv1_3_DISABLE
+
+#define SSL_NAME_LEN 256
+#define SSL_RSA_LEN 4096
+
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L) // 0xmnnffppsL
+#define ssl_client() (ssl_context(SSLv23_client_method()))
+#define ssl_server() (ssl_context(SSLv23_server_method()))
+#else
+#define ssl_client() (ssl_context(TLS_client_method()))
+#define ssl_server() (ssl_context(TLS_server_method()))
+#endif
+
+extern int ssl_errno;
+extern int ssl_io(SSL *,int,int,unsigned int);
+extern SSL_CTX *ssl_context(const SSL_METHOD *);
+extern int ssl_timeoutconn(SSL *,unsigned int);
+extern int ssl_timeoutaccept(SSL *,unsigned int);
+extern SSL *ssl_new(SSL_CTX *,int);
+extern int ssl_certkey(SSL_CTX *,const char *,const char *,pem_password_cb *);
+extern int ssl_chainfile(SSL_CTX *,const char *,const char *,pem_password_cb *);
+extern int ssl_ca(SSL_CTX *,const char *,const char *,int);
+extern int ssl_cca(SSL_CTX *,const char *);
+extern int ssl_ciphers(SSL_CTX *,const char *);
+extern int ssl_verify(SSL *,const char *,stralloc *);
+extern int ssl_params_rsa(SSL_CTX *,int);
+extern int ssl_params_dh(SSL_CTX *,const char *);
+extern int ssl_server_env(SSL *,stralloc *);
+extern int ssl_client_env(SSL *,stralloc *);
+extern int ssl_error(int (*)(const char *));
+extern char *ssl_error_str(int);
+
+#define ssl_errstr() (SSL_load_error_strings())
+#define ssl_free(ssl) (SSL_free((ssl)))
+#define ssl_close(ssl) (close(SSL_get_fd((ssl))))
+
+#define ssl_pending(ssl) (SSL_pending((ssl)))
+#define ssl_shutdown(ssl) (SSL_shutdown((ssl)))
+#define ssl_shutdown_pending(ssl) (SSL_get_shutdown((ssl)) & SSL_RECEIVED_SHUTDOWN)
+#define ssl_shutdown_sent(ssl) (SSL_get_shutdown((ssl)) & SSL_SENT_SHUTDOWN)
+
+#endif
diff --git a/src/warn-auto.sh b/src/warn-auto.sh
new file mode 100644
index 0000000..36d2313
--- /dev/null
+++ b/src/warn-auto.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+# WARNING: This file was auto-generated. Do not edit!
diff --git a/src/warn-shsgr b/src/warn-shsgr
new file mode 100644
index 0000000..37c351e
--- /dev/null
+++ b/src/warn-shsgr
@@ -0,0 +1,3 @@
+Oops. Your getgroups() returned 0, and setgroups() failed; this means
+that I can't reliably do my shsgr test. Please either ``make'' as root
+or ``make'' while you're in one or more supplementary groups.
diff --git a/src/x86cpuid.c b/src/x86cpuid.c
new file mode 100644
index 0000000..f81c593
--- /dev/null
+++ b/src/x86cpuid.c
@@ -0,0 +1,40 @@
+/* Public domain. */
+
+#include <signal.h>
+
+void nope()
+{
+ exit(1);
+}
+
+int main()
+{
+ unsigned long x[4];
+ unsigned long y[4];
+ int i;
+ int j;
+ char c;
+
+ signal(SIGILL,nope);
+
+ x[0] = 0;
+ x[1] = 0;
+ x[2] = 0;
+ x[3] = 0;
+
+ asm volatile(".byte 15;.byte 162" : "=a"(x[0]),"=b"(x[1]),"=c"(x[3]),"=d"(x[2]) : "0"(0) );
+ if (!x[0]) return 0;
+ asm volatile(".byte 15;.byte 162" : "=a"(y[0]),"=b"(y[1]),"=c"(y[2]),"=d"(y[3]) : "0"(1) );
+
+ for (i = 1;i < 4;++i)
+ for (j = 0;j < 4;++j) {
+ c = x[i] >> (8 * j);
+ if (c < 32) c = 32;
+ if (c > 126) c = 126;
+ putchar(c);
+ }
+
+ printf("-%08x-%08x\n",y[0],y[3]);
+
+ return 0;
+}