/* GnuTLS glue for GNU Emacs.
- Copyright (C) 2010-2011 Free Software Foundation, Inc.
+ Copyright (C) 2010-2012 Free Software Foundation, Inc.
This file is part of GNU Emacs.
(gnutls_session_t, const void *, size_t));
DEF_GNUTLS_FN (const char *, gnutls_strerror, (int));
DEF_GNUTLS_FN (void, gnutls_transport_set_errno, (gnutls_session_t, int));
+DEF_GNUTLS_FN (const char *, gnutls_check_version, (const char *));
DEF_GNUTLS_FN (void, gnutls_transport_set_lowat, (gnutls_session_t, int));
DEF_GNUTLS_FN (void, gnutls_transport_set_ptr2,
(gnutls_session_t, gnutls_transport_ptr_t,
LOAD_GNUTLS_FN (library, gnutls_record_send);
LOAD_GNUTLS_FN (library, gnutls_strerror);
LOAD_GNUTLS_FN (library, gnutls_transport_set_errno);
- LOAD_GNUTLS_FN (library, gnutls_transport_set_lowat);
+ LOAD_GNUTLS_FN (library, gnutls_check_version);
+ /* We don't need to call gnutls_transport_set_lowat in GnuTLS 2.11.1
+ and later, and the function was removed entirely in 3.0.0. */
+ if (!fn_gnutls_check_version ("2.11.1"))
+ LOAD_GNUTLS_FN (library, gnutls_transport_set_lowat);
LOAD_GNUTLS_FN (library, gnutls_transport_set_ptr2);
LOAD_GNUTLS_FN (library, gnutls_transport_set_pull_function);
LOAD_GNUTLS_FN (library, gnutls_transport_set_push_function);
#endif /* !WINDOWSNT */
\f
+/* Function to log a simple message. */
static void
gnutls_log_function (int level, const char* string)
{
message ("gnutls.c: [%d] %s", level, string);
}
+/* Function to log a message and a string. */
static void
gnutls_log_function2 (int level, const char* string, const char* extra)
{
message ("gnutls.c: [%d] %s %s", level, string, extra);
}
+/* Function to log a message and an integer. */
+static void
+gnutls_log_function2i (int level, const char* string, int extra)
+{
+ message ("gnutls.c: [%d] %s %d", level, string, extra);
+}
+
static int
emacs_gnutls_handshake (struct Lisp_Process *proc)
{
(Note: this is probably not strictly necessary as the lowat
value is only used when no custom pull/push functions are
set.) */
- fn_gnutls_transport_set_lowat (state, 0);
+ /* 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
{
ret = fn_gnutls_handshake (state);
emacs_gnutls_handle_error (state, ret);
+ QUIT;
}
while (ret < 0 && fn_gnutls_error_is_fatal (ret) == 0);
EMACS_INT bytes_written;
gnutls_session_t state = proc->gnutls_state;
- if (proc->gnutls_initstage != GNUTLS_STAGE_READY) {
+ if (proc->gnutls_initstage != GNUTLS_STAGE_READY)
+ {
#ifdef EWOULDBLOCK
- errno = EWOULDBLOCK;
+ errno = EWOULDBLOCK;
#endif
#ifdef EAGAIN
- errno = EAGAIN;
+ errno = EAGAIN;
#endif
- return 0;
- }
+ return 0;
+ }
bytes_written = 0;
if (rtnval < 0)
{
- if (rtnval == GNUTLS_E_AGAIN || rtnval == GNUTLS_E_INTERRUPTED)
+ if (rtnval == GNUTLS_E_INTERRUPTED)
continue;
else
- break;
+ {
+ /* 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;
ssize_t rtnval;
gnutls_session_t state = proc->gnutls_state;
+ int log_level = proc->gnutls_log_level;
+
if (proc->gnutls_initstage != GNUTLS_STAGE_READY)
{
- emacs_gnutls_handshake (proc);
- return -1;
+ /* 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)
defsubr (&Sgnutls_available_p);
DEFVAR_INT ("gnutls-log-level", global_gnutls_log_level,
- doc: /* Logging level used by the GnuTLS functions. */);
+ doc: /* Logging level used by the GnuTLS functions.
+Set this larger than 0 to get debug output in the *Messages* buffer.
+1 is for important messages, 2 is for debug data, and higher numbers
+are as per the GnuTLS logging conventions. */);
global_gnutls_log_level = 0;
}