diff options
| author | Lars Ingebrigtsen | 2016-02-16 15:56:56 +1100 |
|---|---|---|
| committer | Lars Ingebrigtsen | 2016-02-16 15:56:56 +1100 |
| commit | ac6e085cf6b26257cfe181f17828432d414cb3a6 (patch) | |
| tree | 9d382588ce869a2b816373e278b2805ffbb90f59 /src | |
| parent | c43bb7f136ab9e9439a4b5c045040a12cbe8bda0 (diff) | |
| download | emacs-ac6e085cf6b26257cfe181f17828432d414cb3a6.tar.gz emacs-ac6e085cf6b26257cfe181f17828432d414cb3a6.zip | |
Implement asynch TLS negotiation
* src/gnutls.c (gnutls_try_handshake): Factor out into its own
function.
(emacs_gnutls_handshake): Use it.
(emacs_gnutls_read): Just return instead of retrying the handshake.
* src/process.c (finish_after_tls_connection): Factor out
into its own function.
(connect_network_socket): Use it.
(wait_reading_process_output): Retry TLS handshakes.
(wait_reading_process_output): Defer sentinel until TLS completes.
Diffstat (limited to 'src')
| -rw-r--r-- | src/gnutls.c | 71 | ||||
| -rw-r--r-- | src/gnutls.h | 1 | ||||
| -rw-r--r-- | src/process.c | 131 |
3 files changed, 119 insertions, 84 deletions
diff --git a/src/gnutls.c b/src/gnutls.c index 948a0c56f14..6573c87cf78 100644 --- a/src/gnutls.c +++ b/src/gnutls.c | |||
| @@ -397,11 +397,42 @@ gnutls_log_function2i (int level, const char *string, int extra) | |||
| 397 | message ("gnutls.c: [%d] %s %d", level, string, extra); | 397 | message ("gnutls.c: [%d] %s %d", level, string, extra); |
| 398 | } | 398 | } |
| 399 | 399 | ||
| 400 | int | ||
| 401 | gnutls_try_handshake (struct Lisp_Process *proc) | ||
| 402 | { | ||
| 403 | gnutls_session_t state = proc->gnutls_state; | ||
| 404 | int ret; | ||
| 405 | |||
| 406 | do | ||
| 407 | { | ||
| 408 | ret = gnutls_handshake (state); | ||
| 409 | emacs_gnutls_handle_error (state, ret); | ||
| 410 | QUIT; | ||
| 411 | } | ||
| 412 | while (ret < 0 && gnutls_error_is_fatal (ret) == 0 && | ||
| 413 | ! proc->is_non_blocking_client); | ||
| 414 | |||
| 415 | proc->gnutls_initstage = GNUTLS_STAGE_HANDSHAKE_TRIED; | ||
| 416 | |||
| 417 | if (proc->is_non_blocking_client) | ||
| 418 | proc->gnutls_p = 1; | ||
| 419 | |||
| 420 | if (ret == GNUTLS_E_SUCCESS) | ||
| 421 | { | ||
| 422 | /* Here we're finally done. */ | ||
| 423 | proc->gnutls_initstage = GNUTLS_STAGE_READY; | ||
| 424 | } | ||
| 425 | else | ||
| 426 | { | ||
| 427 | //check_memory_full (gnutls_alert_send_appropriate (state, ret)); | ||
| 428 | } | ||
| 429 | return ret; | ||
| 430 | } | ||
| 431 | |||
| 400 | static int | 432 | static int |
| 401 | emacs_gnutls_handshake (struct Lisp_Process *proc) | 433 | emacs_gnutls_handshake (struct Lisp_Process *proc) |
| 402 | { | 434 | { |
| 403 | gnutls_session_t state = proc->gnutls_state; | 435 | gnutls_session_t state = proc->gnutls_state; |
| 404 | int ret; | ||
| 405 | 436 | ||
| 406 | if (proc->gnutls_initstage < GNUTLS_STAGE_HANDSHAKE_CANDO) | 437 | if (proc->gnutls_initstage < GNUTLS_STAGE_HANDSHAKE_CANDO) |
| 407 | return -1; | 438 | return -1; |
| @@ -443,26 +474,7 @@ emacs_gnutls_handshake (struct Lisp_Process *proc) | |||
| 443 | proc->gnutls_initstage = GNUTLS_STAGE_TRANSPORT_POINTERS_SET; | 474 | proc->gnutls_initstage = GNUTLS_STAGE_TRANSPORT_POINTERS_SET; |
| 444 | } | 475 | } |
| 445 | 476 | ||
| 446 | do | 477 | return gnutls_try_handshake (proc); |
| 447 | { | ||
| 448 | ret = gnutls_handshake (state); | ||
| 449 | emacs_gnutls_handle_error (state, ret); | ||
| 450 | QUIT; | ||
| 451 | } | ||
| 452 | while (ret < 0 && gnutls_error_is_fatal (ret) == 0); | ||
| 453 | |||
| 454 | proc->gnutls_initstage = GNUTLS_STAGE_HANDSHAKE_TRIED; | ||
| 455 | |||
| 456 | if (ret == GNUTLS_E_SUCCESS) | ||
| 457 | { | ||
| 458 | /* Here we're finally done. */ | ||
| 459 | proc->gnutls_initstage = GNUTLS_STAGE_READY; | ||
| 460 | } | ||
| 461 | else | ||
| 462 | { | ||
| 463 | check_memory_full (gnutls_alert_send_appropriate (state, ret)); | ||
| 464 | } | ||
| 465 | return ret; | ||
| 466 | } | 478 | } |
| 467 | 479 | ||
| 468 | ptrdiff_t | 480 | ptrdiff_t |
| @@ -531,23 +543,8 @@ emacs_gnutls_read (struct Lisp_Process *proc, char *buf, ptrdiff_t nbyte) | |||
| 531 | int log_level = proc->gnutls_log_level; | 543 | int log_level = proc->gnutls_log_level; |
| 532 | 544 | ||
| 533 | if (proc->gnutls_initstage != GNUTLS_STAGE_READY) | 545 | if (proc->gnutls_initstage != GNUTLS_STAGE_READY) |
| 534 | { | 546 | return -1; |
| 535 | /* If the handshake count is under the limit, try the handshake | ||
| 536 | again and increment the handshake count. This count is kept | ||
| 537 | per process (connection), not globally. */ | ||
| 538 | if (proc->gnutls_handshakes_tried < GNUTLS_EMACS_HANDSHAKES_LIMIT) | ||
| 539 | { | ||
| 540 | proc->gnutls_handshakes_tried++; | ||
| 541 | emacs_gnutls_handshake (proc); | ||
| 542 | GNUTLS_LOG2i (5, log_level, "Retried handshake", | ||
| 543 | proc->gnutls_handshakes_tried); | ||
| 544 | return -1; | ||
| 545 | } | ||
| 546 | 547 | ||
| 547 | GNUTLS_LOG (2, log_level, "Giving up on handshake; resetting retries"); | ||
| 548 | proc->gnutls_handshakes_tried = 0; | ||
| 549 | return 0; | ||
| 550 | } | ||
| 551 | rtnval = gnutls_record_recv (state, buf, nbyte); | 548 | rtnval = gnutls_record_recv (state, buf, nbyte); |
| 552 | if (rtnval >= 0) | 549 | if (rtnval >= 0) |
| 553 | return rtnval; | 550 | return rtnval; |
diff --git a/src/gnutls.h b/src/gnutls.h index 8e879c168bd..cb521350b9d 100644 --- a/src/gnutls.h +++ b/src/gnutls.h | |||
| @@ -84,6 +84,7 @@ extern void emacs_gnutls_transport_set_errno (gnutls_session_t state, int err); | |||
| 84 | #endif | 84 | #endif |
| 85 | extern Lisp_Object emacs_gnutls_deinit (Lisp_Object); | 85 | extern Lisp_Object emacs_gnutls_deinit (Lisp_Object); |
| 86 | extern Lisp_Object emacs_gnutls_global_init (void); | 86 | extern Lisp_Object emacs_gnutls_global_init (void); |
| 87 | extern int gnutls_try_handshake (struct Lisp_Process *p); | ||
| 87 | 88 | ||
| 88 | #endif | 89 | #endif |
| 89 | 90 | ||
diff --git a/src/process.c b/src/process.c index 9a3bcaed389..ec31ea87c7b 100644 --- a/src/process.c +++ b/src/process.c | |||
| @@ -281,9 +281,7 @@ static int max_input_desc; | |||
| 281 | 281 | ||
| 282 | /* Indexed by descriptor, gives the process (if any) for that descriptor. */ | 282 | /* Indexed by descriptor, gives the process (if any) for that descriptor. */ |
| 283 | static Lisp_Object chan_process[FD_SETSIZE]; | 283 | static Lisp_Object chan_process[FD_SETSIZE]; |
| 284 | #ifdef HAVE_GETADDRINFO_A | ||
| 285 | static void wait_for_socket_fds (Lisp_Object process, char *name); | 284 | static void wait_for_socket_fds (Lisp_Object process, char *name); |
| 286 | #endif | ||
| 287 | 285 | ||
| 288 | /* Alist of elements (NAME . PROCESS). */ | 286 | /* Alist of elements (NAME . PROCESS). */ |
| 289 | static Lisp_Object Vprocess_alist; | 287 | static Lisp_Object Vprocess_alist; |
| @@ -3038,7 +3036,45 @@ void set_network_socket_coding_system (Lisp_Object proc) | |||
| 3038 | = !(!NILP (tem) || NILP (p->buffer) || !inherit_process_coding_system); | 3036 | = !(!NILP (tem) || NILP (p->buffer) || !inherit_process_coding_system); |
| 3039 | } | 3037 | } |
| 3040 | 3038 | ||
| 3041 | void connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses) | 3039 | #ifdef HAVE_GNUTLS |
| 3040 | void | ||
| 3041 | finish_after_tls_connection (Lisp_Object proc) | ||
| 3042 | { | ||
| 3043 | struct Lisp_Process *p = XPROCESS (proc); | ||
| 3044 | Lisp_Object contact = p->childp; | ||
| 3045 | Lisp_Object result = Qt; | ||
| 3046 | |||
| 3047 | if (!NILP (Ffboundp (Qnsm_verify_connection))) | ||
| 3048 | result = call3 (Qnsm_verify_connection, | ||
| 3049 | proc, | ||
| 3050 | Fplist_get (contact, QChost), | ||
| 3051 | Fplist_get (contact, QCservice)); | ||
| 3052 | |||
| 3053 | if (NILP (result)) | ||
| 3054 | { | ||
| 3055 | pset_status (p, list2 (Qfailed, | ||
| 3056 | build_string ("The Network Security Manager stopped the connections"))); | ||
| 3057 | deactivate_process (proc); | ||
| 3058 | } | ||
| 3059 | else | ||
| 3060 | { | ||
| 3061 | /* If we cleared the connection wait mask before we did | ||
| 3062 | the TLS setup, then we have to say that the process | ||
| 3063 | is finally "open" here. */ | ||
| 3064 | if (! FD_ISSET (p->outfd, &connect_wait_mask)) | ||
| 3065 | { | ||
| 3066 | pset_status (p, Qrun); | ||
| 3067 | /* Execute the sentinel here. If we had relied on | ||
| 3068 | status_notify to do it later, it will read input | ||
| 3069 | from the process before calling the sentinel. */ | ||
| 3070 | exec_sentinel (proc, build_string ("open\n")); | ||
| 3071 | } | ||
| 3072 | } | ||
| 3073 | } | ||
| 3074 | #endif | ||
| 3075 | |||
| 3076 | void | ||
| 3077 | connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses) | ||
| 3042 | { | 3078 | { |
| 3043 | ptrdiff_t count = SPECPDL_INDEX (); | 3079 | ptrdiff_t count = SPECPDL_INDEX (); |
| 3044 | ptrdiff_t count1; | 3080 | ptrdiff_t count1; |
| @@ -3359,8 +3395,10 @@ void connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses) | |||
| 3359 | boot = Fgnutls_boot (proc, XCAR (params), XCDR (params)); | 3395 | boot = Fgnutls_boot (proc, XCAR (params), XCDR (params)); |
| 3360 | p->gnutls_boot_parameters = Qnil; | 3396 | p->gnutls_boot_parameters = Qnil; |
| 3361 | 3397 | ||
| 3362 | if (NILP (boot) || STRINGP (boot) || | 3398 | if (p->gnutls_initstage == GNUTLS_STAGE_READY) |
| 3363 | p->gnutls_initstage != GNUTLS_STAGE_READY) | 3399 | /* Run sentinels, etc. */ |
| 3400 | finish_after_tls_connection (proc); | ||
| 3401 | else if (p->gnutls_initstage != GNUTLS_STAGE_HANDSHAKE_TRIED) | ||
| 3364 | { | 3402 | { |
| 3365 | deactivate_process (proc); | 3403 | deactivate_process (proc); |
| 3366 | if (NILP (boot)) | 3404 | if (NILP (boot)) |
| @@ -3369,37 +3407,6 @@ void connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses) | |||
| 3369 | else | 3407 | else |
| 3370 | pset_status (p, list2 (Qfailed, boot)); | 3408 | pset_status (p, list2 (Qfailed, boot)); |
| 3371 | } | 3409 | } |
| 3372 | else | ||
| 3373 | { | ||
| 3374 | Lisp_Object result = Qt; | ||
| 3375 | |||
| 3376 | if (!NILP (Ffboundp (Qnsm_verify_connection))) | ||
| 3377 | result = call3 (Qnsm_verify_connection, | ||
| 3378 | proc, | ||
| 3379 | Fplist_get (contact, QChost), | ||
| 3380 | Fplist_get (contact, QCservice)); | ||
| 3381 | |||
| 3382 | if (NILP (result)) | ||
| 3383 | { | ||
| 3384 | pset_status (p, list2 (Qfailed, | ||
| 3385 | build_string ("The Network Security Manager stopped the connections"))); | ||
| 3386 | deactivate_process (proc); | ||
| 3387 | } | ||
| 3388 | else | ||
| 3389 | { | ||
| 3390 | /* If we cleared the connection wait mask before we did | ||
| 3391 | the TLS setup, then we have to say that the process | ||
| 3392 | is finally "open" here. */ | ||
| 3393 | if (! FD_ISSET (p->outfd, &connect_wait_mask)) | ||
| 3394 | { | ||
| 3395 | pset_status (p, Qrun); | ||
| 3396 | /* Execute the sentinel here. If we had relied on | ||
| 3397 | status_notify to do it later, it will read input | ||
| 3398 | from the process before calling the sentinel. */ | ||
| 3399 | exec_sentinel (proc, build_string ("open\n")); | ||
| 3400 | } | ||
| 3401 | } | ||
| 3402 | } | ||
| 3403 | } | 3410 | } |
| 3404 | #endif | 3411 | #endif |
| 3405 | 3412 | ||
| @@ -4747,8 +4754,8 @@ static void | |||
| 4747 | wait_for_tls_negotiation (Lisp_Object process) | 4754 | wait_for_tls_negotiation (Lisp_Object process) |
| 4748 | { | 4755 | { |
| 4749 | #ifdef HAVE_GNUTLS | 4756 | #ifdef HAVE_GNUTLS |
| 4750 | while (EQ (XPROCESS (process)->status, Qconnect) && | 4757 | while (XPROCESS (process)->gnutls_p && |
| 4751 | !NILP (XPROCESS (process)->gnutls_boot_parameters)) | 4758 | XPROCESS (process)->gnutls_initstage != GNUTLS_STAGE_READY) |
| 4752 | { | 4759 | { |
| 4753 | printf("Waiting for TLS...\n"); | 4760 | printf("Waiting for TLS...\n"); |
| 4754 | wait_reading_process_output (0, 20 * 1000 * 1000, 0, 0, Qnil, NULL, 0); | 4761 | wait_reading_process_output (0, 20 * 1000 * 1000, 0, 0, Qnil, NULL, 0); |
| @@ -4881,7 +4888,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, | |||
| 4881 | if (! NILP (wait_for_cell) && ! NILP (XCAR (wait_for_cell))) | 4888 | if (! NILP (wait_for_cell) && ! NILP (XCAR (wait_for_cell))) |
| 4882 | break; | 4889 | break; |
| 4883 | 4890 | ||
| 4884 | #ifdef HAVE_GETADDRINFO_A | 4891 | #if defined (HAVE_GETADDRINFO_A) || defined (HAVE_GNUTLS) |
| 4885 | { | 4892 | { |
| 4886 | Lisp_Object ip_addresses; | 4893 | Lisp_Object ip_addresses; |
| 4887 | Lisp_Object process_list_head, aproc; | 4894 | Lisp_Object process_list_head, aproc; |
| @@ -4891,17 +4898,41 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, | |||
| 4891 | { | 4898 | { |
| 4892 | p = XPROCESS (aproc); | 4899 | p = XPROCESS (aproc); |
| 4893 | 4900 | ||
| 4894 | if (p->dns_requests && | 4901 | if (! wait_proc || p == wait_proc) |
| 4895 | (! wait_proc || p == wait_proc)) | ||
| 4896 | { | 4902 | { |
| 4897 | ip_addresses = check_for_dns (aproc); | 4903 | #ifdef HAVE_GETADDRINFO_A |
| 4898 | if (!NILP (ip_addresses) && | 4904 | /* Check for pending DNS requests. */ |
| 4899 | !EQ (ip_addresses, Qt)) | 4905 | if (p->dns_requests) |
| 4900 | connect_network_socket (aproc, ip_addresses); | 4906 | { |
| 4907 | ip_addresses = check_for_dns (aproc); | ||
| 4908 | if (!NILP (ip_addresses) && | ||
| 4909 | !EQ (ip_addresses, Qt)) | ||
| 4910 | connect_network_socket (aproc, ip_addresses); | ||
| 4911 | } | ||
| 4912 | #endif | ||
| 4913 | #ifdef HAVE_GNUTLS | ||
| 4914 | /* Continue TLS negotiation. */ | ||
| 4915 | if (p->gnutls_initstage == GNUTLS_STAGE_HANDSHAKE_TRIED && | ||
| 4916 | p->is_non_blocking_client) | ||
| 4917 | { | ||
| 4918 | gnutls_try_handshake (p); | ||
| 4919 | p->gnutls_handshakes_tried++; | ||
| 4920 | |||
| 4921 | if (p->gnutls_initstage == GNUTLS_STAGE_READY) | ||
| 4922 | finish_after_tls_connection (aproc); | ||
| 4923 | else if (p->gnutls_handshakes_tried > | ||
| 4924 | GNUTLS_EMACS_HANDSHAKES_LIMIT) | ||
| 4925 | { | ||
| 4926 | deactivate_process (proc); | ||
| 4927 | pset_status (p, list2 (Qfailed, | ||
| 4928 | build_string ("TLS negotiation failed"))); | ||
| 4929 | } | ||
| 4930 | } | ||
| 4931 | #endif | ||
| 4901 | } | 4932 | } |
| 4902 | } | 4933 | } |
| 4903 | } | 4934 | } |
| 4904 | #endif /* HAVE_GETADDRINFO_A */ | 4935 | #endif /* GETADDRINFO_A or GNUTLS */ |
| 4905 | 4936 | ||
| 4906 | /* Compute time from now till when time limit is up. */ | 4937 | /* Compute time from now till when time limit is up. */ |
| 4907 | /* Exit if already run out. */ | 4938 | /* Exit if already run out. */ |
| @@ -5522,7 +5553,13 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, | |||
| 5522 | } | 5553 | } |
| 5523 | else | 5554 | else |
| 5524 | { | 5555 | { |
| 5525 | if (NILP (p->gnutls_boot_parameters)) | 5556 | #ifdef HAVE_GNUTLS |
| 5557 | /* If we have an incompletely set up TLS connection, | ||
| 5558 | then defer the sentinel signalling until | ||
| 5559 | later. */ | ||
| 5560 | if (NILP (p->gnutls_boot_parameters) && | ||
| 5561 | !p->gnutls_p) | ||
| 5562 | #endif | ||
| 5526 | { | 5563 | { |
| 5527 | pset_status (p, Qrun); | 5564 | pset_status (p, Qrun); |
| 5528 | /* Execute the sentinel here. If we had relied on | 5565 | /* Execute the sentinel here. If we had relied on |