EMACS_INT emacs_gnutls_read (struct Lisp_Process *proc, char *buf, EMACS_INT nbyte) { ssize_t rtnval; gnutls_session_t state = proc->gnutls_state; if (proc->gnutls_initstage != GNUTLS_STAGE_READY) { emacs_gnutls_handshake (proc); return -1; } rtnval = fn_gnutls_record_recv (state, buf, nbyte); if (rtnval >= 0) return rtnval; else if (rtnval == GNUTLS_E_UNEXPECTED_PACKET_LENGTH) /* The peer closed the connection. */ return 0; else if (emacs_gnutls_handle_error (state, rtnval) == 0) /* non-fatal error */ return -1; else { /* a fatal error occurred */ return 0; } }
EMACS_INT emacs_gnutls_write (struct Lisp_Process *proc, const char *buf, EMACS_INT nbyte) { ssize_t rtnval = 0; EMACS_INT bytes_written; gnutls_session_t state = proc->gnutls_state; if (proc->gnutls_initstage != GNUTLS_STAGE_READY) { #ifdef EWOULDBLOCK errno = EWOULDBLOCK; #endif #ifdef EAGAIN errno = EAGAIN; #endif return 0; } bytes_written = 0; while (nbyte > 0) { rtnval = fn_gnutls_record_send (state, buf, nbyte); if (rtnval < 0) { if (rtnval == GNUTLS_E_INTERRUPTED) continue; else { /* If we get GNUTLS_E_AGAIN, then set errno appropriately so that send_process retries the correct way instead of erroring out. */ if (rtnval == GNUTLS_E_AGAIN) { #ifdef EWOULDBLOCK errno = EWOULDBLOCK; #endif #ifdef EAGAIN errno = EAGAIN; #endif } break; } } buf += rtnval; nbyte -= rtnval; bytes_written += rtnval; } emacs_gnutls_handle_error (state, rtnval); return (bytes_written); }
EMACS_INT emacs_gnutls_read (struct Lisp_Process *proc, char *buf, EMACS_INT nbyte) { ssize_t rtnval; gnutls_session_t state = proc->gnutls_state; 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; } GNUTLS_LOG (2, log_level, "Giving up on handshake; resetting retries"); proc->gnutls_handshakes_tried = 0; return 0; } rtnval = fn_gnutls_record_recv (state, buf, nbyte); if (rtnval >= 0) return rtnval; else if (rtnval == GNUTLS_E_UNEXPECTED_PACKET_LENGTH) /* The peer closed the connection. */ return 0; else if (emacs_gnutls_handle_error (state, rtnval) == 0) /* non-fatal error */ return -1; else { /* a fatal error occurred */ return 0; } }
EMACS_INT emacs_gnutls_write (struct Lisp_Process *proc, const char *buf, EMACS_INT nbyte) { ssize_t rtnval = 0; EMACS_INT bytes_written; gnutls_session_t state = proc->gnutls_state; if (proc->gnutls_initstage != GNUTLS_STAGE_READY) { #ifdef EWOULDBLOCK errno = EWOULDBLOCK; #endif #ifdef EAGAIN errno = EAGAIN; #endif return 0; } bytes_written = 0; while (nbyte > 0) { rtnval = fn_gnutls_record_send (state, buf, nbyte); if (rtnval < 0) { if (rtnval == GNUTLS_E_AGAIN || rtnval == GNUTLS_E_INTERRUPTED) continue; else break; } buf += rtnval; nbyte -= rtnval; bytes_written += rtnval; } emacs_gnutls_handle_error (state, rtnval); return (bytes_written); }
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; if (proc->gnutls_initstage < GNUTLS_STAGE_TRANSPORT_POINTERS_SET) { #ifdef WINDOWSNT /* On W32 we cannot transfer socket handles between different runtime libraries, so we tell GnuTLS to use our special push/pull functions. */ fn_gnutls_transport_set_ptr2 (state, (gnutls_transport_ptr_t) proc, (gnutls_transport_ptr_t) proc); fn_gnutls_transport_set_push_function (state, &emacs_gnutls_push); fn_gnutls_transport_set_pull_function (state, &emacs_gnutls_pull); /* For non blocking sockets or other custom made pull/push functions the gnutls_transport_set_lowat must be called, with a zero low water mark value. (GnuTLS 2.10.4 documentation) (Note: this is probably not strictly necessary as the lowat value is only used when no custom pull/push functions are set.) */ /* According to GnuTLS NEWS file, lowat level has been set to zero by default in version 2.11.1, and the function gnutls_transport_set_lowat was removed from the library in version 2.99.0. */ if (!fn_gnutls_check_version ("2.11.1")) fn_gnutls_transport_set_lowat (state, 0); #else /* This is how GnuTLS takes sockets: as file descriptors passed in. For an Emacs process socket, infd and outfd are the same but we use this two-argument version for clarity. */ fn_gnutls_transport_set_ptr2 (state, (gnutls_transport_ptr_t) (long) proc->infd, (gnutls_transport_ptr_t) (long) proc->outfd); #endif proc->gnutls_initstage = GNUTLS_STAGE_TRANSPORT_POINTERS_SET; } do { ret = fn_gnutls_handshake (state); emacs_gnutls_handle_error (state, ret); } while (ret < 0 && fn_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 { fn_gnutls_alert_send_appropriate (state, ret); } return ret; }