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