11 #include <time.h> |
11 #include <time.h> |
12 |
12 |
13 #include <assert.h> |
13 #include <assert.h> |
14 |
14 |
15 /** |
15 /** |
16 * Register for events based on the session's gnutls_record_get_direction(). |
16 * Enable the TCP events based on the session's gnutls_record_get_direction(). |
17 */ |
17 */ |
18 static err_t sock_gnutls_ev_enable (struct sock_gnutls *sock, struct error_info *err) |
18 static err_t sock_gnutls_ev_enable (struct sock_gnutls *sock, error_t *err) |
19 { |
19 { |
20 int ret; |
20 int ret; |
|
21 short mask; |
21 |
22 |
22 // gnutls_record_get_direction tells us what I/O operation gnutls would have required for the last |
23 // gnutls_record_get_direction tells us what I/O operation gnutls would have required for the last |
23 // operation, so we can use that to determine what events to register |
24 // operation, so we can use that to determine what events to register |
24 switch ((ret = gnutls_record_get_direction(sock->session))) { |
25 switch ((ret = gnutls_record_get_direction(sock->session))) { |
25 case 0: |
26 case 0: |
26 // read more data |
27 // read more data |
27 sock_fd_enable_events(SOCK_GNUTLS_FD(sock), EV_READ); |
28 mask = EV_READ; |
28 break; |
29 break; |
29 |
30 |
30 case 1: |
31 case 1: |
31 // write buffer full |
32 // write buffer full |
32 sock_fd_enable_events(SOCK_GNUTLS_FD(sock), EV_WRITE); |
33 mask = EV_WRITE; |
33 break; |
34 break; |
34 |
35 |
35 default: |
36 default: |
36 // random error |
37 // random error |
37 RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_GET_DIRECTION, ret); |
38 RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_GET_DIRECTION, ret); |
38 } |
39 } |
39 |
40 |
40 // ok... wait |
41 // do the enabling |
|
42 if ((ERROR_CODE(err) = transport_fd_enable(SOCK_GNUTLS_FD(sock), EV_READ))) |
|
43 return ERROR_CODE(err); |
|
44 |
|
45 |
41 return SUCCESS; |
46 return SUCCESS; |
42 } |
47 } |
43 |
48 |
44 /** |
49 /** |
45 * Translate a set of gnutls_certificate_status_t values to a constant error message |
50 * Translate a set of gnutls_certificate_status_t values to a constant error message |
175 error: |
180 error: |
176 return -ERROR_CODE(err); |
181 return -ERROR_CODE(err); |
177 } |
182 } |
178 |
183 |
179 /** |
184 /** |
180 * Our SOCK_STREAM event handler. Drive the handshake if that's current, otherwise, invoke user callbacks. |
185 * Our transport_fd event handler. Drive the handshake if that's current, otherwise, invoke user callbacks. |
181 * |
186 */ |
182 * XXX: this is ugly. This sock_stream-level separation doesn't really work that well. |
187 static void sock_gnutls_on_event (struct transport_fd *fd, short what, void *arg) |
183 */ |
|
184 static void sock_gnutls_event_handler (int fd, short what, void *arg) |
|
185 { |
188 { |
186 struct sock_gnutls *sock = arg; |
189 struct sock_gnutls *sock = arg; |
187 struct error_info err; |
190 error_t err; |
188 |
191 |
189 (void) fd; |
192 (void) fd; |
|
193 |
|
194 // XXX: timeouts |
190 (void) what; |
195 (void) what; |
191 |
196 |
192 // are we in the handshake cycle? |
197 // are we in the handshake cycle? |
193 if (sock->handshake) { |
198 if (sock->handshake) { |
194 RESET_ERROR(&err); |
199 RESET_ERROR(&err); |
195 |
200 |
|
201 // perform the next handshake step |
196 if (sock_gnutls_handshake(sock, &err) == 0) { |
202 if (sock_gnutls_handshake(sock, &err) == 0) { |
197 // wait for the next handshake step |
203 // handshake continues |
198 |
204 |
199 } else if (SOCK_GNUTLS_BASE(sock)->conn_cb_func) { |
205 } else if (SOCK_GNUTLS_TRANSPORT(sock)->connected) { |
200 // the async connect process has now completed, either succesfully or with an error |
206 // the async connect process has now completed, either succesfully or with an error |
201 // invoke the user connect callback directly with appropriate error |
207 // invoke the user connect callback directly with appropriate error |
202 sock_stream_invoke_conn_cb(SOCK_GNUTLS_BASE(sock), ERROR_CODE(&err) ? &err : NULL, true); |
208 transport_connected(SOCK_GNUTLS_TRANSPORT(sock), ERROR_CODE(&err) ? &err : NULL, true); |
203 |
209 |
204 } else { |
210 } else { |
205 // re-handshake completed, so continue with the sock_stream_callbacks, so the user can call sock_gnutls_read/write |
|
206 |
|
207 if (ERROR_CODE(&err)) |
211 if (ERROR_CODE(&err)) |
208 // XXX: bad, since we can't report this directly... we need to let the user call _read/write, and get |
212 // the re-handshake failed, so this transport is dead |
209 // the error from there |
213 transport_error(SOCK_GNUTLS_TRANSPORT(sock), &err); |
210 log_warn_err(&err, "sock_gnutls_handshake failed"); |
214 |
211 |
215 else |
212 // continue where we left off |
216 // re-handshake completed, so continue with the transport_callbacks |
213 sock_stream_invoke_callbacks(SOCK_GNUTLS_BASE(sock), sock->ev_mask); |
217 transport_fd_invoke(SOCK_GNUTLS_FD(sock), what); |
214 } |
218 } |
215 |
219 |
216 } else { |
220 } else { |
217 // normal sock_stream operation |
221 // normal sock_stream operation |
218 // gnutls might be able to proceed now, so ask user to try what didn't work before now, using the mask given to |
222 // gnutls might be able to proceed now, so invoke user callbacks |
219 // event_enable(). |
223 transport_fd_invoke(SOCK_GNUTLS_FD(sock), what); |
220 sock_stream_invoke_callbacks(SOCK_GNUTLS_BASE(sock), sock->ev_mask); |
224 } |
221 } |
225 } |
222 } |
226 |
223 |
227 static err_t sock_gnutls_read (transport_t *transport, void *buf, size_t *len, error_t *err) |
224 static err_t sock_gnutls_read (struct sock_stream *base_sock, void *buf, size_t *len, struct error_info *err) |
228 { |
225 { |
229 struct sock_gnutls *sock = transport_check(transport, &sock_gnutls_type); |
226 struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls); |
|
227 int ret; |
230 int ret; |
228 |
231 |
229 // read gnutls record |
232 // read gnutls record |
230 ret = gnutls_record_recv(sock->session, buf, *len); |
233 do { |
|
234 ret = gnutls_record_recv(sock->session, buf, *len); |
|
235 |
|
236 } while (ret == GNUTLS_E_INTERRUPTED); |
231 |
237 |
232 // errors |
238 // errors |
233 // XXX: E_INTERRUPTED, E_REHANDSHAKE? |
239 // XXX: E_REHANDSHAKE? |
|
240 if (ret < 0 && ret != GNUTLS_E_AGAIN) |
|
241 RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_RECV, ret); |
|
242 |
|
243 else if (ret == 0) |
|
244 return SET_ERROR(err, ERR_READ_EOF); |
|
245 |
|
246 |
|
247 // EAGAIN? |
|
248 if (ret < 0) { |
|
249 *len = 0; |
|
250 |
|
251 } else { |
|
252 // updated length |
|
253 *len = ret; |
|
254 |
|
255 } |
|
256 |
|
257 return SUCCESS; |
|
258 } |
|
259 |
|
260 static err_t sock_gnutls_write (transport_t *transport, const void *buf, size_t *len, error_t *err) |
|
261 { |
|
262 struct sock_gnutls *sock = transport_check(transport, &sock_gnutls_type); |
|
263 int ret; |
|
264 |
|
265 // read gnutls record |
|
266 do { |
|
267 ret = gnutls_record_send(sock->session, buf, *len); |
|
268 |
|
269 } while (ret == GNUTLS_E_INTERRUPTED); |
|
270 |
|
271 // errors |
234 if (ret < 0 && ret != GNUTLS_E_AGAIN) |
272 if (ret < 0 && ret != GNUTLS_E_AGAIN) |
235 RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_RECV, ret); |
273 RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_RECV, ret); |
236 |
274 |
237 else if (ret == 0) |
275 else if (ret == 0) |
238 return SET_ERROR(err, ERR_READ_EOF); |
276 return SET_ERROR(err, ERR_READ_EOF); |
243 *len = 0; |
281 *len = 0; |
244 |
282 |
245 } else { |
283 } else { |
246 // updated length |
284 // updated length |
247 *len = ret; |
285 *len = ret; |
248 |
|
249 } |
286 } |
250 |
287 |
251 return SUCCESS; |
288 return SUCCESS; |
252 } |
289 } |
253 |
290 |
254 static err_t sock_gnutls_write (struct sock_stream *base_sock, const void *buf, size_t *len, struct error_info *err) |
291 static void _sock_gnutls_destroy (transport_t *transport) |
255 { |
292 { |
256 struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls); |
293 struct sock_gnutls *sock = transport_check(transport, &sock_gnutls_type); |
257 int ret; |
294 |
258 |
295 // die |
259 // read gnutls record |
|
260 ret = gnutls_record_send(sock->session, buf, *len); |
|
261 |
|
262 // errors |
|
263 if (ret < 0 && ret != GNUTLS_E_AGAIN) |
|
264 RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_RECV, ret); |
|
265 |
|
266 else if (ret == 0) |
|
267 return SET_ERROR(err, ERR_READ_EOF); |
|
268 |
|
269 |
|
270 // eagain? |
|
271 if (ret < 0) { |
|
272 *len = 0; |
|
273 |
|
274 } else { |
|
275 // updated length |
|
276 *len = ret; |
|
277 } |
|
278 |
|
279 return SUCCESS; |
|
280 } |
|
281 |
|
282 static err_t sock_gnutls_event_init (struct sock_stream *base_sock) |
|
283 { |
|
284 struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls); |
|
285 |
|
286 (void) sock; |
|
287 |
|
288 // already setup, ok |
|
289 return SUCCESS; |
|
290 } |
|
291 |
|
292 static err_t sock_gnutls_event_enable (struct sock_stream *base_sock, short mask) |
|
293 { |
|
294 struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls); |
|
295 |
|
296 // store the ev_mask. We don't care about it here, because we assume that event_enable is only called once read or |
|
297 // write, respectively, return zero. This is really the only case we can handle with gnutls. |
|
298 sock->ev_mask = mask; |
|
299 |
|
300 // then wait for the event |
|
301 return sock_gnutls_ev_enable(sock, SOCK_GNUTLS_ERR(sock)); |
|
302 } |
|
303 |
|
304 static void sock_gnutls_release (struct sock_stream *base_sock) |
|
305 { |
|
306 struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls); |
|
307 |
|
308 // DIEEEE |
|
309 sock_gnutls_destroy(sock); |
296 sock_gnutls_destroy(sock); |
310 } |
297 } |
311 |
298 |
312 /** |
299 /** |
313 * Our sock_tcp-invoked connect handler |
300 * Our sock_tcp-invoked connect handler |
314 */ |
301 */ |
315 static void sock_gnutls_on_connect (struct sock_stream *base_sock, struct error_info *tcp_err) |
302 static void sock_gnutls__connected (transport_t *transport, const error_t *tcp_err) |
316 { |
303 { |
317 struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls); |
304 struct sock_gnutls *sock = transport_check(transport, &sock_gnutls_type); |
318 struct error_info err; |
305 error_t err; |
319 |
306 |
320 // trap errors to let the user handle them directly |
307 // trap errors to let the user handle them directly |
321 if (tcp_err) |
308 if (tcp_err) |
322 JUMP_SET_ERROR_INFO(&err, tcp_err); |
309 JUMP_SET_ERROR_INFO(&err, tcp_err); |
323 |
310 |
324 // bind default transport functions (recv/send) to use the TCP fd |
311 // bind default transport functions (recv/send) to use the TCP fd |
325 gnutls_transport_set_ptr(sock->session, (gnutls_transport_ptr_t) (long int) SOCK_GNUTLS_FD(sock)->fd); |
312 gnutls_transport_set_ptr(sock->session, (gnutls_transport_ptr_t) (long int) SOCK_GNUTLS_FD(sock)->fd); |
326 |
313 |
327 // add ourselves as the event handler |
314 // add ourselves as the event handler |
328 if ((ERROR_CODE(&err) = sock_fd_init_ev(SOCK_GNUTLS_FD(sock), &sock_gnutls_event_handler, sock))) |
315 if ((ERROR_CODE(&err) = transport_fd_setup(SOCK_GNUTLS_FD(sock), sock_gnutls_on_event, sock))) |
329 goto error; |
316 goto error; |
330 |
317 |
331 // start handshake |
318 // start handshake |
332 if (sock_gnutls_handshake(sock, &err)) |
319 if (sock_gnutls_handshake(sock, &err)) |
333 // this should complete with SUCCESS if it returns >0 |
320 // this should complete with SUCCESS if it returns >0 |
336 // ok, so we wait... |
323 // ok, so we wait... |
337 return; |
324 return; |
338 |
325 |
339 error: |
326 error: |
340 // tell the user |
327 // tell the user |
341 SOCK_GNUTLS_BASE(sock)->conn_cb_func(SOCK_GNUTLS_BASE(sock), &err, SOCK_GNUTLS_BASE(sock)->conn_cb_arg); |
328 transport_connected(transport, &err, true); |
342 } |
329 } |
343 |
330 |
344 /* |
331 struct transport_type sock_gnutls_type = { |
345 * Our sock_stream_Type |
|
346 */ |
|
347 struct sock_stream_type sock_gnutls_type = { |
|
348 .methods = { |
332 .methods = { |
349 .read = &sock_gnutls_read, |
333 .read = sock_gnutls_read, |
350 .write = &sock_gnutls_write, |
334 .write = sock_gnutls_write, |
351 .event_init = &sock_gnutls_event_init, |
335 .destroy = _sock_gnutls_destroy, |
352 .event_enable = &sock_gnutls_event_enable, |
336 ._connected = sock_gnutls__connected, |
353 .release = &sock_gnutls_release, |
|
354 ._conn_cb = &sock_gnutls_on_connect, |
|
355 }, |
337 }, |
356 }; |
338 }; |
357 |
339 |
358 /* |
340 /* |
359 * Global shared anonymous client credentials |
341 * Global shared anonymous client credentials |
451 { |
433 { |
452 if (--cred->refcount == 0) |
434 if (--cred->refcount == 0) |
453 sock_ssl_client_cred_destroy(cred); |
435 sock_ssl_client_cred_destroy(cred); |
454 } |
436 } |
455 |
437 |
456 err_t sock_ssl_connect_async (struct sock_stream **sock_ptr, |
438 err_t sock_ssl_connect (const struct transport_info *info, transport_t **transport_ptr, |
457 const char *hostname, const char *service, |
439 const char *hostname, const char *service, |
458 struct sock_ssl_client_cred *cred, |
440 struct sock_ssl_client_cred *cred, |
459 sock_stream_connect_cb cb_func, void *cb_arg, |
441 error_t *err |
460 struct error_info *err |
|
461 ) |
442 ) |
462 { |
443 { |
463 struct sock_gnutls *sock = NULL; |
444 struct sock_gnutls *sock = NULL; |
464 |
445 |
465 // alloc |
446 // alloc |
466 if ((sock = calloc(1, sizeof(*sock))) == NULL) |
447 if ((sock = calloc(1, sizeof(*sock))) == NULL) |
467 return SET_ERROR(err, ERR_CALLOC); |
448 return SET_ERROR(err, ERR_CALLOC); |
468 |
449 |
469 // initialize base |
450 // initialize base |
470 sock_stream_init(SOCK_GNUTLS_BASE(sock), &sock_gnutls_type, cb_func, cb_arg); |
451 transport_init(SOCK_GNUTLS_TRANSPORT(sock), &sock_gnutls_type, info); |
471 |
452 |
472 if (!cred) { |
453 if (!cred) { |
473 // default credentials |
454 // default credentials |
474 cred = &sock_gnutls_client_cred_anon; |
455 cred = &sock_gnutls_client_cred_anon; |
475 |
456 |