summaryrefslogtreecommitdiffstats
path: root/openssl/demos/tunala/sm.c
blob: 5658dfff2d8d8a4a9c766f16b53a5bd85b4fecaf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#include "tunala.h"

#ifndef NO_TUNALA

void state_machine_init(state_machine_t * machine)
{
    machine->ssl = NULL;
    machine->bio_intossl = machine->bio_fromssl = NULL;
    buffer_init(&machine->clean_in);
    buffer_init(&machine->clean_out);
    buffer_init(&machine->dirty_in);
    buffer_init(&machine->dirty_out);
}

void state_machine_close(state_machine_t * machine)
{
    if (machine->ssl)
        SSL_free(machine->ssl);
    /*
     * SSL_free seems to decrement the reference counts already so doing this
     * goes kaboom.
     */
# if 0
    if (machine->bio_intossl)
        BIO_free(machine->bio_intossl);
    if (machine->bio_fromssl)
        BIO_free(machine->bio_fromssl);
# endif
    buffer_close(&machine->clean_in);
    buffer_close(&machine->clean_out);
    buffer_close(&machine->dirty_in);
    buffer_close(&machine->dirty_out);
    state_machine_init(machine);
}

buffer_t *state_machine_get_buffer(state_machine_t * machine,
                                   sm_buffer_t type)
{
    switch (type) {
    case SM_CLEAN_IN:
        return &machine->clean_in;
    case SM_CLEAN_OUT:
        return &machine->clean_out;
    case SM_DIRTY_IN:
        return &machine->dirty_in;
    case SM_DIRTY_OUT:
        return &machine->dirty_out;
    default:
        break;
    }
    /* Should never get here */
    abort();
    return NULL;
}

SSL *state_machine_get_SSL(state_machine_t * machine)
{
    return machine->ssl;
}

int state_machine_set_SSL(state_machine_t * machine, SSL *ssl, int is_server)
{
    if (machine->ssl)
        /* Shouldn't ever be set twice */
        abort();
    machine->ssl = ssl;
    /* Create the BIOs to handle the dirty side of the SSL */
    if ((machine->bio_intossl = BIO_new(BIO_s_mem())) == NULL)
        abort();
    if ((machine->bio_fromssl = BIO_new(BIO_s_mem())) == NULL)
        abort();
    /* Hook up the BIOs on the dirty side of the SSL */
    SSL_set_bio(machine->ssl, machine->bio_intossl, machine->bio_fromssl);
    if (is_server)
        SSL_set_accept_state(machine->ssl);
    else
        SSL_set_connect_state(machine->ssl);
    /*
     * If we're the first one to generate traffic - do it now otherwise we go
     * into the next select empty-handed and our peer will not send data but
     * will similarly wait for us.
     */
    return state_machine_churn(machine);
}

/* Performs the data-IO loop and returns zero if the machine should close */
int state_machine_churn(state_machine_t * machine)
{
    unsigned int loop;
    if (machine->ssl == NULL) {
        if (buffer_empty(&machine->clean_out))
            /* Time to close this state-machine altogether */
            return 0;
        else
            /* Still buffered data on the clean side to go out */
            return 1;
    }
    /*
     * Do this loop twice to cover any dependencies about which precise order
     * of reads and writes is required.
     */
    for (loop = 0; loop < 2; loop++) {
        buffer_to_SSL(&machine->clean_in, machine->ssl);
        buffer_to_BIO(&machine->dirty_in, machine->bio_intossl);
        buffer_from_SSL(&machine->clean_out, machine->ssl);
        buffer_from_BIO(&machine->dirty_out, machine->bio_fromssl);
    }
    /*
     * We close on the SSL side if the info callback noticed some problems or
     * an SSL shutdown was underway and shutdown traffic had all been sent.
     */
    if (SSL_get_app_data(machine->ssl) || (SSL_get_shutdown(machine->ssl) &&
                                           buffer_empty(&machine->dirty_out)))
    {
        /* Great, we can seal off the dirty side completely */
        if (!state_machine_close_dirty(machine))
            return 0;
    }
    /*
     * Either the SSL is alive and well, or the closing process still has
     * outgoing data waiting to be sent
     */
    return 1;
}

/* Called when the clean side of the SSL has lost its connection */
int state_machine_close_clean(state_machine_t * machine)
{
    /*
     * Well, first thing to do is null out the clean-side buffers - they're
     * no use any more.
     */
    buffer_close(&machine->clean_in);
    buffer_close(&machine->clean_out);
    /* And start an SSL shutdown */
    if (machine->ssl)
        SSL_shutdown(machine->ssl);
    /* This is an "event", so flush the SSL of any generated traffic */
    state_machine_churn(machine);
    if (buffer_empty(&machine->dirty_in) && buffer_empty(&machine->dirty_out))
        return 0;
    return 1;
}

/*
 * Called when the dirty side of the SSL has lost its connection. This is
 * pretty terminal as all that can be left to do is send any buffered output
 * on the clean side - after that, we're done.
 */
int state_machine_close_dirty(state_machine_t * machine)
{
    buffer_close(&machine->dirty_in);
    buffer_close(&machine->dirty_out);
    buffer_close(&machine->clean_in);
    if (machine->ssl)
        SSL_free(machine->ssl);
    machine->ssl = NULL;
    machine->bio_intossl = machine->bio_fromssl = NULL;
    if (buffer_empty(&machine->clean_out))
        return 0;
    return 1;
}

#endif                          /* !defined(NO_TUNALA) */