1#include "tunala.h"
2
3#ifndef NO_TUNALA
4
5void state_machine_init(state_machine_t *machine)
6{
7	machine->ssl = NULL;
8	machine->bio_intossl = machine->bio_fromssl = NULL;
9	buffer_init(&machine->clean_in);
10	buffer_init(&machine->clean_out);
11	buffer_init(&machine->dirty_in);
12	buffer_init(&machine->dirty_out);
13}
14
15void state_machine_close(state_machine_t *machine)
16{
17	if(machine->ssl)
18		SSL_free(machine->ssl);
19/* SSL_free seems to decrement the reference counts already so doing this goes
20 * kaboom. */
21#if 0
22	if(machine->bio_intossl)
23		BIO_free(machine->bio_intossl);
24	if(machine->bio_fromssl)
25		BIO_free(machine->bio_fromssl);
26#endif
27	buffer_close(&machine->clean_in);
28	buffer_close(&machine->clean_out);
29	buffer_close(&machine->dirty_in);
30	buffer_close(&machine->dirty_out);
31	state_machine_init(machine);
32}
33
34buffer_t *state_machine_get_buffer(state_machine_t *machine, sm_buffer_t type)
35{
36	switch(type) {
37	case SM_CLEAN_IN:
38		return &machine->clean_in;
39	case SM_CLEAN_OUT:
40		return &machine->clean_out;
41	case SM_DIRTY_IN:
42		return &machine->dirty_in;
43	case SM_DIRTY_OUT:
44		return &machine->dirty_out;
45	default:
46		break;
47	}
48	/* Should never get here */
49	abort();
50	return NULL;
51}
52
53SSL *state_machine_get_SSL(state_machine_t *machine)
54{
55	return machine->ssl;
56}
57
58int state_machine_set_SSL(state_machine_t *machine, SSL *ssl, int is_server)
59{
60	if(machine->ssl)
61		/* Shouldn't ever be set twice */
62		abort();
63	machine->ssl = ssl;
64	/* Create the BIOs to handle the dirty side of the SSL */
65	if((machine->bio_intossl = BIO_new(BIO_s_mem())) == NULL)
66		abort();
67	if((machine->bio_fromssl = BIO_new(BIO_s_mem())) == NULL)
68		abort();
69	/* Hook up the BIOs on the dirty side of the SSL */
70	SSL_set_bio(machine->ssl, machine->bio_intossl, machine->bio_fromssl);
71	if(is_server)
72		SSL_set_accept_state(machine->ssl);
73	else
74		SSL_set_connect_state(machine->ssl);
75	/* If we're the first one to generate traffic - do it now otherwise we
76	 * go into the next select empty-handed and our peer will not send data
77	 * but will similarly wait for us. */
78	return state_machine_churn(machine);
79}
80
81/* Performs the data-IO loop and returns zero if the machine should close */
82int state_machine_churn(state_machine_t *machine)
83{
84	unsigned int loop;
85	if(machine->ssl == NULL) {
86		if(buffer_empty(&machine->clean_out))
87			/* Time to close this state-machine altogether */
88			return 0;
89		else
90			/* Still buffered data on the clean side to go out */
91			return 1;
92	}
93	/* Do this loop twice to cover any dependencies about which precise
94	 * order of reads and writes is required. */
95	for(loop = 0; loop < 2; loop++) {
96		buffer_to_SSL(&machine->clean_in, machine->ssl);
97		buffer_to_BIO(&machine->dirty_in, machine->bio_intossl);
98		buffer_from_SSL(&machine->clean_out, machine->ssl);
99		buffer_from_BIO(&machine->dirty_out, machine->bio_fromssl);
100	}
101	/* We close on the SSL side if the info callback noticed some problems
102	 * or an SSL shutdown was underway and shutdown traffic had all been
103	 * sent. */
104	if(SSL_get_app_data(machine->ssl) || (SSL_get_shutdown(machine->ssl) &&
105				buffer_empty(&machine->dirty_out))) {
106		/* Great, we can seal off the dirty side completely */
107		if(!state_machine_close_dirty(machine))
108			return 0;
109	}
110	/* Either the SSL is alive and well, or the closing process still has
111	 * outgoing data waiting to be sent */
112	return 1;
113}
114
115/* Called when the clean side of the SSL has lost its connection */
116int state_machine_close_clean(state_machine_t *machine)
117{
118	/* Well, first thing to do is null out the clean-side buffers - they're
119	 * no use any more. */
120	buffer_close(&machine->clean_in);
121	buffer_close(&machine->clean_out);
122	/* And start an SSL shutdown */
123	if(machine->ssl)
124		SSL_shutdown(machine->ssl);
125	/* This is an "event", so flush the SSL of any generated traffic */
126	state_machine_churn(machine);
127	if(buffer_empty(&machine->dirty_in) &&
128			buffer_empty(&machine->dirty_out))
129		return 0;
130	return 1;
131}
132
133/* Called when the dirty side of the SSL has lost its connection. This is pretty
134 * terminal as all that can be left to do is send any buffered output on the
135 * clean side - after that, we're done. */
136int state_machine_close_dirty(state_machine_t *machine)
137{
138	buffer_close(&machine->dirty_in);
139	buffer_close(&machine->dirty_out);
140	buffer_close(&machine->clean_in);
141	if(machine->ssl)
142		SSL_free(machine->ssl);
143	machine->ssl = NULL;
144	machine->bio_intossl = machine->bio_fromssl = NULL;
145	if(buffer_empty(&machine->clean_out))
146		return 0;
147	return 1;
148}
149
150#endif /* !defined(NO_TUNALA) */
151
152