packages/net/stunnel/patches/106-stunnel-xforwardedfor.patch
jow 7f2ef75144 [packages] stunnel: add X-Forward-For support for proxying http traffic (#6753)
git-svn-id: svn://svn.openwrt.org/openwrt/packages@19859 3c298f89-4303-0410-b956-a3cf2f4a3e73
2010-02-25 14:30:06 +00:00

4928 lines
159 KiB
Diff
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

--- 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 [<filename>] | \-fd n | \-help | \-version | \-sockets
+.IP "\fB\s-1WIN32:\s0\fR" 4
+.IX Item "WIN32:"
+\&\fBstunnel\fR [ [\-install | \-uninstall | \-start | \-stop]
+ [\-quiet] [<filename>] ] | \-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 "<filename>"
+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 | <engine id>" 4
+.IX Item "engine = auto | <engine id>"
+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<connect> option) on Linux >=2.6.28
+\& remote mode (I<connect> option) 2.2.x
+\& local mode (I<exec> 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<71>
.Sp
Actuellement g<>r<EFBFBD>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<71>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 <20>\ exec\ <20>
--- 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 <buffer> between positions <start> and <stop>
+ * to insert <string> of length <len>. <start> and <stop> are updated to their
+ * new respective values, and the number of characters inserted is returned.
+ * If <len> is too long, nothing is done and -1 is returned.
+ * Note that neither <string> nor <buffer> 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_ptr<BUFFSIZE && !SSL_read_wants_write;
+ //ssl_rd && c->ssl_ptr<BUFFSIZE && !SSL_read_wants_write;
+ ssl_rd && c->ssl_ptr<c->buffsize && !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_ptr<BUFFSIZE)
+ //if(sock_rd && c->sock_ptr<BUFFSIZE)
+ if(sock_rd && c->sock_ptr<c->buffsize)
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. <ssl_ptr> points to the
+ * first byte of unread data, and <last> 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 <Michal.Trojnara@mirt.net>
+ *
+ * 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 <http://www.gnu.org/licenses>.
+ *
+ * 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_ptr<BUFFSIZE && !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_ptr<BUFFSIZE)
+ s_poll_add(&c->fds, 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_try<address_list->num; 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 <Michal.Trojnara@mirt.net>
+ *
+ * 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 <http://www.gnu.org/licenses>.
+ *
+ * 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 <starlet.h>
+#endif /* __vms */
+
+/* For nsr-tandem-nsk architecture */
+#ifdef __TANDEM
+#include <floss.h>
+#endif
+
+/* threads model */
+#ifdef USE_UCONTEXT
+#define __MAKECONTEXT_V2_SOURCE
+#include <ucontext.h>
+#endif
+
+#ifdef USE_PTHREAD
+#define THREADS
+#define _REENTRANT
+#define _THREAD_SAFE
+#include <pthread.h>
+#endif
+
+/* TCP wrapper */
+#if HAVE_TCPD_H && HAVE_LIBWRAP
+#define USE_LIBWRAP
+#endif
+
+/* Must be included before sys/stat.h for Ultrix */
+#include <sys/types.h> /* u_short, u_long */
+/* General headers */
+#include <stdio.h>
+/* Must be included before sys/stat.h for Ultrix */
+#ifndef _WIN32_WCE
+#include <errno.h>
+#endif
+#include <stdlib.h>
+#include <stdarg.h> /* va_ */
+#include <string.h>
+#include <ctype.h> /* isalnum */
+#include <time.h>
+#include <sys/stat.h> /* stat */
+#include <setjmp.h>
+
+/**************************************** 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 <winsock.h>
+#else
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#endif
+#include <windows.h>
+
+#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 <process.h> /* _beginthread */
+#include <tchar.h>
+
+#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 <inet.h>
+#include <unistd.h>
+#else /* __vms */
+#include <syslog.h>
+#endif /* __vms */
+
+ /* Unix-specific headers */
+#include <signal.h> /* signal */
+#include <sys/wait.h> /* wait */
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h> /* getrlimit */
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* getpid, fork, execvp, exit */
+#endif
+#ifdef HAVE_STROPTS_H
+#include <stropts.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h> /* for aix */
+#endif
+
+#ifndef BROKEN_POLL
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#define USE_POLL
+#else /* HAVE_POLL_H */
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#define USE_POLL
+#endif /* HAVE_SYS_POLL_H */
+#endif /* HAVE_POLL_H */
+#endif /* BROKEN_POLL */
+
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h> /* for FIONBIO */
+#endif
+#include <pwd.h>
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef __BEOS__
+#include <posix/grp.h>
+#endif
+#include <fcntl.h>
+
+#include <netinet/in.h> /* struct sockaddr_in */
+#include <sys/socket.h> /* getpeername */
+#include <arpa/inet.h> /* inet_ntoa */
+#include <sys/time.h> /* select */
+#include <sys/ioctl.h> /* ioctl */
+#include <netinet/tcp.h>
+#include <netdb.h>
+#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 <openssl/opensslconf.h>
+#if !defined(OPENSSL_THREADS) && defined(USE_PTHREAD)
+#error OpenSSL library compiled without thread support
+#endif /* !OPENSSL_THREADS && USE_PTHREAD */
+
+#include <openssl/lhash.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/crypto.h> /* for CRYPTO_* and SSLeay_version */
+#include <openssl/rand.h>
+#include <openssl/md4.h>
+#include <openssl/des.h>
+
+#ifdef HAVE_OSSL_ENGINE_H
+#include <openssl/engine.h>
+#endif /* HAVE_OSSL_ENGINE_H */
+
+#if SSLEAY_VERSION_NUMBER >= 0x00907000L
+#include <openssl/ocsp.h>
+#endif /* OpenSSL-0.9.7 */
+
+#ifdef USE_FIPS
+#include <openssl/fips.h>
+#include <openssl/fips_rand.h>
+#endif /* USE_FIPS */
+
+#else /* HAVE_OPENSSL */
+
+#include <lhash.h>
+#include <ssl.h>
+#include <err.h>
+#include <crypto.h>
+#include <md4.h>
+#include <des.h>
+
+#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 <Michal.Trojnara@mirt.net>
+ *
+ * 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 <http://www.gnu.org/licenses>.
+ *
+ * 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(&section->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(&section->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(&section->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(&section->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(&section->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(&section->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(&section->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(&section->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
+ "[<filename>] ] "
+#ifndef USE_WIN32
+ "-fd <n> "
+#endif
+ "| -help | -version | -sockets");
+ s_log(LOG_RAW, " <filename> - 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 <n> - 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 && i<max_arg) {
+ retval[i++]=ptr;
+ while(*ptr && !isspace((unsigned char)*ptr))
+ ++ptr;
+ while(*ptr && isspace((unsigned char)*ptr))
+ *ptr++='\0';
+ }
+ retval[i]=NULL; /* to show that it's null-terminated */
+ return retval;
+}
+#endif
+
+/* Parse out the facility/debug level stuff */
+
+typedef struct {
+ char *name;
+ int value;
+} facilitylevel;
+
+static int parse_debug_level(char *arg) {
+ char arg_copy[STRLEN];
+ char *string;
+ facilitylevel *fl;
+
+/* Facilities only make sense on unix */
+#if !defined (USE_WIN32) && !defined (__vms)
+ facilitylevel facilities[] = {
+ {"auth", LOG_AUTH}, {"cron", LOG_CRON}, {"daemon", LOG_DAEMON},
+ {"kern", LOG_KERN}, {"lpr", LOG_LPR}, {"mail", LOG_MAIL},
+ {"news", LOG_NEWS}, {"syslog", LOG_SYSLOG}, {"user", LOG_USER},
+ {"uucp", LOG_UUCP}, {"local0", LOG_LOCAL0}, {"local1", LOG_LOCAL1},
+ {"local2", LOG_LOCAL2}, {"local3", LOG_LOCAL3}, {"local4", LOG_LOCAL4},
+ {"local5", LOG_LOCAL5}, {"local6", LOG_LOCAL6}, {"local7", LOG_LOCAL7},
+
+ /* Some that are not on all unicies */
+#ifdef LOG_AUTHPRIV
+ {"authpriv", LOG_AUTHPRIV},
+#endif
+#ifdef LOG_FTP
+ {"ftp", LOG_FTP},
+#endif
+#ifdef LOG_NTP
+ {"ntp", LOG_NTP},
+#endif
+ {NULL, 0}
+ };
+#endif /* USE_WIN32, __vms */
+
+ facilitylevel levels[] = {
+ {"emerg", LOG_EMERG}, {"alert", LOG_ALERT},
+ {"crit", LOG_CRIT}, {"err", LOG_ERR},
+ {"warning", LOG_WARNING}, {"notice", LOG_NOTICE},
+ {"info", LOG_INFO}, {"debug", LOG_DEBUG},
+ {NULL, -1}
+ };
+
+ safecopy(arg_copy, arg);
+ string = arg_copy;
+
+/* Facilities only make sense on Unix */
+#if !defined (USE_WIN32) && !defined (__vms)
+ if(strchr(string, '.')) { /* We have a facility specified */
+ options.facility=-1;
+ string=strtok(arg_copy, "."); /* break it up */
+
+ for(fl=facilities; fl->name; ++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(&section->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 <Michal.Trojnara@mirt.net>
+ *
+ * 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 <http://www.gnu.org/licenses>.
+ *
+ * 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 <ylo@cs.hut.fi> */
+
+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 */