diff --git a/src/dnet.c b/src/dnet.c index 4d9f732..c7cdade 100644 --- a/src/dnet.c +++ b/src/dnet.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #endif @@ -36,6 +37,7 @@ struct socket { int avail_bytes; /* Available bytes in buffer */ char *bufptr; /* Pointer to Internal buffer */ char *buf; /* The buffer. */ + char errstr[256]; /* Last error description (TLS or system) */ #ifdef HAVE_LIBSSL SSL_CTX *ctx; /* OpenSSL CTX struct, used for TLS */ SSL *ssl; /* OpenSSL SSL struct, used for TLS */ @@ -108,6 +110,7 @@ dnetConnect(const char *hostname, unsigned int port) if (sd > 0) { if (connect(sd, (SA *)&sin, sizeof(SIN)) >= 0) { ret = xmalloc(sizeof(struct socket)); + memset(ret, 0, sizeof(struct socket)); ret->sock = sd; ret->buf = xmalloc(MAXSOCKBUF); ret->bufptr = ret->buf; @@ -154,6 +157,23 @@ _genRandomSeed(void) } #endif +#ifdef HAVE_LIBSSL +/** + * Records the last OpenSSL error into the socket so that it can be + * retrieved later with dnetGetErr(). + */ +static void +_setSslErr(dsocket *sd) +{ + unsigned long err = ERR_get_error(); + if (err) { + ERR_error_string_n(err, sd->errstr, sizeof(sd->errstr)); + } else { + snprintf(sd->errstr, sizeof(sd->errstr), "unknown OpenSSL error"); + } +} +#endif + /** * This will allow you to use TLS over an existing connection. * Will return error if a connection has not already been established. @@ -169,21 +189,35 @@ dnetUseTls(dsocket *sd) SSL_load_error_strings(); if (SSL_library_init() == -1) { + _setSslErr(sd); return ERROR; } _genRandomSeed(); - sd->ctx = SSL_CTX_new(TLSv1_client_method()); +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + /* + * TLS_client_method() negotiates the highest version mutually + * supported with the server. The deprecated version-specific + * TLSv1_client_method() pins the protocol to TLS 1.0, which + * modern servers (and modern system OpenSSL configs) reject. + */ + sd->ctx = SSL_CTX_new(TLS_client_method()); +#else + sd->ctx = SSL_CTX_new(SSLv23_client_method()); +#endif if (!sd->ctx) { + _setSslErr(sd); return ERROR; } sd->ssl = SSL_new(sd->ctx); if (!sd->ssl) { + _setSslErr(sd); SSL_CTX_free(sd->ctx); sd->ctx = NULL; return ERROR; } SSL_set_fd(sd->ssl, sd->sock); if (SSL_connect(sd->ssl) == -1) { + _setSslErr(sd); SSL_CTX_free(sd->ctx); SSL_free(sd->ssl); sd->ssl = NULL; @@ -445,12 +479,16 @@ dnetEof(dsocket *sd) } /** - * Returns the error string from the system which is - * determined by errnum (errno). + * Returns the error string describing the last error on the socket. + * Prefers the TLS/OpenSSL error recorded by dnetUseTls() and falls + * back to the system errno string. */ char * dnetGetErr(dsocket *sd) { + if (sd->errstr[0] != '\0') { + return sd->errstr; + } return strerror(sd->errnum); }