From f8dd3cde0ef83a30ba4308e02db8105ef82a54f2 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Thu, 25 Feb 2010 14:30:06 +0000 Subject: [PATCH] [packages] stunnel: add X-Forward-For support for proxying http traffic (#6753) SVN-Revision: 19859 --- net/stunnel/Makefile | 2 +- .../patches/106-stunnel-xforwardedfor.patch | 4927 +++++++++++++++++ 2 files changed, 4928 insertions(+), 1 deletion(-) create mode 100644 net/stunnel/patches/106-stunnel-xforwardedfor.patch diff --git a/net/stunnel/Makefile b/net/stunnel/Makefile index 1cfa7e8a8b..12b7f7ced3 100644 --- a/net/stunnel/Makefile +++ b/net/stunnel/Makefile @@ -3,7 +3,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=stunnel PKG_VERSION:=4.29 -PKG_RELEASE:=3 +PKG_RELEASE:=4 PKG_MD5SUM:=14dc3f8412947f0548975cbce74d6863 PKG_SOURCE_URL:=http://www.stunnel.org/download/stunnel/src/ diff --git a/net/stunnel/patches/106-stunnel-xforwardedfor.patch b/net/stunnel/patches/106-stunnel-xforwardedfor.patch new file mode 100644 index 0000000000..2c0f1076d2 --- /dev/null +++ b/net/stunnel/patches/106-stunnel-xforwardedfor.patch @@ -0,0 +1,4927 @@ +--- a/doc/stunnel.8 ++++ b/doc/stunnel.8 +@@ -497,7 +497,10 @@ time to keep an idle connection + .IP "\fBtransparent\fR = yes | no (Unix only)" 4 + .IX Item "transparent = yes | no (Unix only)" + transparent proxy mode +-.Sp ++.IP "\fBxforwardedfor\fR = yes | no" 4 ++.IX Item "xforwardedfor = yes | no" ++append an 'X-Forwarded-For:' HTTP request header providing the ++client's IP address to the server. + Re-write address to appear as if wrapped daemon is connecting + from the \s-1SSL\s0 client machine instead of the machine running \fBstunnel\fR. + .Sp +--- /dev/null ++++ b/doc/stunnel.8.orig +@@ -0,0 +1,729 @@ ++.\" Automatically generated by Pod::Man 2.1801 (Pod::Simple 3.05) ++.\" ++.\" Standard preamble: ++.\" ======================================================================== ++.de Sp \" Vertical space (when we can't use .PP) ++.if t .sp .5v ++.if n .sp ++.. ++.de Vb \" Begin verbatim text ++.ft CW ++.nf ++.ne \\$1 ++.. ++.de Ve \" End verbatim text ++.ft R ++.fi ++.. ++.\" Set up some character translations and predefined strings. \*(-- will ++.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left ++.\" double quote, and \*(R" will give a right double quote. \*(C+ will ++.\" give a nicer C++. Capital omega is used to do unbreakable dashes and ++.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, ++.\" nothing in troff, for use with C<>. ++.tr \(*W- ++.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' ++.ie n \{\ ++. ds -- \(*W- ++. ds PI pi ++. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch ++. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch ++. ds L" "" ++. ds R" "" ++. ds C` "" ++. ds C' "" ++'br\} ++.el\{\ ++. ds -- \|\(em\| ++. ds PI \(*p ++. ds L" `` ++. ds R" '' ++'br\} ++.\" ++.\" Escape single quotes in literal strings from groff's Unicode transform. ++.ie \n(.g .ds Aq \(aq ++.el .ds Aq ' ++.\" ++.\" If the F register is turned on, we'll generate index entries on stderr for ++.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index ++.\" entries marked with X<> in POD. Of course, you'll have to process the ++.\" output yourself in some meaningful fashion. ++.ie \nF \{\ ++. de IX ++. tm Index:\\$1\t\\n%\t"\\$2" ++.. ++. nr % 0 ++. rr F ++.\} ++.el \{\ ++. de IX ++.. ++.\} ++.\" ======================================================================== ++.\" ++.IX Title "STUNNEL 8" ++.TH STUNNEL 8 "2009.11.20" "4.29" "stunnel" ++.\" For nroff, turn off justification. Always turn off hyphenation; it makes ++.\" way too many mistakes in technical documents. ++.if n .ad l ++.nh ++.SH "NAME" ++stunnel \- universal SSL tunnel ++.SH "SYNOPSIS" ++.IX Header "SYNOPSIS" ++.IP "\fBUnix:\fR" 4 ++.IX Item "Unix:" ++\&\fBstunnel\fR [] | \-fd n | \-help | \-version | \-sockets ++.IP "\fB\s-1WIN32:\s0\fR" 4 ++.IX Item "WIN32:" ++\&\fBstunnel\fR [ [\-install | \-uninstall | \-start | \-stop] ++ [\-quiet] [] ] | \-help | \-version | \-sockets ++.SH "DESCRIPTION" ++.IX Header "DESCRIPTION" ++The \fBstunnel\fR program is designed to work as \fI\s-1SSL\s0\fR encryption wrapper ++between remote clients and local (\fIinetd\fR\-startable) or remote ++servers. The concept is that having non-SSL aware daemons running on ++your system you can easily set them up to communicate with clients over ++secure \s-1SSL\s0 channels. ++.PP ++\&\fBstunnel\fR can be used to add \s-1SSL\s0 functionality to commonly used \fIInetd\fR ++daemons like \s-1POP\-2\s0, \s-1POP\-3\s0, and \s-1IMAP\s0 servers, to standalone daemons like ++\&\s-1NNTP\s0, \s-1SMTP\s0 and \s-1HTTP\s0, and in tunneling \s-1PPP\s0 over network sockets without ++changes to the source code. ++.PP ++This product includes cryptographic software written by ++Eric Young (eay@cryptsoft.com) ++.SH "OPTIONS" ++.IX Header "OPTIONS" ++.IP "<\fBfilename\fR>" 4 ++.IX Item "" ++Use specified configuration file ++.IP "\fB\-fd n\fR (Unix only)" 4 ++.IX Item "-fd n (Unix only)" ++Read the config file from specified file descriptor ++.IP "\fB\-help\fR" 4 ++.IX Item "-help" ++Print \fBstunnel\fR help menu ++.IP "\fB\-version\fR" 4 ++.IX Item "-version" ++Print \fBstunnel\fR version and compile time defaults ++.IP "\fB\-sockets\fR" 4 ++.IX Item "-sockets" ++Print default socket options ++.IP "\fB\-install\fR (\s-1NT/2000/XP\s0 only)" 4 ++.IX Item "-install (NT/2000/XP only)" ++Install \s-1NT\s0 Service ++.IP "\fB\-uninstall\fR (\s-1NT/2000/XP\s0 only)" 4 ++.IX Item "-uninstall (NT/2000/XP only)" ++Uninstall \s-1NT\s0 Service ++.IP "\fB\-start\fR (\s-1NT/2000/XP\s0 only)" 4 ++.IX Item "-start (NT/2000/XP only)" ++Start \s-1NT\s0 Service ++.IP "\fB\-stop\fR (\s-1NT/2000/XP\s0 only)" 4 ++.IX Item "-stop (NT/2000/XP only)" ++Stop \s-1NT\s0 Service ++.IP "\fB\-quiet\fR (\s-1NT/2000/XP\s0 only)" 4 ++.IX Item "-quiet (NT/2000/XP only)" ++Don't display a message box when successfully installed or uninstalled \s-1NT\s0 service ++.SH "CONFIGURATION FILE" ++.IX Header "CONFIGURATION FILE" ++Each line of the configuration file can be either: ++.IP "\(bu" 4 ++an empty line (ignored) ++.IP "\(bu" 4 ++a comment starting with ';' (ignored) ++.IP "\(bu" 4 ++an 'option_name = option_value' pair ++.IP "\(bu" 4 ++\&'[service_name]' indicating a start of a service definition ++.SS "\s-1GLOBAL\s0 \s-1OPTIONS\s0" ++.IX Subsection "GLOBAL OPTIONS" ++.IP "\fBchroot\fR = directory (Unix only)" 4 ++.IX Item "chroot = directory (Unix only)" ++directory to chroot \fBstunnel\fR process ++.Sp ++\&\fBchroot\fR keeps \fBstunnel\fR in chrooted jail. \fICApath\fR, \fICRLpath\fR, \fIpid\fR ++and \fIexec\fR are located inside the jail and the patches have to be relative ++to the directory specified with \fBchroot\fR. ++.Sp ++To have libwrap (\s-1TCP\s0 Wrappers) control effective in a chrooted environment ++you also have to copy its configuration files (/etc/hosts.allow and ++/etc/hosts.deny) there. ++.IP "\fBcompression\fR = zlib | rle" 4 ++.IX Item "compression = zlib | rle" ++select data compression algorithm ++.Sp ++default: no compression ++.Sp ++zlib compression of OpenSSL 0.9.8 or above is not backward compatible with ++OpenSSL 0.9.7. ++.Sp ++rle compression is currently not implemented by the OpenSSL library. ++.IP "\fBdebug\fR = [facility.]level" 4 ++.IX Item "debug = [facility.]level" ++debugging level ++.Sp ++Level is a one of the syslog level names or numbers ++emerg (0), alert (1), crit (2), err (3), warning (4), notice (5), ++info (6), or debug (7). All logs for the specified level and ++all levels numerically less than it will be shown. Use \fBdebug = debug\fR or ++\&\fBdebug = 7\fR for greatest debugging output. The default is notice (5). ++.Sp ++The syslog facility 'daemon' will be used unless a facility name is supplied. ++(Facilities are not supported on Win32.) ++.Sp ++Case is ignored for both facilities and levels. ++.IP "\fB\s-1EGD\s0\fR = egd path (Unix only)" 4 ++.IX Item "EGD = egd path (Unix only)" ++path to Entropy Gathering Daemon socket ++.Sp ++Entropy Gathering Daemon socket to use to feed OpenSSL random number ++generator. (Available only if compiled with OpenSSL 0.9.5a or higher) ++.IP "\fBengine\fR = auto | " 4 ++.IX Item "engine = auto | " ++select hardware engine ++.Sp ++default: software-only cryptography ++.Sp ++There's an example in '\s-1EXAMPLES\s0' section. ++.IP "\fBengineCtrl\fR = command[:parameter]" 4 ++.IX Item "engineCtrl = command[:parameter]" ++control hardware engine ++.Sp ++Special commands \*(L"\s-1LOAD\s0\*(R" and \*(L"\s-1INIT\s0\*(R" can be used to load and initialize the ++engine cryptogaphic module. ++.IP "\fBfips\fR = yes | no" 4 ++.IX Item "fips = yes | no" ++Enable or disable \s-1FIPS\s0 140\-2 mode. ++.Sp ++This option allows to disable entering \s-1FIPS\s0 mode if stunnel was compiled with ++\&\s-1FIPS\s0 140\-2 support. ++.Sp ++default: yes ++.IP "\fBforeground\fR = yes | no (Unix only)" 4 ++.IX Item "foreground = yes | no (Unix only)" ++foreground mode ++.Sp ++Stay in foreground (don't fork) and log to stderr ++instead of via syslog (unless \fBoutput\fR is specified). ++.Sp ++default: background in daemon mode ++.IP "\fBoutput\fR = file" 4 ++.IX Item "output = file" ++append log messages to a file instead of using syslog ++.Sp ++/dev/stdout device can be used to redirect log messages to the standard ++output (for example to log them with daemontools splogger). ++.IP "\fBpid\fR = file (Unix only)" 4 ++.IX Item "pid = file (Unix only)" ++pid file location ++.Sp ++If the argument is empty, then no pid file will be created. ++.Sp ++\&\fIpid\fR path is relative to \fIchroot\fR directory if specified. ++.IP "\fBRNDbytes\fR = bytes" 4 ++.IX Item "RNDbytes = bytes" ++bytes to read from random seed files ++.Sp ++Number of bytes of data read from random seed files. With \s-1SSL\s0 versions ++less than 0.9.5a, also determines how many bytes of data are considered ++sufficient to seed the \s-1PRNG\s0. More recent OpenSSL versions have a builtin ++function to determine when sufficient randomness is available. ++.IP "\fBRNDfile\fR = file" 4 ++.IX Item "RNDfile = file" ++path to file with random seed data ++.Sp ++The \s-1SSL\s0 library will use data from this file first to seed the random ++number generator. ++.IP "\fBRNDoverwrite\fR = yes | no" 4 ++.IX Item "RNDoverwrite = yes | no" ++overwrite the random seed files with new random data ++.Sp ++default: yes ++.IP "\fBservice\fR = servicename" 4 ++.IX Item "service = servicename" ++use specified string as the service name ++.Sp ++\&\fBOn Unix:\fR \fIinetd\fR mode service name for \s-1TCP\s0 Wrapper library. ++.Sp ++\&\fBOn \s-1NT/2000/XP:\s0\fR \s-1NT\s0 service name in the Control Panel. ++.Sp ++default: stunnel ++.IP "\fBsetgid\fR = groupname (Unix only)" 4 ++.IX Item "setgid = groupname (Unix only)" ++\&\fIsetgid()\fR to groupname in daemon mode and clears all other groups ++.IP "\fBsetuid\fR = username (Unix only)" 4 ++.IX Item "setuid = username (Unix only)" ++\&\fIsetuid()\fR to username in daemon mode ++.IP "\fBsocket\fR = a|l|r:option=value[:value]" 4 ++.IX Item "socket = a|l|r:option=value[:value]" ++Set an option on accept/local/remote socket ++.Sp ++The values for linger option are l_onof:l_linger. ++The values for time are tv_sec:tv_usec. ++.Sp ++Examples: ++.Sp ++.Vb 11 ++\& socket = l:SO_LINGER=1:60 ++\& set one minute timeout for closing local socket ++\& socket = r:TCP_NODELAY=1 ++\& turn off the Nagle algorithm for remote sockets ++\& socket = r:SO_OOBINLINE=1 ++\& place out\-of\-band data directly into the ++\& receive data stream for remote sockets ++\& socket = a:SO_REUSEADDR=0 ++\& disable address reuse (enabled by default) ++\& socket = a:SO_BINDTODEVICE=lo ++\& only accept connections on loopback interface ++.Ve ++.IP "\fBsyslog\fR = yes | no (Unix only)" 4 ++.IX Item "syslog = yes | no (Unix only)" ++enable logging via syslog ++.Sp ++default: yes ++.IP "\fBtaskbar\fR = yes | no (\s-1WIN32\s0 only)" 4 ++.IX Item "taskbar = yes | no (WIN32 only)" ++enable the taskbar icon ++.Sp ++default: yes ++.SS "SERVICE-LEVEL \s-1OPTIONS\s0" ++.IX Subsection "SERVICE-LEVEL OPTIONS" ++Each configuration section begins with service name in square brackets. ++The service name is used for libwrap (\s-1TCP\s0 Wrappers) access control and lets ++you distinguish \fBstunnel\fR services in your log files. ++.PP ++Note that if you wish to run \fBstunnel\fR in \fIinetd\fR mode (where it ++is provided a network socket by a server such as \fIinetd\fR, \fIxinetd\fR, ++or \fItcpserver\fR) then you should read the section entitled \fI\s-1INETD\s0 \s-1MODE\s0\fR ++below. ++.IP "\fBaccept\fR = [host:]port" 4 ++.IX Item "accept = [host:]port" ++accept connections on specified host:port ++.Sp ++If no host specified, defaults to all \s-1IP\s0 addresses for the local host. ++.IP "\fBCApath\fR = directory" 4 ++.IX Item "CApath = directory" ++Certificate Authority directory ++.Sp ++This is the directory in which \fBstunnel\fR will look for certificates when using ++the \fIverify\fR. Note that the certificates in this directory should be named ++\&\s-1XXXXXXXX\s0.0 where \s-1XXXXXXXX\s0 is the hash value of the \s-1DER\s0 encoded subject of the ++cert (the first 4 bytes of the \s-1MD5\s0 hash in least significant byte order). ++.Sp ++\&\fICApath\fR path is relative to \fIchroot\fR directory if specified. ++.IP "\fBCAfile\fR = certfile" 4 ++.IX Item "CAfile = certfile" ++Certificate Authority file ++.Sp ++This file contains multiple \s-1CA\s0 certificates, used with the \fIverify\fR. ++.IP "\fBcert\fR = pemfile" 4 ++.IX Item "cert = pemfile" ++certificate chain \s-1PEM\s0 file name ++.Sp ++A \s-1PEM\s0 is always needed in server mode. ++Specifying this flag in client mode will use this certificate chain ++as a client side certificate chain. Using client side certs is optional. ++The certificates must be in \s-1PEM\s0 format and must be sorted starting with the ++certificate to the highest level (root \s-1CA\s0). ++.IP "\fBciphers\fR = cipherlist" 4 ++.IX Item "ciphers = cipherlist" ++Select permitted \s-1SSL\s0 ciphers ++.Sp ++A colon delimited list of the ciphers to allow in the \s-1SSL\s0 connection. ++For example \s-1DES\-CBC3\-SHA:IDEA\-CBC\-MD5\s0 ++.IP "\fBclient\fR = yes | no" 4 ++.IX Item "client = yes | no" ++client mode (remote service uses \s-1SSL\s0) ++.Sp ++default: no (server mode) ++.IP "\fBconnect\fR = [host:]port" 4 ++.IX Item "connect = [host:]port" ++connect to a remote host:port ++.Sp ++If no host is specified, the host defaults to localhost. ++.Sp ++Multiple \fBconnect\fR options are allowed in a single service section. ++.Sp ++If host resolves to multiple addresses and/or if multiple \fBconnect\fR ++options are specified, then the remote address is chosen using a ++round-robin algorithm. ++.IP "\fBCRLpath\fR = directory" 4 ++.IX Item "CRLpath = directory" ++Certificate Revocation Lists directory ++.Sp ++This is the directory in which \fBstunnel\fR will look for CRLs when ++using the \fIverify\fR. Note that the CRLs in this directory should ++be named \s-1XXXXXXXX\s0.0 where \s-1XXXXXXXX\s0 is the hash value of the \s-1CRL\s0. ++.Sp ++\&\fICRLpath\fR path is relative to \fIchroot\fR directory if specified. ++.IP "\fBCRLfile\fR = certfile" 4 ++.IX Item "CRLfile = certfile" ++Certificate Revocation Lists file ++.Sp ++This file contains multiple CRLs, used with the \fIverify\fR. ++.IP "\fBdelay\fR = yes | no" 4 ++.IX Item "delay = yes | no" ++delay \s-1DNS\s0 lookup for 'connect' option ++.IP "\fBengineNum\fR = engine number" 4 ++.IX Item "engineNum = engine number" ++select engine number to read private key ++.Sp ++The engines are numbered starting from 1. ++.IP "\fBexec\fR = executable_path (Unix only)" 4 ++.IX Item "exec = executable_path (Unix only)" ++execute local inetd-type program ++.Sp ++\&\fIexec\fR path is relative to \fIchroot\fR directory if specified. ++.ie n .IP "\fBexecargs\fR = $0 $1 $2 ... (Unix only)" 4 ++.el .IP "\fBexecargs\fR = \f(CW$0\fR \f(CW$1\fR \f(CW$2\fR ... (Unix only)" 4 ++.IX Item "execargs = $0 $1 $2 ... (Unix only)" ++arguments for \fIexec\fR including program name ($0) ++.Sp ++Quoting is currently not supported. ++Arguments are separated with arbitrary number of whitespaces. ++.IP "\fBfailover\fR = rr | prio" 4 ++.IX Item "failover = rr | prio" ++Failover strategy for multiple \*(L"connect\*(R" targets. ++.Sp ++.Vb 2 ++\& rr (round robin) \- fair load distribution ++\& prio (priority) \- use the order specified in config file ++.Ve ++.Sp ++default: rr ++.IP "\fBident\fR = username" 4 ++.IX Item "ident = username" ++use \s-1IDENT\s0 (\s-1RFC\s0 1413) username checking ++.IP "\fBkey\fR = keyfile" 4 ++.IX Item "key = keyfile" ++private key for certificate specified with \fIcert\fR option ++.Sp ++Private key is needed to authenticate certificate owner. ++Since this file should be kept secret it should only be readable ++to its owner. On Unix systems you can use the following command: ++.Sp ++.Vb 1 ++\& chmod 600 keyfile ++.Ve ++.Sp ++default: value of \fIcert\fR option ++.IP "\fBlocal\fR = host" 4 ++.IX Item "local = host" ++\&\s-1IP\s0 of the outgoing interface is used as source for remote connections. ++Use this option to bind a static local \s-1IP\s0 address, instead. ++.IP "\fB\s-1OCSP\s0\fR = url" 4 ++.IX Item "OCSP = url" ++select \s-1OCSP\s0 server for certificate verification ++.IP "\fBOCSPflag\fR = flag" 4 ++.IX Item "OCSPflag = flag" ++specify \s-1OCSP\s0 server flag ++.Sp ++Several \fIOCSPflag\fR can be used to specify multiple flags. ++.Sp ++currently supported flags: \s-1NOCERTS\s0, \s-1NOINTERN\s0 \s-1NOSIGS\s0, \s-1NOCHAIN\s0, \s-1NOVERIFY\s0, ++\&\s-1NOEXPLICIT\s0, \s-1NOCASIGN\s0, \s-1NODELEGATED\s0, \s-1NOCHECKS\s0, \s-1TRUSTOTHER\s0, \s-1RESPID_KEY\s0, \s-1NOTIME\s0 ++.IP "\fBoptions\fR = SSL_options" 4 ++.IX Item "options = SSL_options" ++OpenSSL library options ++.Sp ++The parameter is the OpenSSL option name as described in the ++\&\fI\fISSL_CTX_set_options\fI\|(3ssl)\fR manual, but without \fI\s-1SSL_OP_\s0\fR prefix. ++Several \fIoptions\fR can be used to specify multiple options. ++.Sp ++For example for compatibility with erroneous Eudora \s-1SSL\s0 implementation ++the following option can be used: ++.Sp ++.Vb 1 ++\& options = DONT_INSERT_EMPTY_FRAGMENTS ++.Ve ++.IP "\fBprotocol\fR = proto" 4 ++.IX Item "protocol = proto" ++application protocol to negotiate \s-1SSL\s0 ++.Sp ++currently supported: cifs, connect, imap, nntp, pop3, smtp, pgsql ++.IP "\fBprotocolAuthentication\fR = auth_type" 4 ++.IX Item "protocolAuthentication = auth_type" ++authentication type for protocol negotiations ++.Sp ++currently supported: basic, \s-1NTLM\s0 ++.Sp ++Currently authentication type only applies to 'connect' protocol. ++.Sp ++default: basic ++.IP "\fBprotocolHost\fR = host:port" 4 ++.IX Item "protocolHost = host:port" ++destination address for protocol negotiations ++.IP "\fBprotocolPassword\fR = password" 4 ++.IX Item "protocolPassword = password" ++password for protocol negotiations ++.IP "\fBprotocolUsername\fR = username" 4 ++.IX Item "protocolUsername = username" ++username for protocol negotiations ++.IP "\fBpty\fR = yes | no (Unix only)" 4 ++.IX Item "pty = yes | no (Unix only)" ++allocate pseudo terminal for 'exec' option ++.IP "\fBretry\fR = yes | no (Unix only)" 4 ++.IX Item "retry = yes | no (Unix only)" ++reconnect a connect+exec section after it's disconnected ++.Sp ++default: no ++.IP "\fBsession\fR = timeout" 4 ++.IX Item "session = timeout" ++session cache timeout ++.IP "\fBsessiond\fR = host:port" 4 ++.IX Item "sessiond = host:port" ++address of sessiond \s-1SSL\s0 cache server ++.IP "\fBsslVersion\fR = version" 4 ++.IX Item "sslVersion = version" ++select version of \s-1SSL\s0 protocol ++.Sp ++Allowed options: all, SSLv2, SSLv3, TLSv1 ++.IP "\fBstack\fR = bytes (except for \s-1FORK\s0 model)" 4 ++.IX Item "stack = bytes (except for FORK model)" ++thread stack size ++.IP "\fBTIMEOUTbusy\fR = seconds" 4 ++.IX Item "TIMEOUTbusy = seconds" ++time to wait for expected data ++.IP "\fBTIMEOUTclose\fR = seconds" 4 ++.IX Item "TIMEOUTclose = seconds" ++time to wait for close_notify (set to 0 for buggy \s-1MSIE\s0) ++.IP "\fBTIMEOUTconnect\fR = seconds" 4 ++.IX Item "TIMEOUTconnect = seconds" ++time to wait to connect a remote host ++.IP "\fBTIMEOUTidle\fR = seconds" 4 ++.IX Item "TIMEOUTidle = seconds" ++time to keep an idle connection ++.IP "\fBtransparent\fR = yes | no (Unix only)" 4 ++.IX Item "transparent = yes | no (Unix only)" ++transparent proxy mode ++.Sp ++Re-write address to appear as if wrapped daemon is connecting ++from the \s-1SSL\s0 client machine instead of the machine running \fBstunnel\fR. ++.Sp ++This option is currently available in: ++.Sp ++.Vb 3 ++\& remote mode (I option) on Linux >=2.6.28 ++\& remote mode (I option) 2.2.x ++\& local mode (I option) ++.Ve ++.Sp ++\&\fBRemote mode\fR (either 2.2.x and >=2.6.28) requires stunnel to be executed as ++root. \fBsetuid\fR option will also break this functionality. ++.Sp ++\&\fBLinux >=2.6.28\fR requires the following setup for iptables and routing ++(possibly in /etc/rc.local or equivalent file): ++.Sp ++.Vb 6 ++\& iptables \-t mangle \-N DIVERT ++\& iptables \-t mangle \-A PREROUTING \-p tcp \-m socket \-j DIVERT ++\& iptables \-t mangle \-A DIVERT \-j MARK \-\-set\-mark 1 ++\& iptables \-t mangle \-A DIVERT \-j ACCEPT ++\& ip rule add fwmark 1 lookup 100 ++\& ip route add local 0.0.0.0/0 dev lo table 100 ++.Ve ++.Sp ++\&\fBLinux 2.2.x\fR requires kernel to be compiled with \fItransparent proxy\fR option. ++Connected service must be installed on a separate host. ++Routing towards the clients has to go through the stunnel box. ++.Sp ++\&\fBLocal mode\fR works by LD_PRELOADing env.so shared library. ++.IP "\fBverify\fR = level" 4 ++.IX Item "verify = level" ++verify peer certificate ++.Sp ++.Vb 4 ++\& level 1 \- verify peer certificate if present ++\& level 2 \- verify peer certificate ++\& level 3 \- verify peer with locally installed certificate ++\& default \- no verify ++.Ve ++.Sp ++It is important to understand, that this option was solely designed for access ++control and not for authorization. Specifically for level 2 every non-revoked ++certificate is accepted regardless of its Common Name. For this reason a ++dedicated \s-1CA\s0 should be used with level 2, and not a generic \s-1CA\s0 commonly used ++for webservers. Level 3 is preferred for point-to-point connections. ++.SH "RETURN VALUE" ++.IX Header "RETURN VALUE" ++\&\fBstunnel\fR returns zero on success, non-zero on error. ++.SH "EXAMPLES" ++.IX Header "EXAMPLES" ++In order to provide \s-1SSL\s0 encapsulation to your local \fIimapd\fR service, use ++.PP ++.Vb 4 ++\& [imapd] ++\& accept = 993 ++\& exec = /usr/sbin/imapd ++\& execargs = imapd ++.Ve ++.PP ++If you want to provide tunneling to your \fIpppd\fR daemon on port 2020, ++use something like ++.PP ++.Vb 5 ++\& [vpn] ++\& accept = 2020 ++\& exec = /usr/sbin/pppd ++\& execargs = pppd local ++\& pty = yes ++.Ve ++.PP ++If you want to use \fBstunnel\fR in \fIinetd\fR mode to launch your imapd ++process, you'd use this \fIstunnel.conf\fR. ++Note there must be no \fI[service_name]\fR section. ++.PP ++.Vb 2 ++\& exec = /usr/sbin/imapd ++\& execargs = imapd ++.Ve ++.PP ++Here is an example of advanced engine configuration to read private key from an ++OpenSC engine ++.PP ++.Vb 7 ++\& engine=dynamic ++\& engineCtrl=SO_PATH:/usr/lib/opensc/engine_pkcs11.so ++\& engineCtrl=ID:pkcs11 ++\& engineCtrl=LIST_ADD:1 ++\& engineCtrl=LOAD ++\& engineCtrl=MODULE_PATH:/usr/lib/pkcs11/opensc\-pkcs11.so ++\& engineCtrl=INIT ++\& ++\& [service] ++\& engineNum=1 ++\& key=id_45 ++.Ve ++.SH "FILES" ++.IX Header "FILES" ++.IP "\fIstunnel.conf\fR" 4 ++.IX Item "stunnel.conf" ++\&\fBstunnel\fR configuration file ++.IP "\fIstunnel.pem\fR" 4 ++.IX Item "stunnel.pem" ++\&\fBstunnel\fR certificate and private key ++.SH "BUGS" ++.IX Header "BUGS" ++Option \fIexecargs\fR does not support quoting. ++.SH "RESTRICTIONS" ++.IX Header "RESTRICTIONS" ++\&\fBstunnel\fR cannot be used for the \s-1FTP\s0 daemon because of the nature ++of the \s-1FTP\s0 protocol which utilizes multiple ports for data transfers. ++There are available \s-1SSL\s0 enabled versions of \s-1FTP\s0 and telnet daemons, however. ++.SH "NOTES" ++.IX Header "NOTES" ++.SS "\s-1INETD\s0 \s-1MODE\s0" ++.IX Subsection "INETD MODE" ++The most common use of \fBstunnel\fR is to listen on a network ++port and establish communication with either a new port ++via the connect option, or a new program via the \fIexec\fR option. ++However there is a special case when you wish to have ++some other program accept incoming connections and ++launch \fBstunnel\fR, for example with \fIinetd\fR, \fIxinetd\fR, ++or \fItcpserver\fR. ++.PP ++For example, if you have the following line in \fIinetd.conf\fR: ++.PP ++.Vb 1 ++\& imaps stream tcp nowait root /usr/bin/stunnel stunnel /etc/stunnel/imaps.conf ++.Ve ++.PP ++In these cases, the \fIinetd\fR\-style program is responsible ++for binding a network socket (\fIimaps\fR above) and handing ++it to \fBstunnel\fR when a connection is received. ++Thus you do not want \fBstunnel\fR to have any \fIaccept\fR option. ++All the \fIService Level Options\fR should be placed in the ++global options section, and no \fI[service_name]\fR section ++will be present. See the \fI\s-1EXAMPLES\s0\fR section for example ++configurations. ++.SS "\s-1CERTIFICATES\s0" ++.IX Subsection "CERTIFICATES" ++Each \s-1SSL\s0 enabled daemon needs to present a valid X.509 certificate ++to the peer. It also needs a private key to decrypt the incoming ++data. The easiest way to obtain a certificate and a key is to ++generate them with the free \fIOpenSSL\fR package. You can find more ++information on certificates generation on pages listed below. ++.PP ++The order of contents of the \fI.pem\fR file is important. It should contain the ++unencrypted private key first, then a signed certificate (not certificate ++request). There should be also empty lines after certificate and private key. ++Plaintext certificate information appended on the top of generated certificate ++should be discarded. So the file should look like this: ++.PP ++.Vb 8 ++\& \-\-\-\-\-BEGIN RSA PRIVATE KEY\-\-\-\-\- ++\& [encoded key] ++\& \-\-\-\-\-END RSA PRIVATE KEY\-\-\-\-\- ++\& [empty line] ++\& \-\-\-\-\-BEGIN CERTIFICATE\-\-\-\-\- ++\& [encoded certificate] ++\& \-\-\-\-\-END CERTIFICATE\-\-\-\-\- ++\& [empty line] ++.Ve ++.SS "\s-1RANDOMNESS\s0" ++.IX Subsection "RANDOMNESS" ++\&\fBstunnel\fR needs to seed the \s-1PRNG\s0 (pseudo random number generator) in ++order for \s-1SSL\s0 to use good randomness. The following sources are loaded ++in order until sufficient random data has been gathered: ++.IP "\(bu" 4 ++The file specified with the \fIRNDfile\fR flag. ++.IP "\(bu" 4 ++The file specified by the \s-1RANDFILE\s0 environment variable, if set. ++.IP "\(bu" 4 ++The file .rnd in your home directory, if \s-1RANDFILE\s0 not set. ++.IP "\(bu" 4 ++The file specified with '\-\-with\-random' at compile time. ++.IP "\(bu" 4 ++The contents of the screen if running on Windows. ++.IP "\(bu" 4 ++The egd socket specified with the \fI\s-1EGD\s0\fR flag. ++.IP "\(bu" 4 ++The egd socket specified with '\-\-with\-egd\-sock' at compile time. ++.IP "\(bu" 4 ++The /dev/urandom device. ++.PP ++With recent (>=OpenSSL 0.9.5a) version of \s-1SSL\s0 it will stop loading ++random data automatically when sufficient entropy has been gathered. ++With previous versions it will continue to gather from all the above ++sources since no \s-1SSL\s0 function exists to tell when enough data is available. ++.PP ++Note that on Windows machines that do not have console user interaction ++(mouse movements, creating windows, etc) the screen contents are not ++variable enough to be sufficient, and you should provide a random file ++for use with the \fIRNDfile\fR flag. ++.PP ++Note that the file specified with the \fIRNDfile\fR flag should contain ++random data \*(-- that means it should contain different information ++each time \fBstunnel\fR is run. This is handled automatically ++unless the \fIRNDoverwrite\fR flag is used. If you wish to update this file ++manually, the \fIopenssl rand\fR command in recent versions of OpenSSL, ++would be useful. ++.PP ++One important note \*(-- if /dev/urandom is available, OpenSSL has a habit of ++seeding the \s-1PRNG\s0 with it even when checking the random state, so on ++systems with /dev/urandom you're likely to use it even though it's listed ++at the very bottom of the list above. This isn't \fBstunnel's\fR behaviour, it's ++OpenSSLs. ++.SH "SEE ALSO" ++.IX Header "SEE ALSO" ++.IP "\fItcpd\fR\|(8)" 4 ++.IX Item "tcpd" ++access control facility for internet services ++.IP "\fIinetd\fR\|(8)" 4 ++.IX Item "inetd" ++internet 'super\-server' ++.IP "\fIhttp://stunnel.mirt.net/\fR" 4 ++.IX Item "http://stunnel.mirt.net/" ++\&\fBstunnel\fR homepage ++.IP "\fIhttp://www.stunnel.org/\fR" 4 ++.IX Item "http://www.stunnel.org/" ++\&\fBstunnel\fR Frequently Asked Questions ++.IP "\fIhttp://www.openssl.org/\fR" 4 ++.IX Item "http://www.openssl.org/" ++OpenSSL project website ++.SH "AUTHOR" ++.IX Header "AUTHOR" ++.IP "Michal Trojnara" 4 ++.IX Item "Michal Trojnara" ++<\fIMichal.Trojnara@mirt.net\fR> +--- a/doc/stunnel.fr.8 ++++ b/doc/stunnel.fr.8 +@@ -445,6 +445,10 @@ Cette option permet de relier une adress + Négocie avec \s-1SSL\s0 selon le protocole indiqué + .Sp + Actuellement gérés\ : cifs, nntp, pop3, smtp ++.IP "\fBxforwardedfor\fR = yes | no" 4 ++.IX Item "xforwardedfor = yes | no" ++Ajoute un en-tête 'X-Forwarded-For:' dans la requête HTTP fournissant ++au serveur l'adresse IP du client. + .IP "\fBpty\fR = yes | no (Unix seulement)" 4 + .IX Item "pty = yes | no (Unix seulement)" + Alloue un pseudo-terminal pour l'option «\ exec\ » +--- a/src/client.c ++++ b/src/client.c +@@ -90,6 +90,12 @@ CLI *alloc_client_session(LOCAL_OPTIONS + return NULL; + } + c->opt=opt; ++ /* some options need space to add some information */ ++ if (c->opt->option.xforwardedfor) ++ c->buffsize = BUFFSIZE - BUFF_RESERVED; ++ else ++ c->buffsize = BUFFSIZE; ++ c->crlf_seen=0; + c->local_rfd.fd=rfd; + c->local_wfd.fd=wfd; + return c; +@@ -382,6 +388,29 @@ static void init_ssl(CLI *c) { + } + } + ++/* Moves all data from the buffer between positions and ++ * to insert of length . and are updated to their ++ * new respective values, and the number of characters inserted is returned. ++ * If is too long, nothing is done and -1 is returned. ++ * Note that neither nor can be NULL. ++*/ ++static int buffer_insert_with_len(char *buffer, int *start, int *stop, int limit, char *string, int len) { ++ if (len > limit - *stop) ++ return -1; ++ if (*start > *stop) ++ return -1; ++ memmove(buffer + *start + len, buffer + *start, *stop - *start); ++ memcpy(buffer + *start, string, len); ++ *start += len; ++ *stop += len; ++ return len; ++} ++ ++static int buffer_insert(char *buffer, int *start, int *stop, int limit, char *string) { ++ return buffer_insert_with_len(buffer, start, stop, limit, string, strlen(string)); ++} ++ ++ + /****************************** some defines for transfer() */ + /* is socket/SSL open for read/write? */ + #define sock_rd (c->sock_rfd->rd) +@@ -416,13 +445,16 @@ static void transfer(CLI *c) { + check_SSL_pending=0; + + SSL_read_wants_read= +- ssl_rd && c->ssl_ptrssl_ptrssl_ptrbuffsize && !SSL_read_wants_write; ++ + SSL_write_wants_write= + ssl_wr && c->sock_ptr && !SSL_write_wants_read; + + /****************************** setup c->fds structure */ + s_poll_init(&c->fds); /* initialize the structure */ +- if(sock_rd && c->sock_ptrsock_ptrsock_ptrbuffsize) + s_poll_add(&c->fds, c->sock_rfd->fd, 1, 0); + if(SSL_read_wants_read || + SSL_write_wants_read || +@@ -521,7 +553,8 @@ static void transfer(CLI *c) { + break; + default: + memmove(c->ssl_buff, c->ssl_buff+num, c->ssl_ptr-num); +- if(c->ssl_ptr==BUFFSIZE) /* buffer was previously full */ ++ //if(c->ssl_ptr==BUFFSIZE) /* buffer was previously full */ ++ if(c->ssl_ptr>=c->buffsize) /* buffer was previously full */ + check_SSL_pending=1; /* check for data buffered by SSL */ + c->ssl_ptr-=num; + c->sock_bytes+=num; +@@ -581,7 +614,8 @@ static void transfer(CLI *c) { + /****************************** read from socket */ + if(sock_rd && sock_can_rd) { + num=readsocket(c->sock_rfd->fd, +- c->sock_buff+c->sock_ptr, BUFFSIZE-c->sock_ptr); ++ //c->sock_buff+c->sock_ptr, BUFFSIZE-c->sock_ptr); ++ c->sock_buff+c->sock_ptr, c->buffsize-c->sock_ptr); + switch(num) { + case -1: + parse_socket_error(c, "readsocket"); +@@ -601,10 +635,73 @@ static void transfer(CLI *c) { + (SSL_read_wants_write && ssl_can_wr) || + (check_SSL_pending && SSL_pending(c->ssl))) { + SSL_read_wants_write=0; +- num=SSL_read(c->ssl, c->ssl_buff+c->ssl_ptr, BUFFSIZE-c->ssl_ptr); ++ //num=SSL_read(c->ssl, c->ssl_buff+c->ssl_ptr, BUFFSIZE-c->ssl_ptr); ++ num=SSL_read(c->ssl, c->ssl_buff+c->ssl_ptr, c->buffsize-c->ssl_ptr); + switch(err=SSL_get_error(c->ssl, num)) { + case SSL_ERROR_NONE: ++ //c->ssl_ptr+=num; ++ if (c->buffsize != BUFFSIZE && c->opt->option.xforwardedfor) { /* some work left to do */ ++ int last = c->ssl_ptr; ++ c->ssl_ptr += num; ++ ++ /* Look for end of HTTP headers between last and ssl_ptr. ++ * To achieve this reliably, we have to count the number of ++ * successive [CR]LF and to memorize it in case it's spread ++ * over multiple segments. --WT. ++ */ ++ while (last < c->ssl_ptr) { ++ if (c->ssl_buff[last] == '\n') { ++ if (++c->crlf_seen == 2) ++ break; ++ } else if (last < c->ssl_ptr - 1 && ++ c->ssl_buff[last] == '\r' && ++ c->ssl_buff[last+1] == '\n') { ++ if (++c->crlf_seen == 2) ++ break; ++ last++; ++ } else if (c->ssl_buff[last] != '\r') ++ /* don't refuse '\r' because we may get a '\n' on next read */ ++ c->crlf_seen = 0; ++ last++; ++ } ++ if (c->crlf_seen >= 2) { ++ /* We have all the HTTP headers now. We don't need to ++ * reserve any space anymore. points to the ++ * first byte of unread data, and points to the ++ * exact location where we want to insert our headers, ++ * which is right before the empty line. ++ */ ++ c->buffsize = BUFFSIZE; ++ ++ if (c->opt->option.xforwardedfor) { ++ /* X-Forwarded-For: xxxx \r\n\0 */ ++ char xforw[17 + IPLEN + 3]; ++ ++ /* We will insert our X-Forwarded-For: header here. ++ * We need to write the IP address, but if we use ++ * sprintf, it will pad with the terminating 0. ++ * So we will pass via a temporary buffer allocated ++ * on the stack. ++ */ ++ memcpy(xforw, "X-Forwarded-For: ", 17); ++ if (getnameinfo(&c->peer_addr.addr[0].sa, ++ addr_len(c->peer_addr.addr[0]), ++ xforw + 17, IPLEN, NULL, 0, ++ NI_NUMERICHOST) == 0) { ++ strcat(xforw + 17, "\r\n"); ++ buffer_insert(c->ssl_buff, &last, &c->ssl_ptr, ++ c->buffsize, xforw); ++ } ++ /* last still points to the \r\n and ssl_ptr to the ++ * end of the buffer, so we may add as many headers ++ * as wee need to. ++ */ ++ } ++ } ++ } ++ else + c->ssl_ptr+=num; ++ + watchdog=0; /* reset watchdog */ + break; + case SSL_ERROR_WANT_WRITE: +--- /dev/null ++++ b/src/client.c.orig +@@ -0,0 +1,1042 @@ ++/* ++ * stunnel Universal SSL tunnel ++ * Copyright (C) 1998-2009 Michal Trojnara ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * See the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, see . ++ * ++ * Linking stunnel statically or dynamically with other modules is making ++ * a combined work based on stunnel. Thus, the terms and conditions of ++ * the GNU General Public License cover the whole combination. ++ * ++ * In addition, as a special exception, the copyright holder of stunnel ++ * gives you permission to combine stunnel with free software programs or ++ * libraries that are released under the GNU LGPL and with code included ++ * in the standard release of OpenSSL under the OpenSSL License (or ++ * modified versions of such code, with unchanged license). You may copy ++ * and distribute such a system following the terms of the GNU GPL for ++ * stunnel and the licenses of the other code concerned. ++ * ++ * Note that people who make modified versions of stunnel are not obligated ++ * to grant this special exception for their modified versions; it is their ++ * choice whether to do so. The GNU General Public License gives permission ++ * to release a modified version without this exception; this exception ++ * also makes it possible to release a modified version which carries ++ * forward this exception. ++ */ ++ ++/* Undefine if you have problems with make_sockets() */ ++#define INET_SOCKET_PAIR ++ ++#include "common.h" ++#include "prototypes.h" ++ ++#ifndef SHUT_RD ++#define SHUT_RD 0 ++#endif ++#ifndef SHUT_WR ++#define SHUT_WR 1 ++#endif ++#ifndef SHUT_RDWR ++#define SHUT_RDWR 2 ++#endif ++ ++#if SSLEAY_VERSION_NUMBER >= 0x0922 ++static char *sid_ctx="stunnel SID"; ++ /* const allowed here */ ++#endif ++ ++static void do_client(CLI *); ++static void run_client(CLI *); ++static void init_local(CLI *); ++static void init_remote(CLI *); ++static void init_ssl(CLI *); ++static void transfer(CLI *); ++static void parse_socket_error(CLI *, const char *); ++ ++static void print_cipher(CLI *); ++static void auth_user(CLI *); ++static int connect_local(CLI *); ++#ifndef USE_WIN32 ++static void make_sockets(CLI *, int [2]); ++#endif ++static int connect_remote(CLI *); ++static void local_bind(CLI *c); ++static void print_bound_address(CLI *); ++static void reset(int, char *); ++ ++int max_clients; ++#ifndef USE_WIN32 ++int max_fds; ++#endif ++ ++/* Allocate local data structure for the new thread */ ++CLI *alloc_client_session(LOCAL_OPTIONS *opt, int rfd, int wfd) { ++ CLI *c; ++ ++ c=calloc(1, sizeof(CLI)); ++ if(!c) { ++ s_log(LOG_ERR, "Memory allocation failed"); ++ return NULL; ++ } ++ c->opt=opt; ++ c->local_rfd.fd=rfd; ++ c->local_wfd.fd=wfd; ++ return c; ++} ++ ++void *client(void *arg) { ++ CLI *c=arg; ++ ++#ifdef DEBUG_STACK_SIZE ++ stack_info(1); /* initialize */ ++#endif ++ s_log(LOG_DEBUG, "%s started", c->opt->servname); ++#ifndef USE_WIN32 ++ if(c->opt->option.remote && c->opt->option.program) { ++ /* connect and exec options specified together */ ++ /* -> spawn a local program instead of stdio */ ++ while((c->local_rfd.fd=c->local_wfd.fd=connect_local(c))>=0) { ++ run_client(c); ++ if(!c->opt->option.retry) ++ break; ++ sleep(1); /* FIXME: not a good idea in ucontext threading */ ++ } ++ } else ++#endif ++ { ++ if(alloc_fd(c->local_rfd.fd)) ++ return NULL; ++ if(c->local_wfd.fd!=c->local_rfd.fd) ++ if(alloc_fd(c->local_wfd.fd)) ++ return NULL; ++ run_client(c); ++ } ++ free(c); ++#ifdef DEBUG_STACK_SIZE ++ stack_info(0); /* display computed value */ ++#endif ++#if defined(USE_WIN32) && !defined(_WIN32_WCE) ++ _endthread(); ++#endif ++#ifdef USE_UCONTEXT ++ s_log(LOG_DEBUG, "Context %ld closed", ready_head->id); ++ s_poll_wait(NULL, 0, 0); /* wait on poll() */ ++ s_log(LOG_ERR, "INTERNAL ERROR: failed to drop context"); ++#endif ++ return NULL; ++} ++ ++static void run_client(CLI *c) { ++ int error; ++ ++ c->remote_fd.fd=-1; ++ c->fd=-1; ++ c->ssl=NULL; ++ c->sock_bytes=c->ssl_bytes=0; ++ ++ error=setjmp(c->err); ++ if(!error) ++ do_client(c); ++ ++ s_log(LOG_NOTICE, ++ "Connection %s: %d bytes sent to SSL, %d bytes sent to socket", ++ error==1 ? "reset" : "closed", c->ssl_bytes, c->sock_bytes); ++ ++ /* Cleanup IDENT socket */ ++ if(c->fd>=0) ++ closesocket(c->fd); ++ ++ /* Cleanup SSL */ ++ if(c->ssl) { /* SSL initialized */ ++ SSL_set_shutdown(c->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); ++ SSL_free(c->ssl); ++ ERR_remove_state(0); ++ } ++ ++ /* Cleanup remote socket */ ++ if(c->remote_fd.fd>=0) { /* Remote socket initialized */ ++ if(error==1 && c->remote_fd.is_socket) ++ reset(c->remote_fd.fd, "linger (remote)"); ++ closesocket(c->remote_fd.fd); ++ } ++ ++ /* Cleanup local socket */ ++ if(c->local_rfd.fd>=0) { /* Local socket initialized */ ++ if(c->local_rfd.fd==c->local_wfd.fd) { ++ if(error==1 && c->local_rfd.is_socket) ++ reset(c->local_rfd.fd, "linger (local)"); ++ closesocket(c->local_rfd.fd); ++ } else { /* STDIO */ ++ if(error==1 && c->local_rfd.is_socket) ++ reset(c->local_rfd.fd, "linger (local_rfd)"); ++ if(error==1 && c->local_wfd.is_socket) ++ reset(c->local_wfd.fd, "linger (local_wfd)"); ++ } ++ } ++#ifdef USE_FORK ++ if(!c->opt->option.remote) /* 'exec' specified */ ++ child_status(); /* null SIGCHLD handler was used */ ++#else ++ enter_critical_section(CRIT_CLIENTS); /* for multi-cpu machines */ ++ s_log(LOG_DEBUG, "%s finished (%d left)", c->opt->servname, ++ --num_clients); ++ leave_critical_section(CRIT_CLIENTS); ++#endif ++} ++ ++static void do_client(CLI *c) { ++ init_local(c); ++ if(!c->opt->option.client && !c->opt->protocol) { ++ /* Server mode and no protocol negotiation needed */ ++ init_ssl(c); ++ init_remote(c); ++ } else { ++ init_remote(c); ++ negotiate(c); ++ init_ssl(c); ++ } ++ transfer(c); ++} ++ ++static void init_local(CLI *c) { ++ SOCKADDR_UNION addr; ++ socklen_t addrlen; ++ ++ addrlen=sizeof addr; ++ if(getpeername(c->local_rfd.fd, &addr.sa, &addrlen)<0) { ++ strcpy(c->accepted_address, "NOT A SOCKET"); ++ c->local_rfd.is_socket=0; ++ c->local_wfd.is_socket=0; /* TODO: It's not always true */ ++#ifdef USE_WIN32 ++ if(get_last_socket_error()!=ENOTSOCK) { ++#else ++ if(c->opt->option.transparent || get_last_socket_error()!=ENOTSOCK) { ++#endif ++ sockerror("getpeerbyname"); ++ longjmp(c->err, 1); ++ } ++ /* Ignore ENOTSOCK error so 'local' doesn't have to be a socket */ ++ } else { /* success */ ++ /* copy addr to c->peer_addr */ ++ memcpy(&c->peer_addr.addr[0], &addr, sizeof addr); ++ c->peer_addr.num=1; ++ s_ntop(c->accepted_address, &c->peer_addr.addr[0]); ++ c->local_rfd.is_socket=1; ++ c->local_wfd.is_socket=1; /* TODO: It's not always true */ ++ /* It's a socket: lets setup options */ ++ if(set_socket_options(c->local_rfd.fd, 1)<0) ++ longjmp(c->err, 1); ++#ifdef USE_LIBWRAP ++ auth_libwrap(c); ++#endif /* USE_LIBWRAP */ ++ auth_user(c); ++ s_log(LOG_NOTICE, "%s accepted connection from %s", ++ c->opt->servname, c->accepted_address); ++ } ++} ++ ++static void init_remote(CLI *c) { ++ /* create connection to host/service */ ++ if(c->opt->source_addr.num) ++ memcpy(&c->bind_addr, &c->opt->source_addr, sizeof(SOCKADDR_LIST)); ++#ifndef USE_WIN32 ++ else if(c->opt->option.transparent) ++ memcpy(&c->bind_addr, &c->peer_addr, sizeof(SOCKADDR_LIST)); ++#endif ++ else { ++ c->bind_addr.num=0; /* don't bind connecting socket */ ++ } ++ ++ /* setup c->remote_fd, now */ ++ if(c->opt->option.remote) { ++ c->remote_fd.fd=connect_remote(c); ++ } else /* NOT in remote mode */ ++ c->remote_fd.fd=connect_local(c); ++ c->remote_fd.is_socket=1; /* Always! */ ++#ifndef USE_WIN32 ++ if(c->remote_fd.fd>=max_fds) { ++ s_log(LOG_ERR, "Remote file descriptor out of range (%d>=%d)", ++ c->remote_fd.fd, max_fds); ++ longjmp(c->err, 1); ++ } ++#endif ++ s_log(LOG_DEBUG, "Remote FD=%d initialized", c->remote_fd.fd); ++ if(set_socket_options(c->remote_fd.fd, 2)<0) ++ longjmp(c->err, 1); ++} ++ ++static void init_ssl(CLI *c) { ++ int i, err; ++ SSL_SESSION *old_session; ++ ++ if(!(c->ssl=SSL_new(c->opt->ctx))) { ++ sslerror("SSL_new"); ++ longjmp(c->err, 1); ++ } ++ SSL_set_ex_data(c->ssl, cli_index, c); /* for callbacks */ ++#if SSLEAY_VERSION_NUMBER >= 0x0922 ++ SSL_set_session_id_context(c->ssl, (unsigned char *)sid_ctx, ++ strlen(sid_ctx)); ++#endif ++ if(c->opt->option.client) { ++ if(c->opt->session) { ++ enter_critical_section(CRIT_SESSION); ++ SSL_set_session(c->ssl, c->opt->session); ++ leave_critical_section(CRIT_SESSION); ++ } ++ SSL_set_fd(c->ssl, c->remote_fd.fd); ++ SSL_set_connect_state(c->ssl); ++ } else { ++ if(c->local_rfd.fd==c->local_wfd.fd) ++ SSL_set_fd(c->ssl, c->local_rfd.fd); ++ else { ++ /* Does it make sence to have SSL on STDIN/STDOUT? */ ++ SSL_set_rfd(c->ssl, c->local_rfd.fd); ++ SSL_set_wfd(c->ssl, c->local_wfd.fd); ++ } ++ SSL_set_accept_state(c->ssl); ++ } ++ ++ /* Setup some values for transfer() function */ ++ if(c->opt->option.client) { ++ c->sock_rfd=&(c->local_rfd); ++ c->sock_wfd=&(c->local_wfd); ++ c->ssl_rfd=c->ssl_wfd=&(c->remote_fd); ++ } else { ++ c->sock_rfd=c->sock_wfd=&(c->remote_fd); ++ c->ssl_rfd=&(c->local_rfd); ++ c->ssl_wfd=&(c->local_wfd); ++ } ++ ++ while(1) { ++ /* crude workaround for random MT-safety problems in OpenSSL */ ++ /* performance penalty is not huge, as it's a non-blocking code */ ++ enter_critical_section(CRIT_SSL); ++ if(c->opt->option.client) ++ i=SSL_connect(c->ssl); ++ else ++ i=SSL_accept(c->ssl); ++ leave_critical_section(CRIT_SSL); ++ err=SSL_get_error(c->ssl, i); ++ if(err==SSL_ERROR_NONE) ++ break; /* ok -> done */ ++ if(err==SSL_ERROR_WANT_READ || err==SSL_ERROR_WANT_WRITE) { ++ s_poll_init(&c->fds); ++ s_poll_add(&c->fds, c->ssl_rfd->fd, ++ err==SSL_ERROR_WANT_READ, ++ err==SSL_ERROR_WANT_WRITE); ++ switch(s_poll_wait(&c->fds, c->opt->timeout_busy, 0)) { ++ case -1: ++ sockerror("init_ssl: s_poll_wait"); ++ longjmp(c->err, 1); ++ case 0: ++ s_log(LOG_INFO, "init_ssl: s_poll_wait timeout"); ++ longjmp(c->err, 1); ++ case 1: ++ break; /* OK */ ++ default: ++ s_log(LOG_ERR, "init_ssl: s_poll_wait unknown result"); ++ longjmp(c->err, 1); ++ } ++ continue; /* ok -> retry */ ++ } ++ if(err==SSL_ERROR_SYSCALL) { ++ switch(get_last_socket_error()) { ++ case EINTR: ++ case EAGAIN: ++ continue; ++ } ++ } ++ if(c->opt->option.client) ++ sslerror("SSL_connect"); ++ else ++ sslerror("SSL_accept"); ++ longjmp(c->err, 1); ++ } ++ if(SSL_session_reused(c->ssl)) { ++ s_log(LOG_INFO, "SSL %s: previous session reused", ++ c->opt->option.client ? "connected" : "accepted"); ++ } else { /* a new session was negotiated */ ++ if(c->opt->option.client) { ++ s_log(LOG_INFO, "SSL connected: new session negotiated"); ++ enter_critical_section(CRIT_SESSION); ++ old_session=c->opt->session; ++ c->opt->session=SSL_get1_session(c->ssl); /* store it */ ++ if(old_session) ++ SSL_SESSION_free(old_session); /* release the old one */ ++ leave_critical_section(CRIT_SESSION); ++ } else ++ s_log(LOG_INFO, "SSL accepted: new session negotiated"); ++ print_cipher(c); ++ } ++} ++ ++/****************************** some defines for transfer() */ ++/* is socket/SSL open for read/write? */ ++#define sock_rd (c->sock_rfd->rd) ++#define sock_wr (c->sock_wfd->wr) ++#define ssl_rd (c->ssl_rfd->rd) ++#define ssl_wr (c->ssl_wfd->wr) ++/* NOTE: above defines are related to the logical data stream, ++ * not the underlying file descriptors */ ++ ++/* is socket/SSL ready for read/write? */ ++#define sock_can_rd (s_poll_canread(&c->fds, c->sock_rfd->fd)) ++#define sock_can_wr (s_poll_canwrite(&c->fds, c->sock_wfd->fd)) ++#define ssl_can_rd (s_poll_canread(&c->fds, c->ssl_rfd->fd)) ++#define ssl_can_wr (s_poll_canwrite(&c->fds, c->ssl_wfd->fd)) ++ ++/****************************** transfer data */ ++static void transfer(CLI *c) { ++ int watchdog=0; /* a counter to detect an infinite loop */ ++ int error; ++ socklen_t optlen; ++ int num, err, check_SSL_pending; ++ int SSL_shutdown_wants_read=0, SSL_shutdown_wants_write=0; ++ int SSL_write_wants_read=0, SSL_write_wants_write=0; ++ int SSL_read_wants_read=0, SSL_read_wants_write=0; ++ ++ c->sock_ptr=c->ssl_ptr=0; ++ sock_rd=sock_wr=ssl_rd=ssl_wr=1; ++ ++ do { /* main loop */ ++ /* set flag to try and read any buffered SSL data ++ * if we made room in the buffer by writing to the socket */ ++ check_SSL_pending=0; ++ ++ SSL_read_wants_read= ++ ssl_rd && c->ssl_ptrsock_ptr && !SSL_write_wants_read; ++ ++ /****************************** setup c->fds structure */ ++ s_poll_init(&c->fds); /* initialize the structure */ ++ if(sock_rd && c->sock_ptrfds, c->sock_rfd->fd, 1, 0); ++ if(SSL_read_wants_read || ++ SSL_write_wants_read || ++ SSL_shutdown_wants_read) ++ s_poll_add(&c->fds, c->ssl_rfd->fd, 1, 0); ++ if(sock_wr && c->ssl_ptr) ++ s_poll_add(&c->fds, c->sock_wfd->fd, 0, 1); ++ if(SSL_read_wants_write || ++ SSL_write_wants_write || ++ SSL_shutdown_wants_write) ++ s_poll_add(&c->fds, c->ssl_wfd->fd, 0, 1); ++ ++ /****************************** wait for an event */ ++ err=s_poll_wait(&c->fds, (sock_rd && ssl_rd) /* both peers open */ || ++ c->ssl_ptr /* data buffered to write to socket */ || ++ c->sock_ptr /* data buffered to write to SSL */ ? ++ c->opt->timeout_idle : c->opt->timeout_close, 0); ++ switch(err) { ++ case -1: ++ sockerror("transfer: s_poll_wait"); ++ longjmp(c->err, 1); ++ case 0: /* timeout */ ++ if((sock_rd && ssl_rd) || c->ssl_ptr || c->sock_ptr) { ++ s_log(LOG_INFO, "s_poll_wait timeout: connection reset"); ++ longjmp(c->err, 1); ++ } else { /* already closing connection */ ++ s_log(LOG_INFO, "s_poll_wait timeout: connection close"); ++ return; /* OK */ ++ } ++ } ++ if(!(sock_can_rd || sock_can_wr || ssl_can_rd || ssl_can_wr)) { ++ s_log(LOG_ERR, "INTERNAL ERROR: " ++ "s_poll_wait returned %d, but no descriptor is ready", err); ++ longjmp(c->err, 1); ++ } ++ if(!sock_rd && sock_can_rd) { ++ optlen=sizeof error; ++ if(getsockopt(c->sock_rfd->fd, SOL_SOCKET, SO_ERROR, ++ (void *)&error, &optlen)) ++ error=get_last_socket_error(); /* failed -> ask why */ ++ if(error) { /* really an error? */ ++ s_log(LOG_ERR, "Closed socket ready to read: %s (%d)", ++ my_strerror(error), error); ++ longjmp(c->err, 1); ++ } ++ if(c->ssl_ptr) { /* anything left to write */ ++ s_log(LOG_ERR, "Closed socket ready to read - reset"); ++ longjmp(c->err, 1); ++ } ++ s_log(LOG_INFO, "Closed socket ready to read - write close"); ++ sock_wr=0; /* no further write allowed */ ++ shutdown(c->sock_wfd->fd, SHUT_WR); /* send TCP FIN */ ++ } ++ ++ /****************************** send SSL close_notify message */ ++ if(SSL_shutdown_wants_read || SSL_shutdown_wants_write) { ++ SSL_shutdown_wants_read=SSL_shutdown_wants_write=0; ++ num=SSL_shutdown(c->ssl); /* send close_notify */ ++ if(num<0) /* -1 - not completed */ ++ err=SSL_get_error(c->ssl, num); ++ else /* 0 or 1 - success */ ++ err=SSL_ERROR_NONE; ++ switch(err) { ++ case SSL_ERROR_NONE: /* the shutdown was successfully completed */ ++ s_log(LOG_INFO, "SSL_shutdown successfully sent close_notify"); ++ break; ++ case SSL_ERROR_WANT_WRITE: ++ s_log(LOG_DEBUG, "SSL_shutdown returned WANT_WRITE: retrying"); ++ SSL_shutdown_wants_write=1; ++ break; ++ case SSL_ERROR_WANT_READ: ++ s_log(LOG_DEBUG, "SSL_shutdown returned WANT_READ: retrying"); ++ SSL_shutdown_wants_read=1; ++ break; ++ case SSL_ERROR_SYSCALL: /* socket error */ ++ parse_socket_error(c, "SSL_shutdown"); ++ break; ++ case SSL_ERROR_SSL: /* SSL error */ ++ sslerror("SSL_shutdown"); ++ longjmp(c->err, 1); ++ default: ++ s_log(LOG_ERR, "SSL_shutdown/SSL_get_error returned %d", err); ++ longjmp(c->err, 1); ++ } ++ } ++ ++ /****************************** write to socket */ ++ if(sock_wr && sock_can_wr) { ++ num=writesocket(c->sock_wfd->fd, c->ssl_buff, c->ssl_ptr); ++ switch(num) { ++ case -1: /* error */ ++ parse_socket_error(c, "writesocket"); ++ break; ++ case 0: ++ s_log(LOG_DEBUG, "No data written to the socket: retrying"); ++ break; ++ default: ++ memmove(c->ssl_buff, c->ssl_buff+num, c->ssl_ptr-num); ++ if(c->ssl_ptr==BUFFSIZE) /* buffer was previously full */ ++ check_SSL_pending=1; /* check for data buffered by SSL */ ++ c->ssl_ptr-=num; ++ c->sock_bytes+=num; ++ watchdog=0; /* reset watchdog */ ++ } ++ } ++ ++ /****************************** write to SSL */ ++ if((SSL_write_wants_read && ssl_can_rd) || ++ (SSL_write_wants_write && ssl_can_wr)) { ++ SSL_write_wants_read=0; ++ num=SSL_write(c->ssl, c->sock_buff, c->sock_ptr); ++ switch(err=SSL_get_error(c->ssl, num)) { ++ case SSL_ERROR_NONE: ++ memmove(c->sock_buff, c->sock_buff+num, c->sock_ptr-num); ++ c->sock_ptr-=num; ++ c->ssl_bytes+=num; ++ watchdog=0; /* reset watchdog */ ++ break; ++ case SSL_ERROR_WANT_WRITE: /* nothing unexpected */ ++ break; ++ case SSL_ERROR_WANT_READ: ++ s_log(LOG_DEBUG, "SSL_write returned WANT_READ: retrying"); ++ SSL_write_wants_read=1; ++ break; ++ case SSL_ERROR_WANT_X509_LOOKUP: ++ s_log(LOG_DEBUG, ++ "SSL_write returned WANT_X509_LOOKUP: retrying"); ++ break; ++ case SSL_ERROR_SYSCALL: /* socket error */ ++ if(!num) { /* EOF */ ++ if(c->sock_ptr) { ++ s_log(LOG_ERR, ++ "SSL socket closed on SSL_write " ++ "with %d byte(s) in buffer", ++ c->sock_ptr); ++ longjmp(c->err, 1); /* reset the socket */ ++ } ++ s_log(LOG_DEBUG, "SSL socket closed on SSL_write"); ++ ssl_rd=ssl_wr=0; /* buggy or SSLv2 peer: no close_notify */ ++ } else ++ parse_socket_error(c, "SSL_write"); ++ break; ++ case SSL_ERROR_ZERO_RETURN: /* close_notify received */ ++ s_log(LOG_DEBUG, "SSL closed on SSL_write"); ++ ssl_rd=0; ++ break; ++ case SSL_ERROR_SSL: ++ sslerror("SSL_write"); ++ longjmp(c->err, 1); ++ default: ++ s_log(LOG_ERR, "SSL_write/SSL_get_error returned %d", err); ++ longjmp(c->err, 1); ++ } ++ } ++ ++ /****************************** read from socket */ ++ if(sock_rd && sock_can_rd) { ++ num=readsocket(c->sock_rfd->fd, ++ c->sock_buff+c->sock_ptr, BUFFSIZE-c->sock_ptr); ++ switch(num) { ++ case -1: ++ parse_socket_error(c, "readsocket"); ++ break; ++ case 0: /* close */ ++ s_log(LOG_DEBUG, "Socket closed on read"); ++ sock_rd=0; ++ break; ++ default: ++ c->sock_ptr+=num; ++ watchdog=0; /* reset watchdog */ ++ } ++ } ++ ++ /****************************** read from SSL */ ++ if((SSL_read_wants_read && ssl_can_rd) || ++ (SSL_read_wants_write && ssl_can_wr) || ++ (check_SSL_pending && SSL_pending(c->ssl))) { ++ SSL_read_wants_write=0; ++ num=SSL_read(c->ssl, c->ssl_buff+c->ssl_ptr, BUFFSIZE-c->ssl_ptr); ++ switch(err=SSL_get_error(c->ssl, num)) { ++ case SSL_ERROR_NONE: ++ c->ssl_ptr+=num; ++ watchdog=0; /* reset watchdog */ ++ break; ++ case SSL_ERROR_WANT_WRITE: ++ s_log(LOG_DEBUG, "SSL_read returned WANT_WRITE: retrying"); ++ SSL_read_wants_write=1; ++ break; ++ case SSL_ERROR_WANT_READ: /* nothing unexpected */ ++ break; ++ case SSL_ERROR_WANT_X509_LOOKUP: ++ s_log(LOG_DEBUG, ++ "SSL_read returned WANT_X509_LOOKUP: retrying"); ++ break; ++ case SSL_ERROR_SYSCALL: ++ if(!num) { /* EOF */ ++ if(c->sock_ptr) { ++ s_log(LOG_ERR, ++ "SSL socket closed on SSL_read " ++ "with %d byte(s) in buffer", ++ c->sock_ptr); ++ longjmp(c->err, 1); /* reset the socket */ ++ } ++ s_log(LOG_DEBUG, "SSL socket closed on SSL_read"); ++ ssl_rd=ssl_wr=0; /* buggy or SSLv2 peer: no close_notify */ ++ } else ++ parse_socket_error(c, "SSL_read"); ++ break; ++ case SSL_ERROR_ZERO_RETURN: /* close_notify received */ ++ s_log(LOG_DEBUG, "SSL closed on SSL_read"); ++ ssl_rd=0; ++ break; ++ case SSL_ERROR_SSL: ++ sslerror("SSL_read"); ++ longjmp(c->err, 1); ++ default: ++ s_log(LOG_ERR, "SSL_read/SSL_get_error returned %d", err); ++ longjmp(c->err, 1); ++ } ++ } ++ ++ /****************************** check write shutdown conditions */ ++ if(sock_wr && !ssl_rd && !c->ssl_ptr) { ++ s_log(LOG_DEBUG, "Socket write shutdown"); ++ sock_wr=0; /* no further write allowed */ ++ shutdown(c->sock_wfd->fd, SHUT_WR); /* send TCP FIN */ ++ } ++ if(ssl_wr && !sock_rd && !c->sock_ptr) { ++ s_log(LOG_DEBUG, "SSL write shutdown"); ++ ssl_wr=0; /* no further write allowed */ ++ if(strcmp(SSL_get_version(c->ssl), "SSLv2")) { /* SSLv3, TLSv1 */ ++ SSL_shutdown_wants_write=1; /* initiate close_notify */ ++ } else { /* no alerts in SSLv2 including close_notify alert */ ++ shutdown(c->sock_rfd->fd, SHUT_RD); /* notify the kernel */ ++ shutdown(c->sock_wfd->fd, SHUT_WR); /* send TCP FIN */ ++ SSL_set_shutdown(c->ssl, /* notify the OpenSSL library */ ++ SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); ++ ssl_rd=0; /* no further read allowed */ ++ } ++ } ++ ++ /****************************** check watchdog */ ++ if(++watchdog>100) { /* loop executes without transferring any data */ ++ s_log(LOG_ERR, ++ "transfer() loop executes not transferring any data"); ++ s_log(LOG_ERR, ++ "please report the problem to Michal.Trojnara@mirt.net"); ++ s_log(LOG_ERR, "socket open: rd=%s wr=%s, ssl open: rd=%s wr=%s", ++ sock_rd ? "yes" : "no", sock_wr ? "yes" : "no", ++ ssl_rd ? "yes" : "no", ssl_wr ? "yes" : "no"); ++ s_log(LOG_ERR, "socket ready: rd=%s wr=%s, ssl ready: rd=%s wr=%s", ++ sock_can_rd ? "yes" : "no", sock_can_wr ? "yes" : "no", ++ ssl_can_rd ? "yes" : "no", ssl_can_wr ? "yes" : "no"); ++ s_log(LOG_ERR, ++ "wants: SSL_read rd=%s wr=%s, " ++ "SSL_write rd=%s wr=%s, " ++ "SSL_shutdown rd=%s wr=%s", ++ SSL_read_wants_read ? "yes" : "no", ++ SSL_read_wants_write ? "yes" : "no", ++ SSL_write_wants_read ? "yes" : "no", ++ SSL_write_wants_write ? "yes" : "no", ++ SSL_shutdown_wants_read ? "yes" : "no", ++ SSL_shutdown_wants_write ? "yes" : "no"); ++ s_log(LOG_ERR, "socket input buffer: %d byte(s), " ++ "ssl input buffer: %d byte(s)", c->sock_ptr, c->ssl_ptr); ++ s_log(LOG_ERR, "check_SSL_pending=%d", check_SSL_pending); ++ longjmp(c->err, 1); ++ } ++ ++ } while(sock_wr || ssl_wr || ++ SSL_shutdown_wants_read || SSL_shutdown_wants_write); ++} ++ ++static void parse_socket_error(CLI *c, const char *text) { ++ switch(get_last_socket_error()) { ++ case EINTR: ++ s_log(LOG_DEBUG, "%s interrupted by a signal: retrying", text); ++ return; ++ case EWOULDBLOCK: ++ s_log(LOG_NOTICE, "%s would block: retrying", text); ++ sleep(1); /* Microsoft bug KB177346 */ ++ return; ++#if EAGAIN!=EWOULDBLOCK ++ case EAGAIN: ++ s_log(LOG_DEBUG, "%s temporary lack of resources: retrying", text); ++ return; ++#endif ++ default: ++ sockerror(text); ++ longjmp(c->err, 1); ++ } ++} ++ ++static void print_cipher(CLI *c) { /* print negotiated cipher */ ++#if SSLEAY_VERSION_NUMBER <= 0x0800 ++ s_log(LOG_INFO, "%s opened with SSLv%d, cipher %s", ++ c->opt->servname, ssl->session->ssl_version, SSL_get_cipher(c->ssl)); ++#else ++ SSL_CIPHER *cipher; ++ char buf[STRLEN], *i, *j; ++ ++ cipher=(SSL_CIPHER *)SSL_get_current_cipher(c->ssl); ++ SSL_CIPHER_description(cipher, buf, STRLEN); ++ i=j=buf; ++ do { ++ switch(*i) { ++ case ' ': ++ *j++=' '; ++ while(i[1]==' ') ++ ++i; ++ break; ++ case '\n': ++ break; ++ default: ++ *j++=*i; ++ } ++ } while(*i++); ++ s_log(LOG_INFO, "Negotiated ciphers: %s", buf); ++#endif ++} ++ ++static void auth_user(CLI *c) { ++#ifndef _WIN32_WCE ++ struct servent *s_ent; /* structure for getservbyname */ ++#endif ++ SOCKADDR_UNION ident; /* IDENT socket name */ ++ char name[STRLEN]; ++ ++ if(!c->opt->username) ++ return; /* -u option not specified */ ++ if((c->fd= ++ socket(c->peer_addr.addr[0].sa.sa_family, SOCK_STREAM, 0))<0) { ++ sockerror("socket (auth_user)"); ++ longjmp(c->err, 1); ++ } ++ if(alloc_fd(c->fd)) ++ longjmp(c->err, 1); ++ memcpy(&ident, &c->peer_addr.addr[0], sizeof ident); ++#ifndef _WIN32_WCE ++ s_ent=getservbyname("auth", "tcp"); ++ if(s_ent) { ++ ident.in.sin_port=s_ent->s_port; ++ } else ++#endif ++ { ++ s_log(LOG_WARNING, "Unknown service 'auth': using default 113"); ++ ident.in.sin_port=htons(113); ++ } ++ if(connect_blocking(c, &ident, addr_len(ident))) ++ longjmp(c->err, 1); ++ s_log(LOG_DEBUG, "IDENT server connected"); ++ fdprintf(c, c->fd, "%u , %u", ++ ntohs(c->peer_addr.addr[0].in.sin_port), ++ ntohs(c->opt->local_addr.addr[0].in.sin_port)); ++ if(fdscanf(c, c->fd, "%*[^:]: USERID :%*[^:]:%s", name)!=1) { ++ s_log(LOG_ERR, "Incorrect data from IDENT server"); ++ longjmp(c->err, 1); ++ } ++ closesocket(c->fd); ++ c->fd=-1; /* avoid double close on cleanup */ ++ if(strcmp(name, c->opt->username)) { ++ safestring(name); ++ s_log(LOG_WARNING, "Connection from %s REFUSED by IDENT (user %s)", ++ c->accepted_address, name); ++ longjmp(c->err, 1); ++ } ++ s_log(LOG_INFO, "IDENT authentication passed"); ++} ++ ++static int connect_local(CLI *c) { /* spawn local process */ ++#if defined (USE_WIN32) || defined (__vms) ++ s_log(LOG_ERR, "LOCAL MODE NOT SUPPORTED ON WIN32 and OpenVMS PLATFORM"); ++ longjmp(c->err, 1); ++ return -1; /* some C compilers require a return value */ ++#else /* USE_WIN32, __vms */ ++ char env[3][STRLEN], name[STRLEN], *portname; ++ int fd[2], pid; ++ X509 *peer; ++#ifdef HAVE_PTHREAD_SIGMASK ++ sigset_t newmask; ++#endif ++ ++ if (c->opt->option.pty) { ++ char tty[STRLEN]; ++ ++ if(pty_allocate(fd, fd+1, tty, STRLEN)) ++ longjmp(c->err, 1); ++ s_log(LOG_DEBUG, "%s allocated", tty); ++ } else ++ make_sockets(c, fd); ++ pid=fork(); ++ c->pid=(unsigned long)pid; ++ switch(pid) { ++ case -1: /* error */ ++ closesocket(fd[0]); ++ closesocket(fd[1]); ++ ioerror("fork"); ++ longjmp(c->err, 1); ++ case 0: /* child */ ++ closesocket(fd[0]); ++ dup2(fd[1], 0); ++ dup2(fd[1], 1); ++ if(!options.option.foreground) ++ dup2(fd[1], 2); ++ closesocket(fd[1]); ++ safecopy(env[0], "REMOTE_HOST="); ++ safeconcat(env[0], c->accepted_address); ++ portname=strrchr(env[0], ':'); ++ if(portname) /* strip the port name */ ++ *portname='\0'; ++ putenv(env[0]); ++ if(c->opt->option.transparent) { ++ putenv("LD_PRELOAD=" LIBDIR "/libstunnel.so"); ++ /* For Tru64 _RLD_LIST is used instead */ ++ putenv("_RLD_LIST=" LIBDIR "/libstunnel.so:DEFAULT"); ++ } ++ if(c->ssl) { ++ peer=SSL_get_peer_certificate(c->ssl); ++ if(peer) { ++ safecopy(env[1], "SSL_CLIENT_DN="); ++ X509_NAME_oneline(X509_get_subject_name(peer), name, STRLEN); ++ safestring(name); ++ safeconcat(env[1], name); ++ putenv(env[1]); ++ safecopy(env[2], "SSL_CLIENT_I_DN="); ++ X509_NAME_oneline(X509_get_issuer_name(peer), name, STRLEN); ++ safestring(name); ++ safeconcat(env[2], name); ++ putenv(env[2]); ++ X509_free(peer); ++ } ++ } ++#ifdef HAVE_PTHREAD_SIGMASK ++ sigemptyset(&newmask); ++ sigprocmask(SIG_SETMASK, &newmask, NULL); ++#endif ++ execvp(c->opt->execname, c->opt->execargs); ++ ioerror(c->opt->execname); /* execv failed */ ++ _exit(1); ++ default: ++ break; ++ } ++ /* parent */ ++ s_log(LOG_INFO, "Local mode child started (PID=%lu)", c->pid); ++ closesocket(fd[1]); ++#ifdef FD_CLOEXEC ++ fcntl(fd[0], F_SETFD, FD_CLOEXEC); ++#endif ++ return fd[0]; ++#endif /* USE_WIN32,__vms */ ++} ++ ++#ifndef USE_WIN32 ++ ++static void make_sockets(CLI *c, int fd[2]) { /* make a pair of connected sockets */ ++#ifdef INET_SOCKET_PAIR ++ SOCKADDR_UNION addr; ++ socklen_t addrlen; ++ int s; /* temporary socket awaiting for connection */ ++ ++ if((s=socket(AF_INET, SOCK_STREAM, 0))<0) { ++ sockerror("socket#1"); ++ longjmp(c->err, 1); ++ } ++ if((fd[1]=socket(AF_INET, SOCK_STREAM, 0))<0) { ++ sockerror("socket#2"); ++ longjmp(c->err, 1); ++ } ++ addrlen=sizeof addr; ++ memset(&addr, 0, addrlen); ++ addr.in.sin_family=AF_INET; ++ addr.in.sin_addr.s_addr=htonl(INADDR_LOOPBACK); ++ addr.in.sin_port=htons(0); /* dynamic port allocation */ ++ if(bind(s, &addr.sa, addrlen)) ++ log_error(LOG_DEBUG, get_last_socket_error(), "bind#1"); ++ if(bind(fd[1], &addr.sa, addrlen)) ++ log_error(LOG_DEBUG, get_last_socket_error(), "bind#2"); ++ if(listen(s, 5)) { ++ sockerror("listen"); ++ longjmp(c->err, 1); ++ } ++ if(getsockname(s, &addr.sa, &addrlen)) { ++ sockerror("getsockname"); ++ longjmp(c->err, 1); ++ } ++ if(connect(fd[1], &addr.sa, addrlen)) { ++ sockerror("connect"); ++ longjmp(c->err, 1); ++ } ++ if((fd[0]=accept(s, &addr.sa, &addrlen))<0) { ++ sockerror("accept"); ++ longjmp(c->err, 1); ++ } ++ closesocket(s); /* don't care about the result */ ++#else ++ if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) { ++ sockerror("socketpair"); ++ longjmp(c->err, 1); ++ } ++#endif ++} ++#endif ++ ++static int connect_remote(CLI *c) { /* connect to remote host */ ++ SOCKADDR_UNION addr; ++ SOCKADDR_LIST resolved_list, *address_list; ++ int fd, ind_try, ind_cur; ++ ++ /* setup address_list */ ++ if(c->opt->option.delayed_lookup) { ++ resolved_list.num=0; ++ if(!name2addrlist(&resolved_list, ++ c->opt->remote_address, DEFAULT_LOOPBACK)) { ++ s_log(LOG_ERR, "No host resolved"); ++ longjmp(c->err, 1); ++ } ++ address_list=&resolved_list; ++ } else /* use pre-resolved addresses */ ++ address_list=&c->opt->remote_addr; ++ ++ /* try to connect each host from the list */ ++ for(ind_try=0; ind_trynum; ind_try++) { ++ if(c->opt->failover==FAILOVER_RR) { ++ ind_cur=address_list->cur; ++ /* the race condition here can be safely ignored */ ++ address_list->cur=(ind_cur+1)%address_list->num; ++ } else { /* FAILOVER_PRIO */ ++ ind_cur=ind_try; /* ignore address_list->cur */ ++ } ++ memcpy(&addr, address_list->addr+ind_cur, sizeof addr); ++ ++ if((c->fd=socket(addr.sa.sa_family, SOCK_STREAM, 0))<0) { ++ sockerror("remote socket"); ++ longjmp(c->err, 1); ++ } ++ if(alloc_fd(c->fd)) ++ longjmp(c->err, 1); ++ ++ if(c->bind_addr.num) /* explicit local bind or transparent proxy */ ++ local_bind(c); ++ ++ if(connect_blocking(c, &addr, addr_len(addr))) { ++ closesocket(c->fd); ++ c->fd=-1; ++ continue; /* next IP */ ++ } ++ print_bound_address(c); ++ fd=c->fd; ++ c->fd=-1; ++ return fd; /* success! */ ++ } ++ longjmp(c->err, 1); ++ return -1; /* some C compilers require a return value */ ++} ++ ++static void local_bind(CLI *c) { ++ SOCKADDR_UNION addr; ++ ++#ifdef IP_TRANSPARENT ++ int on=1; ++ if(setsockopt(c->fd, SOL_IP, IP_TRANSPARENT, &on, sizeof on)) ++ sockerror("setsockopt IP_TRANSPARENT"); ++ /* ignore the error to retain Linux 2.2 compatibility */ ++ /* the error will be handled by bind(), anyway */ ++#endif /* IP_TRANSPARENT */ ++ ++ memcpy(&addr, &c->bind_addr.addr[0], sizeof addr); ++ if(ntohs(addr.in.sin_port)>=1024) { /* security check */ ++ if(!bind(c->fd, &addr.sa, addr_len(addr))) { ++ s_log(LOG_INFO, "local_bind succeeded on the original port"); ++ return; /* success */ ++ } ++ if(get_last_socket_error()!=EADDRINUSE ++#ifndef USE_WIN32 ++ || !c->opt->option.transparent ++#endif /* USE_WIN32 */ ++ ) { ++ sockerror("local_bind (original port)"); ++ longjmp(c->err, 1); ++ } ++ } ++ ++ addr.in.sin_port=htons(0); /* retry with ephemeral port */ ++ if(!bind(c->fd, &addr.sa, addr_len(addr))) { ++ s_log(LOG_INFO, "local_bind succeeded on an ephemeral port"); ++ return; /* success */ ++ } ++ sockerror("local_bind (ephemeral port)"); ++ longjmp(c->err, 1); ++} ++ ++static void print_bound_address(CLI *c) { ++ char txt[IPLEN]; ++ SOCKADDR_UNION addr; ++ socklen_t addrlen=sizeof addr; ++ ++ memset(&addr, 0, addrlen); ++ if(getsockname(c->fd, (struct sockaddr *)&addr, &addrlen)) { ++ sockerror("getsockname"); ++ } else { ++ s_ntop(txt, &addr); ++ s_log(LOG_NOTICE,"%s connected remote server from %s", ++ c->opt->servname, txt); ++ } ++} ++ ++static void reset(int fd, char *txt) { ++ /* Set lingering on a socket if needed*/ ++ struct linger l; ++ ++ l.l_onoff=1; ++ l.l_linger=0; ++ if(setsockopt(fd, SOL_SOCKET, SO_LINGER, (void *)&l, sizeof l)) ++ log_error(LOG_DEBUG, get_last_socket_error(), txt); ++} ++ ++/* End of client.c */ +--- a/src/common.h ++++ b/src/common.h +@@ -53,6 +53,9 @@ + /* I/O buffer size */ + #define BUFFSIZE 16384 + ++/* maximum space reserved for header insertion in BUFFSIZE */ ++#define BUFF_RESERVED 1024 ++ + /* Length of strings (including the terminating '\0' character) */ + /* It can't be lower than 256 bytes or NTLM authentication will break */ + #define STRLEN 256 +--- /dev/null ++++ b/src/common.h.orig +@@ -0,0 +1,429 @@ ++/* ++ * stunnel Universal SSL tunnel ++ * Copyright (C) 1998-2009 Michal Trojnara ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * See the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, see . ++ * ++ * Linking stunnel statically or dynamically with other modules is making ++ * a combined work based on stunnel. Thus, the terms and conditions of ++ * the GNU General Public License cover the whole combination. ++ * ++ * In addition, as a special exception, the copyright holder of stunnel ++ * gives you permission to combine stunnel with free software programs or ++ * libraries that are released under the GNU LGPL and with code included ++ * in the standard release of OpenSSL under the OpenSSL License (or ++ * modified versions of such code, with unchanged license). You may copy ++ * and distribute such a system following the terms of the GNU GPL for ++ * stunnel and the licenses of the other code concerned. ++ * ++ * Note that people who make modified versions of stunnel are not obligated ++ * to grant this special exception for their modified versions; it is their ++ * choice whether to do so. The GNU General Public License gives permission ++ * to release a modified version without this exception; this exception ++ * also makes it possible to release a modified version which carries ++ * forward this exception. ++ */ ++ ++#ifndef COMMON_H ++#define COMMON_H ++ ++#ifndef VERSION ++#define VERSION "unknown" ++#endif ++ ++/**************************************** Common constants */ ++ ++#define LIBWRAP_CLIENTS 5 ++ ++/* CPU stack size */ ++#define DEFAULT_STACK_SIZE 65536 ++/* #define DEBUG_STACK_SIZE */ ++ ++/* I/O buffer size */ ++#define BUFFSIZE 16384 ++ ++/* Length of strings (including the terminating '\0' character) */ ++/* It can't be lower than 256 bytes or NTLM authentication will break */ ++#define STRLEN 256 ++ ++/* IP address and TCP port textual representation length */ ++#define IPLEN 128 ++ ++/* How many bytes of random input to read from files for PRNG */ ++/* OpenSSL likes at least 128 bits, so 64 bytes seems plenty. */ ++#define RANDOM_BYTES 64 ++ ++/* For FormatGuard */ ++/* #define __NO_FORMATGUARD_ */ ++ ++/**************************************** Platform */ ++ ++#ifdef USE_WIN32 ++#define USE_IPv6 ++/* #define USE_FIPS */ ++#endif ++ ++#ifdef _WIN32_WCE ++#define USE_WIN32 ++typedef int socklen_t; ++#define EINTR WSAEINTR ++#define EMFILE WSAEMFILE ++#endif ++ ++#ifdef USE_WIN32 ++#define HAVE_OPENSSL ++#define HAVE_OSSL_ENGINE_H ++/* prevent including wincrypt.h, as it defines it's own OCSP_RESPONSE */ ++#define __WINCRYPT_H__ ++#endif ++ ++/**************************************** Generic headers */ ++ ++#ifdef __vms ++#include ++#endif /* __vms */ ++ ++/* For nsr-tandem-nsk architecture */ ++#ifdef __TANDEM ++#include ++#endif ++ ++/* threads model */ ++#ifdef USE_UCONTEXT ++#define __MAKECONTEXT_V2_SOURCE ++#include ++#endif ++ ++#ifdef USE_PTHREAD ++#define THREADS ++#define _REENTRANT ++#define _THREAD_SAFE ++#include ++#endif ++ ++/* TCP wrapper */ ++#if HAVE_TCPD_H && HAVE_LIBWRAP ++#define USE_LIBWRAP ++#endif ++ ++/* Must be included before sys/stat.h for Ultrix */ ++#include /* u_short, u_long */ ++/* General headers */ ++#include ++/* Must be included before sys/stat.h for Ultrix */ ++#ifndef _WIN32_WCE ++#include ++#endif ++#include ++#include /* va_ */ ++#include ++#include /* isalnum */ ++#include ++#include /* stat */ ++#include ++ ++/**************************************** WIN32 headers */ ++ ++#ifdef USE_WIN32 ++ ++#ifndef HOST ++#ifdef __MINGW32__ ++#define HOST "x86-pc-mingw32-gnu" ++#else ++#ifdef _MSC_VER ++#define _QUOTEME(x) #x ++#define QUOTEME(x) _QUOTEME(x) ++#define HOST "x86-pc-msvc-" ## QUOTEME(_MSC_VER) ++#else ++#define HOST "x86-pc-unknown" ++#endif ++#endif ++#endif ++ ++typedef unsigned char u8; ++typedef unsigned short u16; ++typedef unsigned long u32; ++ ++#define HAVE_SNPRINTF ++#define snprintf _snprintf ++#define HAVE_VSNPRINTF ++#define vsnprintf _vsnprintf ++#define strcasecmp _stricmp ++#define strncasecmp _strnicmp ++#define sleep(c) Sleep(1000*(c)) ++ ++#define get_last_socket_error() WSAGetLastError() ++#define get_last_error() GetLastError() ++#define readsocket(s,b,n) recv((s),(b),(n),0) ++#define writesocket(s,b,n) send((s),(b),(n),0) ++ ++/* #define FD_SETSIZE 4096 */ ++/* #define Win32_Winsock */ ++#define __USE_W32_SOCKETS ++ ++/* Winsock2 header for IPv6 definitions */ ++#ifdef _WIN32_WCE ++#include ++#else ++#include ++#include ++#endif ++#include ++ ++#define ECONNRESET WSAECONNRESET ++#define ENOTSOCK WSAENOTSOCK ++#define ENOPROTOOPT WSAENOPROTOOPT ++#define EINPROGRESS WSAEINPROGRESS ++#define EWOULDBLOCK WSAEWOULDBLOCK ++#define EISCONN WSAEISCONN ++#define EADDRINUSE WSAEADDRINUSE ++ ++#ifdef EINVAL ++#undef EINVAL ++#endif ++#define EINVAL WSAEINVAL ++ ++#include /* _beginthread */ ++#include ++ ++#define NO_IDEA ++#define OPENSSL_NO_IDEA ++ ++/**************************************** non-WIN32 headers */ ++ ++#else /* USE_WIN32 */ ++ ++#if SIZEOF_UNSIGNED_CHAR == 1 ++typedef unsigned char u8; ++#endif ++ ++#if SIZEOF_UNSIGNED_SHORT == 2 ++typedef unsigned short u16; ++#else ++typedef unsigned int u16; ++#endif ++ ++#if SIZEOF_UNSIGNED_INT == 4 ++typedef unsigned int u32; ++#else ++typedef unsigned long u32; ++#endif ++ ++#ifdef __INNOTEK_LIBC__ ++# define get_last_socket_error() sock_errno() ++# define get_last_error() errno ++# define readsocket(s,b,n) recv((s),(b),(n),0) ++# define writesocket(s,b,n) send((s),(b),(n),0) ++# define closesocket(s) close(s) ++# define ioctlsocket(a,b,c) so_ioctl((a),(b),(c)) ++#else ++#define get_last_socket_error() errno ++#define get_last_error() errno ++#define readsocket(s,b,n) read((s),(b),(n)) ++#define writesocket(s,b,n) write((s),(b),(n)) ++#define closesocket(s) close(s) ++#define ioctlsocket(a,b,c) ioctl((a),(b),(c)) ++#endif ++ /* OpenVMS compatibility */ ++#ifdef __vms ++#define libdir "__NA__" ++#define PIDFILE "SYS$LOGIN:STUNNEL.PID" ++#ifdef __alpha ++#define HOST "alpha-openvms" ++#else ++#define HOST "vax-openvms" ++#endif ++#include ++#include ++#else /* __vms */ ++#include ++#endif /* __vms */ ++ ++ /* Unix-specific headers */ ++#include /* signal */ ++#include /* wait */ ++#ifdef HAVE_SYS_RESOURCE_H ++#include /* getrlimit */ ++#endif ++#ifdef HAVE_UNISTD_H ++#include /* getpid, fork, execvp, exit */ ++#endif ++#ifdef HAVE_STROPTS_H ++#include ++#endif ++#ifdef HAVE_SYS_SELECT_H ++#include /* for aix */ ++#endif ++ ++#ifndef BROKEN_POLL ++#ifdef HAVE_POLL_H ++#include ++#define USE_POLL ++#else /* HAVE_POLL_H */ ++#ifdef HAVE_SYS_POLL_H ++#include ++#define USE_POLL ++#endif /* HAVE_SYS_POLL_H */ ++#endif /* HAVE_POLL_H */ ++#endif /* BROKEN_POLL */ ++ ++#ifdef HAVE_SYS_FILIO_H ++#include /* for FIONBIO */ ++#endif ++#include ++#ifdef HAVE_GRP_H ++#include ++#endif ++#ifdef __BEOS__ ++#include ++#endif ++#include ++ ++#include /* struct sockaddr_in */ ++#include /* getpeername */ ++#include /* inet_ntoa */ ++#include /* select */ ++#include /* ioctl */ ++#include ++#include ++#ifndef INADDR_ANY ++#define INADDR_ANY (u32)0x00000000 ++#endif ++#ifndef INADDR_LOOPBACK ++#define INADDR_LOOPBACK (u32)0x7F000001 ++#endif ++ ++#if defined(HAVE_WAITPID) ++/* For SYSV systems */ ++#define wait_for_pid(a, b, c) waitpid((a), (b), (c)) ++#define HAVE_WAIT_FOR_PID 1 ++#elif defined(HAVE_WAIT4) ++/* For BSD systems */ ++#define wait_for_pid(a, b, c) wait4((a), (b), (c), NULL) ++#define HAVE_WAIT_FOR_PID 1 ++#endif ++ ++/* SunOS 4 */ ++#if defined(sun) && !defined(__svr4__) && !defined(__SVR4) ++#define atexit(a) on_exit((a), NULL) ++extern int sys_nerr; ++extern char *sys_errlist[]; ++#define strerror(num) ((num)==0 ? "No error" : \ ++ ((num)>=sys_nerr ? "Unknown error" : sys_errlist[num])) ++#endif /* SunOS 4 */ ++ ++/* AIX does not have SOL_TCP defined */ ++#ifndef SOL_TCP ++#define SOL_TCP SOL_SOCKET ++#endif /* SOL_TCP */ ++ ++/* Linux */ ++#ifdef __linux__ ++#ifndef IP_TRANSPARENT ++/* old kernel headers without IP_TRANSPARENT definition */ ++#define IP_TRANSPARENT 19 ++#endif /* IP_TRANSPARENT */ ++#endif /* __linux__ */ ++ ++#endif /* USE_WIN32 */ ++ ++/**************************************** OpenSSL headers */ ++ ++#ifdef HAVE_OPENSSL ++ ++#define OPENSSL_THREAD_DEFINES ++#include ++#if !defined(OPENSSL_THREADS) && defined(USE_PTHREAD) ++#error OpenSSL library compiled without thread support ++#endif /* !OPENSSL_THREADS && USE_PTHREAD */ ++ ++#include ++#include ++#include ++#include /* for CRYPTO_* and SSLeay_version */ ++#include ++#include ++#include ++ ++#ifdef HAVE_OSSL_ENGINE_H ++#include ++#endif /* HAVE_OSSL_ENGINE_H */ ++ ++#if SSLEAY_VERSION_NUMBER >= 0x00907000L ++#include ++#endif /* OpenSSL-0.9.7 */ ++ ++#ifdef USE_FIPS ++#include ++#include ++#endif /* USE_FIPS */ ++ ++#else /* HAVE_OPENSSL */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#endif /* HAVE_OPENSSL */ ++ ++/**************************************** Other defines */ ++ ++/* Safe copy for strings declarated as char[STRLEN] */ ++#define safecopy(dst, src) \ ++ (dst[STRLEN-1]='\0', strncpy((dst), (src), STRLEN-1)) ++#define safeconcat(dst, src) \ ++ (dst[STRLEN-1]='\0', strncat((dst), (src), STRLEN-strlen(dst)-1)) ++/* change all non-printable characters to '.' */ ++#define safestring(s) \ ++ do {unsigned char *p; for(p=(unsigned char *)(s); *p; p++) \ ++ if(!isprint((int)*p)) *p='.';} while(0) ++/* change all unsafe characters to '.' */ ++#define safename(s) \ ++ do {unsigned char *p; for(p=(s); *p; p++) \ ++ if(!isalnum((int)*p)) *p='.';} while(0) ++ ++/* Some definitions for IPv6 support */ ++#if defined(USE_IPv6) ++#define addr_len(x) ((x).sa.sa_family==AF_INET ? \ ++ sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)) ++#else ++#define addr_len(x) (sizeof(struct sockaddr_in)) ++#endif ++ ++/* Always use IPv4 defaults! */ ++#define DEFAULT_LOOPBACK "127.0.0.1" ++#define DEFAULT_ANY "0.0.0.0" ++#if 0 ++#define DEFAULT_LOOPBACK "::1" ++#define DEFAULT_ANY "::" ++#endif ++ ++#if defined (USE_WIN32) || defined (__vms) ++#define LOG_EMERG 0 ++#define LOG_ALERT 1 ++#define LOG_CRIT 2 ++#define LOG_ERR 3 ++#define LOG_WARNING 4 ++#define LOG_NOTICE 5 ++#define LOG_INFO 6 ++#define LOG_DEBUG 7 ++#endif /* defined (USE_WIN32) || defined (__vms) */ ++#define LOG_RAW -1 ++ ++#endif /* defined COMMON_H */ ++ ++/* End of common.h */ +--- a/src/options.c ++++ b/src/options.c +@@ -781,6 +781,28 @@ static char *service_options(CMD cmd, LO + } + #endif + ++ /* xforwardedfor */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->option.xforwardedfor=0; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "xforwardedfor")) ++ break; ++ if(!strcasecmp(arg, "yes")) ++ section->option.xforwardedfor=1; ++ else if(!strcasecmp(arg, "no")) ++ section->option.xforwardedfor=0; ++ else ++ return "argument should be either 'yes' or 'no'"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log("%-15s = yes|no append an HTTP X-Forwarded-For header","xforwardedfor"); ++ break; ++ } ++ + /* exec */ + #ifndef USE_WIN32 + switch(cmd) { +--- /dev/null ++++ b/src/options.c.orig +@@ -0,0 +1,1994 @@ ++/* ++ * stunnel Universal SSL tunnel ++ * Copyright (C) 1998-2009 Michal Trojnara ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * See the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, see . ++ * ++ * Linking stunnel statically or dynamically with other modules is making ++ * a combined work based on stunnel. Thus, the terms and conditions of ++ * the GNU General Public License cover the whole combination. ++ * ++ * In addition, as a special exception, the copyright holder of stunnel ++ * gives you permission to combine stunnel with free software programs or ++ * libraries that are released under the GNU LGPL and with code included ++ * in the standard release of OpenSSL under the OpenSSL License (or ++ * modified versions of such code, with unchanged license). You may copy ++ * and distribute such a system following the terms of the GNU GPL for ++ * stunnel and the licenses of the other code concerned. ++ * ++ * Note that people who make modified versions of stunnel are not obligated ++ * to grant this special exception for their modified versions; it is their ++ * choice whether to do so. The GNU General Public License gives permission ++ * to release a modified version without this exception; this exception ++ * also makes it possible to release a modified version which carries ++ * forward this exception. ++ */ ++ ++#include "common.h" ++#include "prototypes.h" ++ ++#if defined(_WIN32_WCE) && !defined(CONFDIR) ++#define CONFDIR "\\stunnel" ++#endif ++ ++#ifdef USE_WIN32 ++#define CONFSEPARATOR "\\" ++#else ++#define CONFSEPARATOR "/" ++#endif ++ ++#define CONFLINELEN (16*1024) ++ ++static void section_validate(char *, int, LOCAL_OPTIONS *, int); ++static void config_error(char *, int, char *); ++static char *stralloc(char *); ++#ifndef USE_WIN32 ++static char **argalloc(char *); ++#endif ++ ++static int parse_debug_level(char *); ++static int parse_ssl_option(char *); ++static int print_socket_options(void); ++static void print_option(char *, int, OPT_UNION *); ++static int parse_socket_option(char *); ++static char *parse_ocsp_url(LOCAL_OPTIONS *, char *); ++static unsigned long parse_ocsp_flag(char *); ++ ++GLOBAL_OPTIONS options; ++LOCAL_OPTIONS local_options; ++ ++typedef enum { ++ CMD_INIT, /* initialize */ ++ CMD_EXEC, ++ CMD_DEFAULT, ++ CMD_HELP ++} CMD; ++ ++static char *option_not_found= ++ "Specified option name is not valid here"; ++ ++static char *global_options(CMD cmd, char *opt, char *arg) { ++ char *tmpstr; ++#ifndef USE_WIN32 ++ struct group *gr; ++ struct passwd *pw; ++#endif ++ ++ if(cmd==CMD_DEFAULT || cmd==CMD_HELP) { ++ s_log(LOG_RAW, "Global options"); ++ } ++ ++ /* chroot */ ++#ifdef HAVE_CHROOT ++ switch(cmd) { ++ case CMD_INIT: ++ options.chroot_dir=NULL; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "chroot")) ++ break; ++ options.chroot_dir=stralloc(arg); ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = directory to chroot stunnel process", "chroot"); ++ break; ++ } ++#endif /* HAVE_CHROOT */ ++ ++ /* compression */ ++ switch(cmd) { ++ case CMD_INIT: ++ options.compression=COMP_NONE; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "compression")) ++ break; ++ if(!strcasecmp(arg, "zlib")) ++ options.compression=COMP_ZLIB; ++ else if(!strcasecmp(arg, "rle")) ++ options.compression=COMP_RLE; ++ else ++ return "Compression type should be either 'zlib' or 'rle'"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = zlib|rle compression type", ++ "compression"); ++ break; ++ } ++ ++ /* debug */ ++ switch(cmd) { ++ case CMD_INIT: ++ options.debug_level=5; ++#if !defined (USE_WIN32) && !defined (__vms) ++ options.facility=LOG_DAEMON; ++#endif ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "debug")) ++ break; ++ if(!parse_debug_level(arg)) ++ return "Illegal debug argument"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ s_log(LOG_RAW, "%-15s = %d", "debug", options.debug_level); ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = [facility].level (e.g. daemon.info)", "debug"); ++ break; ++ } ++ ++ /* EGD is only supported when compiled with OpenSSL 0.9.5a or later */ ++#if SSLEAY_VERSION_NUMBER >= 0x0090581fL ++ switch(cmd) { ++ case CMD_INIT: ++ options.egd_sock=NULL; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "EGD")) ++ break; ++ options.egd_sock=stralloc(arg); ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++#ifdef EGD_SOCKET ++ s_log(LOG_RAW, "%-15s = %s", "EGD", EGD_SOCKET); ++#endif ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = path to Entropy Gathering Daemon socket", "EGD"); ++ break; ++ } ++#endif /* OpenSSL 0.9.5a */ ++ ++#ifdef HAVE_OSSL_ENGINE_H ++ /* engine */ ++ switch(cmd) { ++ case CMD_INIT: ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "engine")) ++ break; ++ open_engine(arg); ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = auto|engine_id", ++ "engine"); ++ break; ++ } ++ ++ /* engineCtrl */ ++ switch(cmd) { ++ case CMD_INIT: ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "engineCtrl")) ++ break; ++ tmpstr=strchr(arg, ':'); ++ if(tmpstr) ++ *tmpstr++='\0'; ++ ctrl_engine(arg, tmpstr); ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = cmd[:arg]", ++ "engineCtrl"); ++ break; ++ } ++#endif ++ ++ /* fips */ ++#ifdef USE_FIPS ++ switch(cmd) { ++ case CMD_INIT: ++ options.option.fips=1; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "fips")) ++ break; ++ if(!strcasecmp(arg, "yes")) ++ options.option.fips=1; ++ else if(!strcasecmp(arg, "no")) ++ options.option.fips=0; ++ else ++ return "Argument should be either 'yes' or 'no'"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = yes|no FIPS 140-2 mode", ++ "fips"); ++ break; ++ } ++#endif /* USE_FIPS */ ++ ++ /* foreground */ ++#ifndef USE_WIN32 ++ switch(cmd) { ++ case CMD_INIT: ++ options.option.foreground=0; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "foreground")) ++ break; ++ if(!strcasecmp(arg, "yes")) ++ options.option.foreground=1; ++ else if(!strcasecmp(arg, "no")) ++ options.option.foreground=0; ++ else ++ return "Argument should be either 'yes' or 'no'"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = yes|no foreground mode (don't fork, log to stderr)", ++ "foreground"); ++ break; ++ } ++#endif ++ ++ /* output */ ++ switch(cmd) { ++ case CMD_INIT: ++ options.output_file=NULL; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "output")) ++ break; ++ options.output_file=stralloc(arg); ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = file to append log messages", "output"); ++ break; ++ } ++ ++ /* pid */ ++#ifndef USE_WIN32 ++ switch(cmd) { ++ case CMD_INIT: ++ options.pidfile=PIDFILE; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "pid")) ++ break; ++ if(arg[0]) /* is argument not empty? */ ++ options.pidfile=stralloc(arg); ++ else ++ options.pidfile=NULL; /* empty -> do not create a pid file */ ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ s_log(LOG_RAW, "%-15s = %s", "pid", PIDFILE); ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = pid file (empty to disable creating)", "pid"); ++ break; ++ } ++#endif ++ ++ /* RNDbytes */ ++ switch(cmd) { ++ case CMD_INIT: ++ options.random_bytes=RANDOM_BYTES; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "RNDbytes")) ++ break; ++ options.random_bytes=atoi(arg); ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ s_log(LOG_RAW, "%-15s = %d", "RNDbytes", RANDOM_BYTES); ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = bytes to read from random seed files", "RNDbytes"); ++ break; ++ } ++ ++ /* RNDfile */ ++ switch(cmd) { ++ case CMD_INIT: ++ options.rand_file=NULL; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "RNDfile")) ++ break; ++ options.rand_file=stralloc(arg); ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++#ifdef RANDOM_FILE ++ s_log(LOG_RAW, "%-15s = %s", "RNDfile", RANDOM_FILE); ++#endif ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = path to file with random seed data", "RNDfile"); ++ break; ++ } ++ ++ /* RNDoverwrite */ ++ switch(cmd) { ++ case CMD_INIT: ++ options.option.rand_write=1; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "RNDoverwrite")) ++ break; ++ if(!strcasecmp(arg, "yes")) ++ options.option.rand_write=1; ++ else if(!strcasecmp(arg, "no")) ++ options.option.rand_write=0; ++ else ++ return "Argument should be either 'yes' or 'no'"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ s_log(LOG_RAW, "%-15s = yes", "RNDoverwrite"); ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = yes|no overwrite seed datafiles with new random data", ++ "RNDoverwrite"); ++ break; ++ } ++ ++ /* service */ ++ switch(cmd) { ++ case CMD_INIT: ++ local_options.servname=stralloc("stunnel"); ++#if defined(USE_WIN32) && !defined(_WIN32_WCE) ++ options.win32_service="stunnel"; ++#endif ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "service")) ++ break; ++ local_options.servname=stralloc(arg); ++#if defined(USE_WIN32) && !defined(_WIN32_WCE) ++ options.win32_service=stralloc(arg); ++#endif ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++#if defined(USE_WIN32) && !defined(_WIN32_WCE) ++ s_log(LOG_RAW, "%-15s = %s", "service", options.win32_service); ++#endif ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = service name", "service"); ++ break; ++ } ++ ++#ifndef USE_WIN32 ++ /* setgid */ ++ switch(cmd) { ++ case CMD_INIT: ++ options.gid=0; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "setgid")) ++ break; ++ gr=getgrnam(arg); ++ if(gr) ++ options.gid=gr->gr_gid; ++ else if(atoi(arg)) /* numerical? */ ++ options.gid=atoi(arg); ++ else ++ return "Illegal GID"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = groupname for setgid()", "setgid"); ++ break; ++ } ++#endif ++ ++#ifndef USE_WIN32 ++ /* setuid */ ++ switch(cmd) { ++ case CMD_INIT: ++ options.uid=0; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "setuid")) ++ break; ++ pw=getpwnam(arg); ++ if(pw) ++ options.uid=pw->pw_uid; ++ else if(atoi(arg)) /* numerical? */ ++ options.uid=atoi(arg); ++ else ++ return "Illegal UID"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = username for setuid()", "setuid"); ++ break; ++ } ++#endif ++ ++ /* socket */ ++ switch(cmd) { ++ case CMD_INIT: ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "socket")) ++ break; ++ if(!parse_socket_option(arg)) ++ return "Illegal socket option"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = a|l|r:option=value[:value]", "socket"); ++ s_log(LOG_RAW, "%18sset an option on accept/local/remote socket", ""); ++ break; ++ } ++ ++ /* syslog */ ++#ifndef USE_WIN32 ++ switch(cmd) { ++ case CMD_INIT: ++ options.option.syslog=1; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "syslog")) ++ break; ++ if(!strcasecmp(arg, "yes")) ++ options.option.syslog=1; ++ else if(!strcasecmp(arg, "no")) ++ options.option.syslog=0; ++ else ++ return "Argument should be either 'yes' or 'no'"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = yes|no send logging messages to syslog", ++ "syslog"); ++ break; ++ } ++#endif ++ ++ /* taskbar */ ++#ifdef USE_WIN32 ++ switch(cmd) { ++ case CMD_INIT: ++ options.option.taskbar=1; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "taskbar")) ++ break; ++ if(!strcasecmp(arg, "yes")) ++ options.option.taskbar=1; ++ else if(!strcasecmp(arg, "no")) ++ options.option.taskbar=0; ++ else ++ return "Argument should be either 'yes' or 'no'"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ s_log(LOG_RAW, "%-15s = yes", "taskbar"); ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = yes|no enable the taskbar icon", "taskbar"); ++ break; ++ } ++#endif ++ ++ if(cmd==CMD_EXEC) ++ return option_not_found; ++ return NULL; /* OK */ ++} ++ ++static char *service_options(CMD cmd, LOCAL_OPTIONS *section, ++ char *opt, char *arg) { ++ int tmpnum; ++ ++ if(cmd==CMD_DEFAULT || cmd==CMD_HELP) { ++ s_log(LOG_RAW, " "); ++ s_log(LOG_RAW, "Service-level options"); ++ } ++ ++ /* accept */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->option.accept=0; ++ memset(§ion->local_addr, 0, sizeof(SOCKADDR_LIST)); ++ section->local_addr.addr[0].in.sin_family=AF_INET; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "accept")) ++ break; ++ section->option.accept=1; ++ if(!name2addrlist(§ion->local_addr, arg, DEFAULT_ANY)) ++ return "Failed to resolve accepting address"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = [host:]port accept connections on specified host:port", ++ "accept"); ++ break; ++ } ++ ++ /* CApath */ ++ switch(cmd) { ++ case CMD_INIT: ++#if 0 ++ section->ca_dir=(char *)X509_get_default_cert_dir(); ++#endif ++ section->ca_dir=NULL; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "CApath")) ++ break; ++ if(arg[0]) /* not empty */ ++ section->ca_dir=stralloc(arg); ++ else ++ section->ca_dir=NULL; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++#if 0 ++ s_log(LOG_RAW, "%-15s = %s", "CApath", ++ section->ca_dir ? section->ca_dir : "(none)"); ++#endif ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = CA certificate directory for 'verify' option", ++ "CApath"); ++ break; ++ } ++ ++ /* CAfile */ ++ switch(cmd) { ++ case CMD_INIT: ++#if 0 ++ section->ca_file=(char *)X509_get_default_certfile(); ++#endif ++ section->ca_file=NULL; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "CAfile")) ++ break; ++ if(arg[0]) /* not empty */ ++ section->ca_file=stralloc(arg); ++ else ++ section->ca_file=NULL; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++#if 0 ++ s_log(LOG_RAW, "%-15s = %s", "CAfile", ++ section->ca_file ? section->ca_file : "(none)"); ++#endif ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = CA certificate file for 'verify' option", ++ "CAfile"); ++ break; ++ } ++ ++ /* cert */ ++ switch(cmd) { ++ case CMD_INIT: ++#ifdef CONFDIR ++ section->cert=CONFDIR CONFSEPARATOR "stunnel.pem"; ++#else ++ section->cert="stunnel.pem"; ++#endif ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "cert")) ++ break; ++ section->cert=stralloc(arg); ++ section->option.cert=1; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ s_log(LOG_RAW, "%-15s = %s", "cert", section->cert); ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = certificate chain", "cert"); ++ break; ++ } ++ ++ /* ciphers */ ++#ifdef USE_FIPS ++#define STUNNEL_DEFAULT_CIPHER_LIST "FIPS" ++#else ++#define STUNNEL_DEFAULT_CIPHER_LIST SSL_DEFAULT_CIPHER_LIST ++#endif /* USE_FIPS */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->cipher_list=STUNNEL_DEFAULT_CIPHER_LIST; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "ciphers")) ++ break; ++ section->cipher_list=stralloc(arg); ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ s_log(LOG_RAW, "%-15s = %s", "ciphers", STUNNEL_DEFAULT_CIPHER_LIST); ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = list of permitted SSL ciphers", "ciphers"); ++ break; ++ } ++ ++ /* client */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->option.client=0; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "client")) ++ break; ++ if(!strcasecmp(arg, "yes")) ++ section->option.client=1; ++ else if(!strcasecmp(arg, "no")) ++ section->option.client=0; ++ else ++ return "Argument should be either 'yes' or 'no'"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = yes|no client mode (remote service uses SSL)", ++ "client"); ++ break; ++ } ++ ++ /* connect */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->option.remote=0; ++ section->remote_address=NULL; ++ section->remote_addr.num=0; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "connect")) ++ break; ++ section->option.remote=1; ++ section->remote_address=stralloc(arg); ++ if(!section->option.delayed_lookup && ++ !name2addrlist(§ion->remote_addr, arg, DEFAULT_LOOPBACK)) { ++ s_log(LOG_RAW, "Cannot resolve '%s' - delaying DNS lookup", arg); ++ section->option.delayed_lookup=1; ++ } ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = [host:]port connect remote host:port", ++ "connect"); ++ break; ++ } ++ ++ /* CRLpath */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->crl_dir=NULL; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "CRLpath")) ++ break; ++ if(arg[0]) /* not empty */ ++ section->crl_dir=stralloc(arg); ++ else ++ section->crl_dir=NULL; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = CRL directory", "CRLpath"); ++ break; ++ } ++ ++ /* CRLfile */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->crl_file=NULL; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "CRLfile")) ++ break; ++ if(arg[0]) /* not empty */ ++ section->crl_file=stralloc(arg); ++ else ++ section->crl_file=NULL; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = CRL file", "CRLfile"); ++ break; ++ } ++ ++ /* delay */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->option.delayed_lookup=0; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "delay")) ++ break; ++ if(!strcasecmp(arg, "yes")) ++ section->option.delayed_lookup=1; ++ else if(!strcasecmp(arg, "no")) ++ section->option.delayed_lookup=0; ++ else ++ return "Argument should be either 'yes' or 'no'"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = yes|no delay DNS lookup for 'connect' option", ++ "delay"); ++ break; ++ } ++ ++#ifdef HAVE_OSSL_ENGINE_H ++ /* engineNum */ ++ switch(cmd) { ++ case CMD_INIT: ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "engineNum")) ++ break; ++ section->engine=get_engine(atoi(arg)); ++ if(!section->engine) ++ return "Illegal engine number"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = number of engine to read the key from", ++ "engineNum"); ++ break; ++ } ++#endif ++ ++ /* exec */ ++#ifndef USE_WIN32 ++ switch(cmd) { ++ case CMD_INIT: ++ section->option.program=0; ++ section->execname=NULL; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "exec")) ++ break; ++ section->option.program=1; ++ section->execname=stralloc(arg); ++ if(!section->execargs) { ++ section->execargs=calloc(2, sizeof(char *)); ++ section->execargs[0]=section->execname; ++ section->execargs[1]=NULL; /* to show that it's null-terminated */ ++ } ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = file execute local inetd-type program", ++ "exec"); ++ break; ++ } ++#endif ++ ++ /* execargs */ ++#ifndef USE_WIN32 ++ switch(cmd) { ++ case CMD_INIT: ++ section->execargs=NULL; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "execargs")) ++ break; ++ section->execargs=argalloc(arg); ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = arguments for 'exec' (including $0)", ++ "execargs"); ++ break; ++ } ++#endif ++ ++ /* failover */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->failover=FAILOVER_RR; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "failover")) ++ break; ++ if(!strcasecmp(arg, "rr")) ++ section->failover=FAILOVER_RR; ++ else if(!strcasecmp(arg, "prio")) ++ section->failover=FAILOVER_PRIO; ++ else ++ return "Argument should be either 'rr' or 'prio'"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = rr|prio chose failover strategy", ++ "failover"); ++ break; ++ } ++ ++ /* ident */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->username=NULL; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "ident")) ++ break; ++ section->username=stralloc(arg); ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = username for IDENT (RFC 1413) checking", "ident"); ++ break; ++ } ++ ++ /* key */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->key=NULL; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "key")) ++ break; ++ section->key=stralloc(arg); ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ s_log(LOG_RAW, "%-15s = %s", "key", section->cert); /* set in stunnel.c */ ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = certificate private key", "key"); ++ break; ++ } ++ ++ /* local */ ++ switch(cmd) { ++ case CMD_INIT: ++ memset(§ion->source_addr, 0, sizeof(SOCKADDR_LIST)); ++ section->source_addr.addr[0].in.sin_family=AF_INET; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "local")) ++ break; ++ if(!hostport2addrlist(§ion->source_addr, arg, "0")) ++ return "Failed to resolve local address"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = IP address to be used as source for remote" ++ " connections", "local"); ++ break; ++ } ++ ++#if SSLEAY_VERSION_NUMBER >= 0x00907000L ++ /* OCSP */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->option.ocsp=0; ++ memset(§ion->ocsp_addr, 0, sizeof(SOCKADDR_LIST)); ++ section->ocsp_addr.addr[0].in.sin_family=AF_INET; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "ocsp")) ++ break; ++ section->option.ocsp=1; ++ return parse_ocsp_url(section, arg); ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = OCSP server URL", "ocsp"); ++ break; ++ } ++ ++ /* OCSPflag */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->ocsp_flags=0; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "OCSPflag")) ++ break; ++ tmpnum=parse_ocsp_flag(arg); ++ if(!tmpnum) ++ return "Illegal OCSP flag"; ++ section->ocsp_flags|=tmpnum; ++ return NULL; ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = OCSP server flags", "OCSPflag"); ++ break; ++ } ++#endif /* OpenSSL-0.9.7 */ ++ ++ /* options */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->ssl_options=0; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "options")) ++ break; ++ tmpnum=parse_ssl_option(arg); ++ if(!tmpnum) ++ return "Illegal SSL option"; ++ section->ssl_options|=tmpnum; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = SSL option", "options"); ++ s_log(LOG_RAW, "%18sset an SSL option", ""); ++ break; ++ } ++ ++ /* protocol */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->protocol=NULL; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "protocol")) ++ break; ++ section->protocol=stralloc(arg); ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = protocol to negotiate before SSL initialization", ++ "protocol"); ++ s_log(LOG_RAW, "%18scurrently supported: cifs, connect, nntp, pop3, smtp", ""); ++ break; ++ } ++ ++ /* protocolAuthentication */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->protocol_authentication="basic"; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "protocolAuthentication")) ++ break; ++ section->protocol_authentication=stralloc(arg); ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = authentication type for protocol negotiations", ++ "protocolAuthentication"); ++ break; ++ } ++ ++ /* protocolHost */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->protocol_host=NULL; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "protocolHost")) ++ break; ++ section->protocol_host=stralloc(arg); ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = host:port for protocol negotiations", ++ "protocolHost"); ++ break; ++ } ++ ++ /* protocolPassword */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->protocol_password=NULL; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "protocolPassword")) ++ break; ++ section->protocol_password=stralloc(arg); ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = password for protocol negotiations", ++ "protocolPassword"); ++ break; ++ } ++ ++ /* protocolUsername */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->protocol_username=NULL; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "protocolUsername")) ++ break; ++ section->protocol_username=stralloc(arg); ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = username for protocol negotiations", ++ "protocolUsername"); ++ break; ++ } ++ ++ /* pty */ ++#ifndef USE_WIN32 ++ switch(cmd) { ++ case CMD_INIT: ++ section->option.pty=0; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "pty")) ++ break; ++ if(!strcasecmp(arg, "yes")) ++ section->option.pty=1; ++ else if(!strcasecmp(arg, "no")) ++ section->option.pty=0; ++ else ++ return "Argument should be either 'yes' or 'no'"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = yes|no allocate pseudo terminal for 'exec' option", ++ "pty"); ++ break; ++ } ++#endif ++ ++ /* retry */ ++#ifndef USE_WIN32 ++ switch(cmd) { ++ case CMD_INIT: ++ section->option.retry=0; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "retry")) ++ break; ++ if(!strcasecmp(arg, "yes")) ++ section->option.retry=1; ++ else if(!strcasecmp(arg, "no")) ++ section->option.retry=0; ++ else ++ return "Argument should be either 'yes' or 'no'"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = yes|no retry connect+exec section", ++ "retry"); ++ break; ++ } ++#endif ++ ++ /* session */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->session_timeout=300; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "session")) ++ break; ++ if(atoi(arg)>0) ++ section->session_timeout=atoi(arg); ++ else ++ return "Illegal session timeout"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ s_log(LOG_RAW, "%-15s = %ld seconds", "session", section->session_timeout); ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = session cache timeout (in seconds)", "session"); ++ break; ++ } ++ ++ /* sessiond */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->option.sessiond=0; ++ memset(§ion->sessiond_addr, 0, sizeof(SOCKADDR_LIST)); ++ section->sessiond_addr.addr[0].in.sin_family=AF_INET; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "sessiond")) ++ break; ++ section->option.sessiond=1; ++#ifdef SSL_OP_NO_TICKET ++ /* disable RFC4507 support introduced in OpenSSL 0.9.8f */ ++ /* this prevents session callbacks from beeing executed */ ++ section->ssl_options|=SSL_OP_NO_TICKET; ++#endif ++ if(!name2addrlist(§ion->sessiond_addr, arg, DEFAULT_LOOPBACK)) ++ return "Failed to resolve sessiond server address"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = [host:]port use sessiond at host:port", ++ "sessiond"); ++ break; ++ } ++ ++#ifndef USE_FORK ++ /* stack */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->stack_size=DEFAULT_STACK_SIZE; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "stack")) ++ break; ++ if(atoi(arg)>0) ++ section->stack_size=atoi(arg); ++ else ++ return "Illegal thread stack size"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ s_log(LOG_RAW, "%-15s = %d bytes", "stack", section->stack_size); ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = thread stack size (in bytes)", "stack"); ++ break; ++ } ++#endif ++ ++ /* sslVersion */ ++ switch(cmd) { ++ case CMD_INIT: ++#ifdef USE_FIPS ++ section->client_method=(SSL_METHOD *)TLSv1_client_method(); ++ section->server_method=(SSL_METHOD *)TLSv1_server_method(); ++#else ++ section->client_method=(SSL_METHOD *)SSLv3_client_method(); ++ section->server_method=(SSL_METHOD *)SSLv23_server_method(); ++#endif ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "sslVersion")) ++ break; ++ if(!strcasecmp(arg, "all")) { ++ section->client_method=(SSL_METHOD *)SSLv23_client_method(); ++ section->server_method=(SSL_METHOD *)SSLv23_server_method(); ++ } else if(!strcasecmp(arg, "SSLv2")) { ++ section->client_method=(SSL_METHOD *)SSLv2_client_method(); ++ section->server_method=(SSL_METHOD *)SSLv2_server_method(); ++ } else if(!strcasecmp(arg, "SSLv3")) { ++ section->client_method=(SSL_METHOD *)SSLv3_client_method(); ++ section->server_method=(SSL_METHOD *)SSLv3_server_method(); ++ } else if(!strcasecmp(arg, "TLSv1")) { ++ section->client_method=(SSL_METHOD *)TLSv1_client_method(); ++ section->server_method=(SSL_METHOD *)TLSv1_server_method(); ++ } else ++ return "Incorrect version of SSL protocol"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++#ifdef USE_FIPS ++ s_log(LOG_RAW, "%-15s = TLSv1", "sslVersion"); ++#else ++ s_log(LOG_RAW, "%-15s = SSLv3 for client, all for server", "sslVersion"); ++#endif ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = all|SSLv2|SSLv3|TLSv1 SSL method", "sslVersion"); ++ break; ++ } ++ ++ /* TIMEOUTbusy */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->timeout_busy=300; /* 5 minutes */ ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "TIMEOUTbusy")) ++ break; ++ if(atoi(arg)>0) ++ section->timeout_busy=atoi(arg); ++ else ++ return "Illegal busy timeout"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ s_log(LOG_RAW, "%-15s = %d seconds", "TIMEOUTbusy", section->timeout_busy); ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = seconds to wait for expected data", "TIMEOUTbusy"); ++ break; ++ } ++ ++ /* TIMEOUTclose */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->timeout_close=60; /* 1 minute */ ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "TIMEOUTclose")) ++ break; ++ if(atoi(arg)>0 || !strcmp(arg, "0")) ++ section->timeout_close=atoi(arg); ++ else ++ return "Illegal close timeout"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ s_log(LOG_RAW, "%-15s = %d seconds", "TIMEOUTclose", section->timeout_close); ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = seconds to wait for close_notify" ++ " (set to 0 for buggy MSIE)", "TIMEOUTclose"); ++ break; ++ } ++ ++ /* TIMEOUTconnect */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->timeout_connect=10; /* 10 seconds */ ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "TIMEOUTconnect")) ++ break; ++ if(atoi(arg)>0 || !strcmp(arg, "0")) ++ section->timeout_connect=atoi(arg); ++ else ++ return "Illegal connect timeout"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ s_log(LOG_RAW, "%-15s = %d seconds", "TIMEOUTconnect", ++ section->timeout_connect); ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = seconds to connect remote host", "TIMEOUTconnect"); ++ break; ++ } ++ ++ /* TIMEOUTidle */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->timeout_idle=43200; /* 12 hours */ ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "TIMEOUTidle")) ++ break; ++ if(atoi(arg)>0) ++ section->timeout_idle=atoi(arg); ++ else ++ return "Illegal idle timeout"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ s_log(LOG_RAW, "%-15s = %d seconds", "TIMEOUTidle", section->timeout_idle); ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = seconds to keep an idle connection", "TIMEOUTidle"); ++ break; ++ } ++ ++ /* transparent */ ++#ifndef USE_WIN32 ++ switch(cmd) { ++ case CMD_INIT: ++ section->option.transparent=0; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "transparent")) ++ break; ++ if(!strcasecmp(arg, "yes")) ++ section->option.transparent=1; ++ else if(!strcasecmp(arg, "no")) ++ section->option.transparent=0; ++ else ++ return "Argument should be either 'yes' or 'no'"; ++ return NULL; /* OK */ ++ case CMD_DEFAULT: ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = yes|no transparent proxy mode", ++ "transparent"); ++ break; ++ } ++#endif ++ ++ /* verify */ ++ switch(cmd) { ++ case CMD_INIT: ++ section->verify_level=-1; ++ section->verify_use_only_my=0; ++ break; ++ case CMD_EXEC: ++ if(strcasecmp(opt, "verify")) ++ break; ++ section->verify_level=SSL_VERIFY_NONE; ++ switch(atoi(arg)) { ++ case 3: ++ section->verify_use_only_my=1; ++ case 2: ++ section->verify_level|=SSL_VERIFY_FAIL_IF_NO_PEER_CERT; ++ case 1: ++ section->verify_level|=SSL_VERIFY_PEER; ++ case 0: ++ return NULL; /* OK */ ++ default: ++ return "Bad verify level"; ++ } ++ case CMD_DEFAULT: ++ s_log(LOG_RAW, "%-15s = none", "verify"); ++ break; ++ case CMD_HELP: ++ s_log(LOG_RAW, "%-15s = level of peer certificate verification", "verify"); ++ s_log(LOG_RAW, "%18slevel 1 - verify peer certificate if present", ""); ++ s_log(LOG_RAW, "%18slevel 2 - require valid peer certificate always", ""); ++ s_log(LOG_RAW, "%18slevel 3 - verify peer with locally installed certificate", ++ ""); ++ break; ++ } ++ ++ if(cmd==CMD_EXEC) ++ return option_not_found; ++ return NULL; /* OK */ ++} ++ ++static void syntax(char *confname) { ++ s_log(LOG_RAW, " "); ++ s_log(LOG_RAW, "Syntax:"); ++ s_log(LOG_RAW, "stunnel " ++#ifdef USE_WIN32 ++#ifndef _WIN32_WCE ++ "[ [-install | -uninstall] " ++#endif ++ "[-quiet] " ++#endif ++ "[] ] " ++#ifndef USE_WIN32 ++ "-fd " ++#endif ++ "| -help | -version | -sockets"); ++ s_log(LOG_RAW, " - use specified config file instead of %s", ++ confname); ++#ifdef USE_WIN32 ++#ifndef _WIN32_WCE ++ s_log(LOG_RAW, " -install - install NT service"); ++ s_log(LOG_RAW, " -uninstall - uninstall NT service"); ++#endif ++ s_log(LOG_RAW, " -quiet - don't display a message box on success"); ++#else ++ s_log(LOG_RAW, " -fd - read the config file from a file descriptor"); ++#endif ++ s_log(LOG_RAW, " -help - get config file help"); ++ s_log(LOG_RAW, " -version - display version and defaults"); ++ s_log(LOG_RAW, " -sockets - display default socket options"); ++ die(1); ++} ++ ++void parse_config(char *name, char *parameter) { ++#ifdef CONFDIR ++ char *default_config_file=CONFDIR CONFSEPARATOR "stunnel.conf"; ++#else ++ char *default_config_file="stunnel.conf"; ++#endif ++ DISK_FILE *df; ++ char confline[CONFLINELEN], *arg, *opt, *errstr, *filename; ++ int line_number, i; ++#ifdef MAX_FD ++ int sections=0; ++#endif ++ LOCAL_OPTIONS *section, *new_section; ++ ++ memset(&options, 0, sizeof(GLOBAL_OPTIONS)); /* reset global options */ ++ ++ memset(&local_options, 0, sizeof(LOCAL_OPTIONS)); /* reset local options */ ++ local_options.next=NULL; ++ section=&local_options; ++ ++ global_options(CMD_INIT, NULL, NULL); ++ service_options(CMD_INIT, section, NULL, NULL); ++ if(!name) ++ name=default_config_file; ++ if(!strcasecmp(name, "-help")) { ++ global_options(CMD_HELP, NULL, NULL); ++ service_options(CMD_HELP, section, NULL, NULL); ++ die(1); ++ } ++ if(!strcasecmp(name, "-version")) { ++ stunnel_info(1); ++ s_log(LOG_RAW, " "); ++ global_options(CMD_DEFAULT, NULL, NULL); ++ service_options(CMD_DEFAULT, section, NULL, NULL); ++ die(1); ++ } ++ if(!strcasecmp(name, "-sockets")) { ++ print_socket_options(); ++ die(1); ++ } ++#ifndef USE_WIN32 ++ if(!strcasecmp(name, "-fd")) { ++ if(!parameter) { ++ s_log(LOG_RAW, "No file descriptor specified"); ++ syntax(default_config_file); ++ } ++ for(arg=parameter, i=0; *arg; ++arg) { ++ if(*arg<'0' || *arg>'9') { ++ s_log(LOG_RAW, "Invalid file descriptor %s", parameter); ++ syntax(default_config_file); ++ } ++ i=10*i+*arg-'0'; ++ } ++ df=file_fdopen(i); ++ if(!df) { ++ s_log(LOG_RAW, "Invalid file descriptor %s", parameter); ++ syntax(default_config_file); ++ } ++ filename="descriptor"; ++ } else ++#endif ++ { ++ df=file_open(name, 0); ++ if(!df) ++ syntax(default_config_file); ++ filename=name; ++ } ++ line_number=0; ++ while(file_getline(df, confline, CONFLINELEN)) { ++ ++line_number; ++ opt=confline; ++ while(isspace((unsigned char)*opt)) ++ ++opt; /* remove initial whitespaces */ ++ for(i=strlen(opt)-1; i>=0 && isspace((unsigned char)opt[i]); --i) ++ opt[i]='\0'; /* remove trailing whitespaces */ ++ if(opt[0]=='\0' || opt[0]=='#' || opt[0]==';') /* empty or comment */ ++ continue; ++ if(opt[0]=='[' && opt[strlen(opt)-1]==']') { /* new section */ ++ section_validate(filename, line_number, section, 0); ++ ++opt; ++ opt[strlen(opt)-1]='\0'; ++ new_section=calloc(1, sizeof(LOCAL_OPTIONS)); ++ if(!new_section) { ++ s_log(LOG_RAW, "Fatal memory allocation error"); ++ die(2); ++ } ++ memcpy(new_section, &local_options, sizeof(LOCAL_OPTIONS)); ++ new_section->servname=stralloc(opt); ++ new_section->session=NULL; ++ new_section->next=NULL; ++ section->next=new_section; ++ section=new_section; ++#ifdef MAX_FD ++ if(++sections>MAX_FD) ++ config_error(filename, line_number, "Too many sections"); ++#endif ++ continue; ++ } ++ arg=strchr(confline, '='); ++ if(!arg) ++ config_error(filename, line_number, "No '=' found"); ++ *arg++='\0'; /* split into option name and argument value */ ++ for(i=strlen(opt)-1; i>=0 && isspace((unsigned char)opt[i]); --i) ++ opt[i]='\0'; /* remove trailing whitespaces */ ++ while(isspace((unsigned char)*arg)) ++ ++arg; /* remove initial whitespaces */ ++ errstr=service_options(CMD_EXEC, section, opt, arg); ++ if(section==&local_options && errstr==option_not_found) ++ errstr=global_options(CMD_EXEC, opt, arg); ++ config_error(filename, line_number, errstr); ++ } ++ section_validate(filename, line_number, section, 1); ++ file_close(df); ++ if(!local_options.next) { /* inetd mode */ ++ if (section->option.accept) { ++ s_log(LOG_RAW, "accept option is not allowed in inetd mode"); ++ s_log(LOG_RAW, "remove accept option or define a [section]"); ++ die(1); ++ } ++ if (!section->option.remote && !section->execname) { ++ s_log(LOG_RAW, "inetd mode must define a remote host or an executable"); ++ die(1); ++ } ++ } ++} ++ ++static void section_validate(char *filename, int line_number, ++ LOCAL_OPTIONS *section, int final) { ++ if(section==&local_options) { /* global options just configured */ ++#ifdef HAVE_OSSL_ENGINE_H ++ close_engine(); ++#endif ++ ssl_configure(); /* configure global SSL settings */ ++ if(!final) /* no need to validate defaults */ ++ return; ++ } ++ if(!section->option.client) ++ section->option.cert=1; /* Server always needs a certificate */ ++ context_init(section); /* initialize SSL context */ ++ ++ if(section==&local_options) { /* inetd mode */ ++ if(section->option.accept) ++ config_error(filename, line_number, ++ "accept is not allowed in inetd mode"); ++ /* TODO: some additional checks could be useful ++ if((unsigned int)section->option.program + ++ (unsigned int)section->option.remote != 1) ++ config_error(filename, line_number, ++ "Single endpoint is required in inetd mode"); ++ */ ++ return; ++ } ++ ++ /* standalone mode */ ++#ifdef USE_WIN32 ++ if(!section->option.accept || !section->option.remote) ++#else ++ if((unsigned int)section->option.accept + ++ (unsigned int)section->option.program + ++ (unsigned int)section->option.remote != 2) ++#endif ++ config_error(filename, line_number, ++ "Each service section must define exactly two endpoints"); ++ return; /* All tests passed -- continue program execution */ ++} ++ ++static void config_error(char *name, int num, char *str) { ++ if(!str) /* NULL -> no error */ ++ return; ++ s_log(LOG_RAW, "file %s line %d: %s", name, num, str); ++ die(1); ++} ++ ++static char *stralloc(char *str) { /* Allocate static string */ ++ char *retval; ++ ++ retval=calloc(strlen(str)+1, 1); ++ if(!retval) { ++ s_log(LOG_RAW, "Fatal memory allocation error"); ++ die(2); ++ } ++ strcpy(retval, str); ++ return retval; ++} ++ ++#ifndef USE_WIN32 ++static char **argalloc(char *str) { /* Allocate 'exec' argumets */ ++ int max_arg, i; ++ char *ptr, **retval; ++ ++ max_arg=strlen(str)/2+1; ++ ptr=stralloc(str); ++ retval=calloc(max_arg+1, sizeof(char *)); ++ if(!retval) { ++ s_log(LOG_RAW, "Fatal memory allocation error"); ++ die(2); ++ } ++ i=0; ++ while(*ptr && iname; ++fl) { ++ if(!strcasecmp(fl->name, string)) { ++ options.facility = fl->value; ++ break; ++ } ++ } ++ if(options.facility==-1) ++ return 0; /* FAILED */ ++ string=strtok(NULL, "."); /* set to the remainder */ ++ } ++#endif /* USE_WIN32, __vms */ ++ ++ /* Time to check the syslog level */ ++ if(string && strlen(string)==1 && *string>='0' && *string<='7') { ++ options.debug_level=*string-'0'; ++ return 1; /* OK */ ++ } ++ options.debug_level=8; /* illegal level */ ++ for(fl=levels; fl->name; ++fl) { ++ if(!strcasecmp(fl->name, string)) { ++ options.debug_level=fl->value; ++ break; ++ } ++ } ++ if (options.debug_level==8) ++ return 0; /* FAILED */ ++ return 1; /* OK */ ++} ++ ++/* Parse out SSL options stuff */ ++ ++static int parse_ssl_option(char *arg) { ++ struct { ++ char *name; ++ long value; ++ } ssl_opts[] = { ++ {"MICROSOFT_SESS_ID_BUG", SSL_OP_MICROSOFT_SESS_ID_BUG}, ++ {"NETSCAPE_CHALLENGE_BUG", SSL_OP_NETSCAPE_CHALLENGE_BUG}, ++ {"NETSCAPE_REUSE_CIPHER_CHANGE_BUG", ++ SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG}, ++ {"SSLREF2_REUSE_CERT_TYPE_BUG", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG}, ++ {"MICROSOFT_BIG_SSLV3_BUFFER", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER}, ++ {"MSIE_SSLV2_RSA_PADDING", SSL_OP_MSIE_SSLV2_RSA_PADDING}, ++ {"SSLEAY_080_CLIENT_DH_BUG", SSL_OP_SSLEAY_080_CLIENT_DH_BUG}, ++ {"TLS_D5_BUG", SSL_OP_TLS_D5_BUG}, ++ {"TLS_BLOCK_PADDING_BUG", SSL_OP_TLS_BLOCK_PADDING_BUG}, ++ {"DONT_INSERT_EMPTY_FRAGMENTS", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS}, ++#ifdef SSL_OP_NO_QUERY_MTU ++ {"NO_QUERY_MTU", SSL_OP_NO_QUERY_MTU}, ++#endif ++#ifdef SSL_OP_COOKIE_EXCHANGE ++ {"COOKIE_EXCHANGE", SSL_OP_COOKIE_EXCHANGE}, ++#endif ++#ifdef SSL_OP_NO_TICKET ++ {"NO_TICKET", SSL_OP_NO_TICKET}, ++#endif ++ {"NO_SESSION_RESUMPTION_ON_RENEGOTIATION", ++ SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION}, ++#ifdef SSL_OP_NO_COMPRESSION ++ {"NO_COMPRESSION", SSL_OP_NO_COMPRESSION}, ++#endif ++#ifdef SSL_OP_SINGLE_ECDH_USE ++ {"SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE}, ++#endif ++ {"SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE}, ++ {"EPHEMERAL_RSA", SSL_OP_EPHEMERAL_RSA}, ++ {"CIPHER_SERVER_PREFERENCE", SSL_OP_CIPHER_SERVER_PREFERENCE}, ++ {"TLS_ROLLBACK_BUG", SSL_OP_TLS_ROLLBACK_BUG}, ++ {"NO_SSLv2", SSL_OP_NO_SSLv2}, ++ {"NO_SSLv3", SSL_OP_NO_SSLv3}, ++ {"NO_TLSv1", SSL_OP_NO_TLSv1}, ++ {"PKCS1_CHECK_1", SSL_OP_PKCS1_CHECK_1}, ++ {"PKCS1_CHECK_2", SSL_OP_PKCS1_CHECK_2}, ++ {"NETSCAPE_CA_DN_BUG", SSL_OP_NETSCAPE_CA_DN_BUG}, ++ {"NETSCAPE_DEMO_CIPHER_CHANGE_BUG", ++ SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG}, ++#ifdef SSL_OP_CRYPTOPRO_TLSEXT_BUG ++ {"CRYPTOPRO_TLSEXT_BUG", SSL_OP_CRYPTOPRO_TLSEXT_BUG}, ++#endif ++ {"ALL", SSL_OP_ALL}, ++ {NULL, 0} ++ }, *option; ++ ++ for(option=ssl_opts; option->name; ++option) ++ if(!strcasecmp(option->name, arg)) ++ return option->value; ++ return 0; /* FAILED */ ++} ++ ++/* Parse out the socket options stuff */ ++ ++static int on=1; ++ ++#define DEF_VALS {NULL, NULL, NULL} ++#define DEF_ACCEPT {(void *)&on, NULL, NULL} ++ ++SOCK_OPT sock_opts[] = { ++ {"SO_DEBUG", SOL_SOCKET, SO_DEBUG, TYPE_FLAG, DEF_VALS}, ++ {"SO_DONTROUTE", SOL_SOCKET, SO_DONTROUTE, TYPE_FLAG, DEF_VALS}, ++ {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, TYPE_FLAG, DEF_VALS}, ++ {"SO_LINGER", SOL_SOCKET, SO_LINGER, TYPE_LINGER, DEF_VALS}, ++ {"SO_OOBINLINE", SOL_SOCKET, SO_OOBINLINE, TYPE_FLAG, DEF_VALS}, ++ {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, TYPE_INT, DEF_VALS}, ++ {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, TYPE_INT, DEF_VALS}, ++#ifdef SO_RCVLOWAT ++ {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, TYPE_INT, DEF_VALS}, ++#endif ++#ifdef SO_SNDLOWAT ++ {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, TYPE_INT, DEF_VALS}, ++#endif ++#ifdef SO_RCVTIMEO ++ {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, TYPE_TIMEVAL, DEF_VALS}, ++#endif ++#ifdef SO_SNDTIMEO ++ {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, TYPE_TIMEVAL, DEF_VALS}, ++#endif ++ {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, TYPE_FLAG, DEF_ACCEPT}, ++#ifdef SO_BINDTODEVICE ++ {"SO_BINDTODEVICE", SOL_SOCKET, SO_BINDTODEVICE, TYPE_STRING, DEF_VALS}, ++#endif ++#ifdef TCP_KEEPCNT ++ {"TCP_KEEPCNT", SOL_TCP, TCP_KEEPCNT, TYPE_INT, DEF_VALS}, ++#endif ++#ifdef TCP_KEEPIDLE ++ {"TCP_KEEPIDLE", SOL_TCP, TCP_KEEPIDLE, TYPE_INT, DEF_VALS}, ++#endif ++#ifdef TCP_KEEPINTVL ++ {"TCP_KEEPINTVL", SOL_TCP, TCP_KEEPINTVL, TYPE_INT, DEF_VALS}, ++#endif ++#ifdef IP_TOS ++ {"IP_TOS", IPPROTO_IP, IP_TOS, TYPE_INT, DEF_VALS}, ++#endif ++#ifdef IP_TTL ++ {"IP_TTL", IPPROTO_IP, IP_TTL, TYPE_INT, DEF_VALS}, ++#endif ++#ifdef IP_MAXSEG ++ {"TCP_MAXSEG", IPPROTO_TCP, TCP_MAXSEG, TYPE_INT, DEF_VALS}, ++#endif ++ {"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, TYPE_FLAG, DEF_VALS}, ++ {NULL, 0, 0, TYPE_NONE, DEF_VALS} ++}; ++ ++static int print_socket_options(void) { ++ int fd; ++ socklen_t optlen; ++ SOCK_OPT *ptr; ++ OPT_UNION val; ++ char line[STRLEN]; ++ ++ fd=socket(AF_INET, SOCK_STREAM, 0); ++ ++ s_log(LOG_RAW, "Socket option defaults:"); ++ s_log(LOG_RAW, " %-16s%-10s%-10s%-10s%-10s", ++ "Option", "Accept", "Local", "Remote", "OS default"); ++ for(ptr=sock_opts; ptr->opt_str; ++ptr) { ++ /* display option name */ ++ sprintf(line, " %-16s", ptr->opt_str); ++ /* display stunnel default values */ ++ print_option(line, ptr->opt_type, ptr->opt_val[0]); ++ print_option(line, ptr->opt_type, ptr->opt_val[1]); ++ print_option(line, ptr->opt_type, ptr->opt_val[2]); ++ /* display OS default value */ ++ optlen=sizeof val; ++ if(getsockopt(fd, ptr->opt_level, ++ ptr->opt_name, (void *)&val, &optlen)) { ++ if(get_last_socket_error()!=ENOPROTOOPT) { ++ s_log(LOG_RAW, "%s", line); /* dump the name and assigned values */ ++ sockerror("getsockopt"); ++ return 0; /* FAILED */ ++ } ++ safeconcat(line, " -- "); /* write-only value */ ++ } else ++ print_option(line, ptr->opt_type, &val); ++ s_log(LOG_RAW, "%s", line); ++ } ++ return 1; /* OK */ ++} ++ ++static void print_option(char *line, int type, OPT_UNION *val) { ++ char text[STRLEN]; ++ ++ if(!val) { ++ safecopy(text, " -- "); ++ } else { ++ switch(type) { ++ case TYPE_FLAG: ++ case TYPE_INT: ++ sprintf(text, "%10d", val->i_val); ++ break; ++ case TYPE_LINGER: ++ sprintf(text, "%d:%-8d", ++ val->linger_val.l_onoff, val->linger_val.l_linger); ++ break; ++ case TYPE_TIMEVAL: ++ sprintf(text, "%6d:%-3d", ++ (int)val->timeval_val.tv_sec, (int)val->timeval_val.tv_usec); ++ break; ++ case TYPE_STRING: ++ sprintf(text, "%10s", val->c_val); ++ break; ++ default: ++ safecopy(text, " Ooops? "); /* Internal error? */ ++ } ++ } ++ safeconcat(line, text); ++} ++ ++static int parse_socket_option(char *arg) { ++ int socket_type; /* 0-accept, 1-local, 2-remote */ ++ char *opt_val_str, *opt_val2_str; ++ SOCK_OPT *ptr; ++ ++ if(arg[1]!=':') ++ return 0; /* FAILED */ ++ switch(arg[0]) { ++ case 'a': ++ socket_type=0; break; ++ case 'l': ++ socket_type=1; break; ++ case 'r': ++ socket_type=2; break; ++ default: ++ return 0; /* FAILED */ ++ } ++ arg+=2; ++ opt_val_str=strchr(arg, '='); ++ if(!opt_val_str) /* No '='? */ ++ return 0; /* FAILED */ ++ *opt_val_str++='\0'; ++ ptr=sock_opts; ++ for(;;) { ++ if(!ptr->opt_str) ++ return 0; /* FAILED */ ++ if(!strcmp(arg, ptr->opt_str)) ++ break; /* option name found */ ++ ++ptr; ++ } ++ ptr->opt_val[socket_type]=calloc(1, sizeof(OPT_UNION)); ++ switch(ptr->opt_type) { ++ case TYPE_FLAG: ++ case TYPE_INT: ++ ptr->opt_val[socket_type]->i_val=atoi(opt_val_str); ++ return 1; /* OK */ ++ case TYPE_LINGER: ++ opt_val2_str=strchr(opt_val_str, ':'); ++ if(opt_val2_str) { ++ *opt_val2_str++='\0'; ++ ptr->opt_val[socket_type]->linger_val.l_linger=atoi(opt_val2_str); ++ } else { ++ ptr->opt_val[socket_type]->linger_val.l_linger=0; ++ } ++ ptr->opt_val[socket_type]->linger_val.l_onoff=atoi(opt_val_str); ++ return 1; /* OK */ ++ case TYPE_TIMEVAL: ++ opt_val2_str=strchr(opt_val_str, ':'); ++ if(opt_val2_str) { ++ *opt_val2_str++='\0'; ++ ptr->opt_val[socket_type]->timeval_val.tv_usec=atoi(opt_val2_str); ++ } else { ++ ptr->opt_val[socket_type]->timeval_val.tv_usec=0; ++ } ++ ptr->opt_val[socket_type]->timeval_val.tv_sec=atoi(opt_val_str); ++ return 1; /* OK */ ++ case TYPE_STRING: ++ if(strlen(opt_val_str)+1>sizeof(OPT_UNION)) ++ return 0; /* FAILED */ ++ strcpy(ptr->opt_val[socket_type]->c_val, opt_val_str); ++ return 1; /* OK */ ++ default: ++ ; /* ANSI C compiler needs it */ ++ } ++ return 0; /* FAILED */ ++} ++ ++/* Parse out OCSP URL */ ++ ++static char *parse_ocsp_url(LOCAL_OPTIONS *section, char *arg) { ++ char *host, *port, *path; ++ int ssl; ++ ++ if(!OCSP_parse_url(arg, &host, &port, &path, &ssl)) ++ return "Failed to parse OCSP URL"; ++ if(ssl) ++ return "SSL not supported for OCSP" ++ " - additional stunnel service needs to be defined"; ++ if(!hostport2addrlist(§ion->ocsp_addr, host, port)) ++ return "Failed to resolve OCSP server address"; ++ section->ocsp_path=stralloc(path); ++ if(host) ++ OPENSSL_free(host); ++ if(port) ++ OPENSSL_free(port); ++ if(path) ++ OPENSSL_free(path); ++ return NULL; /* OK! */ ++} ++ ++/* Parse out OCSP flags stuff */ ++ ++static unsigned long parse_ocsp_flag(char *arg) { ++ struct { ++ char *name; ++ unsigned long value; ++ } ocsp_opts[] = { ++ {"NOCERTS", OCSP_NOCERTS}, ++ {"NOINTERN", OCSP_NOINTERN}, ++ {"NOSIGS", OCSP_NOSIGS}, ++ {"NOCHAIN", OCSP_NOCHAIN}, ++ {"NOVERIFY", OCSP_NOVERIFY}, ++ {"NOEXPLICIT", OCSP_NOEXPLICIT}, ++ {"NOCASIGN", OCSP_NOCASIGN}, ++ {"NODELEGATED", OCSP_NODELEGATED}, ++ {"NOCHECKS", OCSP_NOCHECKS}, ++ {"TRUSTOTHER", OCSP_TRUSTOTHER}, ++ {"RESPID_KEY", OCSP_RESPID_KEY}, ++ {"NOTIME", OCSP_NOTIME}, ++ {NULL, 0} ++ }, *option; ++ ++ for(option=ocsp_opts; option->name; ++option) ++ if(!strcasecmp(option->name, arg)) ++ return option->value; ++ return 0; /* FAILED */ ++} ++ ++/* End of options.c */ +--- a/src/prototypes.h ++++ b/src/prototypes.h +@@ -231,6 +231,7 @@ typedef struct local_options { + unsigned int remote:1; + unsigned int retry:1; /* loop remote+program */ + unsigned int sessiond:1; ++ unsigned int xforwardedfor:1; + #ifndef USE_WIN32 + unsigned int program:1; + unsigned int pty:1; +@@ -334,6 +335,8 @@ typedef struct { + FD *ssl_rfd, *ssl_wfd; /* Read and write SSL descriptors */ + int sock_bytes, ssl_bytes; /* Bytes written to socket and ssl */ + s_poll_set fds; /* File descriptors */ ++ int buffsize; /* current buffer size, may be lower than BUFFSIZE */ ++ int crlf_seen; /* the number of successive CRLF seen */ + } CLI; + + extern int max_clients; +--- /dev/null ++++ b/src/prototypes.h.orig +@@ -0,0 +1,470 @@ ++/* ++ * stunnel Universal SSL tunnel ++ * Copyright (C) 1998-2009 Michal Trojnara ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ * See the GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, see . ++ * ++ * Linking stunnel statically or dynamically with other modules is making ++ * a combined work based on stunnel. Thus, the terms and conditions of ++ * the GNU General Public License cover the whole combination. ++ * ++ * In addition, as a special exception, the copyright holder of stunnel ++ * gives you permission to combine stunnel with free software programs or ++ * libraries that are released under the GNU LGPL and with code included ++ * in the standard release of OpenSSL under the OpenSSL License (or ++ * modified versions of such code, with unchanged license). You may copy ++ * and distribute such a system following the terms of the GNU GPL for ++ * stunnel and the licenses of the other code concerned. ++ * ++ * Note that people who make modified versions of stunnel are not obligated ++ * to grant this special exception for their modified versions; it is their ++ * choice whether to do so. The GNU General Public License gives permission ++ * to release a modified version without this exception; this exception ++ * also makes it possible to release a modified version which carries ++ * forward this exception. ++ */ ++ ++#ifndef PROTOTYPES_H ++#define PROTOTYPES_H ++ ++#include "common.h" ++ ++/**************************************** Network data structure */ ++ ++#define MAX_HOSTS 16 ++ ++typedef union sockaddr_union { ++ struct sockaddr sa; ++ struct sockaddr_in in; ++#if defined(USE_IPv6) ++ struct sockaddr_in6 in6; ++#endif ++} SOCKADDR_UNION; ++ ++typedef struct sockaddr_list { /* list of addresses */ ++ SOCKADDR_UNION addr[MAX_HOSTS]; /* the list of addresses */ ++ u16 cur; /* current address for round-robin */ ++ u16 num; /* how many addresses are used */ ++} SOCKADDR_LIST; ++ ++#ifdef __INNOTEK_LIBC__ ++#define socklen_t __socklen_t ++#define strcasecmp stricmp ++#define strncasecmp strnicmp ++#define NI_NUMERICHOST 1 ++#define NI_NUMERICSERV 2 ++#endif ++ ++ ++/**************************************** Prototypes for stunnel.c */ ++ ++extern volatile int num_clients; ++ ++void main_initialize(char *, char *); ++void main_execute(void); ++#if !defined (USE_WIN32) && !defined (__vms) && !defined(USE_OS2) ++void drop_privileges(void); ++#endif ++void stunnel_info(int); ++void die(int); ++ ++/**************************************** Prototypes for log.c */ ++ ++void log_open(void); ++void log_close(void); ++void log_flush(void); ++void s_log(int, const char *, ...) ++#ifdef __GNUC__ ++ __attribute__ ((format (printf, 2, 3))); ++#else ++ ; ++#endif ++void ioerror(const char *); ++void sockerror(const char *); ++void log_error(int, int, const char *); ++char *my_strerror(int); ++ ++/**************************************** Prototypes for pty.c */ ++/* Based on Public Domain code by Tatu Ylonen */ ++ ++int pty_allocate(int *, int *, char *, int); ++#if 0 ++void pty_release(char *); ++void pty_make_controlling_tty(int *, char *); ++#endif ++ ++/**************************************** Prototypes for ssl.c */ ++ ++typedef enum { ++ COMP_NONE, COMP_ZLIB, COMP_RLE ++} COMP_TYPE; ++ ++extern int cli_index, opt_index;; ++ ++void ssl_init(void); ++void ssl_configure(void); ++#ifdef HAVE_OSSL_ENGINE_H ++void open_engine(const char *); ++void ctrl_engine(const char *, const char *); ++void close_engine(void); ++ENGINE *get_engine(int); ++#endif ++ ++/**************************************** Prototypes for options.c */ ++ ++typedef struct { ++ /* some data for SSL initialization in ssl.c */ ++ COMP_TYPE compression; /* compression type */ ++ char *egd_sock; /* entropy gathering daemon socket */ ++ char *rand_file; /* file with random data */ ++ int random_bytes; /* how many random bytes to read */ ++ ++ /* some global data for stunnel.c */ ++#ifndef USE_WIN32 ++#ifdef HAVE_CHROOT ++ char *chroot_dir; ++#endif ++ unsigned long dpid; ++ char *pidfile; ++ int uid, gid; ++#endif ++ ++ /* Win32 specific data for gui.c */ ++#if defined(USE_WIN32) && !defined(_WIN32_WCE) ++ char *win32_service; ++#endif ++ ++ /* logging-support data for log.c */ ++ int debug_level; /* debug level for logging */ ++#ifndef USE_WIN32 ++ int facility; /* debug facility for syslog */ ++#endif ++ char *output_file; ++ ++ /* on/off switches */ ++ struct { ++ unsigned int rand_write:1; /* overwrite rand_file */ ++#ifdef USE_WIN32 ++ unsigned int taskbar:1; /* enable the taskbar icon */ ++#else /* !USE_WIN32 */ ++ unsigned int foreground:1; ++ unsigned int syslog:1; ++#endif ++#ifdef USE_FIPS ++ unsigned int fips:1; /* enable FIPS 140-2 mode */ ++#endif ++ } option; ++} GLOBAL_OPTIONS; ++ ++extern GLOBAL_OPTIONS options; ++ ++typedef struct local_options { ++ SSL_CTX *ctx; /* SSL context */ ++ X509_STORE *revocation_store; /* cert store for CRL checking */ ++#ifdef HAVE_OSSL_ENGINE_H ++ ENGINE *engine; /* engine to read the private key */ ++#endif ++ struct local_options *next; /* next node in the services list */ ++ char *servname; /* service name for logging & permission checking */ ++ SSL_SESSION *session; /* jecently used session */ ++ char local_address[IPLEN]; /* dotted-decimal address to bind */ ++#ifndef USE_FORK ++ int stack_size; /* stack size for this thread */ ++#endif ++ ++ /* service-specific data for ctx.c */ ++ char *ca_dir; /* directory for hashed certs */ ++ char *ca_file; /* file containing bunches of certs */ ++ char *crl_dir; /* directory for hashed CRLs */ ++ char *crl_file; /* file containing bunches of CRLs */ ++ char *cipher_list; ++ char *cert; /* cert filename */ ++ char *key; /* pem (priv key/cert) filename */ ++ long session_timeout; ++ int verify_level; ++ int verify_use_only_my; ++ long ssl_options; ++#if SSLEAY_VERSION_NUMBER >= 0x00907000L ++ SOCKADDR_LIST ocsp_addr; ++ char *ocsp_path; ++ unsigned long ocsp_flags; ++#endif /* OpenSSL-0.9.7 */ ++ SSL_METHOD *client_method, *server_method; ++ SOCKADDR_LIST sessiond_addr; ++ ++ /* service-specific data for client.c */ ++ int fd; /* file descriptor accepting connections for this service */ ++ char *execname, **execargs; /* program name and arguments for local mode */ ++ SOCKADDR_LIST local_addr, remote_addr, source_addr; ++ char *username; ++ char *remote_address; ++ int timeout_busy; /* maximum waiting for data time */ ++ int timeout_close; /* maximum close_notify time */ ++ int timeout_connect; /* maximum connect() time */ ++ int timeout_idle; /* maximum idle connection time */ ++ enum {FAILOVER_RR, FAILOVER_PRIO} failover; /* failover strategy */ ++ ++ /* protocol name for protocol.c */ ++ char *protocol; ++ char *protocol_host; ++ char *protocol_username; ++ char *protocol_password; ++ char *protocol_authentication; ++ ++ /* on/off switches */ ++ struct { ++ unsigned int cert:1; ++ unsigned int client:1; ++ unsigned int delayed_lookup:1; ++ unsigned int accept:1; ++ unsigned int remote:1; ++ unsigned int retry:1; /* loop remote+program */ ++ unsigned int sessiond:1; ++#ifndef USE_WIN32 ++ unsigned int program:1; ++ unsigned int pty:1; ++ unsigned int transparent:1; ++#endif ++#if SSLEAY_VERSION_NUMBER >= 0x00907000L ++ unsigned int ocsp:1; ++#endif ++ } option; ++} LOCAL_OPTIONS; ++ ++extern LOCAL_OPTIONS local_options; ++ ++typedef enum { ++ TYPE_NONE, TYPE_FLAG, TYPE_INT, TYPE_LINGER, TYPE_TIMEVAL, TYPE_STRING ++} VAL_TYPE; ++ ++typedef union { ++ int i_val; ++ long l_val; ++ char c_val[16]; ++ struct linger linger_val; ++ struct timeval timeval_val; ++} OPT_UNION; ++ ++typedef struct { ++ char *opt_str; ++ int opt_level; ++ int opt_name; ++ VAL_TYPE opt_type; ++ OPT_UNION *opt_val[3]; ++} SOCK_OPT; ++ ++void parse_config(char *, char *); ++ ++/**************************************** Prototypes for ctx.c */ ++ ++void context_init(LOCAL_OPTIONS *); ++void sslerror(char *); ++ ++/**************************************** Prototypes for verify.c */ ++ ++void verify_init(LOCAL_OPTIONS *); ++ ++/**************************************** Prototypes for network.c */ ++ ++#ifdef USE_POLL ++#define MAX_FD 256 ++#endif ++ ++typedef struct { ++#ifdef USE_POLL ++ struct pollfd ufds[MAX_FD]; ++ unsigned int nfds; ++#else ++ fd_set irfds, iwfds, orfds, owfds; ++ int max; ++#endif ++} s_poll_set; ++ ++void s_poll_init(s_poll_set *); ++void s_poll_add(s_poll_set *, int, int, int); ++int s_poll_canread(s_poll_set *, int); ++int s_poll_canwrite(s_poll_set *, int); ++int s_poll_wait(s_poll_set *, int, int); ++ ++#ifndef USE_WIN32 ++int signal_pipe_init(void); ++void child_status(void); /* dead libwrap or 'exec' process detected */ ++#endif ++int set_socket_options(int, int); ++int alloc_fd(int); ++void setnonblock(int, unsigned long); ++ ++/**************************************** Prototypes for client.c */ ++ ++typedef struct { ++ int fd; /* File descriptor */ ++ int rd; /* Open for read */ ++ int wr; /* Open for write */ ++ int is_socket; /* File descriptor is a socket */ ++} FD; ++ ++typedef struct { ++ LOCAL_OPTIONS *opt; ++ char accepted_address[IPLEN]; /* text */ ++ SOCKADDR_LIST peer_addr; /* Peer address */ ++ FD local_rfd, local_wfd; /* Read and write local descriptors */ ++ FD remote_fd; /* Remote file descriptor */ ++ SSL *ssl; /* SSL Connection */ ++ SOCKADDR_LIST bind_addr; ++ /* IP for explicit local bind or transparent proxy */ ++ unsigned long pid; /* PID of local process */ ++ int fd; /* Temporary file descriptor */ ++ jmp_buf err; ++ ++ char sock_buff[BUFFSIZE]; /* Socket read buffer */ ++ char ssl_buff[BUFFSIZE]; /* SSL read buffer */ ++ int sock_ptr, ssl_ptr; /* Index of first unused byte in buffer */ ++ FD *sock_rfd, *sock_wfd; /* Read and write socket descriptors */ ++ FD *ssl_rfd, *ssl_wfd; /* Read and write SSL descriptors */ ++ int sock_bytes, ssl_bytes; /* Bytes written to socket and ssl */ ++ s_poll_set fds; /* File descriptors */ ++} CLI; ++ ++extern int max_clients; ++#ifndef USE_WIN32 ++extern int max_fds; ++#endif ++ ++CLI *alloc_client_session(LOCAL_OPTIONS *, int, int); ++void *client(void *); ++ ++/**************************************** Prototypes for network.c */ ++ ++int connect_blocking(CLI *, SOCKADDR_UNION *, socklen_t); ++void write_blocking(CLI *, int fd, void *, int); ++void read_blocking(CLI *, int fd, void *, int); ++void fdputline(CLI *, int, const char *); ++void fdgetline(CLI *, int, char *); ++/* descriptor versions of fprintf/fscanf */ ++int fdprintf(CLI *, int, const char *, ...) ++#ifdef __GNUC__ ++ __attribute__ ((format (printf, 3, 4))); ++#else ++ ; ++#endif ++int fdscanf(CLI *, int, const char *, char *) ++#ifdef __GNUC__ ++ __attribute__ ((format (scanf, 3, 0))); ++#else ++ ; ++#endif ++ ++/**************************************** Prototype for protocol.c */ ++ ++void negotiate(CLI *c); ++ ++/**************************************** Prototypes for resolver.c */ ++ ++int name2addrlist(SOCKADDR_LIST *, char *, char *); ++int hostport2addrlist(SOCKADDR_LIST *, char *, char *); ++char *s_ntop(char *, SOCKADDR_UNION *); ++ ++/**************************************** Prototypes for sthreads.c */ ++ ++typedef enum { ++ CRIT_KEYGEN, CRIT_INET, CRIT_CLIENTS, CRIT_WIN_LOG, CRIT_SESSION, ++ CRIT_LIBWRAP, CRIT_SSL, CRIT_SECTIONS ++} SECTION_CODE; ++ ++void enter_critical_section(SECTION_CODE); ++void leave_critical_section(SECTION_CODE); ++void sthreads_init(void); ++unsigned long stunnel_process_id(void); ++unsigned long stunnel_thread_id(void); ++int create_client(int, int, CLI *, void *(*)(void *)); ++#ifdef USE_UCONTEXT ++typedef struct CONTEXT_STRUCTURE { ++ char *stack; /* CPU stack for this thread */ ++ unsigned long id; ++ ucontext_t ctx; ++ s_poll_set *fds; ++ int ready; /* number of ready file descriptors */ ++ time_t finish; /* when to finish poll() for this context */ ++ struct CONTEXT_STRUCTURE *next; /* next context on a list */ ++} CONTEXT; ++extern CONTEXT *ready_head, *ready_tail; ++extern CONTEXT *waiting_head, *waiting_tail; ++#endif ++#ifdef _WIN32_WCE ++int _beginthread(void (*)(void *), int, void *); ++void _endthread(void); ++#endif ++#ifdef DEBUG_STACK_SIZE ++void stack_info(int); ++#endif ++ ++/**************************************** Prototypes for gui.c */ ++ ++typedef struct { ++ LOCAL_OPTIONS *section; ++ char pass[PEM_BUFSIZE]; ++} UI_DATA; ++ ++#ifdef USE_WIN32 ++void win_log(char *); ++void exit_win32(int); ++int passwd_cb(char *, int, int, void *); ++#ifdef HAVE_OSSL_ENGINE_H ++int pin_cb(UI *, UI_STRING *); ++#endif ++ ++#ifndef _WIN32_WCE ++typedef int (CALLBACK * GETADDRINFO) (const char *, ++ const char *, const struct addrinfo *, struct addrinfo **); ++typedef void (CALLBACK * FREEADDRINFO) (struct addrinfo FAR *); ++typedef int (CALLBACK * GETNAMEINFO) (const struct sockaddr *, socklen_t, ++ char *, size_t, char *, size_t, int); ++extern GETADDRINFO s_getaddrinfo; ++extern FREEADDRINFO s_freeaddrinfo; ++extern GETNAMEINFO s_getnameinfo; ++#endif /* ! _WIN32_WCE */ ++#endif /* USE_WIN32 */ ++ ++/**************************************** Prototypes for file.c */ ++ ++typedef struct disk_file { ++#ifdef USE_WIN32 ++ HANDLE fh; ++#else ++ int fd; ++#endif ++ /* the inteface is prepared to easily implement buffering if needed */ ++} DISK_FILE; ++ ++#ifndef USE_WIN32 ++DISK_FILE *file_fdopen(int); ++#endif ++DISK_FILE *file_open(char *, int); ++void file_close(DISK_FILE *); ++int file_getline(DISK_FILE *, char *, int); ++int file_putline(DISK_FILE *, char *); ++ ++#ifdef USE_WIN32 ++LPTSTR str2tstr(const LPSTR); ++LPSTR tstr2str(const LPTSTR); ++#endif ++ ++/**************************************** Prototypes for libwrap.c */ ++ ++void libwrap_init(int); ++void auth_libwrap(CLI *); ++ ++#endif /* defined PROTOTYPES_H */ ++ ++/* End of prototypes.h */ -- 2.30.2