xref: /minix3/crypto/external/bsd/openssl/dist/demos/tunala/tunala.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1ebfedea0SLionel Sambuc #if defined(NO_BUFFER) || defined(NO_IP) || defined(NO_OPENSSL)
2ebfedea0SLionel Sambuc # error "Badness, NO_BUFFER, NO_IP or NO_OPENSSL is defined, turn them *off*"
3ebfedea0SLionel Sambuc #endif
4ebfedea0SLionel Sambuc 
5ebfedea0SLionel Sambuc /* Include our bits'n'pieces */
6ebfedea0SLionel Sambuc #include "tunala.h"
7ebfedea0SLionel Sambuc 
8ebfedea0SLionel Sambuc /********************************************/
9ebfedea0SLionel Sambuc /* Our local types that specify our "world" */
10ebfedea0SLionel Sambuc /********************************************/
11ebfedea0SLionel Sambuc 
12*0a6a1f1dSLionel Sambuc /*
13*0a6a1f1dSLionel Sambuc  * These represent running "tunnels". Eg. if you wanted to do SSL in a
14*0a6a1f1dSLionel Sambuc  * "message-passing" scanario, the "int" file-descriptors might be replaced
15*0a6a1f1dSLionel Sambuc  * by thread or process IDs, and the "select" code might be replaced by
16*0a6a1f1dSLionel Sambuc  * message handling code. Whatever.
17*0a6a1f1dSLionel Sambuc  */
18ebfedea0SLionel Sambuc typedef struct _tunala_item_t {
19*0a6a1f1dSLionel Sambuc     /*
20*0a6a1f1dSLionel Sambuc      * The underlying SSL state machine. This is a data-only processing unit
21*0a6a1f1dSLionel Sambuc      * and we communicate with it by talking to its four "buffers".
22*0a6a1f1dSLionel Sambuc      */
23ebfedea0SLionel Sambuc     state_machine_t sm;
24*0a6a1f1dSLionel Sambuc     /*
25*0a6a1f1dSLionel Sambuc      * The file-descriptors for the "dirty" (encrypted) side of the SSL
26ebfedea0SLionel Sambuc      * setup. In actuality, this is typically a socket and both values are
27*0a6a1f1dSLionel Sambuc      * identical.
28*0a6a1f1dSLionel Sambuc      */
29ebfedea0SLionel Sambuc     int dirty_read, dirty_send;
30*0a6a1f1dSLionel Sambuc     /*
31*0a6a1f1dSLionel Sambuc      * The file-descriptors for the "clean" (unencrypted) side of the SSL
32ebfedea0SLionel Sambuc      * setup. These could be stdin/stdout, a socket (both values the same),
33*0a6a1f1dSLionel Sambuc      * or whatever you like.
34*0a6a1f1dSLionel Sambuc      */
35ebfedea0SLionel Sambuc     int clean_read, clean_send;
36ebfedea0SLionel Sambuc } tunala_item_t;
37ebfedea0SLionel Sambuc 
38*0a6a1f1dSLionel Sambuc /*
39*0a6a1f1dSLionel Sambuc  * This structure is used as the data for running the main loop. Namely, in a
40ebfedea0SLionel Sambuc  * network format such as this, it is stuff for select() - but as pointed out,
41ebfedea0SLionel Sambuc  * when moving the real-world to somewhere else, this might be replaced by
42ebfedea0SLionel Sambuc  * something entirely different. It's basically the stuff that controls when
43*0a6a1f1dSLionel Sambuc  * it's time to do some "work".
44*0a6a1f1dSLionel Sambuc  */
45ebfedea0SLionel Sambuc typedef struct _select_sets_t {
46*0a6a1f1dSLionel Sambuc     int max;                    /* As required as the first argument to
47*0a6a1f1dSLionel Sambuc                                  * select() */
48ebfedea0SLionel Sambuc     fd_set reads, sends, excepts; /* As passed to select() */
49ebfedea0SLionel Sambuc } select_sets_t;
50ebfedea0SLionel Sambuc typedef struct _tunala_selector_t {
51ebfedea0SLionel Sambuc     select_sets_t last_selected; /* Results of the last select() */
52ebfedea0SLionel Sambuc     select_sets_t next_select;  /* What we'll next select on */
53ebfedea0SLionel Sambuc } tunala_selector_t;
54ebfedea0SLionel Sambuc 
55*0a6a1f1dSLionel Sambuc /*
56*0a6a1f1dSLionel Sambuc  * This structure is *everything*. We do it to avoid the use of globals so
57*0a6a1f1dSLionel Sambuc  * that, for example, it would be easier to shift things around between
58*0a6a1f1dSLionel Sambuc  * async-IO, thread-based, or multi-fork()ed (or combinations thereof).
59*0a6a1f1dSLionel Sambuc  */
60ebfedea0SLionel Sambuc typedef struct _tunala_world_t {
61ebfedea0SLionel Sambuc     /* The file-descriptor we "listen" on for new connections */
62ebfedea0SLionel Sambuc     int listen_fd;
63ebfedea0SLionel Sambuc     /* The array of tunnels */
64ebfedea0SLionel Sambuc     tunala_item_t *tunnels;
65ebfedea0SLionel Sambuc     /* the number of tunnels in use and allocated, respectively */
66ebfedea0SLionel Sambuc     unsigned int tunnels_used, tunnels_size;
67ebfedea0SLionel Sambuc     /* Our outside "loop" context stuff */
68ebfedea0SLionel Sambuc     tunala_selector_t selector;
69*0a6a1f1dSLionel Sambuc     /*
70*0a6a1f1dSLionel Sambuc      * Our SSL_CTX, which is configured as the SSL client or server and has
71*0a6a1f1dSLionel Sambuc      * the various cert-settings and callbacks configured.
72*0a6a1f1dSLionel Sambuc      */
73ebfedea0SLionel Sambuc     SSL_CTX *ssl_ctx;
74*0a6a1f1dSLionel Sambuc     /*
75*0a6a1f1dSLionel Sambuc      * Simple flag with complex logic :-) Indicates whether we're an SSL
76*0a6a1f1dSLionel Sambuc      * server or an SSL client.
77*0a6a1f1dSLionel Sambuc      */
78ebfedea0SLionel Sambuc     int server_mode;
79ebfedea0SLionel Sambuc } tunala_world_t;
80ebfedea0SLionel Sambuc 
81ebfedea0SLionel Sambuc /*****************************/
82ebfedea0SLionel Sambuc /* Internal static functions */
83ebfedea0SLionel Sambuc /*****************************/
84ebfedea0SLionel Sambuc 
85ebfedea0SLionel Sambuc static SSL_CTX *initialise_ssl_ctx(int server_mode, const char *engine_id,
86*0a6a1f1dSLionel Sambuc                                    const char *CAfile, const char *cert,
87*0a6a1f1dSLionel Sambuc                                    const char *key, const char *dcert,
88*0a6a1f1dSLionel Sambuc                                    const char *dkey, const char *cipher_list,
89*0a6a1f1dSLionel Sambuc                                    const char *dh_file,
90*0a6a1f1dSLionel Sambuc                                    const char *dh_special, int tmp_rsa,
91*0a6a1f1dSLionel Sambuc                                    int ctx_options, int out_state,
92*0a6a1f1dSLionel Sambuc                                    int out_verify, int verify_mode,
93ebfedea0SLionel Sambuc                                    unsigned int verify_depth);
94ebfedea0SLionel Sambuc static void selector_init(tunala_selector_t * selector);
95ebfedea0SLionel Sambuc static void selector_add_listener(tunala_selector_t * selector, int fd);
96*0a6a1f1dSLionel Sambuc static void selector_add_tunala(tunala_selector_t * selector,
97*0a6a1f1dSLionel Sambuc                                 tunala_item_t * t);
98ebfedea0SLionel Sambuc static int selector_select(tunala_selector_t * selector);
99*0a6a1f1dSLionel Sambuc /*
100*0a6a1f1dSLionel Sambuc  * This returns -1 for error, 0 for no new connections, or 1 for success, in
101*0a6a1f1dSLionel Sambuc  * which case *newfd is populated.
102*0a6a1f1dSLionel Sambuc  */
103*0a6a1f1dSLionel Sambuc static int selector_get_listener(tunala_selector_t * selector, int fd,
104*0a6a1f1dSLionel Sambuc                                  int *newfd);
105ebfedea0SLionel Sambuc static int tunala_world_new_item(tunala_world_t * world, int fd,
106*0a6a1f1dSLionel Sambuc                                  const char *ip, unsigned short port,
107*0a6a1f1dSLionel Sambuc                                  int flipped);
108ebfedea0SLionel Sambuc static void tunala_world_del_item(tunala_world_t * world, unsigned int idx);
109ebfedea0SLionel Sambuc static int tunala_item_io(tunala_selector_t * selector, tunala_item_t * item);
110ebfedea0SLionel Sambuc 
111ebfedea0SLionel Sambuc /*********************************************/
112ebfedea0SLionel Sambuc /* MAIN FUNCTION (and its utility functions) */
113ebfedea0SLionel Sambuc /*********************************************/
114ebfedea0SLionel Sambuc 
115ebfedea0SLionel Sambuc static const char *def_proxyhost = "127.0.0.1:443";
116ebfedea0SLionel Sambuc static const char *def_listenhost = "127.0.0.1:8080";
117ebfedea0SLionel Sambuc static int def_max_tunnels = 50;
118ebfedea0SLionel Sambuc static const char *def_cacert = NULL;
119ebfedea0SLionel Sambuc static const char *def_cert = NULL;
120ebfedea0SLionel Sambuc static const char *def_key = NULL;
121ebfedea0SLionel Sambuc static const char *def_dcert = NULL;
122ebfedea0SLionel Sambuc static const char *def_dkey = NULL;
123ebfedea0SLionel Sambuc static const char *def_engine_id = NULL;
124ebfedea0SLionel Sambuc static int def_server_mode = 0;
125ebfedea0SLionel Sambuc static int def_flipped = 0;
126ebfedea0SLionel Sambuc static const char *def_cipher_list = NULL;
127ebfedea0SLionel Sambuc static const char *def_dh_file = NULL;
128ebfedea0SLionel Sambuc static const char *def_dh_special = NULL;
129ebfedea0SLionel Sambuc static int def_tmp_rsa = 1;
130ebfedea0SLionel Sambuc static int def_ctx_options = 0;
131ebfedea0SLionel Sambuc static int def_verify_mode = 0;
132ebfedea0SLionel Sambuc static unsigned int def_verify_depth = 10;
133ebfedea0SLionel Sambuc static int def_out_state = 0;
134ebfedea0SLionel Sambuc static unsigned int def_out_verify = 0;
135ebfedea0SLionel Sambuc static int def_out_totals = 0;
136ebfedea0SLionel Sambuc static int def_out_conns = 0;
137ebfedea0SLionel Sambuc 
138ebfedea0SLionel Sambuc static const char *helpstring =
139ebfedea0SLionel Sambuc     "\n'Tunala' (A tunneler with a New Zealand accent)\n"
140ebfedea0SLionel Sambuc     "Usage: tunala [options], where options are from;\n"
141ebfedea0SLionel Sambuc     " -listen [host:]<port>  (default = 127.0.0.1:8080)\n"
142ebfedea0SLionel Sambuc     " -proxy <host>:<port>   (default = 127.0.0.1:443)\n"
143ebfedea0SLionel Sambuc     " -maxtunnels <num>      (default = 50)\n"
144ebfedea0SLionel Sambuc     " -cacert <path|NULL>    (default = NULL)\n"
145ebfedea0SLionel Sambuc     " -cert <path|NULL>      (default = NULL)\n"
146ebfedea0SLionel Sambuc     " -key <path|NULL>       (default = whatever '-cert' is)\n"
147ebfedea0SLionel Sambuc     " -dcert <path|NULL>     (usually for DSA, default = NULL)\n"
148ebfedea0SLionel Sambuc     " -dkey <path|NULL>      (usually for DSA, default = whatever '-dcert' is)\n"
149ebfedea0SLionel Sambuc     " -engine <id|NULL>      (default = NULL)\n"
150ebfedea0SLionel Sambuc     " -server <0|1>          (default = 0, ie. an SSL client)\n"
151ebfedea0SLionel Sambuc     " -flipped <0|1>         (makes SSL servers be network clients, and vice versa)\n"
152ebfedea0SLionel Sambuc     " -cipher <list>         (specifies cipher list to use)\n"
153ebfedea0SLionel Sambuc     " -dh_file <path>        (a PEM file containing DH parameters to use)\n"
154ebfedea0SLionel Sambuc     " -dh_special <NULL|generate|standard> (see below: def=NULL)\n"
155ebfedea0SLionel Sambuc     " -no_tmp_rsa            (don't generate temporary RSA keys)\n"
156ebfedea0SLionel Sambuc     " -no_ssl2               (disable SSLv2)\n"
157ebfedea0SLionel Sambuc     " -no_ssl3               (disable SSLv3)\n"
158ebfedea0SLionel Sambuc     " -no_tls1               (disable TLSv1)\n"
159ebfedea0SLionel Sambuc     " -v_peer                (verify the peer certificate)\n"
160ebfedea0SLionel Sambuc     " -v_strict              (do not continue if peer doesn't authenticate)\n"
161ebfedea0SLionel Sambuc     " -v_once                (no verification in renegotiates)\n"
162ebfedea0SLionel Sambuc     " -v_depth <num>         (limit certificate chain depth, default = 10)\n"
163ebfedea0SLionel Sambuc     " -out_conns             (prints client connections and disconnections)\n"
164ebfedea0SLionel Sambuc     " -out_state             (prints SSL handshake states)\n"
165ebfedea0SLionel Sambuc     " -out_verify <0|1|2|3>  (prints certificate verification states: def=1)\n"
166ebfedea0SLionel Sambuc     " -out_totals            (prints out byte-totals when a tunnel closes)\n"
167ebfedea0SLionel Sambuc     " -<h|help|?>            (displays this help screen)\n"
168ebfedea0SLionel Sambuc     "Notes:\n"
169ebfedea0SLionel Sambuc     "(1) It is recommended to specify a cert+key when operating as an SSL server.\n"
170ebfedea0SLionel Sambuc     "    If you only specify '-cert', the same file must contain a matching\n"
171ebfedea0SLionel Sambuc     "    private key.\n"
172ebfedea0SLionel Sambuc     "(2) Either dh_file or dh_special can be used to specify where DH parameters\n"
173ebfedea0SLionel Sambuc     "    will be obtained from (or '-dh_special NULL' for the default choice) but\n"
174ebfedea0SLionel Sambuc     "    you cannot specify both. For dh_special, 'generate' will create new DH\n"
175ebfedea0SLionel Sambuc     "    parameters on startup, and 'standard' will use embedded parameters\n"
176ebfedea0SLionel Sambuc     "    instead.\n"
177ebfedea0SLionel Sambuc     "(3) Normally an ssl client connects to an ssl server - so that an 'ssl client\n"
178ebfedea0SLionel Sambuc     "    tunala' listens for 'clean' client connections and proxies ssl, and an\n"
179ebfedea0SLionel Sambuc     "    'ssl server tunala' listens for ssl connections and proxies 'clean'. With\n"
180ebfedea0SLionel Sambuc     "    '-flipped 1', this behaviour is reversed so that an 'ssl server tunala'\n"
181ebfedea0SLionel Sambuc     "    listens for clean client connections and proxies ssl (but participating\n"
182ebfedea0SLionel Sambuc     "    as an ssl *server* in the SSL/TLS protocol), and an 'ssl client tunala'\n"
183ebfedea0SLionel Sambuc     "    listens for ssl connections (participating as an ssl *client* in the\n"
184ebfedea0SLionel Sambuc     "    SSL/TLS protocol) and proxies 'clean' to the end destination. This can\n"
185ebfedea0SLionel Sambuc     "    be useful for allowing network access to 'servers' where only the server\n"
186ebfedea0SLionel Sambuc     "    needs to authenticate the client (ie. the other way is not required).\n"
187ebfedea0SLionel Sambuc     "    Even with client and server authentication, this 'technique' mitigates\n"
188ebfedea0SLionel Sambuc     "    some DoS (denial-of-service) potential as it will be the network client\n"
189ebfedea0SLionel Sambuc     "    having to perform the first private key operation rather than the other\n"
190ebfedea0SLionel Sambuc     "    way round.\n"
191ebfedea0SLionel Sambuc     "(4) The 'technique' used by setting '-flipped 1' is probably compatible with\n"
192ebfedea0SLionel Sambuc     "    absolutely nothing except another complimentary instance of 'tunala'\n"
193ebfedea0SLionel Sambuc     "    running with '-flipped 1'. :-)\n";
194ebfedea0SLionel Sambuc 
195*0a6a1f1dSLionel Sambuc /*
196*0a6a1f1dSLionel Sambuc  * Default DH parameters for use with "-dh_special standard" ... stolen
197*0a6a1f1dSLionel Sambuc  * striaght from s_server.
198*0a6a1f1dSLionel Sambuc  */
199ebfedea0SLionel Sambuc static unsigned char dh512_p[] = {
200ebfedea0SLionel Sambuc     0xDA, 0x58, 0x3C, 0x16, 0xD9, 0x85, 0x22, 0x89, 0xD0, 0xE4, 0xAF, 0x75,
201ebfedea0SLionel Sambuc     0x6F, 0x4C, 0xCA, 0x92, 0xDD, 0x4B, 0xE5, 0x33, 0xB8, 0x04, 0xFB, 0x0F,
202ebfedea0SLionel Sambuc     0xED, 0x94, 0xEF, 0x9C, 0x8A, 0x44, 0x03, 0xED, 0x57, 0x46, 0x50, 0xD3,
203ebfedea0SLionel Sambuc     0x69, 0x99, 0xDB, 0x29, 0xD7, 0x76, 0x27, 0x6B, 0xA2, 0xD3, 0xD4, 0x12,
204ebfedea0SLionel Sambuc     0xE2, 0x18, 0xF4, 0xDD, 0x1E, 0x08, 0x4C, 0xF6, 0xD8, 0x00, 0x3E, 0x7C,
205ebfedea0SLionel Sambuc     0x47, 0x74, 0xE8, 0x33,
206ebfedea0SLionel Sambuc };
207*0a6a1f1dSLionel Sambuc 
208ebfedea0SLionel Sambuc static unsigned char dh512_g[] = {
209ebfedea0SLionel Sambuc     0x02,
210ebfedea0SLionel Sambuc };
211ebfedea0SLionel Sambuc 
212*0a6a1f1dSLionel Sambuc /*
213*0a6a1f1dSLionel Sambuc  * And the function that parses the above "standard" parameters, again,
214*0a6a1f1dSLionel Sambuc  * straight out of s_server.
215*0a6a1f1dSLionel Sambuc  */
get_dh512(void)216ebfedea0SLionel Sambuc static DH *get_dh512(void)
217ebfedea0SLionel Sambuc {
218ebfedea0SLionel Sambuc     DH *dh = NULL;
219ebfedea0SLionel Sambuc 
220*0a6a1f1dSLionel Sambuc     if ((dh = DH_new()) == NULL)
221*0a6a1f1dSLionel Sambuc         return (NULL);
222ebfedea0SLionel Sambuc     dh->p = BN_bin2bn(dh512_p, sizeof(dh512_p), NULL);
223ebfedea0SLionel Sambuc     dh->g = BN_bin2bn(dh512_g, sizeof(dh512_g), NULL);
224ebfedea0SLionel Sambuc     if ((dh->p == NULL) || (dh->g == NULL))
225ebfedea0SLionel Sambuc         return (NULL);
226ebfedea0SLionel Sambuc     return (dh);
227ebfedea0SLionel Sambuc }
228ebfedea0SLionel Sambuc 
229ebfedea0SLionel Sambuc /* Various help/error messages used by main() */
usage(const char * errstr,int isunknownarg)230ebfedea0SLionel Sambuc static int usage(const char *errstr, int isunknownarg)
231ebfedea0SLionel Sambuc {
232ebfedea0SLionel Sambuc     if (isunknownarg)
233ebfedea0SLionel Sambuc         fprintf(stderr, "Error: unknown argument '%s'\n", errstr);
234ebfedea0SLionel Sambuc     else
235ebfedea0SLionel Sambuc         fprintf(stderr, "Error: %s\n", errstr);
236ebfedea0SLionel Sambuc     fprintf(stderr, "%s\n", helpstring);
237ebfedea0SLionel Sambuc     return 1;
238ebfedea0SLionel Sambuc }
239ebfedea0SLionel Sambuc 
err_str0(const char * str0)240ebfedea0SLionel Sambuc static int err_str0(const char *str0)
241ebfedea0SLionel Sambuc {
242ebfedea0SLionel Sambuc     fprintf(stderr, "%s\n", str0);
243ebfedea0SLionel Sambuc     return 1;
244ebfedea0SLionel Sambuc }
245ebfedea0SLionel Sambuc 
err_str1(const char * fmt,const char * str1)246ebfedea0SLionel Sambuc static int err_str1(const char *fmt, const char *str1)
247ebfedea0SLionel Sambuc {
248ebfedea0SLionel Sambuc     fprintf(stderr, fmt, str1);
249ebfedea0SLionel Sambuc     fprintf(stderr, "\n");
250ebfedea0SLionel Sambuc     return 1;
251ebfedea0SLionel Sambuc }
252ebfedea0SLionel Sambuc 
parse_max_tunnels(const char * s,unsigned int * maxtunnels)253ebfedea0SLionel Sambuc static int parse_max_tunnels(const char *s, unsigned int *maxtunnels)
254ebfedea0SLionel Sambuc {
255ebfedea0SLionel Sambuc     unsigned long l;
256ebfedea0SLionel Sambuc     if (!int_strtoul(s, &l) || (l < 1) || (l > 1024)) {
257ebfedea0SLionel Sambuc         fprintf(stderr, "Error, '%s' is an invalid value for "
258ebfedea0SLionel Sambuc                 "maxtunnels\n", s);
259ebfedea0SLionel Sambuc         return 0;
260ebfedea0SLionel Sambuc     }
261ebfedea0SLionel Sambuc     *maxtunnels = (unsigned int)l;
262ebfedea0SLionel Sambuc     return 1;
263ebfedea0SLionel Sambuc }
264ebfedea0SLionel Sambuc 
parse_server_mode(const char * s,int * servermode)265ebfedea0SLionel Sambuc static int parse_server_mode(const char *s, int *servermode)
266ebfedea0SLionel Sambuc {
267ebfedea0SLionel Sambuc     unsigned long l;
268ebfedea0SLionel Sambuc     if (!int_strtoul(s, &l) || (l > 1)) {
269ebfedea0SLionel Sambuc         fprintf(stderr, "Error, '%s' is an invalid value for the "
270ebfedea0SLionel Sambuc                 "server mode\n", s);
271ebfedea0SLionel Sambuc         return 0;
272ebfedea0SLionel Sambuc     }
273ebfedea0SLionel Sambuc     *servermode = (int)l;
274ebfedea0SLionel Sambuc     return 1;
275ebfedea0SLionel Sambuc }
276ebfedea0SLionel Sambuc 
parse_dh_special(const char * s,const char ** dh_special)277ebfedea0SLionel Sambuc static int parse_dh_special(const char *s, const char **dh_special)
278ebfedea0SLionel Sambuc {
279ebfedea0SLionel Sambuc     if ((strcmp(s, "NULL") == 0) || (strcmp(s, "generate") == 0) ||
280ebfedea0SLionel Sambuc         (strcmp(s, "standard") == 0)) {
281ebfedea0SLionel Sambuc         *dh_special = s;
282ebfedea0SLionel Sambuc         return 1;
283ebfedea0SLionel Sambuc     }
284ebfedea0SLionel Sambuc     fprintf(stderr, "Error, '%s' is an invalid value for 'dh_special'\n", s);
285ebfedea0SLionel Sambuc     return 0;
286ebfedea0SLionel Sambuc }
287ebfedea0SLionel Sambuc 
parse_verify_level(const char * s,unsigned int * verify_level)288ebfedea0SLionel Sambuc static int parse_verify_level(const char *s, unsigned int *verify_level)
289ebfedea0SLionel Sambuc {
290ebfedea0SLionel Sambuc     unsigned long l;
291ebfedea0SLionel Sambuc     if (!int_strtoul(s, &l) || (l > 3)) {
292ebfedea0SLionel Sambuc         fprintf(stderr, "Error, '%s' is an invalid value for "
293ebfedea0SLionel Sambuc                 "out_verify\n", s);
294ebfedea0SLionel Sambuc         return 0;
295ebfedea0SLionel Sambuc     }
296ebfedea0SLionel Sambuc     *verify_level = (unsigned int)l;
297ebfedea0SLionel Sambuc     return 1;
298ebfedea0SLionel Sambuc }
299ebfedea0SLionel Sambuc 
parse_verify_depth(const char * s,unsigned int * verify_depth)300ebfedea0SLionel Sambuc static int parse_verify_depth(const char *s, unsigned int *verify_depth)
301ebfedea0SLionel Sambuc {
302ebfedea0SLionel Sambuc     unsigned long l;
303ebfedea0SLionel Sambuc     if (!int_strtoul(s, &l) || (l < 1) || (l > 50)) {
304ebfedea0SLionel Sambuc         fprintf(stderr, "Error, '%s' is an invalid value for "
305ebfedea0SLionel Sambuc                 "verify_depth\n", s);
306ebfedea0SLionel Sambuc         return 0;
307ebfedea0SLionel Sambuc     }
308ebfedea0SLionel Sambuc     *verify_depth = (unsigned int)l;
309ebfedea0SLionel Sambuc     return 1;
310ebfedea0SLionel Sambuc }
311ebfedea0SLionel Sambuc 
312ebfedea0SLionel Sambuc /* Some fprintf format strings used when tunnels close */
313ebfedea0SLionel Sambuc static const char *io_stats_dirty =
314ebfedea0SLionel Sambuc     "    SSL traffic;   %8lu bytes in, %8lu bytes out\n";
315ebfedea0SLionel Sambuc static const char *io_stats_clean =
316ebfedea0SLionel Sambuc     "    clear traffic; %8lu bytes in, %8lu bytes out\n";
317ebfedea0SLionel Sambuc 
main(int argc,char * argv[])318ebfedea0SLionel Sambuc int main(int argc, char *argv[])
319ebfedea0SLionel Sambuc {
320ebfedea0SLionel Sambuc     unsigned int loop;
321ebfedea0SLionel Sambuc     int newfd;
322ebfedea0SLionel Sambuc     tunala_world_t world;
323ebfedea0SLionel Sambuc     tunala_item_t *t_item;
324ebfedea0SLionel Sambuc     const char *proxy_ip;
325ebfedea0SLionel Sambuc     unsigned short proxy_port;
326ebfedea0SLionel Sambuc     /* Overridables */
327ebfedea0SLionel Sambuc     const char *proxyhost = def_proxyhost;
328ebfedea0SLionel Sambuc     const char *listenhost = def_listenhost;
329ebfedea0SLionel Sambuc     unsigned int max_tunnels = def_max_tunnels;
330ebfedea0SLionel Sambuc     const char *cacert = def_cacert;
331ebfedea0SLionel Sambuc     const char *cert = def_cert;
332ebfedea0SLionel Sambuc     const char *key = def_key;
333ebfedea0SLionel Sambuc     const char *dcert = def_dcert;
334ebfedea0SLionel Sambuc     const char *dkey = def_dkey;
335ebfedea0SLionel Sambuc     const char *engine_id = def_engine_id;
336ebfedea0SLionel Sambuc     int server_mode = def_server_mode;
337ebfedea0SLionel Sambuc     int flipped = def_flipped;
338ebfedea0SLionel Sambuc     const char *cipher_list = def_cipher_list;
339ebfedea0SLionel Sambuc     const char *dh_file = def_dh_file;
340ebfedea0SLionel Sambuc     const char *dh_special = def_dh_special;
341ebfedea0SLionel Sambuc     int tmp_rsa = def_tmp_rsa;
342ebfedea0SLionel Sambuc     int ctx_options = def_ctx_options;
343ebfedea0SLionel Sambuc     int verify_mode = def_verify_mode;
344ebfedea0SLionel Sambuc     unsigned int verify_depth = def_verify_depth;
345ebfedea0SLionel Sambuc     int out_state = def_out_state;
346ebfedea0SLionel Sambuc     unsigned int out_verify = def_out_verify;
347ebfedea0SLionel Sambuc     int out_totals = def_out_totals;
348ebfedea0SLionel Sambuc     int out_conns = def_out_conns;
349ebfedea0SLionel Sambuc 
350ebfedea0SLionel Sambuc /* Parse command-line arguments */
351ebfedea0SLionel Sambuc  next_arg:
352*0a6a1f1dSLionel Sambuc     argc--;
353*0a6a1f1dSLionel Sambuc     argv++;
354ebfedea0SLionel Sambuc     if (argc > 0) {
355ebfedea0SLionel Sambuc         if (strcmp(*argv, "-listen") == 0) {
356ebfedea0SLionel Sambuc             if (argc < 2)
357ebfedea0SLionel Sambuc                 return usage("-listen requires an argument", 0);
358*0a6a1f1dSLionel Sambuc             argc--;
359*0a6a1f1dSLionel Sambuc             argv++;
360ebfedea0SLionel Sambuc             listenhost = *argv;
361ebfedea0SLionel Sambuc             goto next_arg;
362ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-proxy") == 0) {
363ebfedea0SLionel Sambuc             if (argc < 2)
364ebfedea0SLionel Sambuc                 return usage("-proxy requires an argument", 0);
365*0a6a1f1dSLionel Sambuc             argc--;
366*0a6a1f1dSLionel Sambuc             argv++;
367ebfedea0SLionel Sambuc             proxyhost = *argv;
368ebfedea0SLionel Sambuc             goto next_arg;
369ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-maxtunnels") == 0) {
370ebfedea0SLionel Sambuc             if (argc < 2)
371ebfedea0SLionel Sambuc                 return usage("-maxtunnels requires an argument", 0);
372*0a6a1f1dSLionel Sambuc             argc--;
373*0a6a1f1dSLionel Sambuc             argv++;
374ebfedea0SLionel Sambuc             if (!parse_max_tunnels(*argv, &max_tunnels))
375ebfedea0SLionel Sambuc                 return 1;
376ebfedea0SLionel Sambuc             goto next_arg;
377ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-cacert") == 0) {
378ebfedea0SLionel Sambuc             if (argc < 2)
379ebfedea0SLionel Sambuc                 return usage("-cacert requires an argument", 0);
380*0a6a1f1dSLionel Sambuc             argc--;
381*0a6a1f1dSLionel Sambuc             argv++;
382ebfedea0SLionel Sambuc             if (strcmp(*argv, "NULL") == 0)
383ebfedea0SLionel Sambuc                 cacert = NULL;
384ebfedea0SLionel Sambuc             else
385ebfedea0SLionel Sambuc                 cacert = *argv;
386ebfedea0SLionel Sambuc             goto next_arg;
387ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-cert") == 0) {
388ebfedea0SLionel Sambuc             if (argc < 2)
389ebfedea0SLionel Sambuc                 return usage("-cert requires an argument", 0);
390*0a6a1f1dSLionel Sambuc             argc--;
391*0a6a1f1dSLionel Sambuc             argv++;
392ebfedea0SLionel Sambuc             if (strcmp(*argv, "NULL") == 0)
393ebfedea0SLionel Sambuc                 cert = NULL;
394ebfedea0SLionel Sambuc             else
395ebfedea0SLionel Sambuc                 cert = *argv;
396ebfedea0SLionel Sambuc             goto next_arg;
397ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-key") == 0) {
398ebfedea0SLionel Sambuc             if (argc < 2)
399ebfedea0SLionel Sambuc                 return usage("-key requires an argument", 0);
400*0a6a1f1dSLionel Sambuc             argc--;
401*0a6a1f1dSLionel Sambuc             argv++;
402ebfedea0SLionel Sambuc             if (strcmp(*argv, "NULL") == 0)
403ebfedea0SLionel Sambuc                 key = NULL;
404ebfedea0SLionel Sambuc             else
405ebfedea0SLionel Sambuc                 key = *argv;
406ebfedea0SLionel Sambuc             goto next_arg;
407ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-dcert") == 0) {
408ebfedea0SLionel Sambuc             if (argc < 2)
409ebfedea0SLionel Sambuc                 return usage("-dcert requires an argument", 0);
410*0a6a1f1dSLionel Sambuc             argc--;
411*0a6a1f1dSLionel Sambuc             argv++;
412ebfedea0SLionel Sambuc             if (strcmp(*argv, "NULL") == 0)
413ebfedea0SLionel Sambuc                 dcert = NULL;
414ebfedea0SLionel Sambuc             else
415ebfedea0SLionel Sambuc                 dcert = *argv;
416ebfedea0SLionel Sambuc             goto next_arg;
417ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-dkey") == 0) {
418ebfedea0SLionel Sambuc             if (argc < 2)
419ebfedea0SLionel Sambuc                 return usage("-dkey requires an argument", 0);
420*0a6a1f1dSLionel Sambuc             argc--;
421*0a6a1f1dSLionel Sambuc             argv++;
422ebfedea0SLionel Sambuc             if (strcmp(*argv, "NULL") == 0)
423ebfedea0SLionel Sambuc                 dkey = NULL;
424ebfedea0SLionel Sambuc             else
425ebfedea0SLionel Sambuc                 dkey = *argv;
426ebfedea0SLionel Sambuc             goto next_arg;
427ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-engine") == 0) {
428ebfedea0SLionel Sambuc             if (argc < 2)
429ebfedea0SLionel Sambuc                 return usage("-engine requires an argument", 0);
430*0a6a1f1dSLionel Sambuc             argc--;
431*0a6a1f1dSLionel Sambuc             argv++;
432ebfedea0SLionel Sambuc             engine_id = *argv;
433ebfedea0SLionel Sambuc             goto next_arg;
434ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-server") == 0) {
435ebfedea0SLionel Sambuc             if (argc < 2)
436ebfedea0SLionel Sambuc                 return usage("-server requires an argument", 0);
437*0a6a1f1dSLionel Sambuc             argc--;
438*0a6a1f1dSLionel Sambuc             argv++;
439ebfedea0SLionel Sambuc             if (!parse_server_mode(*argv, &server_mode))
440ebfedea0SLionel Sambuc                 return 1;
441ebfedea0SLionel Sambuc             goto next_arg;
442ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-flipped") == 0) {
443ebfedea0SLionel Sambuc             if (argc < 2)
444ebfedea0SLionel Sambuc                 return usage("-flipped requires an argument", 0);
445*0a6a1f1dSLionel Sambuc             argc--;
446*0a6a1f1dSLionel Sambuc             argv++;
447ebfedea0SLionel Sambuc             if (!parse_server_mode(*argv, &flipped))
448ebfedea0SLionel Sambuc                 return 1;
449ebfedea0SLionel Sambuc             goto next_arg;
450ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-cipher") == 0) {
451ebfedea0SLionel Sambuc             if (argc < 2)
452ebfedea0SLionel Sambuc                 return usage("-cipher requires an argument", 0);
453*0a6a1f1dSLionel Sambuc             argc--;
454*0a6a1f1dSLionel Sambuc             argv++;
455ebfedea0SLionel Sambuc             cipher_list = *argv;
456ebfedea0SLionel Sambuc             goto next_arg;
457ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-dh_file") == 0) {
458ebfedea0SLionel Sambuc             if (argc < 2)
459ebfedea0SLionel Sambuc                 return usage("-dh_file requires an argument", 0);
460ebfedea0SLionel Sambuc             if (dh_special)
461*0a6a1f1dSLionel Sambuc                 return usage("cannot mix -dh_file with " "-dh_special", 0);
462*0a6a1f1dSLionel Sambuc             argc--;
463*0a6a1f1dSLionel Sambuc             argv++;
464ebfedea0SLionel Sambuc             dh_file = *argv;
465ebfedea0SLionel Sambuc             goto next_arg;
466ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-dh_special") == 0) {
467ebfedea0SLionel Sambuc             if (argc < 2)
468ebfedea0SLionel Sambuc                 return usage("-dh_special requires an argument", 0);
469ebfedea0SLionel Sambuc             if (dh_file)
470*0a6a1f1dSLionel Sambuc                 return usage("cannot mix -dh_file with " "-dh_special", 0);
471*0a6a1f1dSLionel Sambuc             argc--;
472*0a6a1f1dSLionel Sambuc             argv++;
473ebfedea0SLionel Sambuc             if (!parse_dh_special(*argv, &dh_special))
474ebfedea0SLionel Sambuc                 return 1;
475ebfedea0SLionel Sambuc             goto next_arg;
476ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-no_tmp_rsa") == 0) {
477ebfedea0SLionel Sambuc             tmp_rsa = 0;
478ebfedea0SLionel Sambuc             goto next_arg;
479ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-no_ssl2") == 0) {
480ebfedea0SLionel Sambuc             ctx_options |= SSL_OP_NO_SSLv2;
481ebfedea0SLionel Sambuc             goto next_arg;
482ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-no_ssl3") == 0) {
483ebfedea0SLionel Sambuc             ctx_options |= SSL_OP_NO_SSLv3;
484ebfedea0SLionel Sambuc             goto next_arg;
485ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-no_tls1") == 0) {
486ebfedea0SLionel Sambuc             ctx_options |= SSL_OP_NO_TLSv1;
487ebfedea0SLionel Sambuc             goto next_arg;
488ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-v_peer") == 0) {
489ebfedea0SLionel Sambuc             verify_mode |= SSL_VERIFY_PEER;
490ebfedea0SLionel Sambuc             goto next_arg;
491ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-v_strict") == 0) {
492ebfedea0SLionel Sambuc             verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
493ebfedea0SLionel Sambuc             goto next_arg;
494ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-v_once") == 0) {
495ebfedea0SLionel Sambuc             verify_mode |= SSL_VERIFY_CLIENT_ONCE;
496ebfedea0SLionel Sambuc             goto next_arg;
497ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-v_depth") == 0) {
498ebfedea0SLionel Sambuc             if (argc < 2)
499ebfedea0SLionel Sambuc                 return usage("-v_depth requires an argument", 0);
500*0a6a1f1dSLionel Sambuc             argc--;
501*0a6a1f1dSLionel Sambuc             argv++;
502ebfedea0SLionel Sambuc             if (!parse_verify_depth(*argv, &verify_depth))
503ebfedea0SLionel Sambuc                 return 1;
504ebfedea0SLionel Sambuc             goto next_arg;
505ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-out_state") == 0) {
506ebfedea0SLionel Sambuc             out_state = 1;
507ebfedea0SLionel Sambuc             goto next_arg;
508ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-out_verify") == 0) {
509ebfedea0SLionel Sambuc             if (argc < 2)
510ebfedea0SLionel Sambuc                 return usage("-out_verify requires an argument", 0);
511*0a6a1f1dSLionel Sambuc             argc--;
512*0a6a1f1dSLionel Sambuc             argv++;
513ebfedea0SLionel Sambuc             if (!parse_verify_level(*argv, &out_verify))
514ebfedea0SLionel Sambuc                 return 1;
515ebfedea0SLionel Sambuc             goto next_arg;
516ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-out_totals") == 0) {
517ebfedea0SLionel Sambuc             out_totals = 1;
518ebfedea0SLionel Sambuc             goto next_arg;
519ebfedea0SLionel Sambuc         } else if (strcmp(*argv, "-out_conns") == 0) {
520ebfedea0SLionel Sambuc             out_conns = 1;
521ebfedea0SLionel Sambuc             goto next_arg;
522ebfedea0SLionel Sambuc         } else if ((strcmp(*argv, "-h") == 0) ||
523ebfedea0SLionel Sambuc                    (strcmp(*argv, "-help") == 0) ||
524ebfedea0SLionel Sambuc                    (strcmp(*argv, "-?") == 0)) {
525ebfedea0SLionel Sambuc             fprintf(stderr, "%s\n", helpstring);
526ebfedea0SLionel Sambuc             return 0;
527ebfedea0SLionel Sambuc         } else
528ebfedea0SLionel Sambuc             return usage(*argv, 1);
529ebfedea0SLionel Sambuc     }
530ebfedea0SLionel Sambuc     /* Run any sanity checks we want here */
531ebfedea0SLionel Sambuc     if (!cert && !dcert && server_mode)
532ebfedea0SLionel Sambuc         fprintf(stderr, "WARNING: you are running an SSL server without "
533ebfedea0SLionel Sambuc                 "a certificate - this may not work!\n");
534ebfedea0SLionel Sambuc 
535ebfedea0SLionel Sambuc     /* Initialise network stuff */
536ebfedea0SLionel Sambuc     if (!ip_initialise())
537ebfedea0SLionel Sambuc         return err_str0("ip_initialise failed");
538ebfedea0SLionel Sambuc     /* Create the SSL_CTX */
539ebfedea0SLionel Sambuc     if ((world.ssl_ctx = initialise_ssl_ctx(server_mode, engine_id,
540*0a6a1f1dSLionel Sambuc                                             cacert, cert, key, dcert, dkey,
541*0a6a1f1dSLionel Sambuc                                             cipher_list, dh_file, dh_special,
542*0a6a1f1dSLionel Sambuc                                             tmp_rsa, ctx_options, out_state,
543*0a6a1f1dSLionel Sambuc                                             out_verify, verify_mode,
544*0a6a1f1dSLionel Sambuc                                             verify_depth)) == NULL)
545ebfedea0SLionel Sambuc         return err_str1("initialise_ssl_ctx(engine_id=%s) failed",
546ebfedea0SLionel Sambuc                         (engine_id == NULL) ? "NULL" : engine_id);
547ebfedea0SLionel Sambuc     if (engine_id)
548ebfedea0SLionel Sambuc         fprintf(stderr, "Info, engine '%s' initialised\n", engine_id);
549ebfedea0SLionel Sambuc     /* Create the listener */
550ebfedea0SLionel Sambuc     if ((world.listen_fd = ip_create_listener(listenhost)) == -1)
551ebfedea0SLionel Sambuc         return err_str1("ip_create_listener(%s) failed", listenhost);
552ebfedea0SLionel Sambuc     fprintf(stderr, "Info, listening on '%s'\n", listenhost);
553ebfedea0SLionel Sambuc     if (!ip_parse_address(proxyhost, &proxy_ip, &proxy_port, 0))
554ebfedea0SLionel Sambuc         return err_str1("ip_parse_address(%s) failed", proxyhost);
555ebfedea0SLionel Sambuc     fprintf(stderr, "Info, proxying to '%s' (%d.%d.%d.%d:%d)\n", proxyhost,
556ebfedea0SLionel Sambuc             (int)proxy_ip[0], (int)proxy_ip[1],
557ebfedea0SLionel Sambuc             (int)proxy_ip[2], (int)proxy_ip[3], (int)proxy_port);
558ebfedea0SLionel Sambuc     fprintf(stderr, "Info, set maxtunnels to %d\n", (int)max_tunnels);
559ebfedea0SLionel Sambuc     fprintf(stderr, "Info, set to operate as an SSL %s\n",
560ebfedea0SLionel Sambuc             (server_mode ? "server" : "client"));
561ebfedea0SLionel Sambuc     /* Initialise the rest of the stuff */
562ebfedea0SLionel Sambuc     world.tunnels_used = world.tunnels_size = 0;
563ebfedea0SLionel Sambuc     world.tunnels = NULL;
564ebfedea0SLionel Sambuc     world.server_mode = server_mode;
565ebfedea0SLionel Sambuc     selector_init(&world.selector);
566ebfedea0SLionel Sambuc 
567ebfedea0SLionel Sambuc /* We're ready to loop */
568ebfedea0SLionel Sambuc  main_loop:
569ebfedea0SLionel Sambuc     /* Should we listen for *new* tunnels? */
570ebfedea0SLionel Sambuc     if (world.tunnels_used < max_tunnels)
571ebfedea0SLionel Sambuc         selector_add_listener(&world.selector, world.listen_fd);
572ebfedea0SLionel Sambuc     /* We should add in our existing tunnels */
573ebfedea0SLionel Sambuc     for (loop = 0; loop < world.tunnels_used; loop++)
574ebfedea0SLionel Sambuc         selector_add_tunala(&world.selector, world.tunnels + loop);
575ebfedea0SLionel Sambuc     /* Now do the select */
576ebfedea0SLionel Sambuc     switch (selector_select(&world.selector)) {
577ebfedea0SLionel Sambuc     case -1:
578ebfedea0SLionel Sambuc         if (errno != EINTR) {
579*0a6a1f1dSLionel Sambuc             fprintf(stderr, "selector_select returned a " "badness error.\n");
580ebfedea0SLionel Sambuc             goto shouldnt_happen;
581ebfedea0SLionel Sambuc         }
582ebfedea0SLionel Sambuc         fprintf(stderr, "Warn, selector interrupted by a signal\n");
583ebfedea0SLionel Sambuc         goto main_loop;
584ebfedea0SLionel Sambuc     case 0:
585ebfedea0SLionel Sambuc         fprintf(stderr, "Warn, selector_select returned 0 - signal?" "?\n");
586ebfedea0SLionel Sambuc         goto main_loop;
587ebfedea0SLionel Sambuc     default:
588ebfedea0SLionel Sambuc         break;
589ebfedea0SLionel Sambuc     }
590ebfedea0SLionel Sambuc     /* Accept new connection if we should and can */
591*0a6a1f1dSLionel Sambuc     if ((world.tunnels_used < max_tunnels)
592*0a6a1f1dSLionel Sambuc         && (selector_get_listener(&world.selector, world.listen_fd, &newfd) ==
593*0a6a1f1dSLionel Sambuc             1)) {
594ebfedea0SLionel Sambuc         /* We have a new connection */
595ebfedea0SLionel Sambuc         if (!tunala_world_new_item(&world, newfd, proxy_ip,
596ebfedea0SLionel Sambuc                                    proxy_port, flipped))
597ebfedea0SLionel Sambuc             fprintf(stderr, "tunala_world_new_item failed\n");
598ebfedea0SLionel Sambuc         else if (out_conns)
599ebfedea0SLionel Sambuc             fprintf(stderr, "Info, new tunnel opened, now up to "
600ebfedea0SLionel Sambuc                     "%d\n", world.tunnels_used);
601ebfedea0SLionel Sambuc     }
602*0a6a1f1dSLionel Sambuc     /*
603*0a6a1f1dSLionel Sambuc      * Give each tunnel its moment, note the while loop is because it makes
604ebfedea0SLionel Sambuc      * the logic easier than with "for" to deal with an array that may shift
605*0a6a1f1dSLionel Sambuc      * because of deletes.
606*0a6a1f1dSLionel Sambuc      */
607ebfedea0SLionel Sambuc     loop = 0;
608ebfedea0SLionel Sambuc     t_item = world.tunnels;
609ebfedea0SLionel Sambuc     while (loop < world.tunnels_used) {
610ebfedea0SLionel Sambuc         if (!tunala_item_io(&world.selector, t_item)) {
611*0a6a1f1dSLionel Sambuc             /*
612*0a6a1f1dSLionel Sambuc              * We're closing whether for reasons of an error or a natural
613*0a6a1f1dSLionel Sambuc              * close. Don't increment loop or t_item because the next item is
614*0a6a1f1dSLionel Sambuc              * moving to us!
615*0a6a1f1dSLionel Sambuc              */
616ebfedea0SLionel Sambuc             if (!out_totals)
617ebfedea0SLionel Sambuc                 goto skip_totals;
618ebfedea0SLionel Sambuc             fprintf(stderr, "Tunnel closing, traffic stats follow\n");
619ebfedea0SLionel Sambuc             /* Display the encrypted (over the network) stats */
620ebfedea0SLionel Sambuc             fprintf(stderr, io_stats_dirty,
621*0a6a1f1dSLionel Sambuc                     buffer_total_in(state_machine_get_buffer
622*0a6a1f1dSLionel Sambuc                                     (&t_item->sm, SM_DIRTY_IN)),
623*0a6a1f1dSLionel Sambuc                     buffer_total_out(state_machine_get_buffer
624*0a6a1f1dSLionel Sambuc                                      (&t_item->sm, SM_DIRTY_OUT)));
625*0a6a1f1dSLionel Sambuc             /*
626*0a6a1f1dSLionel Sambuc              * Display the local (tunnelled) stats. NB: Data we *receive* is
627*0a6a1f1dSLionel Sambuc              * data sent *out* of the state_machine on its 'clean' side.
628*0a6a1f1dSLionel Sambuc              * Hence the apparent back-to-front OUT/IN mixup here :-)
629*0a6a1f1dSLionel Sambuc              */
630ebfedea0SLionel Sambuc             fprintf(stderr, io_stats_clean,
631*0a6a1f1dSLionel Sambuc                     buffer_total_out(state_machine_get_buffer
632*0a6a1f1dSLionel Sambuc                                      (&t_item->sm, SM_CLEAN_OUT)),
633*0a6a1f1dSLionel Sambuc                     buffer_total_in(state_machine_get_buffer
634*0a6a1f1dSLionel Sambuc                                     (&t_item->sm, SM_CLEAN_IN)));
635ebfedea0SLionel Sambuc  skip_totals:
636ebfedea0SLionel Sambuc             tunala_world_del_item(&world, loop);
637ebfedea0SLionel Sambuc             if (out_conns)
638ebfedea0SLionel Sambuc                 fprintf(stderr, "Info, tunnel closed, down to %d\n",
639ebfedea0SLionel Sambuc                         world.tunnels_used);
640*0a6a1f1dSLionel Sambuc         } else {
641ebfedea0SLionel Sambuc             /* Move to the next item */
642ebfedea0SLionel Sambuc             loop++;
643ebfedea0SLionel Sambuc             t_item++;
644ebfedea0SLionel Sambuc         }
645ebfedea0SLionel Sambuc     }
646ebfedea0SLionel Sambuc     goto main_loop;
647ebfedea0SLionel Sambuc     /* Should never get here */
648ebfedea0SLionel Sambuc  shouldnt_happen:
649ebfedea0SLionel Sambuc     abort();
650ebfedea0SLionel Sambuc     return 1;
651ebfedea0SLionel Sambuc }
652ebfedea0SLionel Sambuc 
653ebfedea0SLionel Sambuc /****************/
654ebfedea0SLionel Sambuc /* OpenSSL bits */
655ebfedea0SLionel Sambuc /****************/
656ebfedea0SLionel Sambuc 
ctx_set_cert(SSL_CTX * ctx,const char * cert,const char * key)657ebfedea0SLionel Sambuc static int ctx_set_cert(SSL_CTX *ctx, const char *cert, const char *key)
658ebfedea0SLionel Sambuc {
659ebfedea0SLionel Sambuc     FILE *fp = NULL;
660ebfedea0SLionel Sambuc     X509 *x509 = NULL;
661ebfedea0SLionel Sambuc     EVP_PKEY *pkey = NULL;
662ebfedea0SLionel Sambuc     int toret = 0;              /* Assume an error */
663ebfedea0SLionel Sambuc 
664ebfedea0SLionel Sambuc     /* cert */
665ebfedea0SLionel Sambuc     if (cert) {
666ebfedea0SLionel Sambuc         if ((fp = fopen(cert, "r")) == NULL) {
667ebfedea0SLionel Sambuc             fprintf(stderr, "Error opening cert file '%s'\n", cert);
668ebfedea0SLionel Sambuc             goto err;
669ebfedea0SLionel Sambuc         }
670ebfedea0SLionel Sambuc         if (!PEM_read_X509(fp, &x509, NULL, NULL)) {
671*0a6a1f1dSLionel Sambuc             fprintf(stderr, "Error reading PEM cert from '%s'\n", cert);
672ebfedea0SLionel Sambuc             goto err;
673ebfedea0SLionel Sambuc         }
674ebfedea0SLionel Sambuc         if (!SSL_CTX_use_certificate(ctx, x509)) {
675*0a6a1f1dSLionel Sambuc             fprintf(stderr, "Error, cert in '%s' can not be used\n", cert);
676ebfedea0SLionel Sambuc             goto err;
677ebfedea0SLionel Sambuc         }
678ebfedea0SLionel Sambuc         /* Clear the FILE* for reuse in the "key" code */
679ebfedea0SLionel Sambuc         fclose(fp);
680ebfedea0SLionel Sambuc         fp = NULL;
681ebfedea0SLionel Sambuc         fprintf(stderr, "Info, operating with cert in '%s'\n", cert);
682*0a6a1f1dSLionel Sambuc         /*
683*0a6a1f1dSLionel Sambuc          * If a cert was given without matching key, we assume the same file
684*0a6a1f1dSLionel Sambuc          * contains the required key.
685*0a6a1f1dSLionel Sambuc          */
686ebfedea0SLionel Sambuc         if (!key)
687ebfedea0SLionel Sambuc             key = cert;
688ebfedea0SLionel Sambuc     } else {
689ebfedea0SLionel Sambuc         if (key)
690ebfedea0SLionel Sambuc             fprintf(stderr, "Error, can't specify a key without a "
691ebfedea0SLionel Sambuc                     "corresponding certificate\n");
692ebfedea0SLionel Sambuc         else
693*0a6a1f1dSLionel Sambuc             fprintf(stderr, "Error, ctx_set_cert called with " "NULLs!\n");
694ebfedea0SLionel Sambuc         goto err;
695ebfedea0SLionel Sambuc     }
696ebfedea0SLionel Sambuc     /* key */
697ebfedea0SLionel Sambuc     if (key) {
698ebfedea0SLionel Sambuc         if ((fp = fopen(key, "r")) == NULL) {
699ebfedea0SLionel Sambuc             fprintf(stderr, "Error opening key file '%s'\n", key);
700ebfedea0SLionel Sambuc             goto err;
701ebfedea0SLionel Sambuc         }
702ebfedea0SLionel Sambuc         if (!PEM_read_PrivateKey(fp, &pkey, NULL, NULL)) {
703*0a6a1f1dSLionel Sambuc             fprintf(stderr, "Error reading PEM key from '%s'\n", key);
704ebfedea0SLionel Sambuc             goto err;
705ebfedea0SLionel Sambuc         }
706ebfedea0SLionel Sambuc         if (!SSL_CTX_use_PrivateKey(ctx, pkey)) {
707*0a6a1f1dSLionel Sambuc             fprintf(stderr, "Error, key in '%s' can not be used\n", key);
708ebfedea0SLionel Sambuc             goto err;
709ebfedea0SLionel Sambuc         }
710ebfedea0SLionel Sambuc         fprintf(stderr, "Info, operating with key in '%s'\n", key);
711ebfedea0SLionel Sambuc     } else
712ebfedea0SLionel Sambuc         fprintf(stderr, "Info, operating without a cert or key\n");
713ebfedea0SLionel Sambuc     /* Success */
714*0a6a1f1dSLionel Sambuc     toret = 1;
715*0a6a1f1dSLionel Sambuc  err:
716ebfedea0SLionel Sambuc     if (x509)
717ebfedea0SLionel Sambuc         X509_free(x509);
718ebfedea0SLionel Sambuc     if (pkey)
719ebfedea0SLionel Sambuc         EVP_PKEY_free(pkey);
720ebfedea0SLionel Sambuc     if (fp)
721ebfedea0SLionel Sambuc         fclose(fp);
722ebfedea0SLionel Sambuc     return toret;
723ebfedea0SLionel Sambuc }
724ebfedea0SLionel Sambuc 
ctx_set_dh(SSL_CTX * ctx,const char * dh_file,const char * dh_special)725*0a6a1f1dSLionel Sambuc static int ctx_set_dh(SSL_CTX *ctx, const char *dh_file,
726*0a6a1f1dSLionel Sambuc                       const char *dh_special)
727ebfedea0SLionel Sambuc {
728ebfedea0SLionel Sambuc     DH *dh = NULL;
729ebfedea0SLionel Sambuc     FILE *fp = NULL;
730ebfedea0SLionel Sambuc 
731ebfedea0SLionel Sambuc     if (dh_special) {
732ebfedea0SLionel Sambuc         if (strcmp(dh_special, "NULL") == 0)
733ebfedea0SLionel Sambuc             return 1;
734ebfedea0SLionel Sambuc         if (strcmp(dh_special, "standard") == 0) {
735ebfedea0SLionel Sambuc             if ((dh = get_dh512()) == NULL) {
736ebfedea0SLionel Sambuc                 fprintf(stderr, "Error, can't parse 'standard'"
737ebfedea0SLionel Sambuc                         " DH parameters\n");
738ebfedea0SLionel Sambuc                 return 0;
739ebfedea0SLionel Sambuc             }
740ebfedea0SLionel Sambuc             fprintf(stderr, "Info, using 'standard' DH parameters\n");
741ebfedea0SLionel Sambuc             goto do_it;
742ebfedea0SLionel Sambuc         }
743ebfedea0SLionel Sambuc         if (strcmp(dh_special, "generate") != 0)
744*0a6a1f1dSLionel Sambuc             /*
745*0a6a1f1dSLionel Sambuc              * This shouldn't happen - screening values is handled in main().
746*0a6a1f1dSLionel Sambuc              */
747ebfedea0SLionel Sambuc             abort();
748ebfedea0SLionel Sambuc         fprintf(stderr, "Info, generating DH parameters ... ");
749ebfedea0SLionel Sambuc         fflush(stderr);
750ebfedea0SLionel Sambuc         if (!(dh = DH_new()) || !DH_generate_parameters_ex(dh, 512,
751*0a6a1f1dSLionel Sambuc                                                            DH_GENERATOR_5,
752*0a6a1f1dSLionel Sambuc                                                            NULL)) {
753ebfedea0SLionel Sambuc             fprintf(stderr, "error!\n");
754ebfedea0SLionel Sambuc             if (dh)
755ebfedea0SLionel Sambuc                 DH_free(dh);
756ebfedea0SLionel Sambuc             return 0;
757ebfedea0SLionel Sambuc         }
758ebfedea0SLionel Sambuc         fprintf(stderr, "complete\n");
759ebfedea0SLionel Sambuc         goto do_it;
760ebfedea0SLionel Sambuc     }
761ebfedea0SLionel Sambuc     /* So, we're loading dh_file */
762ebfedea0SLionel Sambuc     if ((fp = fopen(dh_file, "r")) == NULL) {
763ebfedea0SLionel Sambuc         fprintf(stderr, "Error, couldn't open '%s' for DH parameters\n",
764ebfedea0SLionel Sambuc                 dh_file);
765ebfedea0SLionel Sambuc         return 0;
766ebfedea0SLionel Sambuc     }
767ebfedea0SLionel Sambuc     dh = PEM_read_DHparams(fp, NULL, NULL, NULL);
768ebfedea0SLionel Sambuc     fclose(fp);
769ebfedea0SLionel Sambuc     if (dh == NULL) {
770ebfedea0SLionel Sambuc         fprintf(stderr, "Error, could not parse DH parameters from '%s'\n",
771ebfedea0SLionel Sambuc                 dh_file);
772ebfedea0SLionel Sambuc         return 0;
773ebfedea0SLionel Sambuc     }
774ebfedea0SLionel Sambuc     fprintf(stderr, "Info, using DH parameters from file '%s'\n", dh_file);
775ebfedea0SLionel Sambuc  do_it:
776ebfedea0SLionel Sambuc     SSL_CTX_set_tmp_dh(ctx, dh);
777ebfedea0SLionel Sambuc     DH_free(dh);
778ebfedea0SLionel Sambuc     return 1;
779ebfedea0SLionel Sambuc }
780ebfedea0SLionel Sambuc 
initialise_ssl_ctx(int server_mode,const char * engine_id,const char * CAfile,const char * cert,const char * key,const char * dcert,const char * dkey,const char * cipher_list,const char * dh_file,const char * dh_special,int tmp_rsa,int ctx_options,int out_state,int out_verify,int verify_mode,unsigned int verify_depth)781ebfedea0SLionel Sambuc static SSL_CTX *initialise_ssl_ctx(int server_mode, const char *engine_id,
782*0a6a1f1dSLionel Sambuc                                    const char *CAfile, const char *cert,
783*0a6a1f1dSLionel Sambuc                                    const char *key, const char *dcert,
784*0a6a1f1dSLionel Sambuc                                    const char *dkey, const char *cipher_list,
785*0a6a1f1dSLionel Sambuc                                    const char *dh_file,
786*0a6a1f1dSLionel Sambuc                                    const char *dh_special, int tmp_rsa,
787*0a6a1f1dSLionel Sambuc                                    int ctx_options, int out_state,
788*0a6a1f1dSLionel Sambuc                                    int out_verify, int verify_mode,
789ebfedea0SLionel Sambuc                                    unsigned int verify_depth)
790ebfedea0SLionel Sambuc {
791ebfedea0SLionel Sambuc     SSL_CTX *ctx = NULL, *ret = NULL;
792ebfedea0SLionel Sambuc     const SSL_METHOD *meth;
793ebfedea0SLionel Sambuc     ENGINE *e = NULL;
794ebfedea0SLionel Sambuc 
795ebfedea0SLionel Sambuc     OpenSSL_add_ssl_algorithms();
796ebfedea0SLionel Sambuc     SSL_load_error_strings();
797ebfedea0SLionel Sambuc 
798ebfedea0SLionel Sambuc     meth = (server_mode ? SSLv23_server_method() : SSLv23_client_method());
799ebfedea0SLionel Sambuc     if (meth == NULL)
800ebfedea0SLionel Sambuc         goto err;
801ebfedea0SLionel Sambuc     if (engine_id) {
802ebfedea0SLionel Sambuc         ENGINE_load_builtin_engines();
803ebfedea0SLionel Sambuc         if ((e = ENGINE_by_id(engine_id)) == NULL) {
804ebfedea0SLionel Sambuc             fprintf(stderr, "Error obtaining '%s' engine, openssl "
805ebfedea0SLionel Sambuc                     "errors follow\n", engine_id);
806ebfedea0SLionel Sambuc             goto err;
807ebfedea0SLionel Sambuc         }
808ebfedea0SLionel Sambuc         if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
809ebfedea0SLionel Sambuc             fprintf(stderr, "Error assigning '%s' engine, openssl "
810ebfedea0SLionel Sambuc                     "errors follow\n", engine_id);
811ebfedea0SLionel Sambuc             goto err;
812ebfedea0SLionel Sambuc         }
813ebfedea0SLionel Sambuc         ENGINE_free(e);
814ebfedea0SLionel Sambuc     }
815ebfedea0SLionel Sambuc     if ((ctx = SSL_CTX_new(meth)) == NULL)
816ebfedea0SLionel Sambuc         goto err;
817ebfedea0SLionel Sambuc     /* cacert */
818ebfedea0SLionel Sambuc     if (CAfile) {
819ebfedea0SLionel Sambuc         if (!X509_STORE_load_locations(SSL_CTX_get_cert_store(ctx),
820ebfedea0SLionel Sambuc                                        CAfile, NULL)) {
821*0a6a1f1dSLionel Sambuc             fprintf(stderr, "Error loading CA cert(s) in '%s'\n", CAfile);
822ebfedea0SLionel Sambuc             goto err;
823ebfedea0SLionel Sambuc         }
824*0a6a1f1dSLionel Sambuc         fprintf(stderr, "Info, operating with CA cert(s) in '%s'\n", CAfile);
825ebfedea0SLionel Sambuc     } else
826ebfedea0SLionel Sambuc         fprintf(stderr, "Info, operating without a CA cert(-list)\n");
827ebfedea0SLionel Sambuc     if (!SSL_CTX_set_default_verify_paths(ctx)) {
828ebfedea0SLionel Sambuc         fprintf(stderr, "Error setting default verify paths\n");
829ebfedea0SLionel Sambuc         goto err;
830ebfedea0SLionel Sambuc     }
831ebfedea0SLionel Sambuc 
832ebfedea0SLionel Sambuc     /* cert and key */
833ebfedea0SLionel Sambuc     if ((cert || key) && !ctx_set_cert(ctx, cert, key))
834ebfedea0SLionel Sambuc         goto err;
835ebfedea0SLionel Sambuc     /* dcert and dkey */
836ebfedea0SLionel Sambuc     if ((dcert || dkey) && !ctx_set_cert(ctx, dcert, dkey))
837ebfedea0SLionel Sambuc         goto err;
838ebfedea0SLionel Sambuc     /* temporary RSA key generation */
839ebfedea0SLionel Sambuc     if (tmp_rsa)
840ebfedea0SLionel Sambuc         SSL_CTX_set_tmp_rsa_callback(ctx, cb_generate_tmp_rsa);
841ebfedea0SLionel Sambuc 
842ebfedea0SLionel Sambuc     /* cipher_list */
843ebfedea0SLionel Sambuc     if (cipher_list) {
844ebfedea0SLionel Sambuc         if (!SSL_CTX_set_cipher_list(ctx, cipher_list)) {
845*0a6a1f1dSLionel Sambuc             fprintf(stderr, "Error setting cipher list '%s'\n", cipher_list);
846ebfedea0SLionel Sambuc             goto err;
847ebfedea0SLionel Sambuc         }
848ebfedea0SLionel Sambuc         fprintf(stderr, "Info, set cipher list '%s'\n", cipher_list);
849ebfedea0SLionel Sambuc     } else
850ebfedea0SLionel Sambuc         fprintf(stderr, "Info, operating with default cipher list\n");
851ebfedea0SLionel Sambuc 
852ebfedea0SLionel Sambuc     /* dh_file & dh_special */
853ebfedea0SLionel Sambuc     if ((dh_file || dh_special) && !ctx_set_dh(ctx, dh_file, dh_special))
854ebfedea0SLionel Sambuc         goto err;
855ebfedea0SLionel Sambuc 
856ebfedea0SLionel Sambuc     /* ctx_options */
857ebfedea0SLionel Sambuc     SSL_CTX_set_options(ctx, ctx_options);
858ebfedea0SLionel Sambuc 
859ebfedea0SLionel Sambuc     /* out_state (output of SSL handshake states to screen). */
860ebfedea0SLionel Sambuc     if (out_state)
861ebfedea0SLionel Sambuc         cb_ssl_info_set_output(stderr);
862ebfedea0SLionel Sambuc 
863ebfedea0SLionel Sambuc     /* out_verify */
864ebfedea0SLionel Sambuc     if (out_verify > 0) {
865ebfedea0SLionel Sambuc         cb_ssl_verify_set_output(stderr);
866ebfedea0SLionel Sambuc         cb_ssl_verify_set_level(out_verify);
867ebfedea0SLionel Sambuc     }
868ebfedea0SLionel Sambuc 
869ebfedea0SLionel Sambuc     /* verify_depth */
870ebfedea0SLionel Sambuc     cb_ssl_verify_set_depth(verify_depth);
871ebfedea0SLionel Sambuc 
872ebfedea0SLionel Sambuc     /* Success! (includes setting verify_mode) */
873ebfedea0SLionel Sambuc     SSL_CTX_set_info_callback(ctx, cb_ssl_info);
874ebfedea0SLionel Sambuc     SSL_CTX_set_verify(ctx, verify_mode, cb_ssl_verify);
875ebfedea0SLionel Sambuc     ret = ctx;
876ebfedea0SLionel Sambuc  err:
877ebfedea0SLionel Sambuc     if (!ret) {
878ebfedea0SLionel Sambuc         ERR_print_errors_fp(stderr);
879ebfedea0SLionel Sambuc         if (ctx)
880ebfedea0SLionel Sambuc             SSL_CTX_free(ctx);
881ebfedea0SLionel Sambuc     }
882ebfedea0SLionel Sambuc     return ret;
883ebfedea0SLionel Sambuc }
884ebfedea0SLionel Sambuc 
885ebfedea0SLionel Sambuc /*****************/
886ebfedea0SLionel Sambuc /* Selector bits */
887ebfedea0SLionel Sambuc /*****************/
888ebfedea0SLionel Sambuc 
selector_sets_init(select_sets_t * s)889ebfedea0SLionel Sambuc static void selector_sets_init(select_sets_t * s)
890ebfedea0SLionel Sambuc {
891ebfedea0SLionel Sambuc     s->max = 0;
892ebfedea0SLionel Sambuc     FD_ZERO(&s->reads);
893ebfedea0SLionel Sambuc     FD_ZERO(&s->sends);
894ebfedea0SLionel Sambuc     FD_ZERO(&s->excepts);
895ebfedea0SLionel Sambuc }
896*0a6a1f1dSLionel Sambuc 
selector_init(tunala_selector_t * selector)897ebfedea0SLionel Sambuc static void selector_init(tunala_selector_t * selector)
898ebfedea0SLionel Sambuc {
899ebfedea0SLionel Sambuc     selector_sets_init(&selector->last_selected);
900ebfedea0SLionel Sambuc     selector_sets_init(&selector->next_select);
901ebfedea0SLionel Sambuc }
902ebfedea0SLionel Sambuc 
903ebfedea0SLionel Sambuc #define SEL_EXCEPTS 0x00
904ebfedea0SLionel Sambuc #define SEL_READS   0x01
905ebfedea0SLionel Sambuc #define SEL_SENDS   0x02
selector_add_raw_fd(tunala_selector_t * s,int fd,int flags)906ebfedea0SLionel Sambuc static void selector_add_raw_fd(tunala_selector_t * s, int fd, int flags)
907ebfedea0SLionel Sambuc {
908ebfedea0SLionel Sambuc     FD_SET(fd, &s->next_select.excepts);
909ebfedea0SLionel Sambuc     if (flags & SEL_READS)
910ebfedea0SLionel Sambuc         FD_SET(fd, &s->next_select.reads);
911ebfedea0SLionel Sambuc     if (flags & SEL_SENDS)
912ebfedea0SLionel Sambuc         FD_SET(fd, &s->next_select.sends);
913ebfedea0SLionel Sambuc     /* Adjust "max" */
914ebfedea0SLionel Sambuc     if (s->next_select.max < (fd + 1))
915ebfedea0SLionel Sambuc         s->next_select.max = fd + 1;
916ebfedea0SLionel Sambuc }
917ebfedea0SLionel Sambuc 
selector_add_listener(tunala_selector_t * selector,int fd)918ebfedea0SLionel Sambuc static void selector_add_listener(tunala_selector_t * selector, int fd)
919ebfedea0SLionel Sambuc {
920ebfedea0SLionel Sambuc     selector_add_raw_fd(selector, fd, SEL_READS);
921ebfedea0SLionel Sambuc }
922ebfedea0SLionel Sambuc 
selector_add_tunala(tunala_selector_t * s,tunala_item_t * t)923ebfedea0SLionel Sambuc static void selector_add_tunala(tunala_selector_t * s, tunala_item_t * t)
924ebfedea0SLionel Sambuc {
925ebfedea0SLionel Sambuc     /* Set clean read if sm.clean_in is not full */
926ebfedea0SLionel Sambuc     if (t->clean_read != -1) {
927ebfedea0SLionel Sambuc         selector_add_raw_fd(s, t->clean_read,
928ebfedea0SLionel Sambuc                             (buffer_full(state_machine_get_buffer(&t->sm,
929*0a6a1f1dSLionel Sambuc                                                                   SM_CLEAN_IN))
930*0a6a1f1dSLionel Sambuc                              ? SEL_EXCEPTS : SEL_READS));
931ebfedea0SLionel Sambuc     }
932ebfedea0SLionel Sambuc     /* Set clean send if sm.clean_out is not empty */
933ebfedea0SLionel Sambuc     if (t->clean_send != -1) {
934ebfedea0SLionel Sambuc         selector_add_raw_fd(s, t->clean_send,
935ebfedea0SLionel Sambuc                             (buffer_empty(state_machine_get_buffer(&t->sm,
936*0a6a1f1dSLionel Sambuc                                                                    SM_CLEAN_OUT))
937*0a6a1f1dSLionel Sambuc                              ? SEL_EXCEPTS : SEL_SENDS));
938ebfedea0SLionel Sambuc     }
939ebfedea0SLionel Sambuc     /* Set dirty read if sm.dirty_in is not full */
940ebfedea0SLionel Sambuc     if (t->dirty_read != -1) {
941ebfedea0SLionel Sambuc         selector_add_raw_fd(s, t->dirty_read,
942ebfedea0SLionel Sambuc                             (buffer_full(state_machine_get_buffer(&t->sm,
943*0a6a1f1dSLionel Sambuc                                                                   SM_DIRTY_IN))
944*0a6a1f1dSLionel Sambuc                              ? SEL_EXCEPTS : SEL_READS));
945ebfedea0SLionel Sambuc     }
946ebfedea0SLionel Sambuc     /* Set dirty send if sm.dirty_out is not empty */
947ebfedea0SLionel Sambuc     if (t->dirty_send != -1) {
948ebfedea0SLionel Sambuc         selector_add_raw_fd(s, t->dirty_send,
949ebfedea0SLionel Sambuc                             (buffer_empty(state_machine_get_buffer(&t->sm,
950*0a6a1f1dSLionel Sambuc                                                                    SM_DIRTY_OUT))
951*0a6a1f1dSLionel Sambuc                              ? SEL_EXCEPTS : SEL_SENDS));
952ebfedea0SLionel Sambuc     }
953ebfedea0SLionel Sambuc }
954ebfedea0SLionel Sambuc 
selector_select(tunala_selector_t * selector)955ebfedea0SLionel Sambuc static int selector_select(tunala_selector_t * selector)
956ebfedea0SLionel Sambuc {
957ebfedea0SLionel Sambuc     memcpy(&selector->last_selected, &selector->next_select,
958ebfedea0SLionel Sambuc            sizeof(select_sets_t));
959ebfedea0SLionel Sambuc     selector_sets_init(&selector->next_select);
960ebfedea0SLionel Sambuc     return select(selector->last_selected.max,
961ebfedea0SLionel Sambuc                   &selector->last_selected.reads,
962ebfedea0SLionel Sambuc                   &selector->last_selected.sends,
963ebfedea0SLionel Sambuc                   &selector->last_selected.excepts, NULL);
964ebfedea0SLionel Sambuc }
965ebfedea0SLionel Sambuc 
966*0a6a1f1dSLionel Sambuc /*
967*0a6a1f1dSLionel Sambuc  * This returns -1 for error, 0 for no new connections, or 1 for success, in
968*0a6a1f1dSLionel Sambuc  * which case *newfd is populated.
969*0a6a1f1dSLionel Sambuc  */
selector_get_listener(tunala_selector_t * selector,int fd,int * newfd)970*0a6a1f1dSLionel Sambuc static int selector_get_listener(tunala_selector_t * selector, int fd,
971*0a6a1f1dSLionel Sambuc                                  int *newfd)
972ebfedea0SLionel Sambuc {
973ebfedea0SLionel Sambuc     if (FD_ISSET(fd, &selector->last_selected.excepts))
974ebfedea0SLionel Sambuc         return -1;
975ebfedea0SLionel Sambuc     if (!FD_ISSET(fd, &selector->last_selected.reads))
976ebfedea0SLionel Sambuc         return 0;
977ebfedea0SLionel Sambuc     if ((*newfd = ip_accept_connection(fd)) == -1)
978ebfedea0SLionel Sambuc         return -1;
979ebfedea0SLionel Sambuc     return 1;
980ebfedea0SLionel Sambuc }
981ebfedea0SLionel Sambuc 
982ebfedea0SLionel Sambuc /************************/
983ebfedea0SLionel Sambuc /* "Tunala" world stuff */
984ebfedea0SLionel Sambuc /************************/
985ebfedea0SLionel Sambuc 
tunala_world_make_room(tunala_world_t * world)986ebfedea0SLionel Sambuc static int tunala_world_make_room(tunala_world_t * world)
987ebfedea0SLionel Sambuc {
988ebfedea0SLionel Sambuc     unsigned int newsize;
989ebfedea0SLionel Sambuc     tunala_item_t *newarray;
990ebfedea0SLionel Sambuc 
991ebfedea0SLionel Sambuc     if (world->tunnels_used < world->tunnels_size)
992ebfedea0SLionel Sambuc         return 1;
993ebfedea0SLionel Sambuc     newsize = (world->tunnels_size == 0 ? 16 :
994ebfedea0SLionel Sambuc                ((world->tunnels_size * 3) / 2));
995ebfedea0SLionel Sambuc     if ((newarray = malloc(newsize * sizeof(tunala_item_t))) == NULL)
996ebfedea0SLionel Sambuc         return 0;
997ebfedea0SLionel Sambuc     memset(newarray, 0, newsize * sizeof(tunala_item_t));
998ebfedea0SLionel Sambuc     if (world->tunnels_used > 0)
999ebfedea0SLionel Sambuc         memcpy(newarray, world->tunnels,
1000ebfedea0SLionel Sambuc                world->tunnels_used * sizeof(tunala_item_t));
1001ebfedea0SLionel Sambuc     if (world->tunnels_size > 0)
1002ebfedea0SLionel Sambuc         free(world->tunnels);
1003ebfedea0SLionel Sambuc     /* migrate */
1004ebfedea0SLionel Sambuc     world->tunnels = newarray;
1005ebfedea0SLionel Sambuc     world->tunnels_size = newsize;
1006ebfedea0SLionel Sambuc     return 1;
1007ebfedea0SLionel Sambuc }
1008ebfedea0SLionel Sambuc 
tunala_world_new_item(tunala_world_t * world,int fd,const char * ip,unsigned short port,int flipped)1009ebfedea0SLionel Sambuc static int tunala_world_new_item(tunala_world_t * world, int fd,
1010*0a6a1f1dSLionel Sambuc                                  const char *ip, unsigned short port,
1011*0a6a1f1dSLionel Sambuc                                  int flipped)
1012ebfedea0SLionel Sambuc {
1013ebfedea0SLionel Sambuc     tunala_item_t *item;
1014ebfedea0SLionel Sambuc     int newfd;
1015ebfedea0SLionel Sambuc     SSL *new_ssl = NULL;
1016ebfedea0SLionel Sambuc 
1017ebfedea0SLionel Sambuc     if (!tunala_world_make_room(world))
1018ebfedea0SLionel Sambuc         return 0;
1019ebfedea0SLionel Sambuc     if ((new_ssl = SSL_new(world->ssl_ctx)) == NULL) {
1020ebfedea0SLionel Sambuc         fprintf(stderr, "Error creating new SSL\n");
1021ebfedea0SLionel Sambuc         ERR_print_errors_fp(stderr);
1022ebfedea0SLionel Sambuc         return 0;
1023ebfedea0SLionel Sambuc     }
1024ebfedea0SLionel Sambuc     item = world->tunnels + (world->tunnels_used++);
1025ebfedea0SLionel Sambuc     state_machine_init(&item->sm);
1026ebfedea0SLionel Sambuc     item->clean_read = item->clean_send =
1027ebfedea0SLionel Sambuc         item->dirty_read = item->dirty_send = -1;
1028ebfedea0SLionel Sambuc     if ((newfd = ip_create_connection_split(ip, port)) == -1)
1029ebfedea0SLionel Sambuc         goto err;
1030*0a6a1f1dSLionel Sambuc     /*
1031*0a6a1f1dSLionel Sambuc      * Which way round? If we're a server, "fd" is the dirty side and the
1032ebfedea0SLionel Sambuc      * connection we open is the clean one. For a client, it's the other way
1033ebfedea0SLionel Sambuc      * around. Unless, of course, we're "flipped" in which case everything
1034*0a6a1f1dSLionel Sambuc      * gets reversed. :-)
1035*0a6a1f1dSLionel Sambuc      */
1036*0a6a1f1dSLionel Sambuc     if ((world->server_mode && !flipped) || (!world->server_mode && flipped)) {
1037ebfedea0SLionel Sambuc         item->dirty_read = item->dirty_send = fd;
1038ebfedea0SLionel Sambuc         item->clean_read = item->clean_send = newfd;
1039ebfedea0SLionel Sambuc     } else {
1040ebfedea0SLionel Sambuc         item->clean_read = item->clean_send = fd;
1041ebfedea0SLionel Sambuc         item->dirty_read = item->dirty_send = newfd;
1042ebfedea0SLionel Sambuc     }
1043*0a6a1f1dSLionel Sambuc     /*
1044*0a6a1f1dSLionel Sambuc      * We use the SSL's "app_data" to indicate a call-back induced "kill"
1045*0a6a1f1dSLionel Sambuc      */
1046ebfedea0SLionel Sambuc     SSL_set_app_data(new_ssl, NULL);
1047ebfedea0SLionel Sambuc     if (!state_machine_set_SSL(&item->sm, new_ssl, world->server_mode))
1048ebfedea0SLionel Sambuc         goto err;
1049ebfedea0SLionel Sambuc     return 1;
1050ebfedea0SLionel Sambuc  err:
1051ebfedea0SLionel Sambuc     tunala_world_del_item(world, world->tunnels_used - 1);
1052ebfedea0SLionel Sambuc     return 0;
1053ebfedea0SLionel Sambuc 
1054ebfedea0SLionel Sambuc }
1055ebfedea0SLionel Sambuc 
tunala_world_del_item(tunala_world_t * world,unsigned int idx)1056ebfedea0SLionel Sambuc static void tunala_world_del_item(tunala_world_t * world, unsigned int idx)
1057ebfedea0SLionel Sambuc {
1058ebfedea0SLionel Sambuc     tunala_item_t *item = world->tunnels + idx;
1059ebfedea0SLionel Sambuc     if (item->clean_read != -1)
1060ebfedea0SLionel Sambuc         close(item->clean_read);
1061ebfedea0SLionel Sambuc     if (item->clean_send != item->clean_read)
1062ebfedea0SLionel Sambuc         close(item->clean_send);
1063ebfedea0SLionel Sambuc     item->clean_read = item->clean_send = -1;
1064ebfedea0SLionel Sambuc     if (item->dirty_read != -1)
1065ebfedea0SLionel Sambuc         close(item->dirty_read);
1066ebfedea0SLionel Sambuc     if (item->dirty_send != item->dirty_read)
1067ebfedea0SLionel Sambuc         close(item->dirty_send);
1068ebfedea0SLionel Sambuc     item->dirty_read = item->dirty_send = -1;
1069ebfedea0SLionel Sambuc     state_machine_close(&item->sm);
1070ebfedea0SLionel Sambuc     /* OK, now we fix the item array */
1071ebfedea0SLionel Sambuc     if (idx + 1 < world->tunnels_used)
1072ebfedea0SLionel Sambuc         /* We need to scroll entries to the left */
1073ebfedea0SLionel Sambuc         memmove(world->tunnels + idx,
1074ebfedea0SLionel Sambuc                 world->tunnels + (idx + 1),
1075*0a6a1f1dSLionel Sambuc                 (world->tunnels_used - (idx + 1)) * sizeof(tunala_item_t));
1076ebfedea0SLionel Sambuc     world->tunnels_used--;
1077ebfedea0SLionel Sambuc }
1078ebfedea0SLionel Sambuc 
tunala_item_io(tunala_selector_t * selector,tunala_item_t * item)1079ebfedea0SLionel Sambuc static int tunala_item_io(tunala_selector_t * selector, tunala_item_t * item)
1080ebfedea0SLionel Sambuc {
1081ebfedea0SLionel Sambuc     int c_r, c_s, d_r, d_s;     /* Four boolean flags */
1082ebfedea0SLionel Sambuc 
1083ebfedea0SLionel Sambuc     /* Take ourselves out of the gene-pool if there was an except */
1084ebfedea0SLionel Sambuc     if ((item->clean_read != -1) && FD_ISSET(item->clean_read,
1085*0a6a1f1dSLionel Sambuc                                              &selector->
1086*0a6a1f1dSLionel Sambuc                                              last_selected.excepts))
1087ebfedea0SLionel Sambuc         return 0;
1088ebfedea0SLionel Sambuc     if ((item->clean_send != -1) && FD_ISSET(item->clean_send,
1089*0a6a1f1dSLionel Sambuc                                              &selector->
1090*0a6a1f1dSLionel Sambuc                                              last_selected.excepts))
1091ebfedea0SLionel Sambuc         return 0;
1092ebfedea0SLionel Sambuc     if ((item->dirty_read != -1) && FD_ISSET(item->dirty_read,
1093*0a6a1f1dSLionel Sambuc                                              &selector->
1094*0a6a1f1dSLionel Sambuc                                              last_selected.excepts))
1095ebfedea0SLionel Sambuc         return 0;
1096ebfedea0SLionel Sambuc     if ((item->dirty_send != -1) && FD_ISSET(item->dirty_send,
1097*0a6a1f1dSLionel Sambuc                                              &selector->
1098*0a6a1f1dSLionel Sambuc                                              last_selected.excepts))
1099ebfedea0SLionel Sambuc         return 0;
1100ebfedea0SLionel Sambuc     /* Grab our 4 IO flags */
1101ebfedea0SLionel Sambuc     c_r = c_s = d_r = d_s = 0;
1102ebfedea0SLionel Sambuc     if (item->clean_read != -1)
1103ebfedea0SLionel Sambuc         c_r = FD_ISSET(item->clean_read, &selector->last_selected.reads);
1104ebfedea0SLionel Sambuc     if (item->clean_send != -1)
1105ebfedea0SLionel Sambuc         c_s = FD_ISSET(item->clean_send, &selector->last_selected.sends);
1106ebfedea0SLionel Sambuc     if (item->dirty_read != -1)
1107ebfedea0SLionel Sambuc         d_r = FD_ISSET(item->dirty_read, &selector->last_selected.reads);
1108ebfedea0SLionel Sambuc     if (item->dirty_send != -1)
1109ebfedea0SLionel Sambuc         d_s = FD_ISSET(item->dirty_send, &selector->last_selected.sends);
1110ebfedea0SLionel Sambuc     /* If no IO has happened for us, skip needless data looping */
1111ebfedea0SLionel Sambuc     if (!c_r && !c_s && !d_r && !d_s)
1112ebfedea0SLionel Sambuc         return 1;
1113ebfedea0SLionel Sambuc     if (c_r)
1114ebfedea0SLionel Sambuc         c_r = (buffer_from_fd(state_machine_get_buffer(&item->sm,
1115*0a6a1f1dSLionel Sambuc                                                        SM_CLEAN_IN),
1116*0a6a1f1dSLionel Sambuc                               item->clean_read) <= 0);
1117ebfedea0SLionel Sambuc     if (c_s)
1118ebfedea0SLionel Sambuc         c_s = (buffer_to_fd(state_machine_get_buffer(&item->sm,
1119*0a6a1f1dSLionel Sambuc                                                      SM_CLEAN_OUT),
1120*0a6a1f1dSLionel Sambuc                             item->clean_send) <= 0);
1121ebfedea0SLionel Sambuc     if (d_r)
1122ebfedea0SLionel Sambuc         d_r = (buffer_from_fd(state_machine_get_buffer(&item->sm,
1123*0a6a1f1dSLionel Sambuc                                                        SM_DIRTY_IN),
1124*0a6a1f1dSLionel Sambuc                               item->dirty_read) <= 0);
1125ebfedea0SLionel Sambuc     if (d_s)
1126ebfedea0SLionel Sambuc         d_s = (buffer_to_fd(state_machine_get_buffer(&item->sm,
1127*0a6a1f1dSLionel Sambuc                                                      SM_DIRTY_OUT),
1128*0a6a1f1dSLionel Sambuc                             item->dirty_send) <= 0);
1129ebfedea0SLionel Sambuc     /* If any of the flags is non-zero, that means they need closing */
1130ebfedea0SLionel Sambuc     if (c_r) {
1131ebfedea0SLionel Sambuc         close(item->clean_read);
1132ebfedea0SLionel Sambuc         if (item->clean_send == item->clean_read)
1133ebfedea0SLionel Sambuc             item->clean_send = -1;
1134ebfedea0SLionel Sambuc         item->clean_read = -1;
1135ebfedea0SLionel Sambuc     }
1136ebfedea0SLionel Sambuc     if (c_s && (item->clean_send != -1)) {
1137ebfedea0SLionel Sambuc         close(item->clean_send);
1138ebfedea0SLionel Sambuc         if (item->clean_send == item->clean_read)
1139ebfedea0SLionel Sambuc             item->clean_read = -1;
1140ebfedea0SLionel Sambuc         item->clean_send = -1;
1141ebfedea0SLionel Sambuc     }
1142ebfedea0SLionel Sambuc     if (d_r) {
1143ebfedea0SLionel Sambuc         close(item->dirty_read);
1144ebfedea0SLionel Sambuc         if (item->dirty_send == item->dirty_read)
1145ebfedea0SLionel Sambuc             item->dirty_send = -1;
1146ebfedea0SLionel Sambuc         item->dirty_read = -1;
1147ebfedea0SLionel Sambuc     }
1148ebfedea0SLionel Sambuc     if (d_s && (item->dirty_send != -1)) {
1149ebfedea0SLionel Sambuc         close(item->dirty_send);
1150ebfedea0SLionel Sambuc         if (item->dirty_send == item->dirty_read)
1151ebfedea0SLionel Sambuc             item->dirty_read = -1;
1152ebfedea0SLionel Sambuc         item->dirty_send = -1;
1153ebfedea0SLionel Sambuc     }
1154*0a6a1f1dSLionel Sambuc     /*
1155*0a6a1f1dSLionel Sambuc      * This function name is attributed to the term donated by David Schwartz
1156*0a6a1f1dSLionel Sambuc      * on openssl-dev, message-ID:
1157*0a6a1f1dSLionel Sambuc      * <NCBBLIEPOCbmasEKBEAKEEDGLIAA.davids@webmaster.com>. :-)
1158*0a6a1f1dSLionel Sambuc      */
1159ebfedea0SLionel Sambuc     if (!state_machine_churn(&item->sm))
1160*0a6a1f1dSLionel Sambuc         /*
1161*0a6a1f1dSLionel Sambuc          * If the SSL closes, it will also zero-out the _in buffers and will
1162*0a6a1f1dSLionel Sambuc          * in future process just outgoing data. As and when the outgoing
1163*0a6a1f1dSLionel Sambuc          * data has gone, it will return zero here to tell us to bail out.
1164*0a6a1f1dSLionel Sambuc          */
1165ebfedea0SLionel Sambuc         return 0;
1166ebfedea0SLionel Sambuc     /* Otherwise, we return zero if both sides are dead. */
1167ebfedea0SLionel Sambuc     if (((item->clean_read == -1) || (item->clean_send == -1)) &&
1168ebfedea0SLionel Sambuc         ((item->dirty_read == -1) || (item->dirty_send == -1)))
1169ebfedea0SLionel Sambuc         return 0;
1170*0a6a1f1dSLionel Sambuc     /*
1171*0a6a1f1dSLionel Sambuc      * If only one side closed, notify the SSL of this so it can take
1172*0a6a1f1dSLionel Sambuc      * appropriate action.
1173*0a6a1f1dSLionel Sambuc      */
1174ebfedea0SLionel Sambuc     if ((item->clean_read == -1) || (item->clean_send == -1)) {
1175ebfedea0SLionel Sambuc         if (!state_machine_close_clean(&item->sm))
1176ebfedea0SLionel Sambuc             return 0;
1177ebfedea0SLionel Sambuc     }
1178ebfedea0SLionel Sambuc     if ((item->dirty_read == -1) || (item->dirty_send == -1)) {
1179ebfedea0SLionel Sambuc         if (!state_machine_close_dirty(&item->sm))
1180ebfedea0SLionel Sambuc             return 0;
1181ebfedea0SLionel Sambuc     }
1182ebfedea0SLionel Sambuc     return 1;
1183ebfedea0SLionel Sambuc }
1184