mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-02-23 21:37:34 +00:00
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.
This commit is contained in:
parent
c43bb7f136
commit
ac6e085cf6
3 changed files with 119 additions and 84 deletions
71
src/gnutls.c
71
src/gnutls.c
|
|
@ -397,11 +397,42 @@ gnutls_log_function2i (int level, const char *string, int extra)
|
|||
message ("gnutls.c: [%d] %s %d", level, string, extra);
|
||||
}
|
||||
|
||||
int
|
||||
gnutls_try_handshake (struct Lisp_Process *proc)
|
||||
{
|
||||
gnutls_session_t state = proc->gnutls_state;
|
||||
int ret;
|
||||
|
||||
do
|
||||
{
|
||||
ret = gnutls_handshake (state);
|
||||
emacs_gnutls_handle_error (state, ret);
|
||||
QUIT;
|
||||
}
|
||||
while (ret < 0 && gnutls_error_is_fatal (ret) == 0 &&
|
||||
! proc->is_non_blocking_client);
|
||||
|
||||
proc->gnutls_initstage = GNUTLS_STAGE_HANDSHAKE_TRIED;
|
||||
|
||||
if (proc->is_non_blocking_client)
|
||||
proc->gnutls_p = 1;
|
||||
|
||||
if (ret == GNUTLS_E_SUCCESS)
|
||||
{
|
||||
/* Here we're finally done. */
|
||||
proc->gnutls_initstage = GNUTLS_STAGE_READY;
|
||||
}
|
||||
else
|
||||
{
|
||||
//check_memory_full (gnutls_alert_send_appropriate (state, ret));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
emacs_gnutls_handshake (struct Lisp_Process *proc)
|
||||
{
|
||||
gnutls_session_t state = proc->gnutls_state;
|
||||
int ret;
|
||||
|
||||
if (proc->gnutls_initstage < GNUTLS_STAGE_HANDSHAKE_CANDO)
|
||||
return -1;
|
||||
|
|
@ -443,26 +474,7 @@ emacs_gnutls_handshake (struct Lisp_Process *proc)
|
|||
proc->gnutls_initstage = GNUTLS_STAGE_TRANSPORT_POINTERS_SET;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
ret = gnutls_handshake (state);
|
||||
emacs_gnutls_handle_error (state, ret);
|
||||
QUIT;
|
||||
}
|
||||
while (ret < 0 && gnutls_error_is_fatal (ret) == 0);
|
||||
|
||||
proc->gnutls_initstage = GNUTLS_STAGE_HANDSHAKE_TRIED;
|
||||
|
||||
if (ret == GNUTLS_E_SUCCESS)
|
||||
{
|
||||
/* Here we're finally done. */
|
||||
proc->gnutls_initstage = GNUTLS_STAGE_READY;
|
||||
}
|
||||
else
|
||||
{
|
||||
check_memory_full (gnutls_alert_send_appropriate (state, ret));
|
||||
}
|
||||
return ret;
|
||||
return gnutls_try_handshake (proc);
|
||||
}
|
||||
|
||||
ptrdiff_t
|
||||
|
|
@ -531,23 +543,8 @@ emacs_gnutls_read (struct Lisp_Process *proc, char *buf, ptrdiff_t nbyte)
|
|||
int log_level = proc->gnutls_log_level;
|
||||
|
||||
if (proc->gnutls_initstage != GNUTLS_STAGE_READY)
|
||||
{
|
||||
/* If the handshake count is under the limit, try the handshake
|
||||
again and increment the handshake count. This count is kept
|
||||
per process (connection), not globally. */
|
||||
if (proc->gnutls_handshakes_tried < GNUTLS_EMACS_HANDSHAKES_LIMIT)
|
||||
{
|
||||
proc->gnutls_handshakes_tried++;
|
||||
emacs_gnutls_handshake (proc);
|
||||
GNUTLS_LOG2i (5, log_level, "Retried handshake",
|
||||
proc->gnutls_handshakes_tried);
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
|
||||
GNUTLS_LOG (2, log_level, "Giving up on handshake; resetting retries");
|
||||
proc->gnutls_handshakes_tried = 0;
|
||||
return 0;
|
||||
}
|
||||
rtnval = gnutls_record_recv (state, buf, nbyte);
|
||||
if (rtnval >= 0)
|
||||
return rtnval;
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ extern void emacs_gnutls_transport_set_errno (gnutls_session_t state, int err);
|
|||
#endif
|
||||
extern Lisp_Object emacs_gnutls_deinit (Lisp_Object);
|
||||
extern Lisp_Object emacs_gnutls_global_init (void);
|
||||
extern int gnutls_try_handshake (struct Lisp_Process *p);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
131
src/process.c
131
src/process.c
|
|
@ -281,9 +281,7 @@ static int max_input_desc;
|
|||
|
||||
/* Indexed by descriptor, gives the process (if any) for that descriptor. */
|
||||
static Lisp_Object chan_process[FD_SETSIZE];
|
||||
#ifdef HAVE_GETADDRINFO_A
|
||||
static void wait_for_socket_fds (Lisp_Object process, char *name);
|
||||
#endif
|
||||
|
||||
/* Alist of elements (NAME . PROCESS). */
|
||||
static Lisp_Object Vprocess_alist;
|
||||
|
|
@ -3038,7 +3036,45 @@ void set_network_socket_coding_system (Lisp_Object proc)
|
|||
= !(!NILP (tem) || NILP (p->buffer) || !inherit_process_coding_system);
|
||||
}
|
||||
|
||||
void connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses)
|
||||
#ifdef HAVE_GNUTLS
|
||||
void
|
||||
finish_after_tls_connection (Lisp_Object proc)
|
||||
{
|
||||
struct Lisp_Process *p = XPROCESS (proc);
|
||||
Lisp_Object contact = p->childp;
|
||||
Lisp_Object result = Qt;
|
||||
|
||||
if (!NILP (Ffboundp (Qnsm_verify_connection)))
|
||||
result = call3 (Qnsm_verify_connection,
|
||||
proc,
|
||||
Fplist_get (contact, QChost),
|
||||
Fplist_get (contact, QCservice));
|
||||
|
||||
if (NILP (result))
|
||||
{
|
||||
pset_status (p, list2 (Qfailed,
|
||||
build_string ("The Network Security Manager stopped the connections")));
|
||||
deactivate_process (proc);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If we cleared the connection wait mask before we did
|
||||
the TLS setup, then we have to say that the process
|
||||
is finally "open" here. */
|
||||
if (! FD_ISSET (p->outfd, &connect_wait_mask))
|
||||
{
|
||||
pset_status (p, Qrun);
|
||||
/* Execute the sentinel here. If we had relied on
|
||||
status_notify to do it later, it will read input
|
||||
from the process before calling the sentinel. */
|
||||
exec_sentinel (proc, build_string ("open\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses)
|
||||
{
|
||||
ptrdiff_t count = SPECPDL_INDEX ();
|
||||
ptrdiff_t count1;
|
||||
|
|
@ -3359,8 +3395,10 @@ void connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses)
|
|||
boot = Fgnutls_boot (proc, XCAR (params), XCDR (params));
|
||||
p->gnutls_boot_parameters = Qnil;
|
||||
|
||||
if (NILP (boot) || STRINGP (boot) ||
|
||||
p->gnutls_initstage != GNUTLS_STAGE_READY)
|
||||
if (p->gnutls_initstage == GNUTLS_STAGE_READY)
|
||||
/* Run sentinels, etc. */
|
||||
finish_after_tls_connection (proc);
|
||||
else if (p->gnutls_initstage != GNUTLS_STAGE_HANDSHAKE_TRIED)
|
||||
{
|
||||
deactivate_process (proc);
|
||||
if (NILP (boot))
|
||||
|
|
@ -3369,37 +3407,6 @@ void connect_network_socket (Lisp_Object proc, Lisp_Object ip_addresses)
|
|||
else
|
||||
pset_status (p, list2 (Qfailed, boot));
|
||||
}
|
||||
else
|
||||
{
|
||||
Lisp_Object result = Qt;
|
||||
|
||||
if (!NILP (Ffboundp (Qnsm_verify_connection)))
|
||||
result = call3 (Qnsm_verify_connection,
|
||||
proc,
|
||||
Fplist_get (contact, QChost),
|
||||
Fplist_get (contact, QCservice));
|
||||
|
||||
if (NILP (result))
|
||||
{
|
||||
pset_status (p, list2 (Qfailed,
|
||||
build_string ("The Network Security Manager stopped the connections")));
|
||||
deactivate_process (proc);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If we cleared the connection wait mask before we did
|
||||
the TLS setup, then we have to say that the process
|
||||
is finally "open" here. */
|
||||
if (! FD_ISSET (p->outfd, &connect_wait_mask))
|
||||
{
|
||||
pset_status (p, Qrun);
|
||||
/* Execute the sentinel here. If we had relied on
|
||||
status_notify to do it later, it will read input
|
||||
from the process before calling the sentinel. */
|
||||
exec_sentinel (proc, build_string ("open\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -4747,8 +4754,8 @@ static void
|
|||
wait_for_tls_negotiation (Lisp_Object process)
|
||||
{
|
||||
#ifdef HAVE_GNUTLS
|
||||
while (EQ (XPROCESS (process)->status, Qconnect) &&
|
||||
!NILP (XPROCESS (process)->gnutls_boot_parameters))
|
||||
while (XPROCESS (process)->gnutls_p &&
|
||||
XPROCESS (process)->gnutls_initstage != GNUTLS_STAGE_READY)
|
||||
{
|
||||
printf("Waiting for TLS...\n");
|
||||
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,
|
|||
if (! NILP (wait_for_cell) && ! NILP (XCAR (wait_for_cell)))
|
||||
break;
|
||||
|
||||
#ifdef HAVE_GETADDRINFO_A
|
||||
#if defined (HAVE_GETADDRINFO_A) || defined (HAVE_GNUTLS)
|
||||
{
|
||||
Lisp_Object ip_addresses;
|
||||
Lisp_Object process_list_head, aproc;
|
||||
|
|
@ -4891,17 +4898,41 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
|
|||
{
|
||||
p = XPROCESS (aproc);
|
||||
|
||||
if (p->dns_requests &&
|
||||
(! wait_proc || p == wait_proc))
|
||||
if (! wait_proc || p == wait_proc)
|
||||
{
|
||||
ip_addresses = check_for_dns (aproc);
|
||||
if (!NILP (ip_addresses) &&
|
||||
!EQ (ip_addresses, Qt))
|
||||
connect_network_socket (aproc, ip_addresses);
|
||||
#ifdef HAVE_GETADDRINFO_A
|
||||
/* Check for pending DNS requests. */
|
||||
if (p->dns_requests)
|
||||
{
|
||||
ip_addresses = check_for_dns (aproc);
|
||||
if (!NILP (ip_addresses) &&
|
||||
!EQ (ip_addresses, Qt))
|
||||
connect_network_socket (aproc, ip_addresses);
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_GNUTLS
|
||||
/* Continue TLS negotiation. */
|
||||
if (p->gnutls_initstage == GNUTLS_STAGE_HANDSHAKE_TRIED &&
|
||||
p->is_non_blocking_client)
|
||||
{
|
||||
gnutls_try_handshake (p);
|
||||
p->gnutls_handshakes_tried++;
|
||||
|
||||
if (p->gnutls_initstage == GNUTLS_STAGE_READY)
|
||||
finish_after_tls_connection (aproc);
|
||||
else if (p->gnutls_handshakes_tried >
|
||||
GNUTLS_EMACS_HANDSHAKES_LIMIT)
|
||||
{
|
||||
deactivate_process (proc);
|
||||
pset_status (p, list2 (Qfailed,
|
||||
build_string ("TLS negotiation failed")));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_GETADDRINFO_A */
|
||||
#endif /* GETADDRINFO_A or GNUTLS */
|
||||
|
||||
/* Compute time from now till when time limit is up. */
|
||||
/* Exit if already run out. */
|
||||
|
|
@ -5522,7 +5553,13 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
|
|||
}
|
||||
else
|
||||
{
|
||||
if (NILP (p->gnutls_boot_parameters))
|
||||
#ifdef HAVE_GNUTLS
|
||||
/* If we have an incompletely set up TLS connection,
|
||||
then defer the sentinel signalling until
|
||||
later. */
|
||||
if (NILP (p->gnutls_boot_parameters) &&
|
||||
!p->gnutls_p)
|
||||
#endif
|
||||
{
|
||||
pset_status (p, Qrun);
|
||||
/* Execute the sentinel here. If we had relied on
|
||||
|
|
|
|||
Loading…
Reference in a new issue