346a53fdd2bc938b1c492cdcfbd7b39326eb6a81
[project/ustream-ssl.git] / ustream-ssl.c
1 /*
2  * ustream-ssl - library for SSL over ustream
3  *
4  * Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include <errno.h>
20 #include <libubox/ustream.h>
21
22 #include "ustream-ssl.h"
23 #include "ustream-internal.h"
24
25 static void ustream_ssl_error_cb(struct uloop_timeout *t)
26 {
27         struct ustream_ssl *us = container_of(t, struct ustream_ssl, error_timer);
28         static char buffer[128];
29         int error = us->error;
30
31         if (us->notify_error)
32                 us->notify_error(us, error, __ustream_ssl_strerror(us->error, buffer, sizeof(buffer)));
33 }
34
35 static void ustream_ssl_check_conn(struct ustream_ssl *us)
36 {
37         if (us->connected || us->error)
38                 return;
39
40         if (__ustream_ssl_connect(us) == U_SSL_OK) {
41                 us->connected = true;
42                 if (us->notify_connected)
43                         us->notify_connected(us);
44                 ustream_write_pending(&us->stream);
45         }
46 }
47
48 static bool __ustream_ssl_poll(struct ustream *s)
49 {
50         struct ustream_ssl *us = container_of(s->next, struct ustream_ssl, stream);
51         char *buf;
52         int len, ret;
53         bool more = false;
54
55         ustream_ssl_check_conn(us);
56         if (!us->connected || us->error)
57                 return false;
58
59         do {
60                 buf = ustream_reserve(&us->stream, 1, &len);
61                 if (!len)
62                         break;
63
64                 ret = __ustream_ssl_read(us, buf, len);
65                 switch (ret) {
66                 case U_SSL_PENDING:
67                         return more;
68                 case U_SSL_ERROR:
69                         return false;
70                 case 0:
71                         us->stream.eof = true;
72                         ustream_state_change(&us->stream);
73                         return false;
74                 default:
75                         ustream_fill_read(&us->stream, ret);
76                         more = true;
77                         continue;
78                 }
79         } while (1);
80
81         return more;
82 }
83
84 static void ustream_ssl_notify_read(struct ustream *s, int bytes)
85 {
86         __ustream_ssl_poll(s);
87 }
88
89 static void ustream_ssl_notify_write(struct ustream *s, int bytes)
90 {
91         struct ustream_ssl *us = container_of(s->next, struct ustream_ssl, stream);
92
93         ustream_ssl_check_conn(us);
94         ustream_write_pending(s->next);
95 }
96
97 static void ustream_ssl_notify_state(struct ustream *s)
98 {
99         s->next->write_error = true;
100         ustream_state_change(s->next);
101 }
102
103 static int ustream_ssl_write(struct ustream *s, const char *buf, int len, bool more)
104 {
105         struct ustream_ssl *us = container_of(s, struct ustream_ssl, stream);
106
107         if (!us->connected || us->error)
108                 return 0;
109
110         if (us->conn->w.data_bytes)
111                 return 0;
112
113         return __ustream_ssl_write(us, buf, len);
114 }
115
116 static void ustream_ssl_set_read_blocked(struct ustream *s)
117 {
118         struct ustream_ssl *us = container_of(s, struct ustream_ssl, stream);
119
120         ustream_set_read_blocked(us->conn, !!s->read_blocked);
121 }
122
123 static void ustream_ssl_free(struct ustream *s)
124 {
125         struct ustream_ssl *us = container_of(s, struct ustream_ssl, stream);
126
127         if (us->conn) {
128                 us->conn->next = NULL;
129                 us->conn->notify_read = NULL;
130                 us->conn->notify_write = NULL;
131                 us->conn->notify_state = NULL;
132         }
133
134         uloop_timeout_cancel(&us->error_timer);
135         __ustream_ssl_session_free(us->ssl);
136         us->ctx = NULL;
137         us->ssl = NULL;
138         us->conn = NULL;
139         us->connected = false;
140         us->error = false;
141 }
142
143 static bool ustream_ssl_poll(struct ustream *s)
144 {
145         struct ustream_ssl *us = container_of(s, struct ustream_ssl, stream);
146         bool fd_poll;
147
148         fd_poll = ustream_poll(us->conn);
149         return __ustream_ssl_poll(s) || fd_poll;
150 }
151
152 static void ustream_ssl_stream_init(struct ustream_ssl *us)
153 {
154         struct ustream *conn = us->conn;
155         struct ustream *s = &us->stream;
156
157         conn->notify_read = ustream_ssl_notify_read;
158         conn->notify_write = ustream_ssl_notify_write;
159         conn->notify_state = ustream_ssl_notify_state;
160
161         s->free = ustream_ssl_free;
162         s->write = ustream_ssl_write;
163         s->poll = ustream_ssl_poll;
164         s->set_read_blocked = ustream_ssl_set_read_blocked;
165         ustream_init_defaults(s);
166 }
167
168 static int _ustream_ssl_init(struct ustream_ssl *us, struct ustream *conn, struct ustream_ssl_ctx *ctx, bool server)
169 {
170         us->error_timer.cb = ustream_ssl_error_cb;
171         us->server = server;
172         us->conn = conn;
173         us->ctx = ctx;
174
175         us->ssl = __ustream_ssl_session_new(us->ctx);
176         if (!us->ssl)
177                 return -ENOMEM;
178
179         conn->next = &us->stream;
180         ustream_set_io(ctx, us->ssl, conn);
181         ustream_ssl_stream_init(us);
182         ustream_ssl_check_conn(us);
183
184         return 0;
185 }
186
187 const struct ustream_ssl_ops ustream_ssl_ops = {
188         .context_new = __ustream_ssl_context_new,
189         .context_set_crt_file = __ustream_ssl_set_crt_file,
190         .context_set_key_file = __ustream_ssl_set_key_file,
191         .context_free = __ustream_ssl_context_free,
192         .init = _ustream_ssl_init,
193 };