1109998Smarkm#if defined(NO_BUFFER) || defined(NO_IP) || defined(NO_OPENSSL)
2296465Sdelphij# error "Badness, NO_BUFFER, NO_IP or NO_OPENSSL is defined, turn them *off*"
3109998Smarkm#endif
4109998Smarkm
5109998Smarkm/* Include our bits'n'pieces */
6109998Smarkm#include "tunala.h"
7109998Smarkm
8109998Smarkm/********************************************/
9109998Smarkm/* Our local types that specify our "world" */
10109998Smarkm/********************************************/
11109998Smarkm
12296465Sdelphij/*
13296465Sdelphij * These represent running "tunnels". Eg. if you wanted to do SSL in a
14296465Sdelphij * "message-passing" scanario, the "int" file-descriptors might be replaced
15296465Sdelphij * by thread or process IDs, and the "select" code might be replaced by
16296465Sdelphij * message handling code. Whatever.
17296465Sdelphij */
18109998Smarkmtypedef struct _tunala_item_t {
19296465Sdelphij    /*
20296465Sdelphij     * The underlying SSL state machine. This is a data-only processing unit
21296465Sdelphij     * and we communicate with it by talking to its four "buffers".
22296465Sdelphij     */
23296465Sdelphij    state_machine_t sm;
24296465Sdelphij    /*
25296465Sdelphij     * The file-descriptors for the "dirty" (encrypted) side of the SSL
26296465Sdelphij     * setup. In actuality, this is typically a socket and both values are
27296465Sdelphij     * identical.
28296465Sdelphij     */
29296465Sdelphij    int dirty_read, dirty_send;
30296465Sdelphij    /*
31296465Sdelphij     * The file-descriptors for the "clean" (unencrypted) side of the SSL
32296465Sdelphij     * setup. These could be stdin/stdout, a socket (both values the same),
33296465Sdelphij     * or whatever you like.
34296465Sdelphij     */
35296465Sdelphij    int clean_read, clean_send;
36109998Smarkm} tunala_item_t;
37109998Smarkm
38296465Sdelphij/*
39296465Sdelphij * This structure is used as the data for running the main loop. Namely, in a
40109998Smarkm * network format such as this, it is stuff for select() - but as pointed out,
41109998Smarkm * when moving the real-world to somewhere else, this might be replaced by
42109998Smarkm * something entirely different. It's basically the stuff that controls when
43296465Sdelphij * it's time to do some "work".
44296465Sdelphij */
45109998Smarkmtypedef struct _select_sets_t {
46296465Sdelphij    int max;                    /* As required as the first argument to
47296465Sdelphij                                 * select() */
48296465Sdelphij    fd_set reads, sends, excepts; /* As passed to select() */
49109998Smarkm} select_sets_t;
50109998Smarkmtypedef struct _tunala_selector_t {
51296465Sdelphij    select_sets_t last_selected; /* Results of the last select() */
52296465Sdelphij    select_sets_t next_select;  /* What we'll next select on */
53109998Smarkm} tunala_selector_t;
54109998Smarkm
55296465Sdelphij/*
56296465Sdelphij * This structure is *everything*. We do it to avoid the use of globals so
57296465Sdelphij * that, for example, it would be easier to shift things around between
58296465Sdelphij * async-IO, thread-based, or multi-fork()ed (or combinations thereof).
59296465Sdelphij */
60109998Smarkmtypedef struct _tunala_world_t {
61296465Sdelphij    /* The file-descriptor we "listen" on for new connections */
62296465Sdelphij    int listen_fd;
63296465Sdelphij    /* The array of tunnels */
64296465Sdelphij    tunala_item_t *tunnels;
65296465Sdelphij    /* the number of tunnels in use and allocated, respectively */
66296465Sdelphij    unsigned int tunnels_used, tunnels_size;
67296465Sdelphij    /* Our outside "loop" context stuff */
68296465Sdelphij    tunala_selector_t selector;
69296465Sdelphij    /*
70296465Sdelphij     * Our SSL_CTX, which is configured as the SSL client or server and has
71296465Sdelphij     * the various cert-settings and callbacks configured.
72296465Sdelphij     */
73296465Sdelphij    SSL_CTX *ssl_ctx;
74296465Sdelphij    /*
75296465Sdelphij     * Simple flag with complex logic :-) Indicates whether we're an SSL
76296465Sdelphij     * server or an SSL client.
77296465Sdelphij     */
78296465Sdelphij    int server_mode;
79109998Smarkm} tunala_world_t;
80109998Smarkm
81109998Smarkm/*****************************/
82109998Smarkm/* Internal static functions */
83109998Smarkm/*****************************/
84109998Smarkm
85109998Smarkmstatic SSL_CTX *initialise_ssl_ctx(int server_mode, const char *engine_id,
86296465Sdelphij                                   const char *CAfile, const char *cert,
87296465Sdelphij                                   const char *key, const char *dcert,
88296465Sdelphij                                   const char *dkey, const char *cipher_list,
89296465Sdelphij                                   const char *dh_file,
90296465Sdelphij                                   const char *dh_special, int tmp_rsa,
91296465Sdelphij                                   int ctx_options, int out_state,
92296465Sdelphij                                   int out_verify, int verify_mode,
93296465Sdelphij                                   unsigned int verify_depth);
94296465Sdelphijstatic void selector_init(tunala_selector_t * selector);
95296465Sdelphijstatic void selector_add_listener(tunala_selector_t * selector, int fd);
96296465Sdelphijstatic void selector_add_tunala(tunala_selector_t * selector,
97296465Sdelphij                                tunala_item_t * t);
98296465Sdelphijstatic int selector_select(tunala_selector_t * selector);
99296465Sdelphij/*
100296465Sdelphij * This returns -1 for error, 0 for no new connections, or 1 for success, in
101296465Sdelphij * which case *newfd is populated.
102296465Sdelphij */
103296465Sdelphijstatic int selector_get_listener(tunala_selector_t * selector, int fd,
104296465Sdelphij                                 int *newfd);
105296465Sdelphijstatic int tunala_world_new_item(tunala_world_t * world, int fd,
106296465Sdelphij                                 const char *ip, unsigned short port,
107296465Sdelphij                                 int flipped);
108296465Sdelphijstatic void tunala_world_del_item(tunala_world_t * world, unsigned int idx);
109296465Sdelphijstatic int tunala_item_io(tunala_selector_t * selector, tunala_item_t * item);
110109998Smarkm
111109998Smarkm/*********************************************/
112109998Smarkm/* MAIN FUNCTION (and its utility functions) */
113109998Smarkm/*********************************************/
114109998Smarkm
115109998Smarkmstatic const char *def_proxyhost = "127.0.0.1:443";
116109998Smarkmstatic const char *def_listenhost = "127.0.0.1:8080";
117109998Smarkmstatic int def_max_tunnels = 50;
118109998Smarkmstatic const char *def_cacert = NULL;
119109998Smarkmstatic const char *def_cert = NULL;
120109998Smarkmstatic const char *def_key = NULL;
121109998Smarkmstatic const char *def_dcert = NULL;
122109998Smarkmstatic const char *def_dkey = NULL;
123109998Smarkmstatic const char *def_engine_id = NULL;
124109998Smarkmstatic int def_server_mode = 0;
125109998Smarkmstatic int def_flipped = 0;
126109998Smarkmstatic const char *def_cipher_list = NULL;
127109998Smarkmstatic const char *def_dh_file = NULL;
128109998Smarkmstatic const char *def_dh_special = NULL;
129160814Ssimonstatic int def_tmp_rsa = 1;
130109998Smarkmstatic int def_ctx_options = 0;
131109998Smarkmstatic int def_verify_mode = 0;
132109998Smarkmstatic unsigned int def_verify_depth = 10;
133109998Smarkmstatic int def_out_state = 0;
134109998Smarkmstatic unsigned int def_out_verify = 0;
135109998Smarkmstatic int def_out_totals = 0;
136109998Smarkmstatic int def_out_conns = 0;
137109998Smarkm
138109998Smarkmstatic const char *helpstring =
139296465Sdelphij    "\n'Tunala' (A tunneler with a New Zealand accent)\n"
140296465Sdelphij    "Usage: tunala [options], where options are from;\n"
141296465Sdelphij    " -listen [host:]<port>  (default = 127.0.0.1:8080)\n"
142296465Sdelphij    " -proxy <host>:<port>   (default = 127.0.0.1:443)\n"
143296465Sdelphij    " -maxtunnels <num>      (default = 50)\n"
144296465Sdelphij    " -cacert <path|NULL>    (default = NULL)\n"
145296465Sdelphij    " -cert <path|NULL>      (default = NULL)\n"
146296465Sdelphij    " -key <path|NULL>       (default = whatever '-cert' is)\n"
147296465Sdelphij    " -dcert <path|NULL>     (usually for DSA, default = NULL)\n"
148296465Sdelphij    " -dkey <path|NULL>      (usually for DSA, default = whatever '-dcert' is)\n"
149296465Sdelphij    " -engine <id|NULL>      (default = NULL)\n"
150296465Sdelphij    " -server <0|1>          (default = 0, ie. an SSL client)\n"
151296465Sdelphij    " -flipped <0|1>         (makes SSL servers be network clients, and vice versa)\n"
152296465Sdelphij    " -cipher <list>         (specifies cipher list to use)\n"
153296465Sdelphij    " -dh_file <path>        (a PEM file containing DH parameters to use)\n"
154296465Sdelphij    " -dh_special <NULL|generate|standard> (see below: def=NULL)\n"
155296465Sdelphij    " -no_tmp_rsa            (don't generate temporary RSA keys)\n"
156296465Sdelphij    " -no_ssl2               (disable SSLv2)\n"
157296465Sdelphij    " -no_ssl3               (disable SSLv3)\n"
158296465Sdelphij    " -no_tls1               (disable TLSv1)\n"
159296465Sdelphij    " -v_peer                (verify the peer certificate)\n"
160296465Sdelphij    " -v_strict              (do not continue if peer doesn't authenticate)\n"
161296465Sdelphij    " -v_once                (no verification in renegotiates)\n"
162296465Sdelphij    " -v_depth <num>         (limit certificate chain depth, default = 10)\n"
163296465Sdelphij    " -out_conns             (prints client connections and disconnections)\n"
164296465Sdelphij    " -out_state             (prints SSL handshake states)\n"
165296465Sdelphij    " -out_verify <0|1|2|3>  (prints certificate verification states: def=1)\n"
166296465Sdelphij    " -out_totals            (prints out byte-totals when a tunnel closes)\n"
167296465Sdelphij    " -<h|help|?>            (displays this help screen)\n"
168296465Sdelphij    "Notes:\n"
169296465Sdelphij    "(1) It is recommended to specify a cert+key when operating as an SSL server.\n"
170296465Sdelphij    "    If you only specify '-cert', the same file must contain a matching\n"
171296465Sdelphij    "    private key.\n"
172296465Sdelphij    "(2) Either dh_file or dh_special can be used to specify where DH parameters\n"
173296465Sdelphij    "    will be obtained from (or '-dh_special NULL' for the default choice) but\n"
174296465Sdelphij    "    you cannot specify both. For dh_special, 'generate' will create new DH\n"
175296465Sdelphij    "    parameters on startup, and 'standard' will use embedded parameters\n"
176296465Sdelphij    "    instead.\n"
177296465Sdelphij    "(3) Normally an ssl client connects to an ssl server - so that an 'ssl client\n"
178296465Sdelphij    "    tunala' listens for 'clean' client connections and proxies ssl, and an\n"
179296465Sdelphij    "    'ssl server tunala' listens for ssl connections and proxies 'clean'. With\n"
180296465Sdelphij    "    '-flipped 1', this behaviour is reversed so that an 'ssl server tunala'\n"
181296465Sdelphij    "    listens for clean client connections and proxies ssl (but participating\n"
182296465Sdelphij    "    as an ssl *server* in the SSL/TLS protocol), and an 'ssl client tunala'\n"
183296465Sdelphij    "    listens for ssl connections (participating as an ssl *client* in the\n"
184296465Sdelphij    "    SSL/TLS protocol) and proxies 'clean' to the end destination. This can\n"
185296465Sdelphij    "    be useful for allowing network access to 'servers' where only the server\n"
186296465Sdelphij    "    needs to authenticate the client (ie. the other way is not required).\n"
187296465Sdelphij    "    Even with client and server authentication, this 'technique' mitigates\n"
188296465Sdelphij    "    some DoS (denial-of-service) potential as it will be the network client\n"
189296465Sdelphij    "    having to perform the first private key operation rather than the other\n"
190296465Sdelphij    "    way round.\n"
191296465Sdelphij    "(4) The 'technique' used by setting '-flipped 1' is probably compatible with\n"
192296465Sdelphij    "    absolutely nothing except another complimentary instance of 'tunala'\n"
193296465Sdelphij    "    running with '-flipped 1'. :-)\n";
194109998Smarkm
195296465Sdelphij/*
196296465Sdelphij * Default DH parameters for use with "-dh_special standard" ... stolen
197296465Sdelphij * striaght from s_server.
198296465Sdelphij */
199296465Sdelphijstatic unsigned char dh512_p[] = {
200296465Sdelphij    0xDA, 0x58, 0x3C, 0x16, 0xD9, 0x85, 0x22, 0x89, 0xD0, 0xE4, 0xAF, 0x75,
201296465Sdelphij    0x6F, 0x4C, 0xCA, 0x92, 0xDD, 0x4B, 0xE5, 0x33, 0xB8, 0x04, 0xFB, 0x0F,
202296465Sdelphij    0xED, 0x94, 0xEF, 0x9C, 0x8A, 0x44, 0x03, 0xED, 0x57, 0x46, 0x50, 0xD3,
203296465Sdelphij    0x69, 0x99, 0xDB, 0x29, 0xD7, 0x76, 0x27, 0x6B, 0xA2, 0xD3, 0xD4, 0x12,
204296465Sdelphij    0xE2, 0x18, 0xF4, 0xDD, 0x1E, 0x08, 0x4C, 0xF6, 0xD8, 0x00, 0x3E, 0x7C,
205296465Sdelphij    0x47, 0x74, 0xE8, 0x33,
206296465Sdelphij};
207109998Smarkm
208296465Sdelphijstatic unsigned char dh512_g[] = {
209296465Sdelphij    0x02,
210296465Sdelphij};
211296465Sdelphij
212296465Sdelphij/*
213296465Sdelphij * And the function that parses the above "standard" parameters, again,
214296465Sdelphij * straight out of s_server.
215296465Sdelphij */
216109998Smarkmstatic DH *get_dh512(void)
217296465Sdelphij{
218296465Sdelphij    DH *dh = NULL;
219109998Smarkm
220296465Sdelphij    if ((dh = DH_new()) == NULL)
221296465Sdelphij        return (NULL);
222296465Sdelphij    dh->p = BN_bin2bn(dh512_p, sizeof(dh512_p), NULL);
223296465Sdelphij    dh->g = BN_bin2bn(dh512_g, sizeof(dh512_g), NULL);
224296465Sdelphij    if ((dh->p == NULL) || (dh->g == NULL))
225296465Sdelphij        return (NULL);
226296465Sdelphij    return (dh);
227296465Sdelphij}
228109998Smarkm
229109998Smarkm/* Various help/error messages used by main() */
230109998Smarkmstatic int usage(const char *errstr, int isunknownarg)
231109998Smarkm{
232296465Sdelphij    if (isunknownarg)
233296465Sdelphij        fprintf(stderr, "Error: unknown argument '%s'\n", errstr);
234296465Sdelphij    else
235296465Sdelphij        fprintf(stderr, "Error: %s\n", errstr);
236296465Sdelphij    fprintf(stderr, "%s\n", helpstring);
237296465Sdelphij    return 1;
238109998Smarkm}
239109998Smarkm
240109998Smarkmstatic int err_str0(const char *str0)
241109998Smarkm{
242296465Sdelphij    fprintf(stderr, "%s\n", str0);
243296465Sdelphij    return 1;
244109998Smarkm}
245109998Smarkm
246109998Smarkmstatic int err_str1(const char *fmt, const char *str1)
247109998Smarkm{
248296465Sdelphij    fprintf(stderr, fmt, str1);
249296465Sdelphij    fprintf(stderr, "\n");
250296465Sdelphij    return 1;
251109998Smarkm}
252109998Smarkm
253109998Smarkmstatic int parse_max_tunnels(const char *s, unsigned int *maxtunnels)
254109998Smarkm{
255296465Sdelphij    unsigned long l;
256296465Sdelphij    if (!int_strtoul(s, &l) || (l < 1) || (l > 1024)) {
257296465Sdelphij        fprintf(stderr, "Error, '%s' is an invalid value for "
258296465Sdelphij                "maxtunnels\n", s);
259296465Sdelphij        return 0;
260296465Sdelphij    }
261296465Sdelphij    *maxtunnels = (unsigned int)l;
262296465Sdelphij    return 1;
263109998Smarkm}
264109998Smarkm
265109998Smarkmstatic int parse_server_mode(const char *s, int *servermode)
266109998Smarkm{
267296465Sdelphij    unsigned long l;
268296465Sdelphij    if (!int_strtoul(s, &l) || (l > 1)) {
269296465Sdelphij        fprintf(stderr, "Error, '%s' is an invalid value for the "
270296465Sdelphij                "server mode\n", s);
271296465Sdelphij        return 0;
272296465Sdelphij    }
273296465Sdelphij    *servermode = (int)l;
274296465Sdelphij    return 1;
275109998Smarkm}
276109998Smarkm
277109998Smarkmstatic int parse_dh_special(const char *s, const char **dh_special)
278109998Smarkm{
279296465Sdelphij    if ((strcmp(s, "NULL") == 0) || (strcmp(s, "generate") == 0) ||
280296465Sdelphij        (strcmp(s, "standard") == 0)) {
281296465Sdelphij        *dh_special = s;
282296465Sdelphij        return 1;
283296465Sdelphij    }
284296465Sdelphij    fprintf(stderr, "Error, '%s' is an invalid value for 'dh_special'\n", s);
285296465Sdelphij    return 0;
286109998Smarkm}
287109998Smarkm
288109998Smarkmstatic int parse_verify_level(const char *s, unsigned int *verify_level)
289109998Smarkm{
290296465Sdelphij    unsigned long l;
291296465Sdelphij    if (!int_strtoul(s, &l) || (l > 3)) {
292296465Sdelphij        fprintf(stderr, "Error, '%s' is an invalid value for "
293296465Sdelphij                "out_verify\n", s);
294296465Sdelphij        return 0;
295296465Sdelphij    }
296296465Sdelphij    *verify_level = (unsigned int)l;
297296465Sdelphij    return 1;
298109998Smarkm}
299109998Smarkm
300109998Smarkmstatic int parse_verify_depth(const char *s, unsigned int *verify_depth)
301109998Smarkm{
302296465Sdelphij    unsigned long l;
303296465Sdelphij    if (!int_strtoul(s, &l) || (l < 1) || (l > 50)) {
304296465Sdelphij        fprintf(stderr, "Error, '%s' is an invalid value for "
305296465Sdelphij                "verify_depth\n", s);
306296465Sdelphij        return 0;
307296465Sdelphij    }
308296465Sdelphij    *verify_depth = (unsigned int)l;
309296465Sdelphij    return 1;
310109998Smarkm}
311109998Smarkm
312109998Smarkm/* Some fprintf format strings used when tunnels close */
313109998Smarkmstatic const char *io_stats_dirty =
314296465Sdelphij    "    SSL traffic;   %8lu bytes in, %8lu bytes out\n";
315109998Smarkmstatic const char *io_stats_clean =
316296465Sdelphij    "    clear traffic; %8lu bytes in, %8lu bytes out\n";
317109998Smarkm
318109998Smarkmint main(int argc, char *argv[])
319109998Smarkm{
320296465Sdelphij    unsigned int loop;
321296465Sdelphij    int newfd;
322296465Sdelphij    tunala_world_t world;
323296465Sdelphij    tunala_item_t *t_item;
324296465Sdelphij    const char *proxy_ip;
325296465Sdelphij    unsigned short proxy_port;
326296465Sdelphij    /* Overridables */
327296465Sdelphij    const char *proxyhost = def_proxyhost;
328296465Sdelphij    const char *listenhost = def_listenhost;
329296465Sdelphij    unsigned int max_tunnels = def_max_tunnels;
330296465Sdelphij    const char *cacert = def_cacert;
331296465Sdelphij    const char *cert = def_cert;
332296465Sdelphij    const char *key = def_key;
333296465Sdelphij    const char *dcert = def_dcert;
334296465Sdelphij    const char *dkey = def_dkey;
335296465Sdelphij    const char *engine_id = def_engine_id;
336296465Sdelphij    int server_mode = def_server_mode;
337296465Sdelphij    int flipped = def_flipped;
338296465Sdelphij    const char *cipher_list = def_cipher_list;
339296465Sdelphij    const char *dh_file = def_dh_file;
340296465Sdelphij    const char *dh_special = def_dh_special;
341296465Sdelphij    int tmp_rsa = def_tmp_rsa;
342296465Sdelphij    int ctx_options = def_ctx_options;
343296465Sdelphij    int verify_mode = def_verify_mode;
344296465Sdelphij    unsigned int verify_depth = def_verify_depth;
345296465Sdelphij    int out_state = def_out_state;
346296465Sdelphij    unsigned int out_verify = def_out_verify;
347296465Sdelphij    int out_totals = def_out_totals;
348296465Sdelphij    int out_conns = def_out_conns;
349109998Smarkm
350109998Smarkm/* Parse command-line arguments */
351296465Sdelphij next_arg:
352296465Sdelphij    argc--;
353296465Sdelphij    argv++;
354296465Sdelphij    if (argc > 0) {
355296465Sdelphij        if (strcmp(*argv, "-listen") == 0) {
356296465Sdelphij            if (argc < 2)
357296465Sdelphij                return usage("-listen requires an argument", 0);
358296465Sdelphij            argc--;
359296465Sdelphij            argv++;
360296465Sdelphij            listenhost = *argv;
361296465Sdelphij            goto next_arg;
362296465Sdelphij        } else if (strcmp(*argv, "-proxy") == 0) {
363296465Sdelphij            if (argc < 2)
364296465Sdelphij                return usage("-proxy requires an argument", 0);
365296465Sdelphij            argc--;
366296465Sdelphij            argv++;
367296465Sdelphij            proxyhost = *argv;
368296465Sdelphij            goto next_arg;
369296465Sdelphij        } else if (strcmp(*argv, "-maxtunnels") == 0) {
370296465Sdelphij            if (argc < 2)
371296465Sdelphij                return usage("-maxtunnels requires an argument", 0);
372296465Sdelphij            argc--;
373296465Sdelphij            argv++;
374296465Sdelphij            if (!parse_max_tunnels(*argv, &max_tunnels))
375296465Sdelphij                return 1;
376296465Sdelphij            goto next_arg;
377296465Sdelphij        } else if (strcmp(*argv, "-cacert") == 0) {
378296465Sdelphij            if (argc < 2)
379296465Sdelphij                return usage("-cacert requires an argument", 0);
380296465Sdelphij            argc--;
381296465Sdelphij            argv++;
382296465Sdelphij            if (strcmp(*argv, "NULL") == 0)
383296465Sdelphij                cacert = NULL;
384296465Sdelphij            else
385296465Sdelphij                cacert = *argv;
386296465Sdelphij            goto next_arg;
387296465Sdelphij        } else if (strcmp(*argv, "-cert") == 0) {
388296465Sdelphij            if (argc < 2)
389296465Sdelphij                return usage("-cert requires an argument", 0);
390296465Sdelphij            argc--;
391296465Sdelphij            argv++;
392296465Sdelphij            if (strcmp(*argv, "NULL") == 0)
393296465Sdelphij                cert = NULL;
394296465Sdelphij            else
395296465Sdelphij                cert = *argv;
396296465Sdelphij            goto next_arg;
397296465Sdelphij        } else if (strcmp(*argv, "-key") == 0) {
398296465Sdelphij            if (argc < 2)
399296465Sdelphij                return usage("-key requires an argument", 0);
400296465Sdelphij            argc--;
401296465Sdelphij            argv++;
402296465Sdelphij            if (strcmp(*argv, "NULL") == 0)
403296465Sdelphij                key = NULL;
404296465Sdelphij            else
405296465Sdelphij                key = *argv;
406296465Sdelphij            goto next_arg;
407296465Sdelphij        } else if (strcmp(*argv, "-dcert") == 0) {
408296465Sdelphij            if (argc < 2)
409296465Sdelphij                return usage("-dcert requires an argument", 0);
410296465Sdelphij            argc--;
411296465Sdelphij            argv++;
412296465Sdelphij            if (strcmp(*argv, "NULL") == 0)
413296465Sdelphij                dcert = NULL;
414296465Sdelphij            else
415296465Sdelphij                dcert = *argv;
416296465Sdelphij            goto next_arg;
417296465Sdelphij        } else if (strcmp(*argv, "-dkey") == 0) {
418296465Sdelphij            if (argc < 2)
419296465Sdelphij                return usage("-dkey requires an argument", 0);
420296465Sdelphij            argc--;
421296465Sdelphij            argv++;
422296465Sdelphij            if (strcmp(*argv, "NULL") == 0)
423296465Sdelphij                dkey = NULL;
424296465Sdelphij            else
425296465Sdelphij                dkey = *argv;
426296465Sdelphij            goto next_arg;
427296465Sdelphij        } else if (strcmp(*argv, "-engine") == 0) {
428296465Sdelphij            if (argc < 2)
429296465Sdelphij                return usage("-engine requires an argument", 0);
430296465Sdelphij            argc--;
431296465Sdelphij            argv++;
432296465Sdelphij            engine_id = *argv;
433296465Sdelphij            goto next_arg;
434296465Sdelphij        } else if (strcmp(*argv, "-server") == 0) {
435296465Sdelphij            if (argc < 2)
436296465Sdelphij                return usage("-server requires an argument", 0);
437296465Sdelphij            argc--;
438296465Sdelphij            argv++;
439296465Sdelphij            if (!parse_server_mode(*argv, &server_mode))
440296465Sdelphij                return 1;
441296465Sdelphij            goto next_arg;
442296465Sdelphij        } else if (strcmp(*argv, "-flipped") == 0) {
443296465Sdelphij            if (argc < 2)
444296465Sdelphij                return usage("-flipped requires an argument", 0);
445296465Sdelphij            argc--;
446296465Sdelphij            argv++;
447296465Sdelphij            if (!parse_server_mode(*argv, &flipped))
448296465Sdelphij                return 1;
449296465Sdelphij            goto next_arg;
450296465Sdelphij        } else if (strcmp(*argv, "-cipher") == 0) {
451296465Sdelphij            if (argc < 2)
452296465Sdelphij                return usage("-cipher requires an argument", 0);
453296465Sdelphij            argc--;
454296465Sdelphij            argv++;
455296465Sdelphij            cipher_list = *argv;
456296465Sdelphij            goto next_arg;
457296465Sdelphij        } else if (strcmp(*argv, "-dh_file") == 0) {
458296465Sdelphij            if (argc < 2)
459296465Sdelphij                return usage("-dh_file requires an argument", 0);
460296465Sdelphij            if (dh_special)
461296465Sdelphij                return usage("cannot mix -dh_file with " "-dh_special", 0);
462296465Sdelphij            argc--;
463296465Sdelphij            argv++;
464296465Sdelphij            dh_file = *argv;
465296465Sdelphij            goto next_arg;
466296465Sdelphij        } else if (strcmp(*argv, "-dh_special") == 0) {
467296465Sdelphij            if (argc < 2)
468296465Sdelphij                return usage("-dh_special requires an argument", 0);
469296465Sdelphij            if (dh_file)
470296465Sdelphij                return usage("cannot mix -dh_file with " "-dh_special", 0);
471296465Sdelphij            argc--;
472296465Sdelphij            argv++;
473296465Sdelphij            if (!parse_dh_special(*argv, &dh_special))
474296465Sdelphij                return 1;
475296465Sdelphij            goto next_arg;
476296465Sdelphij        } else if (strcmp(*argv, "-no_tmp_rsa") == 0) {
477296465Sdelphij            tmp_rsa = 0;
478296465Sdelphij            goto next_arg;
479296465Sdelphij        } else if (strcmp(*argv, "-no_ssl2") == 0) {
480296465Sdelphij            ctx_options |= SSL_OP_NO_SSLv2;
481296465Sdelphij            goto next_arg;
482296465Sdelphij        } else if (strcmp(*argv, "-no_ssl3") == 0) {
483296465Sdelphij            ctx_options |= SSL_OP_NO_SSLv3;
484296465Sdelphij            goto next_arg;
485296465Sdelphij        } else if (strcmp(*argv, "-no_tls1") == 0) {
486296465Sdelphij            ctx_options |= SSL_OP_NO_TLSv1;
487296465Sdelphij            goto next_arg;
488296465Sdelphij        } else if (strcmp(*argv, "-v_peer") == 0) {
489296465Sdelphij            verify_mode |= SSL_VERIFY_PEER;
490296465Sdelphij            goto next_arg;
491296465Sdelphij        } else if (strcmp(*argv, "-v_strict") == 0) {
492296465Sdelphij            verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
493296465Sdelphij            goto next_arg;
494296465Sdelphij        } else if (strcmp(*argv, "-v_once") == 0) {
495296465Sdelphij            verify_mode |= SSL_VERIFY_CLIENT_ONCE;
496296465Sdelphij            goto next_arg;
497296465Sdelphij        } else if (strcmp(*argv, "-v_depth") == 0) {
498296465Sdelphij            if (argc < 2)
499296465Sdelphij                return usage("-v_depth requires an argument", 0);
500296465Sdelphij            argc--;
501296465Sdelphij            argv++;
502296465Sdelphij            if (!parse_verify_depth(*argv, &verify_depth))
503296465Sdelphij                return 1;
504296465Sdelphij            goto next_arg;
505296465Sdelphij        } else if (strcmp(*argv, "-out_state") == 0) {
506296465Sdelphij            out_state = 1;
507296465Sdelphij            goto next_arg;
508296465Sdelphij        } else if (strcmp(*argv, "-out_verify") == 0) {
509296465Sdelphij            if (argc < 2)
510296465Sdelphij                return usage("-out_verify requires an argument", 0);
511296465Sdelphij            argc--;
512296465Sdelphij            argv++;
513296465Sdelphij            if (!parse_verify_level(*argv, &out_verify))
514296465Sdelphij                return 1;
515296465Sdelphij            goto next_arg;
516296465Sdelphij        } else if (strcmp(*argv, "-out_totals") == 0) {
517296465Sdelphij            out_totals = 1;
518296465Sdelphij            goto next_arg;
519296465Sdelphij        } else if (strcmp(*argv, "-out_conns") == 0) {
520296465Sdelphij            out_conns = 1;
521296465Sdelphij            goto next_arg;
522296465Sdelphij        } else if ((strcmp(*argv, "-h") == 0) ||
523296465Sdelphij                   (strcmp(*argv, "-help") == 0) ||
524296465Sdelphij                   (strcmp(*argv, "-?") == 0)) {
525296465Sdelphij            fprintf(stderr, "%s\n", helpstring);
526296465Sdelphij            return 0;
527296465Sdelphij        } else
528296465Sdelphij            return usage(*argv, 1);
529296465Sdelphij    }
530296465Sdelphij    /* Run any sanity checks we want here */
531296465Sdelphij    if (!cert && !dcert && server_mode)
532296465Sdelphij        fprintf(stderr, "WARNING: you are running an SSL server without "
533296465Sdelphij                "a certificate - this may not work!\n");
534109998Smarkm
535296465Sdelphij    /* Initialise network stuff */
536296465Sdelphij    if (!ip_initialise())
537296465Sdelphij        return err_str0("ip_initialise failed");
538296465Sdelphij    /* Create the SSL_CTX */
539296465Sdelphij    if ((world.ssl_ctx = initialise_ssl_ctx(server_mode, engine_id,
540296465Sdelphij                                            cacert, cert, key, dcert, dkey,
541296465Sdelphij                                            cipher_list, dh_file, dh_special,
542296465Sdelphij                                            tmp_rsa, ctx_options, out_state,
543296465Sdelphij                                            out_verify, verify_mode,
544296465Sdelphij                                            verify_depth)) == NULL)
545296465Sdelphij        return err_str1("initialise_ssl_ctx(engine_id=%s) failed",
546296465Sdelphij                        (engine_id == NULL) ? "NULL" : engine_id);
547296465Sdelphij    if (engine_id)
548296465Sdelphij        fprintf(stderr, "Info, engine '%s' initialised\n", engine_id);
549296465Sdelphij    /* Create the listener */
550296465Sdelphij    if ((world.listen_fd = ip_create_listener(listenhost)) == -1)
551296465Sdelphij        return err_str1("ip_create_listener(%s) failed", listenhost);
552296465Sdelphij    fprintf(stderr, "Info, listening on '%s'\n", listenhost);
553296465Sdelphij    if (!ip_parse_address(proxyhost, &proxy_ip, &proxy_port, 0))
554296465Sdelphij        return err_str1("ip_parse_address(%s) failed", proxyhost);
555296465Sdelphij    fprintf(stderr, "Info, proxying to '%s' (%d.%d.%d.%d:%d)\n", proxyhost,
556296465Sdelphij            (int)proxy_ip[0], (int)proxy_ip[1],
557296465Sdelphij            (int)proxy_ip[2], (int)proxy_ip[3], (int)proxy_port);
558296465Sdelphij    fprintf(stderr, "Info, set maxtunnels to %d\n", (int)max_tunnels);
559296465Sdelphij    fprintf(stderr, "Info, set to operate as an SSL %s\n",
560296465Sdelphij            (server_mode ? "server" : "client"));
561296465Sdelphij    /* Initialise the rest of the stuff */
562296465Sdelphij    world.tunnels_used = world.tunnels_size = 0;
563296465Sdelphij    world.tunnels = NULL;
564296465Sdelphij    world.server_mode = server_mode;
565296465Sdelphij    selector_init(&world.selector);
566109998Smarkm
567109998Smarkm/* We're ready to loop */
568296465Sdelphij main_loop:
569296465Sdelphij    /* Should we listen for *new* tunnels? */
570296465Sdelphij    if (world.tunnels_used < max_tunnels)
571296465Sdelphij        selector_add_listener(&world.selector, world.listen_fd);
572296465Sdelphij    /* We should add in our existing tunnels */
573296465Sdelphij    for (loop = 0; loop < world.tunnels_used; loop++)
574296465Sdelphij        selector_add_tunala(&world.selector, world.tunnels + loop);
575296465Sdelphij    /* Now do the select */
576296465Sdelphij    switch (selector_select(&world.selector)) {
577296465Sdelphij    case -1:
578296465Sdelphij        if (errno != EINTR) {
579296465Sdelphij            fprintf(stderr, "selector_select returned a " "badness error.\n");
580296465Sdelphij            goto shouldnt_happen;
581296465Sdelphij        }
582296465Sdelphij        fprintf(stderr, "Warn, selector interrupted by a signal\n");
583296465Sdelphij        goto main_loop;
584296465Sdelphij    case 0:
585296465Sdelphij        fprintf(stderr, "Warn, selector_select returned 0 - signal?" "?\n");
586296465Sdelphij        goto main_loop;
587296465Sdelphij    default:
588296465Sdelphij        break;
589296465Sdelphij    }
590296465Sdelphij    /* Accept new connection if we should and can */
591296465Sdelphij    if ((world.tunnels_used < max_tunnels)
592296465Sdelphij        && (selector_get_listener(&world.selector, world.listen_fd, &newfd) ==
593296465Sdelphij            1)) {
594296465Sdelphij        /* We have a new connection */
595296465Sdelphij        if (!tunala_world_new_item(&world, newfd, proxy_ip,
596296465Sdelphij                                   proxy_port, flipped))
597296465Sdelphij            fprintf(stderr, "tunala_world_new_item failed\n");
598296465Sdelphij        else if (out_conns)
599296465Sdelphij            fprintf(stderr, "Info, new tunnel opened, now up to "
600296465Sdelphij                    "%d\n", world.tunnels_used);
601296465Sdelphij    }
602296465Sdelphij    /*
603296465Sdelphij     * Give each tunnel its moment, note the while loop is because it makes
604296465Sdelphij     * the logic easier than with "for" to deal with an array that may shift
605296465Sdelphij     * because of deletes.
606296465Sdelphij     */
607296465Sdelphij    loop = 0;
608296465Sdelphij    t_item = world.tunnels;
609296465Sdelphij    while (loop < world.tunnels_used) {
610296465Sdelphij        if (!tunala_item_io(&world.selector, t_item)) {
611296465Sdelphij            /*
612296465Sdelphij             * We're closing whether for reasons of an error or a natural
613296465Sdelphij             * close. Don't increment loop or t_item because the next item is
614296465Sdelphij             * moving to us!
615296465Sdelphij             */
616296465Sdelphij            if (!out_totals)
617296465Sdelphij                goto skip_totals;
618296465Sdelphij            fprintf(stderr, "Tunnel closing, traffic stats follow\n");
619296465Sdelphij            /* Display the encrypted (over the network) stats */
620296465Sdelphij            fprintf(stderr, io_stats_dirty,
621296465Sdelphij                    buffer_total_in(state_machine_get_buffer
622296465Sdelphij                                    (&t_item->sm, SM_DIRTY_IN)),
623296465Sdelphij                    buffer_total_out(state_machine_get_buffer
624296465Sdelphij                                     (&t_item->sm, SM_DIRTY_OUT)));
625296465Sdelphij            /*
626296465Sdelphij             * Display the local (tunnelled) stats. NB: Data we *receive* is
627296465Sdelphij             * data sent *out* of the state_machine on its 'clean' side.
628296465Sdelphij             * Hence the apparent back-to-front OUT/IN mixup here :-)
629296465Sdelphij             */
630296465Sdelphij            fprintf(stderr, io_stats_clean,
631296465Sdelphij                    buffer_total_out(state_machine_get_buffer
632296465Sdelphij                                     (&t_item->sm, SM_CLEAN_OUT)),
633296465Sdelphij                    buffer_total_in(state_machine_get_buffer
634296465Sdelphij                                    (&t_item->sm, SM_CLEAN_IN)));
635296465Sdelphij skip_totals:
636296465Sdelphij            tunala_world_del_item(&world, loop);
637296465Sdelphij            if (out_conns)
638296465Sdelphij                fprintf(stderr, "Info, tunnel closed, down to %d\n",
639296465Sdelphij                        world.tunnels_used);
640296465Sdelphij        } else {
641296465Sdelphij            /* Move to the next item */
642296465Sdelphij            loop++;
643296465Sdelphij            t_item++;
644296465Sdelphij        }
645296465Sdelphij    }
646296465Sdelphij    goto main_loop;
647296465Sdelphij    /* Should never get here */
648296465Sdelphij shouldnt_happen:
649296465Sdelphij    abort();
650296465Sdelphij    return 1;
651109998Smarkm}
652109998Smarkm
653109998Smarkm/****************/
654109998Smarkm/* OpenSSL bits */
655109998Smarkm/****************/
656109998Smarkm
657109998Smarkmstatic int ctx_set_cert(SSL_CTX *ctx, const char *cert, const char *key)
658109998Smarkm{
659296465Sdelphij    FILE *fp = NULL;
660296465Sdelphij    X509 *x509 = NULL;
661296465Sdelphij    EVP_PKEY *pkey = NULL;
662296465Sdelphij    int toret = 0;              /* Assume an error */
663109998Smarkm
664296465Sdelphij    /* cert */
665296465Sdelphij    if (cert) {
666296465Sdelphij        if ((fp = fopen(cert, "r")) == NULL) {
667296465Sdelphij            fprintf(stderr, "Error opening cert file '%s'\n", cert);
668296465Sdelphij            goto err;
669296465Sdelphij        }
670296465Sdelphij        if (!PEM_read_X509(fp, &x509, NULL, NULL)) {
671296465Sdelphij            fprintf(stderr, "Error reading PEM cert from '%s'\n", cert);
672296465Sdelphij            goto err;
673296465Sdelphij        }
674296465Sdelphij        if (!SSL_CTX_use_certificate(ctx, x509)) {
675296465Sdelphij            fprintf(stderr, "Error, cert in '%s' can not be used\n", cert);
676296465Sdelphij            goto err;
677296465Sdelphij        }
678296465Sdelphij        /* Clear the FILE* for reuse in the "key" code */
679296465Sdelphij        fclose(fp);
680296465Sdelphij        fp = NULL;
681296465Sdelphij        fprintf(stderr, "Info, operating with cert in '%s'\n", cert);
682296465Sdelphij        /*
683296465Sdelphij         * If a cert was given without matching key, we assume the same file
684296465Sdelphij         * contains the required key.
685296465Sdelphij         */
686296465Sdelphij        if (!key)
687296465Sdelphij            key = cert;
688296465Sdelphij    } else {
689296465Sdelphij        if (key)
690296465Sdelphij            fprintf(stderr, "Error, can't specify a key without a "
691296465Sdelphij                    "corresponding certificate\n");
692296465Sdelphij        else
693296465Sdelphij            fprintf(stderr, "Error, ctx_set_cert called with " "NULLs!\n");
694296465Sdelphij        goto err;
695296465Sdelphij    }
696296465Sdelphij    /* key */
697296465Sdelphij    if (key) {
698296465Sdelphij        if ((fp = fopen(key, "r")) == NULL) {
699296465Sdelphij            fprintf(stderr, "Error opening key file '%s'\n", key);
700296465Sdelphij            goto err;
701296465Sdelphij        }
702296465Sdelphij        if (!PEM_read_PrivateKey(fp, &pkey, NULL, NULL)) {
703296465Sdelphij            fprintf(stderr, "Error reading PEM key from '%s'\n", key);
704296465Sdelphij            goto err;
705296465Sdelphij        }
706296465Sdelphij        if (!SSL_CTX_use_PrivateKey(ctx, pkey)) {
707296465Sdelphij            fprintf(stderr, "Error, key in '%s' can not be used\n", key);
708296465Sdelphij            goto err;
709296465Sdelphij        }
710296465Sdelphij        fprintf(stderr, "Info, operating with key in '%s'\n", key);
711296465Sdelphij    } else
712296465Sdelphij        fprintf(stderr, "Info, operating without a cert or key\n");
713296465Sdelphij    /* Success */
714296465Sdelphij    toret = 1;
715296465Sdelphij err:
716296465Sdelphij    if (x509)
717296465Sdelphij        X509_free(x509);
718296465Sdelphij    if (pkey)
719296465Sdelphij        EVP_PKEY_free(pkey);
720296465Sdelphij    if (fp)
721296465Sdelphij        fclose(fp);
722296465Sdelphij    return toret;
723109998Smarkm}
724109998Smarkm
725296465Sdelphijstatic int ctx_set_dh(SSL_CTX *ctx, const char *dh_file,
726296465Sdelphij                      const char *dh_special)
727109998Smarkm{
728296465Sdelphij    DH *dh = NULL;
729296465Sdelphij    FILE *fp = NULL;
730109998Smarkm
731296465Sdelphij    if (dh_special) {
732296465Sdelphij        if (strcmp(dh_special, "NULL") == 0)
733296465Sdelphij            return 1;
734296465Sdelphij        if (strcmp(dh_special, "standard") == 0) {
735296465Sdelphij            if ((dh = get_dh512()) == NULL) {
736296465Sdelphij                fprintf(stderr, "Error, can't parse 'standard'"
737296465Sdelphij                        " DH parameters\n");
738296465Sdelphij                return 0;
739296465Sdelphij            }
740296465Sdelphij            fprintf(stderr, "Info, using 'standard' DH parameters\n");
741296465Sdelphij            goto do_it;
742296465Sdelphij        }
743296465Sdelphij        if (strcmp(dh_special, "generate") != 0)
744296465Sdelphij            /*
745296465Sdelphij             * This shouldn't happen - screening values is handled in main().
746296465Sdelphij             */
747296465Sdelphij            abort();
748296465Sdelphij        fprintf(stderr, "Info, generating DH parameters ... ");
749296465Sdelphij        fflush(stderr);
750296465Sdelphij        if ((dh = DH_generate_parameters(512, DH_GENERATOR_5,
751296465Sdelphij                                         NULL, NULL)) == NULL) {
752296465Sdelphij            fprintf(stderr, "error!\n");
753296465Sdelphij            return 0;
754296465Sdelphij        }
755296465Sdelphij        fprintf(stderr, "complete\n");
756296465Sdelphij        goto do_it;
757296465Sdelphij    }
758296465Sdelphij    /* So, we're loading dh_file */
759296465Sdelphij    if ((fp = fopen(dh_file, "r")) == NULL) {
760296465Sdelphij        fprintf(stderr, "Error, couldn't open '%s' for DH parameters\n",
761296465Sdelphij                dh_file);
762296465Sdelphij        return 0;
763296465Sdelphij    }
764296465Sdelphij    dh = PEM_read_DHparams(fp, NULL, NULL, NULL);
765296465Sdelphij    fclose(fp);
766296465Sdelphij    if (dh == NULL) {
767296465Sdelphij        fprintf(stderr, "Error, could not parse DH parameters from '%s'\n",
768296465Sdelphij                dh_file);
769296465Sdelphij        return 0;
770296465Sdelphij    }
771296465Sdelphij    fprintf(stderr, "Info, using DH parameters from file '%s'\n", dh_file);
772296465Sdelphij do_it:
773296465Sdelphij    SSL_CTX_set_tmp_dh(ctx, dh);
774296465Sdelphij    DH_free(dh);
775296465Sdelphij    return 1;
776109998Smarkm}
777109998Smarkm
778109998Smarkmstatic SSL_CTX *initialise_ssl_ctx(int server_mode, const char *engine_id,
779296465Sdelphij                                   const char *CAfile, const char *cert,
780296465Sdelphij                                   const char *key, const char *dcert,
781296465Sdelphij                                   const char *dkey, const char *cipher_list,
782296465Sdelphij                                   const char *dh_file,
783296465Sdelphij                                   const char *dh_special, int tmp_rsa,
784296465Sdelphij                                   int ctx_options, int out_state,
785296465Sdelphij                                   int out_verify, int verify_mode,
786296465Sdelphij                                   unsigned int verify_depth)
787109998Smarkm{
788296465Sdelphij    SSL_CTX *ctx = NULL, *ret = NULL;
789296465Sdelphij    SSL_METHOD *meth;
790296465Sdelphij    ENGINE *e = NULL;
791109998Smarkm
792296465Sdelphij    OpenSSL_add_ssl_algorithms();
793296465Sdelphij    SSL_load_error_strings();
794109998Smarkm
795296465Sdelphij    meth = (server_mode ? SSLv23_server_method() : SSLv23_client_method());
796296465Sdelphij    if (meth == NULL)
797296465Sdelphij        goto err;
798296465Sdelphij    if (engine_id) {
799296465Sdelphij        ENGINE_load_builtin_engines();
800296465Sdelphij        if ((e = ENGINE_by_id(engine_id)) == NULL) {
801296465Sdelphij            fprintf(stderr, "Error obtaining '%s' engine, openssl "
802296465Sdelphij                    "errors follow\n", engine_id);
803296465Sdelphij            goto err;
804296465Sdelphij        }
805296465Sdelphij        if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
806296465Sdelphij            fprintf(stderr, "Error assigning '%s' engine, openssl "
807296465Sdelphij                    "errors follow\n", engine_id);
808296465Sdelphij            goto err;
809296465Sdelphij        }
810296465Sdelphij        ENGINE_free(e);
811296465Sdelphij    }
812296465Sdelphij    if ((ctx = SSL_CTX_new(meth)) == NULL)
813296465Sdelphij        goto err;
814296465Sdelphij    /* cacert */
815296465Sdelphij    if (CAfile) {
816296465Sdelphij        if (!X509_STORE_load_locations(SSL_CTX_get_cert_store(ctx),
817296465Sdelphij                                       CAfile, NULL)) {
818296465Sdelphij            fprintf(stderr, "Error loading CA cert(s) in '%s'\n", CAfile);
819296465Sdelphij            goto err;
820296465Sdelphij        }
821296465Sdelphij        fprintf(stderr, "Info, operating with CA cert(s) in '%s'\n", CAfile);
822296465Sdelphij    } else
823296465Sdelphij        fprintf(stderr, "Info, operating without a CA cert(-list)\n");
824296465Sdelphij    if (!SSL_CTX_set_default_verify_paths(ctx)) {
825296465Sdelphij        fprintf(stderr, "Error setting default verify paths\n");
826296465Sdelphij        goto err;
827296465Sdelphij    }
828109998Smarkm
829296465Sdelphij    /* cert and key */
830296465Sdelphij    if ((cert || key) && !ctx_set_cert(ctx, cert, key))
831296465Sdelphij        goto err;
832296465Sdelphij    /* dcert and dkey */
833296465Sdelphij    if ((dcert || dkey) && !ctx_set_cert(ctx, dcert, dkey))
834296465Sdelphij        goto err;
835296465Sdelphij    /* temporary RSA key generation */
836296465Sdelphij    if (tmp_rsa)
837296465Sdelphij        SSL_CTX_set_tmp_rsa_callback(ctx, cb_generate_tmp_rsa);
838109998Smarkm
839296465Sdelphij    /* cipher_list */
840296465Sdelphij    if (cipher_list) {
841296465Sdelphij        if (!SSL_CTX_set_cipher_list(ctx, cipher_list)) {
842296465Sdelphij            fprintf(stderr, "Error setting cipher list '%s'\n", cipher_list);
843296465Sdelphij            goto err;
844296465Sdelphij        }
845296465Sdelphij        fprintf(stderr, "Info, set cipher list '%s'\n", cipher_list);
846296465Sdelphij    } else
847296465Sdelphij        fprintf(stderr, "Info, operating with default cipher list\n");
848109998Smarkm
849296465Sdelphij    /* dh_file & dh_special */
850296465Sdelphij    if ((dh_file || dh_special) && !ctx_set_dh(ctx, dh_file, dh_special))
851296465Sdelphij        goto err;
852109998Smarkm
853296465Sdelphij    /* ctx_options */
854296465Sdelphij    SSL_CTX_set_options(ctx, ctx_options);
855109998Smarkm
856296465Sdelphij    /* out_state (output of SSL handshake states to screen). */
857296465Sdelphij    if (out_state)
858296465Sdelphij        cb_ssl_info_set_output(stderr);
859109998Smarkm
860296465Sdelphij    /* out_verify */
861296465Sdelphij    if (out_verify > 0) {
862296465Sdelphij        cb_ssl_verify_set_output(stderr);
863296465Sdelphij        cb_ssl_verify_set_level(out_verify);
864296465Sdelphij    }
865109998Smarkm
866296465Sdelphij    /* verify_depth */
867296465Sdelphij    cb_ssl_verify_set_depth(verify_depth);
868109998Smarkm
869296465Sdelphij    /* Success! (includes setting verify_mode) */
870296465Sdelphij    SSL_CTX_set_info_callback(ctx, cb_ssl_info);
871296465Sdelphij    SSL_CTX_set_verify(ctx, verify_mode, cb_ssl_verify);
872296465Sdelphij    ret = ctx;
873296465Sdelphij err:
874296465Sdelphij    if (!ret) {
875296465Sdelphij        ERR_print_errors_fp(stderr);
876296465Sdelphij        if (ctx)
877296465Sdelphij            SSL_CTX_free(ctx);
878296465Sdelphij    }
879296465Sdelphij    return ret;
880109998Smarkm}
881109998Smarkm
882109998Smarkm/*****************/
883109998Smarkm/* Selector bits */
884109998Smarkm/*****************/
885109998Smarkm
886296465Sdelphijstatic void selector_sets_init(select_sets_t * s)
887109998Smarkm{
888296465Sdelphij    s->max = 0;
889296465Sdelphij    FD_ZERO(&s->reads);
890296465Sdelphij    FD_ZERO(&s->sends);
891296465Sdelphij    FD_ZERO(&s->excepts);
892109998Smarkm}
893296465Sdelphij
894296465Sdelphijstatic void selector_init(tunala_selector_t * selector)
895109998Smarkm{
896296465Sdelphij    selector_sets_init(&selector->last_selected);
897296465Sdelphij    selector_sets_init(&selector->next_select);
898109998Smarkm}
899109998Smarkm
900109998Smarkm#define SEL_EXCEPTS 0x00
901109998Smarkm#define SEL_READS   0x01
902109998Smarkm#define SEL_SENDS   0x02
903296465Sdelphijstatic void selector_add_raw_fd(tunala_selector_t * s, int fd, int flags)
904109998Smarkm{
905296465Sdelphij    FD_SET(fd, &s->next_select.excepts);
906296465Sdelphij    if (flags & SEL_READS)
907296465Sdelphij        FD_SET(fd, &s->next_select.reads);
908296465Sdelphij    if (flags & SEL_SENDS)
909296465Sdelphij        FD_SET(fd, &s->next_select.sends);
910296465Sdelphij    /* Adjust "max" */
911296465Sdelphij    if (s->next_select.max < (fd + 1))
912296465Sdelphij        s->next_select.max = fd + 1;
913109998Smarkm}
914109998Smarkm
915296465Sdelphijstatic void selector_add_listener(tunala_selector_t * selector, int fd)
916109998Smarkm{
917296465Sdelphij    selector_add_raw_fd(selector, fd, SEL_READS);
918109998Smarkm}
919109998Smarkm
920296465Sdelphijstatic void selector_add_tunala(tunala_selector_t * s, tunala_item_t * t)
921109998Smarkm{
922296465Sdelphij    /* Set clean read if sm.clean_in is not full */
923296465Sdelphij    if (t->clean_read != -1) {
924296465Sdelphij        selector_add_raw_fd(s, t->clean_read,
925296465Sdelphij                            (buffer_full(state_machine_get_buffer(&t->sm,
926296465Sdelphij                                                                  SM_CLEAN_IN))
927296465Sdelphij                             ? SEL_EXCEPTS : SEL_READS));
928296465Sdelphij    }
929296465Sdelphij    /* Set clean send if sm.clean_out is not empty */
930296465Sdelphij    if (t->clean_send != -1) {
931296465Sdelphij        selector_add_raw_fd(s, t->clean_send,
932296465Sdelphij                            (buffer_empty(state_machine_get_buffer(&t->sm,
933296465Sdelphij                                                                   SM_CLEAN_OUT))
934296465Sdelphij                             ? SEL_EXCEPTS : SEL_SENDS));
935296465Sdelphij    }
936296465Sdelphij    /* Set dirty read if sm.dirty_in is not full */
937296465Sdelphij    if (t->dirty_read != -1) {
938296465Sdelphij        selector_add_raw_fd(s, t->dirty_read,
939296465Sdelphij                            (buffer_full(state_machine_get_buffer(&t->sm,
940296465Sdelphij                                                                  SM_DIRTY_IN))
941296465Sdelphij                             ? SEL_EXCEPTS : SEL_READS));
942296465Sdelphij    }
943296465Sdelphij    /* Set dirty send if sm.dirty_out is not empty */
944296465Sdelphij    if (t->dirty_send != -1) {
945296465Sdelphij        selector_add_raw_fd(s, t->dirty_send,
946296465Sdelphij                            (buffer_empty(state_machine_get_buffer(&t->sm,
947296465Sdelphij                                                                   SM_DIRTY_OUT))
948296465Sdelphij                             ? SEL_EXCEPTS : SEL_SENDS));
949296465Sdelphij    }
950109998Smarkm}
951109998Smarkm
952296465Sdelphijstatic int selector_select(tunala_selector_t * selector)
953109998Smarkm{
954296465Sdelphij    memcpy(&selector->last_selected, &selector->next_select,
955296465Sdelphij           sizeof(select_sets_t));
956296465Sdelphij    selector_sets_init(&selector->next_select);
957296465Sdelphij    return select(selector->last_selected.max,
958296465Sdelphij                  &selector->last_selected.reads,
959296465Sdelphij                  &selector->last_selected.sends,
960296465Sdelphij                  &selector->last_selected.excepts, NULL);
961109998Smarkm}
962109998Smarkm
963296465Sdelphij/*
964296465Sdelphij * This returns -1 for error, 0 for no new connections, or 1 for success, in
965296465Sdelphij * which case *newfd is populated.
966296465Sdelphij */
967296465Sdelphijstatic int selector_get_listener(tunala_selector_t * selector, int fd,
968296465Sdelphij                                 int *newfd)
969109998Smarkm{
970296465Sdelphij    if (FD_ISSET(fd, &selector->last_selected.excepts))
971296465Sdelphij        return -1;
972296465Sdelphij    if (!FD_ISSET(fd, &selector->last_selected.reads))
973296465Sdelphij        return 0;
974296465Sdelphij    if ((*newfd = ip_accept_connection(fd)) == -1)
975296465Sdelphij        return -1;
976296465Sdelphij    return 1;
977109998Smarkm}
978109998Smarkm
979109998Smarkm/************************/
980109998Smarkm/* "Tunala" world stuff */
981109998Smarkm/************************/
982109998Smarkm
983296465Sdelphijstatic int tunala_world_make_room(tunala_world_t * world)
984109998Smarkm{
985296465Sdelphij    unsigned int newsize;
986296465Sdelphij    tunala_item_t *newarray;
987109998Smarkm
988296465Sdelphij    if (world->tunnels_used < world->tunnels_size)
989296465Sdelphij        return 1;
990296465Sdelphij    newsize = (world->tunnels_size == 0 ? 16 :
991296465Sdelphij               ((world->tunnels_size * 3) / 2));
992296465Sdelphij    if ((newarray = malloc(newsize * sizeof(tunala_item_t))) == NULL)
993296465Sdelphij        return 0;
994296465Sdelphij    memset(newarray, 0, newsize * sizeof(tunala_item_t));
995296465Sdelphij    if (world->tunnels_used > 0)
996296465Sdelphij        memcpy(newarray, world->tunnels,
997296465Sdelphij               world->tunnels_used * sizeof(tunala_item_t));
998296465Sdelphij    if (world->tunnels_size > 0)
999296465Sdelphij        free(world->tunnels);
1000296465Sdelphij    /* migrate */
1001296465Sdelphij    world->tunnels = newarray;
1002296465Sdelphij    world->tunnels_size = newsize;
1003296465Sdelphij    return 1;
1004109998Smarkm}
1005109998Smarkm
1006296465Sdelphijstatic int tunala_world_new_item(tunala_world_t * world, int fd,
1007296465Sdelphij                                 const char *ip, unsigned short port,
1008296465Sdelphij                                 int flipped)
1009109998Smarkm{
1010296465Sdelphij    tunala_item_t *item;
1011296465Sdelphij    int newfd;
1012296465Sdelphij    SSL *new_ssl = NULL;
1013109998Smarkm
1014296465Sdelphij    if (!tunala_world_make_room(world))
1015296465Sdelphij        return 0;
1016296465Sdelphij    if ((new_ssl = SSL_new(world->ssl_ctx)) == NULL) {
1017296465Sdelphij        fprintf(stderr, "Error creating new SSL\n");
1018296465Sdelphij        ERR_print_errors_fp(stderr);
1019296465Sdelphij        return 0;
1020296465Sdelphij    }
1021296465Sdelphij    item = world->tunnels + (world->tunnels_used++);
1022296465Sdelphij    state_machine_init(&item->sm);
1023296465Sdelphij    item->clean_read = item->clean_send =
1024296465Sdelphij        item->dirty_read = item->dirty_send = -1;
1025296465Sdelphij    if ((newfd = ip_create_connection_split(ip, port)) == -1)
1026296465Sdelphij        goto err;
1027296465Sdelphij    /*
1028296465Sdelphij     * Which way round? If we're a server, "fd" is the dirty side and the
1029296465Sdelphij     * connection we open is the clean one. For a client, it's the other way
1030296465Sdelphij     * around. Unless, of course, we're "flipped" in which case everything
1031296465Sdelphij     * gets reversed. :-)
1032296465Sdelphij     */
1033296465Sdelphij    if ((world->server_mode && !flipped) || (!world->server_mode && flipped)) {
1034296465Sdelphij        item->dirty_read = item->dirty_send = fd;
1035296465Sdelphij        item->clean_read = item->clean_send = newfd;
1036296465Sdelphij    } else {
1037296465Sdelphij        item->clean_read = item->clean_send = fd;
1038296465Sdelphij        item->dirty_read = item->dirty_send = newfd;
1039296465Sdelphij    }
1040296465Sdelphij    /*
1041296465Sdelphij     * We use the SSL's "app_data" to indicate a call-back induced "kill"
1042296465Sdelphij     */
1043296465Sdelphij    SSL_set_app_data(new_ssl, NULL);
1044296465Sdelphij    if (!state_machine_set_SSL(&item->sm, new_ssl, world->server_mode))
1045296465Sdelphij        goto err;
1046296465Sdelphij    return 1;
1047296465Sdelphij err:
1048296465Sdelphij    tunala_world_del_item(world, world->tunnels_used - 1);
1049296465Sdelphij    return 0;
1050109998Smarkm
1051109998Smarkm}
1052109998Smarkm
1053296465Sdelphijstatic void tunala_world_del_item(tunala_world_t * world, unsigned int idx)
1054109998Smarkm{
1055296465Sdelphij    tunala_item_t *item = world->tunnels + idx;
1056296465Sdelphij    if (item->clean_read != -1)
1057296465Sdelphij        close(item->clean_read);
1058296465Sdelphij    if (item->clean_send != item->clean_read)
1059296465Sdelphij        close(item->clean_send);
1060296465Sdelphij    item->clean_read = item->clean_send = -1;
1061296465Sdelphij    if (item->dirty_read != -1)
1062296465Sdelphij        close(item->dirty_read);
1063296465Sdelphij    if (item->dirty_send != item->dirty_read)
1064296465Sdelphij        close(item->dirty_send);
1065296465Sdelphij    item->dirty_read = item->dirty_send = -1;
1066296465Sdelphij    state_machine_close(&item->sm);
1067296465Sdelphij    /* OK, now we fix the item array */
1068296465Sdelphij    if (idx + 1 < world->tunnels_used)
1069296465Sdelphij        /* We need to scroll entries to the left */
1070296465Sdelphij        memmove(world->tunnels + idx,
1071296465Sdelphij                world->tunnels + (idx + 1),
1072296465Sdelphij                (world->tunnels_used - (idx + 1)) * sizeof(tunala_item_t));
1073296465Sdelphij    world->tunnels_used--;
1074109998Smarkm}
1075109998Smarkm
1076296465Sdelphijstatic int tunala_item_io(tunala_selector_t * selector, tunala_item_t * item)
1077109998Smarkm{
1078296465Sdelphij    int c_r, c_s, d_r, d_s;     /* Four boolean flags */
1079109998Smarkm
1080296465Sdelphij    /* Take ourselves out of the gene-pool if there was an except */
1081296465Sdelphij    if ((item->clean_read != -1) && FD_ISSET(item->clean_read,
1082296465Sdelphij                                             &selector->
1083296465Sdelphij                                             last_selected.excepts))
1084296465Sdelphij        return 0;
1085296465Sdelphij    if ((item->clean_send != -1) && FD_ISSET(item->clean_send,
1086296465Sdelphij                                             &selector->
1087296465Sdelphij                                             last_selected.excepts))
1088296465Sdelphij        return 0;
1089296465Sdelphij    if ((item->dirty_read != -1) && FD_ISSET(item->dirty_read,
1090296465Sdelphij                                             &selector->
1091296465Sdelphij                                             last_selected.excepts))
1092296465Sdelphij        return 0;
1093296465Sdelphij    if ((item->dirty_send != -1) && FD_ISSET(item->dirty_send,
1094296465Sdelphij                                             &selector->
1095296465Sdelphij                                             last_selected.excepts))
1096296465Sdelphij        return 0;
1097296465Sdelphij    /* Grab our 4 IO flags */
1098296465Sdelphij    c_r = c_s = d_r = d_s = 0;
1099296465Sdelphij    if (item->clean_read != -1)
1100296465Sdelphij        c_r = FD_ISSET(item->clean_read, &selector->last_selected.reads);
1101296465Sdelphij    if (item->clean_send != -1)
1102296465Sdelphij        c_s = FD_ISSET(item->clean_send, &selector->last_selected.sends);
1103296465Sdelphij    if (item->dirty_read != -1)
1104296465Sdelphij        d_r = FD_ISSET(item->dirty_read, &selector->last_selected.reads);
1105296465Sdelphij    if (item->dirty_send != -1)
1106296465Sdelphij        d_s = FD_ISSET(item->dirty_send, &selector->last_selected.sends);
1107296465Sdelphij    /* If no IO has happened for us, skip needless data looping */
1108296465Sdelphij    if (!c_r && !c_s && !d_r && !d_s)
1109296465Sdelphij        return 1;
1110296465Sdelphij    if (c_r)
1111296465Sdelphij        c_r = (buffer_from_fd(state_machine_get_buffer(&item->sm,
1112296465Sdelphij                                                       SM_CLEAN_IN),
1113296465Sdelphij                              item->clean_read) <= 0);
1114296465Sdelphij    if (c_s)
1115296465Sdelphij        c_s = (buffer_to_fd(state_machine_get_buffer(&item->sm,
1116296465Sdelphij                                                     SM_CLEAN_OUT),
1117296465Sdelphij                            item->clean_send) <= 0);
1118296465Sdelphij    if (d_r)
1119296465Sdelphij        d_r = (buffer_from_fd(state_machine_get_buffer(&item->sm,
1120296465Sdelphij                                                       SM_DIRTY_IN),
1121296465Sdelphij                              item->dirty_read) <= 0);
1122296465Sdelphij    if (d_s)
1123296465Sdelphij        d_s = (buffer_to_fd(state_machine_get_buffer(&item->sm,
1124296465Sdelphij                                                     SM_DIRTY_OUT),
1125296465Sdelphij                            item->dirty_send) <= 0);
1126296465Sdelphij    /* If any of the flags is non-zero, that means they need closing */
1127296465Sdelphij    if (c_r) {
1128296465Sdelphij        close(item->clean_read);
1129296465Sdelphij        if (item->clean_send == item->clean_read)
1130296465Sdelphij            item->clean_send = -1;
1131296465Sdelphij        item->clean_read = -1;
1132296465Sdelphij    }
1133296465Sdelphij    if (c_s && (item->clean_send != -1)) {
1134296465Sdelphij        close(item->clean_send);
1135296465Sdelphij        if (item->clean_send == item->clean_read)
1136296465Sdelphij            item->clean_read = -1;
1137296465Sdelphij        item->clean_send = -1;
1138296465Sdelphij    }
1139296465Sdelphij    if (d_r) {
1140296465Sdelphij        close(item->dirty_read);
1141296465Sdelphij        if (item->dirty_send == item->dirty_read)
1142296465Sdelphij            item->dirty_send = -1;
1143296465Sdelphij        item->dirty_read = -1;
1144296465Sdelphij    }
1145296465Sdelphij    if (d_s && (item->dirty_send != -1)) {
1146296465Sdelphij        close(item->dirty_send);
1147296465Sdelphij        if (item->dirty_send == item->dirty_read)
1148296465Sdelphij            item->dirty_read = -1;
1149296465Sdelphij        item->dirty_send = -1;
1150296465Sdelphij    }
1151296465Sdelphij    /*
1152296465Sdelphij     * This function name is attributed to the term donated by David Schwartz
1153296465Sdelphij     * on openssl-dev, message-ID:
1154296465Sdelphij     * <NCBBLIEPOCbmasEKBEAKEEDGLIAA.davids@webmaster.com>. :-)
1155296465Sdelphij     */
1156296465Sdelphij    if (!state_machine_churn(&item->sm))
1157296465Sdelphij        /*
1158296465Sdelphij         * If the SSL closes, it will also zero-out the _in buffers and will
1159296465Sdelphij         * in future process just outgoing data. As and when the outgoing
1160296465Sdelphij         * data has gone, it will return zero here to tell us to bail out.
1161296465Sdelphij         */
1162296465Sdelphij        return 0;
1163296465Sdelphij    /* Otherwise, we return zero if both sides are dead. */
1164296465Sdelphij    if (((item->clean_read == -1) || (item->clean_send == -1)) &&
1165296465Sdelphij        ((item->dirty_read == -1) || (item->dirty_send == -1)))
1166296465Sdelphij        return 0;
1167296465Sdelphij    /*
1168296465Sdelphij     * If only one side closed, notify the SSL of this so it can take
1169296465Sdelphij     * appropriate action.
1170296465Sdelphij     */
1171296465Sdelphij    if ((item->clean_read == -1) || (item->clean_send == -1)) {
1172296465Sdelphij        if (!state_machine_close_clean(&item->sm))
1173296465Sdelphij            return 0;
1174296465Sdelphij    }
1175296465Sdelphij    if ((item->dirty_read == -1) || (item->dirty_send == -1)) {
1176296465Sdelphij        if (!state_machine_close_dirty(&item->sm))
1177296465Sdelphij            return 0;
1178296465Sdelphij    }
1179296465Sdelphij    return 1;
1180109998Smarkm}
1181