diff options
Diffstat (limited to 'src/ne_socket.c')
-rw-r--r-- | src/ne_socket.c | 90 |
1 files changed, 78 insertions, 12 deletions
diff --git a/src/ne_socket.c b/src/ne_socket.c index 7332061..1f27b8e 100644 --- a/src/ne_socket.c +++ b/src/ne_socket.c @@ -179,9 +179,6 @@ typedef struct in_addr ne_inet_addr; /* Socket read timeout */ #define SOCKET_READ_TIMEOUT 120 -/* Internal read retry value */ -#define NE_SOCK_RETRY (-6) - /* Critical I/O functions on a socket: useful abstraction for easily * handling SSL I/O alongside raw socket I/O. */ struct iofns { @@ -674,7 +671,8 @@ static int error_ossl(ne_socket *sock, int sret) unsigned long err; if (errnum == SSL_ERROR_ZERO_RETURN) { - set_error(sock, _("Connection closed")); + set_error(sock, _("Connection closed")); + NE_DEBUG(NE_DBG_SSL, "ssl: Got TLS closure.\n"); return NE_SOCK_CLOSED; } else if (errnum == SSL_ERROR_WANT_READ) { @@ -2007,23 +2005,89 @@ void ne_sock_set_error(ne_socket *sock, const char *format, ...) va_end(params); } -int ne_sock_close(ne_socket *sock) +int ne_sock_shutdown(ne_socket *sock, unsigned int flags) { int ret; - /* Per API description - for an SSL connection, simply send the - * close_notify but do not wait for the peer's response. */ + if (!flags) { + set_error(sock, _("Missing flags for socket shutdown")); + return NE_SOCK_ERROR; + } + +#if defined(HAVE_OPENSSL) + if (sock->ssl) { + int state = SSL_get_shutdown(sock->ssl); + + NE_DEBUG(NE_DBG_SSL, "ssl: Shutdown state: %ssent | %sreceived.\n", + (state & SSL_SENT_SHUTDOWN) ? "" : "not ", + (state & SSL_RECEIVED_SHUTDOWN) ? "" : "not "); + + if ((flags == NE_SOCK_BOTH || flags == NE_SOCK_SEND) + && (state & SSL_SENT_SHUTDOWN) == 0) { + NE_DEBUG(NE_DBG_SSL, "ssl: Sending closure.\n"); + ret = SSL_shutdown(sock->ssl); + + if (ret == 0) { + set_error(sock, _("Incomplete TLS closure")); + return NE_SOCK_RETRY; + } + else if (ret != 1) { + return error_ossl(sock, ret); + } + } + + if (flags == NE_SOCK_RECV || flags == NE_SOCK_BOTH) { + /* Returns whether the receive side is shutdown or not yet. */ + if ((state & SSL_RECEIVED_SHUTDOWN) == 0) { + set_error(sock, _("Incomplete TLS closure")); + return NE_SOCK_RETRY; + } + + /* For recv-only shutdown, must not complete TCP-level + * shutdown until the TLS shutdown is complete. */ + if (flags == NE_SOCK_RECV) { + return 0; + } + } + } +#elif defined(HAVE_GNUTLS) + if (sock->ssl) { + if (flags == NE_SOCK_RECV) { + /* unclear how to handle */ + set_error(sock, _("Incomplete TLS closure")); + return NE_SOCK_RETRY; + } + + ret = gnutls_bye(sock->ssl, + flags == NE_SOCK_SEND ? GNUTLS_SHUT_WR :GNUTLS_SHUT_RDRW); + if (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) { + return NE_SOCK_RETRY; + } + } +#endif + + ret = shutdown(sock->fd, + flags == NE_SOCK_RECV ? SHUT_RD : + (flags == NE_SOCK_SEND ? SHUT_WR : SHUT_RDWR)); + if (ret < 0) { + int errnum = ne_errno; + set_strerror(sock, errnum); + return MAP_ERR(errnum); + } + + return ret; +} + +int ne_sock_close(ne_socket *sock) +{ + int ret = ne_sock_shutdown(sock, NE_SOCK_SEND); + #if defined(HAVE_OPENSSL) if (sock->ssl) { - SSL_shutdown(sock->ssl); SSL_free(sock->ssl); } #elif defined(HAVE_GNUTLS) if (sock->ssl) { - do { - ret = gnutls_bye(sock->ssl, GNUTLS_SHUT_WR); - } while (ret < 0 - && (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN)); gnutls_deinit(sock->ssl); } #endif @@ -2032,6 +2096,8 @@ int ne_sock_close(ne_socket *sock) ret = 0; else ret = ne_close(sock->fd); + sock->fd = -1; + ne_free(sock); return ret; } |