Made socket self-aware of when it is in a fatal state #4697

Added code to cleanly terminate connection on fatal socket state #4697
This commit is contained in:
Adam Potolsky 2015-05-22 10:56:13 -07:00
parent e4f86a8934
commit 5b3fa48902
6 changed files with 84 additions and 40 deletions

View File

@ -68,5 +68,6 @@ public:
virtual void shutdownInput() = 0; virtual void shutdownInput() = 0;
virtual void shutdownOutput() = 0; virtual void shutdownOutput() = 0;
virtual bool isReady() const = 0; virtual bool isReady() const = 0;
virtual bool isFatal() const = 0;
virtual UInt32 getSize() const = 0; virtual UInt32 getSize() const = 0;
}; };

View File

@ -253,6 +253,14 @@ TCPSocket::isReady() const
return (m_inputBuffer.getSize() > 0); return (m_inputBuffer.getSize() > 0);
} }
bool
TCPSocket::isFatal() const
{
// TCP sockets aren't ever left in a fatal state.
LOG((CLOG_ERR "isFatal() not valid for non-secure connections"));
return false;
}
UInt32 UInt32
TCPSocket::getSize() const TCPSocket::getSize() const
{ {
@ -481,6 +489,9 @@ TCPSocket::serviceConnected(ISocketMultiplexerJob* job,
if (n > 0) { if (n > 0) {
s_retryOutputBufferSize = 0; s_retryOutputBufferSize = 0;
} }
else if (n < 0) {
return NULL;
}
} }
else { else {
return job; return job;
@ -537,6 +548,9 @@ TCPSocket::serviceConnected(ISocketMultiplexerJob* job,
if (isSecure()) { if (isSecure()) {
if (isSecureReady()) { if (isSecureReady()) {
n = secureRead(buffer, sizeof(buffer)); n = secureRead(buffer, sizeof(buffer));
if (n < 0) {
return NULL;
}
} }
else { else {
return job; return job;
@ -555,6 +569,9 @@ TCPSocket::serviceConnected(ISocketMultiplexerJob* job,
if (isSecure() && isSecureReady()) { if (isSecure() && isSecureReady()) {
n = secureRead(buffer, sizeof(buffer)); n = secureRead(buffer, sizeof(buffer));
if (n < 0) {
return NULL;
}
} }
else { else {
n = ARCH->readSocket(m_socket, buffer, sizeof(buffer)); n = ARCH->readSocket(m_socket, buffer, sizeof(buffer));

View File

@ -52,6 +52,7 @@ public:
virtual void shutdownInput(); virtual void shutdownInput();
virtual void shutdownOutput(); virtual void shutdownOutput();
virtual bool isReady() const; virtual bool isReady() const;
virtual bool isFatal() const;
virtual UInt32 getSize() const; virtual UInt32 getSize() const;
// IDataSocket overrides // IDataSocket overrides

View File

@ -52,7 +52,8 @@ SecureSocket::SecureSocket(
SocketMultiplexer* socketMultiplexer) : SocketMultiplexer* socketMultiplexer) :
TCPSocket(events, socketMultiplexer), TCPSocket(events, socketMultiplexer),
m_secureReady(false), m_secureReady(false),
m_maxRetry(MAX_RETRY_COUNT) m_maxRetry(MAX_RETRY_COUNT),
m_fatal(false)
{ {
} }
@ -62,12 +63,14 @@ SecureSocket::SecureSocket(
ArchSocket socket) : ArchSocket socket) :
TCPSocket(events, socketMultiplexer, socket), TCPSocket(events, socketMultiplexer, socket),
m_secureReady(false), m_secureReady(false),
m_maxRetry(MAX_RETRY_COUNT) m_maxRetry(MAX_RETRY_COUNT),
m_fatal(false)
{ {
} }
SecureSocket::~SecureSocket() SecureSocket::~SecureSocket()
{ {
isFatal(true);
if (m_ssl->m_ssl != NULL) { if (m_ssl->m_ssl != NULL) {
SSL_shutdown(m_ssl->m_ssl); SSL_shutdown(m_ssl->m_ssl);
@ -78,13 +81,15 @@ SecureSocket::~SecureSocket()
SSL_CTX_free(m_ssl->m_context); SSL_CTX_free(m_ssl->m_context);
m_ssl->m_context = NULL; m_ssl->m_context = NULL;
} }
ARCH->sleep(1);
delete m_ssl; delete m_ssl;
} }
void void
SecureSocket::close() SecureSocket::close()
{ {
isFatal(true);
SSL_shutdown(m_ssl->m_ssl); SSL_shutdown(m_ssl->m_ssl);
TCPSocket::close(); TCPSocket::close();
@ -114,17 +119,23 @@ SecureSocket::secureRead(void* buffer, UInt32 n)
LOG((CLOG_DEBUG2 "reading secure socket")); LOG((CLOG_DEBUG2 "reading secure socket"));
r = SSL_read(m_ssl->m_ssl, buffer, n); r = SSL_read(m_ssl->m_ssl, buffer, n);
bool fatal;
static int retry; static int retry;
checkResult(r, fatal, retry); // Check result will cleanup the connection in the case of a fatal
checkResult(r, retry);
if (retry) { if (retry) {
r = 0; return 0;
}
} }
return r > 0 ? (UInt32)r : 0; if (isFatal()) {
return -1;
}
}
// According to SSL spec, r must not be negative and not have an error code
// from SSL_get_error(). If this happens, it is itself an error. Let the
// parent handle the case
return (UInt32)r;
} }
UInt32 UInt32
@ -132,20 +143,27 @@ SecureSocket::secureWrite(const void* buffer, UInt32 n)
{ {
int r = 0; int r = 0;
if (m_ssl->m_ssl != NULL) { if (m_ssl->m_ssl != NULL) {
LOG((CLOG_DEBUG2 "writing secure socket")); LOG((CLOG_DEBUG2 "writing secure socket:%p", this));
r = SSL_write(m_ssl->m_ssl, buffer, n); r = SSL_write(m_ssl->m_ssl, buffer, n);
bool fatal;
static int retry; static int retry;
checkResult(r, fatal, retry); // Check result will cleanup the connection in the case of a fatal
checkResult(r, retry);
if (retry) { if (retry) {
r = 0; return 0;
}
} }
return r > 0 ? (UInt32)r : 0; if (isFatal()) {
return -1;
}
}
// According to SSL spec, r must not be negative and not have an error code
// from SSL_get_error(). If this happens, it is itself an error. Let the
// parent handle the case
return (UInt32)r;
} }
bool bool
@ -260,12 +278,11 @@ SecureSocket::secureAccept(int socket)
LOG((CLOG_DEBUG2 "accepting secure socket")); LOG((CLOG_DEBUG2 "accepting secure socket"));
int r = SSL_accept(m_ssl->m_ssl); int r = SSL_accept(m_ssl->m_ssl);
bool fatal;
static int retry; static int retry;
checkResult(r, fatal, retry); checkResult(r, retry);
if (fatal) { if (isFatal()) {
// tell user and sleep so the socket isn't hammered. // tell user and sleep so the socket isn't hammered.
LOG((CLOG_ERR "failed to accept secure socket")); LOG((CLOG_ERR "failed to accept secure socket"));
LOG((CLOG_INFO "client connection may not be secure")); LOG((CLOG_INFO "client connection may not be secure"));
@ -304,12 +321,11 @@ SecureSocket::secureConnect(int socket)
LOG((CLOG_DEBUG2 "connecting secure socket")); LOG((CLOG_DEBUG2 "connecting secure socket"));
int r = SSL_connect(m_ssl->m_ssl); int r = SSL_connect(m_ssl->m_ssl);
bool fatal;
static int retry; static int retry;
checkResult(r, fatal, retry); checkResult(r, retry);
if (fatal) { if (isFatal()) {
LOG((CLOG_ERR "failed to connect secure socket")); LOG((CLOG_ERR "failed to connect secure socket"));
return -1; return -1;
} }
@ -362,13 +378,11 @@ SecureSocket::showCertificate()
} }
void void
SecureSocket::checkResult(int n, bool& fatal, int& retry) SecureSocket::checkResult(int n, int& retry)
{ {
// ssl errors are a little quirky. the "want" errors are normal and // ssl errors are a little quirky. the "want" errors are normal and
// should result in a retry. // should result in a retry.
fatal = false;
int errorCode = SSL_get_error(m_ssl->m_ssl, n); int errorCode = SSL_get_error(m_ssl->m_ssl, n);
switch (errorCode) { switch (errorCode) {
case SSL_ERROR_NONE: case SSL_ERROR_NONE:
@ -378,8 +392,8 @@ SecureSocket::checkResult(int n, bool& fatal, int& retry)
case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_ZERO_RETURN:
// connection closed // connection closed
retry = 0; isFatal(true);
LOG((CLOG_DEBUG2 "SSL connection has been closed")); LOG((CLOG_DEBUG "SSL connection has been closed"));
break; break;
case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_READ:
@ -389,7 +403,7 @@ SecureSocket::checkResult(int n, bool& fatal, int& retry)
retry += 1; retry += 1;
// If there are a lot of retrys, it's worth warning about // If there are a lot of retrys, it's worth warning about
if ( retry % 5 == 0 ) { if ( retry % 5 == 0 ) {
LOG((CLOG_INFO "need to retry the same SSL function(%d) retry:%d", errorCode, retry)); LOG((CLOG_DEBUG "need to retry the same SSL function(%d) retry:%d", errorCode, retry));
} }
else if ( retry == (maxRetry() / 2) ) { else if ( retry == (maxRetry() / 2) ) {
LOG((CLOG_WARN "need to retry the same SSL function(%d) retry:%d", errorCode, retry)); LOG((CLOG_WARN "need to retry the same SSL function(%d) retry:%d", errorCode, retry));
@ -416,27 +430,27 @@ SecureSocket::checkResult(int n, bool& fatal, int& retry)
} }
} }
fatal = true; isFatal(true);
break; break;
case SSL_ERROR_SSL: case SSL_ERROR_SSL:
LOG((CLOG_ERR "a failure in the SSL library occurred")); LOG((CLOG_ERR "a failure in the SSL library occurred"));
fatal = true; isFatal(true);
break; break;
default: default:
LOG((CLOG_ERR "unknown secure socket error")); LOG((CLOG_ERR "unknown secure socket error"));
fatal = true; isFatal(true);
break; break;
} }
// If the retry max would exceed the allowed, treat it as a fatal error // If the retry max would exceed the allowed, treat it as a fatal error
if (retry > maxRetry()) { if (retry > maxRetry()) {
LOG((CLOG_ERR "Maximum retry count exceeded:%d",retry)); LOG((CLOG_ERR "Maximum retry count exceeded:%d",retry));
fatal = true; isFatal(true);
} }
if (fatal) { if (isFatal()) {
retry = 0; retry = 0;
showError(); showError();
disconnect(); disconnect();

View File

@ -44,6 +44,8 @@ public:
void secureConnect(); void secureConnect();
void secureAccept(); void secureAccept();
bool isReady() const { return m_secureReady; } bool isReady() const { return m_secureReady; }
bool isFatal() const { return m_fatal; }
void isFatal(bool b) { m_fatal = b; }
bool isSecureReady(); bool isSecureReady();
bool isSecure() { return true; } bool isSecure() { return true; }
UInt32 secureRead(void* buffer, UInt32 n); UInt32 secureRead(void* buffer, UInt32 n);
@ -60,7 +62,7 @@ private:
int secureAccept(int s); int secureAccept(int s);
int secureConnect(int s); int secureConnect(int s);
bool showCertificate(); bool showCertificate();
void checkResult(int n, bool& fatal, int& retry); void checkResult(int n, int& retry);
void showError(const char* reason = NULL); void showError(const char* reason = NULL);
String getError(); String getError();
void disconnect(); void disconnect();
@ -80,5 +82,6 @@ private:
private: private:
Ssl* m_ssl; Ssl* m_ssl;
bool m_secureReady; bool m_secureReady;
bool m_fatal;
int m_maxRetry; int m_maxRetry;
}; };

View File

@ -142,26 +142,34 @@ ClientListener::handleClientConnecting(const Event&, void*)
{ {
// accept client connection // accept client connection
IDataSocket* socket = m_listen->accept(); IDataSocket* socket = m_listen->accept();
synergy::IStream* stream = socket;
if (stream == NULL) { if (socket == NULL) {
return; return;
} }
LOG((CLOG_NOTE "accepted client connection")); LOG((CLOG_NOTE "accepted client connection"));
if (m_useSecureNetwork) {
LOG((CLOG_DEBUG2 "attempting sercure Connection"));
while(!socket->isReady()) {
if(socket->isFatal()) {
m_listen->deleteSocket(socket);
return;
}
LOG((CLOG_DEBUG2 "retrying sercure Connection"));
ARCH->sleep(.5f);
}
}
LOG((CLOG_DEBUG2 "sercure Connection established:%d"));
synergy::IStream* stream = socket;
// filter socket messages, including a packetizing filter // filter socket messages, including a packetizing filter
bool adopt = !m_useSecureNetwork; bool adopt = !m_useSecureNetwork;
stream = new PacketStreamFilter(m_events, stream, adopt); stream = new PacketStreamFilter(m_events, stream, adopt);
assert(m_server != NULL); assert(m_server != NULL);
if (m_useSecureNetwork) {
while(!socket->isReady()) {
ARCH->sleep(.5f);
}
}
// create proxy for unknown client // create proxy for unknown client
ClientProxyUnknown* client = new ClientProxyUnknown(stream, 30.0, m_server, m_events); ClientProxyUnknown* client = new ClientProxyUnknown(stream, 30.0, m_server, m_events);
m_newClients.insert(client); m_newClients.insert(client);