summaryrefslogtreecommitdiff
path: root/src/ne_socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ne_socket.c')
-rw-r--r--src/ne_socket.c90
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;
}