Improvements to error reporting

This commit is contained in:
Fred Nicolson 2019-01-14 13:33:08 +00:00
parent 16bb072c12
commit cb532d41b2
8 changed files with 119 additions and 48 deletions

View File

@ -25,6 +25,7 @@ if(USE_SSL)
FIND_PACKAGE(MBEDTLS) FIND_PACKAGE(MBEDTLS)
INCLUDE_DIRECTORIES(${MBEDTLS_INCLUDE_DIR}) INCLUDE_DIRECTORIES(${MBEDTLS_INCLUDE_DIR})
set(SOURCE_FILES ${SOURCE_FILES} src/SSLSocket.cpp include/frnetlib/SSLSocket.h src/SSLListener.cpp include/frnetlib/SSLListener.h include/frnetlib/SSLContext.h) set(SOURCE_FILES ${SOURCE_FILES} src/SSLSocket.cpp include/frnetlib/SSLSocket.h src/SSLListener.cpp include/frnetlib/SSLListener.h include/frnetlib/SSLContext.h)
ADD_DEFINITIONS(-DUSE_SSL)
endif() endif()
if(BUILD_WEBSOCK) if(BUILD_WEBSOCK)

View File

@ -34,6 +34,11 @@ namespace fr
ParseError = 12, ParseError = 12,
HttpHeaderTooBig = 13, HttpHeaderTooBig = 13,
HttpBodyTooBig = 14, HttpBodyTooBig = 14,
AddressLookupFailure = 15,
SendError = 16,
ReceiveError = 17,
AcceptError = 18,
SSLError = 19,
//Remember to update status_to_string if more are added //Remember to update status_to_string if more are added
}; };
@ -51,6 +56,17 @@ namespace fr
void operator=(const Socket &) =delete; void operator=(const Socket &) =delete;
void operator=(Socket &&) =delete; void operator=(Socket &&) =delete;
/*!
* Converts an fr::Socket::Status value to a printable string
*
* Throws an std::logic_error if status is out of range.
*
* @note This should be called immediately after the error, as errno is used to help generate the string.
* @param status Status value to convert
* @return A string form version
*/
static std::string status_to_string(fr::Socket::Status status);
/*! /*!
* Connects the socket to an address. * Connects the socket to an address.
* *
@ -155,16 +171,6 @@ namespace fr
*/ */
void set_inet_version(IP version); void set_inet_version(IP version);
/*!
* Converts an fr::Socket::Status value to a printable string
*
* Throws an std::logic_error if status is out of range.
*
* @param status Status value to convert
* @return A string form version
*/
static const std::string &status_to_string(fr::Socket::Status status);
/*! /*!
* Ends, and closes the connection. * Ends, and closes the connection.
* There is a distinction between 'disconnect' and 'close_socket', * There is a distinction between 'disconnect' and 'close_socket',

View File

@ -69,6 +69,7 @@ namespace fr
if(response.get_status() != Http::SwitchingProtocols) if(response.get_status() != Http::SwitchingProtocols)
{ {
disconnect(); disconnect();
errno = EPROTO;
return Socket::HandshakeFailed; return Socket::HandshakeFailed;
} }
@ -77,6 +78,7 @@ namespace fr
if(derived_key != Base64::encode(Sha1::sha1_digest(websocket_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))) if(derived_key != Base64::encode(Sha1::sha1_digest(websocket_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")))
{ {
disconnect(); disconnect();
errno = EPROTO;
return Socket::HandshakeFailed; return Socket::HandshakeFailed;
} }

View File

@ -109,7 +109,7 @@ namespace fr
if((error = mbedtls_net_accept(&listen_fd, client_fd.get(), client_ip, sizeof(client_ip), &ip_len)) != 0) if((error = mbedtls_net_accept(&listen_fd, client_fd.get(), client_ip, sizeof(client_ip), &ip_len)) != 0)
{ {
free_contexts(); free_contexts();
return Socket::Error; return Socket::AcceptError;
} }
@ -120,7 +120,8 @@ namespace fr
if(error != MBEDTLS_ERR_SSL_WANT_READ && error != MBEDTLS_ERR_SSL_WANT_WRITE) if(error != MBEDTLS_ERR_SSL_WANT_READ && error != MBEDTLS_ERR_SSL_WANT_WRITE)
{ {
free_contexts(); free_contexts();
return Socket::Status::HandshakeFailed; errno = error;
return Socket::Status::SSLError;
} }
} }

View File

@ -56,7 +56,8 @@ namespace fr
} }
else if(response < 0) else if(response < 0)
{ {
return Socket::Status::Error; errno = response;
return Socket::Status::SSLError;
} }
} }
@ -76,7 +77,8 @@ namespace fr
return Socket::Status::WouldBlock; return Socket::Status::WouldBlock;
} }
return Socket::Status::Error; errno = static_cast<int>(status);
return Socket::Status::SSLError;
} }
} }
else else
@ -95,7 +97,8 @@ namespace fr
continue; //try again, interrupted before anything could be received continue; //try again, interrupted before anything could be received
} }
return Socket::Status::Error; errno = static_cast<int>(status);
return Socket::Status::SSLError;
} }
break; break;
} while(true); } while(true);
@ -131,7 +134,8 @@ namespace fr
int error = 0; int error = 0;
if((error = mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0) if((error = mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0)
{ {
return Socket::Status::Error; errno = error;
return Socket::Status::SSLError;
} }
mbedtls_ssl_conf_authmode(&conf, should_verify ? MBEDTLS_SSL_VERIFY_REQUIRED : MBEDTLS_SSL_VERIFY_NONE); mbedtls_ssl_conf_authmode(&conf, should_verify ? MBEDTLS_SSL_VERIFY_REQUIRED : MBEDTLS_SSL_VERIFY_NONE);
@ -140,12 +144,14 @@ namespace fr
if((error = mbedtls_ssl_setup(ssl.get(), &conf)) != 0) if((error = mbedtls_ssl_setup(ssl.get(), &conf)) != 0)
{ {
return Socket::Status::Error; errno = error;
return Socket::Status::SSLError;
} }
if((error = mbedtls_ssl_set_hostname(ssl.get(), address.c_str())) != 0) if((error = mbedtls_ssl_set_hostname(ssl.get(), address.c_str())) != 0)
{ {
return Socket::Status::Error; errno = error;
return Socket::Status::SSLError;
} }
mbedtls_ssl_set_bio(ssl.get(), ssl_socket_descriptor.get(), mbedtls_net_send, mbedtls_net_recv, nullptr); mbedtls_ssl_set_bio(ssl.get(), ssl_socket_descriptor.get(), mbedtls_net_send, mbedtls_net_recv, nullptr);
@ -155,16 +161,14 @@ namespace fr
{ {
if(error != MBEDTLS_ERR_SSL_WANT_READ && error != MBEDTLS_ERR_SSL_WANT_WRITE) if(error != MBEDTLS_ERR_SSL_WANT_READ && error != MBEDTLS_ERR_SSL_WANT_WRITE)
{ {
return Socket::Status::HandshakeFailed; errno = error;
return Socket::Status::SSLError;
} }
} }
//Verify server certificate //Verify server certificate
if(should_verify && ((flags = mbedtls_ssl_get_verify_result(ssl.get())) != 0)) if(should_verify && ((flags = mbedtls_ssl_get_verify_result(ssl.get())) != 0))
{ {
char verify_buffer[512];
mbedtls_x509_crt_verify_info(verify_buffer, sizeof(verify_buffer), " ! ", flags);
return Socket::Status::VerificationFailed; return Socket::Status::VerificationFailed;
} }

View File

@ -6,6 +6,9 @@
#include <csignal> #include <csignal>
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#ifdef USE_SSL
#include <mbedtls/error.h>
#endif
#include "frnetlib/NetworkEncoding.h" #include "frnetlib/NetworkEncoding.h"
#include "frnetlib/Socket.h" #include "frnetlib/Socket.h"
#include "frnetlib/Sendable.h" #include "frnetlib/Sendable.h"
@ -83,29 +86,81 @@ namespace fr
} }
} }
const std::string &Socket::status_to_string(fr::Socket::Status status) std::string Socket::status_to_string(fr::Socket::Status status)
{ {
static std::vector<std::string> map = { #ifdef _WIN32
"Unknown", auto wsa_err_to_str = [](int err) -> std::string {
"Success", std::string buff(255, '\0');
"Listen Failed", auto len = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), msgbuf, buff.size(), NULL);
"Bind Failed", if(len == 0)
"Disconnected", return "Unknown";
"Error", buff.resize(len);
"Would Block", return buff;
"Connection Failed",
"Handshake Failed",
"Verification Failed",
"Max packet size exceeded",
"Not enough data",
"Parse error",
"HTTP header too big",
"HTTP body too big"
}; };
#define ERR_STR wsa_err_to_str(WSAGetLastError())
#else
#define ERR_STR strerror(errno)
#endif
if(status < 0 || status > map.size()) switch(status)
throw std::logic_error("Socket::status_to_string(): Invalid status value " + std::to_string(status)); {
return map[status]; case Unknown:
return "Unknown";
case Success:
return "Success";
case ListenFailed:
return std::string("Listen Failed (").append(ERR_STR).append(")");
case BindFailed:
return std::string("Bind Failed (").append(ERR_STR).append(")");
case Disconnected:
return "The Socket Is Not Connected";
case Error:
return "Error";
case WouldBlock:
return "Would Block";
case ConnectionFailed:
return "Connection Failed";
case HandshakeFailed:
return "Handshake Failed";
case VerificationFailed:
return "Verification Failed";
case MaxPacketSizeExceeded:
return "Max Packet Size Exceeded";
case NotEnoughData:
return "Not Enough Data";
case ParseError:
return "Parse Error";
case HttpHeaderTooBig:
return "HTTP Header Too Big";
case HttpBodyTooBig:
return "HTTP Body Too Big";
case AddressLookupFailure:
#ifdef _WIN32
return std::string("Address Lookup Failure (").append(wsa_err_to_str(WSAGetLastError())).append(")");
#else
return std::string("Address Lookup Failure (").append(gai_strerror(errno)).append(")");
#endif
case SendError:
return std::string("Send Error (").append(ERR_STR).append(")");
case ReceiveError:
return std::string("Receive Error (").append(ERR_STR).append(")");
case AcceptError:
return std::string("Accept Error (").append(ERR_STR).append(")");
case SSLError:
{
#ifdef USE_SSL
char buff[256] = {0};
mbedtls_strerror(errno, buff, sizeof(buff));
return std::string("SSL Error (").append(buff).append(")");
#else
return "Generic SSL Error";
#endif
}
default:
return "Unknown";
}
return "Internal Error";
} }
void Socket::disconnect() void Socket::disconnect()

View File

@ -36,8 +36,9 @@ namespace fr
if(getaddrinfo(nullptr, port.c_str(), &hints, &info) != 0) if(getaddrinfo(nullptr, port.c_str(), &hints, &info) != 0)
{ {
return Socket::Status::Unknown; return Socket::Status::AddressLookupFailure;
} }
//Try each of the results until we listen successfully //Try each of the results until we listen successfully
addrinfo *c = nullptr; addrinfo *c = nullptr;
for(c = info; c != nullptr; c = c->ai_next) for(c = info; c != nullptr; c = c->ai_next)
@ -107,7 +108,7 @@ namespace fr
socklen_t client_addr_len = sizeof client_addr; socklen_t client_addr_len = sizeof client_addr;
client_descriptor = ::accept(socket_descriptor, (sockaddr*)&client_addr, &client_addr_len); client_descriptor = ::accept(socket_descriptor, (sockaddr*)&client_addr, &client_addr_len);
if(client_descriptor == SOCKET_ERROR) if(client_descriptor == SOCKET_ERROR)
return Socket::Unknown; return Socket::AcceptError;
//Get printable address. If we failed then set it as just 'unknown' //Get printable address. If we failed then set it as just 'unknown'
int err = getnameinfo((sockaddr*)&client_addr, client_addr_len, client_printable_addr, sizeof(client_printable_addr), nullptr, 0, NI_NUMERICHOST); int err = getnameinfo((sockaddr*)&client_addr, client_addr_len, client_printable_addr, sizeof(client_printable_addr), nullptr, 0, NI_NUMERICHOST);

View File

@ -35,7 +35,7 @@ namespace fr
} }
else if(errno != EWOULDBLOCK && errno != EAGAIN) //Don't exit if the socket just couldn't block else if(errno != EWOULDBLOCK && errno != EAGAIN) //Don't exit if the socket just couldn't block
{ {
return Socket::Status::Error; return Socket::Status::SendError;
} }
} }
return Socket::Status::Success; return Socket::Status::Success;
@ -67,7 +67,7 @@ namespace fr
continue; //try again, interrupted before anything could be received continue; //try again, interrupted before anything could be received
} }
return Socket::Status::Error; return Socket::Status::ReceiveError;
} }
break; break;
} while(true); } while(true);
@ -104,9 +104,10 @@ namespace fr
hints.ai_flags = AI_PASSIVE; //Have the IP filled in for us hints.ai_flags = AI_PASSIVE; //Have the IP filled in for us
//Query remote address information //Query remote address information
if(getaddrinfo(address.c_str(), port.c_str(), &hints, &info) != 0) if((ret = getaddrinfo(address.c_str(), port.c_str(), &hints, &info)) != 0)
{ {
return Socket::Status::Error; errno = ret;
return Socket::Status::AddressLookupFailure;
} }
//Try to connect to results returned by getaddrinfo until we succeed/run out of things //Try to connect to results returned by getaddrinfo until we succeed/run out of things