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