diff --git a/include/common/OpenSSLSocket.h b/include/common/OpenSSLSocket.h --- a/include/common/OpenSSLSocket.h +++ b/include/common/OpenSSLSocket.h @@ -1,62 +1,66 @@ #ifndef _OPEN_SSL_SOCKET_H_ #define _OPEN_SSL_SOCKET_H_ #include "GLibraryUtils.h" #include "openssl/bio.h" #include "openssl/ssl.h" #include "openssl/err.h" #define SslSocket_LogFile "LogFile" #define SslSocket_LogFormat "LogFmt" class SslSocket : public GSocketI, virtual public GDom { struct SslSocketPriv *d; GMutex Lock; BIO *Bio; SSL *Ssl; // Local stuff virtual void Log(const char *Str, SocketMsgType Type); void Error(const char *file, int line, const char *Msg); GStream *GetLogStream(); + void PrintInfo(const char *Fmt, ...); + public: + static bool DebugLogging; + SslSocket(GStreamI *logger = NULL, GCapabilityClient *caps = NULL, bool SslOnConnect = false, bool RawLFCheck = false); ~SslSocket(); void SetLogger(GStreamI *logger); void SetSslOnConnect(bool b); // Socket OsSocket Handle(OsSocket Set = INVALID_SOCKET); bool IsOpen(); int Open(const char *HostAddr, int Port); int Close(); bool Listen(int Port = 0); void OnError(int ErrorCode, const char *ErrorDescription); void OnInformation(const char *Str); int GetTimeout(); void SetTimeout(int ms); int Write(const void *Data, int Len, int Flags); int Read(void *Data, int Len, int Flags); void OnWrite(const char *Data, int Len); void OnRead(char *Data, int Len); bool IsReadable(int TimeoutMs = 0); bool IsWritable(int TimeoutMs = 0); bool IsBlocking() { return false; } bool SetVariant(const char *Name, GVariant &Val, char *Arr = NULL); bool GetVariant(const char *Name, GVariant &Val, char *Arr = NULL); GStreamI *Clone(); }; extern bool StartSSL(GAutoString &ErrorMsg); extern void EndSSL(); #endif \ No newline at end of file diff --git a/src/common/INet/OpenSSLSocket.cpp b/src/common/INet/OpenSSLSocket.cpp --- a/src/common/INet/OpenSSLSocket.cpp +++ b/src/common/INet/OpenSSLSocket.cpp @@ -1,1274 +1,1284 @@ /*hdr ** FILE: OpenSSLSocket.cpp ** AUTHOR: Matthew Allen ** DATE: 24/9/2004 ** DESCRIPTION: Open SSL wrapper socket ** ** Copyright (C) 2004, Matthew Allen ** fret@memecode.com ** */ #include #ifndef _WINDOWS #include #endif #include "Lgi.h" #include "OpenSSLSocket.h" #ifdef WIN32 #include #endif #include "GToken.h" #include "GVariant.h" #include "INet.h" #define PATH_OFFSET "../" #ifdef WIN32 #define SSL_LIBRARY "ssleay32" #define EAY_LIBRARY "libeay32" #else #define SSL_LIBRARY "libssl" #endif #define HasntTimedOut() ((To < 0) || (LgiCurrentTime() - Start < To)) static const char* MinimumVersion = "1.0.1g"; -#if 0 -#define DebugTrace LgiTrace -#else -void DebugTrace(const char *fmt, ...) -{ -} -#endif +#define DebugTrace PrintInfo void SSL_locking_function(int mode, int n, const char *file, int line); unsigned long SSL_id_function(); class LibSSL : public GLibrary { public: LibSSL() { #ifdef MAC char Exe[MAX_PATH]; if (LgiGetExeFile(Exe, sizeof(Exe))) { #if 0 // This doesn't appear to actually work :( // Modify $LD_LIBRARY_PATH to include the path to the libraries char NewLdLibPath[MAX_PATH]; LgiMakePath(NewLdLibPath, sizeof(NewLdLibPath), Exe, "Contents/MacOS"); const char *EnvVar = "LD_LIBRARY_PATH"; char *ExistingLibPath = getenv(EnvVar); bool HasMyPath = false; if (ExistingLibPath) { GToken t(ExistingLibPath, LGI_PATH_SEPARATOR); for (int i=0; iGetCurrentFolder(old, sizeof(old)); FileDev->SetCurrentFolder(p); #endif Load(SSL_LIBRARY); #ifdef WIN32 FileDev->SetCurrentFolder(old); #endif } #ifdef MAC if (!IsLoaded()) { Load("/opt/local/lib/" SSL_LIBRARY); } #endif } DynFunc0(int, SSL_library_init); DynFunc1(int, SSL_open, SSL*, s); DynFunc1(int, SSL_connect, SSL*, s); DynFunc0(int, SSL_load_error_strings); DynFunc1(SSL_CTX*, SSL_CTX_new, SSL_METHOD*, meth); DynFunc0(SSL_METHOD*, SSLv23_client_method); DynFunc3(int, SSL_CTX_load_verify_locations, SSL_CTX*, ctx, const char*, CAfile, const char*, CApath); DynFunc1(int, SSL_CTX_free, SSL_CTX*, ctx); DynFunc4(long, SSL_ctrl, SSL*, ssl, int, cmd, long, larg, void*, parg); DynFunc1(int, SSL_shutdown, SSL*, s); DynFunc1(int, SSL_free, SSL*, ssl); DynFunc1(int, SSL_get_fd, const SSL *, s); DynFunc2(int, SSL_set_fd, SSL*, s, int, fd); DynFunc1(SSL*, SSL_new, SSL_CTX*, ctx); DynFunc1(BIO*, BIO_new_ssl_connect, SSL_CTX*, ctx); DynFunc1(X509*, SSL_get_peer_certificate, SSL*, s); DynFunc1(int, SSL_set_connect_state, SSL*, s); DynFunc1(int, SSL_set_accept_state, SSL*, s); DynFunc2(int, SSL_get_error, SSL*, s, int, ret_code); DynFunc3(int, SSL_set_bio, SSL*, s, BIO*, rbio, BIO*, wbio); DynFunc3(int, SSL_write, SSL*, ssl, const void*, buf, int, num); DynFunc3(int, SSL_read, SSL*, ssl, const void*, buf, int, num); #ifdef WIN32 // If this is freaking you out then good... openssl-win32 ships // in 2 DLL's and on Linux everything is 1 shared object. Thus // the code reflects that. }; class LibEAY : public GLibrary { public: LibEAY() : GLibrary(EAY_LIBRARY) { if (!IsLoaded()) { char p[300]; LgiGetExePath(p, sizeof(p)); LgiMakePath(p, sizeof(p), p, PATH_OFFSET "../OpenSSL"); #ifdef WIN32 char old[300]; FileDev->GetCurrentFolder(old, sizeof(old)); FileDev->SetCurrentFolder(p); #endif Load("libeay32"); #ifdef WIN32 FileDev->SetCurrentFolder(old); #endif } } #endif DynFunc1(const char *, SSLeay_version, int, type); DynFunc1(BIO*, BIO_new, BIO_METHOD*, type); DynFunc0(BIO_METHOD*, BIO_s_socket); DynFunc1(BIO*, BIO_new_connect, char *, host_port); DynFunc4(long, BIO_ctrl, BIO*, bp, int, cmd, long, larg, void*, parg); DynFunc3(int, BIO_read, BIO*, b, void*, data, int, len); DynFunc3(int, BIO_write, BIO*, b, const void*, data, int, len); DynFunc1(int, BIO_free, BIO*, a); DynFunc1(int, BIO_free_all, BIO*, a); DynFunc2(int, BIO_test_flags, const BIO *, b, int, flags); DynFunc0(int, ERR_load_BIO_strings); DynFunc0(int, ERR_free_strings); DynFunc1(const char *, ERR_lib_error_string, unsigned long, e); DynFunc1(const char *, ERR_func_error_string, unsigned long, e); DynFunc1(const char *, ERR_reason_error_string, unsigned long, e); DynFunc0(int, EVP_cleanup); DynFunc0(int, OPENSSL_add_all_algorithms_noconf); DynFunc3(char*, X509_NAME_oneline, X509_NAME*, a, char*, buf, int, size); DynFunc1(X509_NAME*, X509_get_subject_name, X509*, a); DynFunc2(char*, ERR_error_string, unsigned long, e, char*, buf); DynFunc0(unsigned long, ERR_get_error); typedef void (*locking_callback)(int mode,int type, const char *file,int line); typedef unsigned long (*id_callback)(); DynFunc1(int, CRYPTO_set_locking_callback, locking_callback, func); DynFunc1(int, CRYPTO_set_id_callback, id_callback, func); DynFunc0(int, CRYPTO_num_locks); }; typedef GArray SslVer; SslVer ParseSslVersion(const char *v) { GToken t(v, "."); SslVer out; for (unsigned i=0; i(SslVer &a, SslVer &b) { return CompareSslVersion(a, b) > 0; } static const char *FileLeaf(const char *f) { const char *l = strrchr(f, DIR_CHAR); return l ? l + 1 : f; } #undef _FL #define _FL FileLeaf(__FILE__), __LINE__ class OpenSSL : #ifdef _WINDOWS public LibEAY, #endif public LibSSL { public: SSL_CTX *Ctx; GArray Locks; GAutoString ErrorMsg; bool IsLoaded() { return LibSSL::IsLoaded() #ifdef _WINDOWS && LibEAY::IsLoaded() #endif ; } bool InitLibrary() { GStringPipe Err; GArray Ver; GArray MinimumVer = ParseSslVersion(MinimumVersion); GToken t; int Len = 0; const char *v = NULL; if (!IsLoaded()) { Err.Print("%s:%i - SSL libraries missing.\n", _FL); goto OnError; } SSL_library_init(); SSL_load_error_strings(); ERR_load_BIO_strings(); OpenSSL_add_all_algorithms(); Len = CRYPTO_num_locks(); Locks.Length(Len); CRYPTO_set_locking_callback(SSL_locking_function); CRYPTO_set_id_callback(SSL_id_function); v = SSLeay_version(SSLEAY_VERSION); if (!v) { Err.Print("%s:%i - SSLeay_version failed.\n", _FL); goto OnError; } t.Parse(v, " "); if (t.Length() < 2) { Err.Print("%s:%i - SSLeay_version: no version\n", _FL); goto OnError; } Ver = ParseSslVersion(t[1]); if (Ver.Length() != 4) { Err.Print("%s:%i - SSLeay_version: not enough tokens\n", _FL); goto OnError; } if (Ver < MinimumVer) { Err.Print("%s:%i - SSL version '%s' is too old (minimum '%s')\n", _FL, t[1], MinimumVersion); goto OnError; } Ctx = SSL_CTX_new(SSLv23_client_method()); if (!Ctx) { long e = ERR_get_error(); char *Msg = ERR_error_string(e, 0); Err.Print("%s:%i - SSL_CTX_new failed with '%s' (%i)\n", _FL, Msg, e); goto OnError; } return true; OnError: ErrorMsg.Reset(Err.NewStr()); return false; } OpenSSL() { Ctx = 0; } ~OpenSSL() { if (Ctx) { SSL_CTX_free(Ctx); Ctx = 0; } Locks.DeleteObjects(); } bool IsOk() { bool Loaded = #ifdef WIN32 LibSSL::IsLoaded() && LibEAY::IsLoaded(); #else IsLoaded(); #endif if (Loaded) return true; // Try and load again... cause the library can be provided by install on demand. #ifdef WIN32 Loaded = LibSSL::Load(SSL_LIBRARY) && LibEAY::Load(EAY_LIBRARY); #else Loaded = Load(SSL_LIBRARY); #endif if (Loaded) InitLibrary(); return Loaded; } }; static OpenSSL *Library = 0; #if defined(WIN32) && defined(_DEBUG) // #define SSL_DEBUG_LOCKING #endif void SSL_locking_function(int mode, int n, const char *file, int line) { LgiAssert(Library != NULL); if (Library) { if (!Library->Locks[n]) { #ifdef SSL_DEBUG_LOCKING sprintf_s(Buf, sizeof(Buf), "SSL[%i] create\n", n); OutputDebugStr(Buf); #endif Library->Locks[n] = new GMutex; } #ifdef SSL_DEBUG_LOCKING sprintf_s(Buf, sizeof(Buf), "SSL[%i] lock=%i, unlock=%i, read=%i, write=%i (mode=0x%x, count=%i, thread=0x%x)\n", n, TestFlag(mode, CRYPTO_LOCK), TestFlag(mode, CRYPTO_UNLOCK), TestFlag(mode, CRYPTO_READ), TestFlag(mode, CRYPTO_WRITE), mode, Library->Locks[n]->GetCount(), LgiGetCurrentThread() ); OutputDebugStr(Buf); #endif if (mode & CRYPTO_LOCK) Library->Locks[n]->Lock((char*)file, line); else if (mode & CRYPTO_UNLOCK) Library->Locks[n]->Unlock(); } } unsigned long SSL_id_function() { return (unsigned long) LgiGetCurrentThread(); } bool StartSSL(GAutoString &ErrorMsg) { static GMutex Lock; if (Lock.Lock(_FL)) { if (!Library) { Library = new OpenSSL; if (Library && !Library->InitLibrary()) { ErrorMsg = Library->ErrorMsg; DeleteObj(Library); } } Lock.Unlock(); } return Library != NULL; } void EndSSL() { DeleteObj(Library); } struct SslSocketPriv { GCapabilityClient *Caps; bool SslOnConnect; bool IsSSL; bool UseSSLrw; bool Opening; int Timeout; bool RawLFCheck; #ifdef _DEBUG bool LastWasCR; #endif // This is just for the UI. GStreamI *Logger; // This is for the connection logging. GAutoString LogFile; GAutoPtr LogStream; int LogFormat; SslSocketPriv() { #ifdef _DEBUG LastWasCR = false; #endif Timeout = 20 * 1000; Opening = false; IsSSL = false; UseSSLrw = false; LogFormat = 0; } }; +bool SslSocket::DebugLogging = false; + SslSocket::SslSocket(GStreamI *logger, GCapabilityClient *caps, bool sslonconnect, bool RawLFCheck) { d = new SslSocketPriv; Bio = 0; Ssl = 0; d->RawLFCheck = RawLFCheck; d->SslOnConnect = sslonconnect; d->Caps = caps; d->Logger = logger; GAutoString ErrMsg; if (StartSSL(ErrMsg)) { #ifdef WIN32 if (Library->IsOk()) { char n[MAX_PATH]; char s[MAX_PATH]; if (GetModuleFileNameA(Library->LibSSL::Handle(), n, sizeof(n))) { sprintf_s(s, sizeof(s), "Using '%s'", n); OnInformation(s); } if (GetModuleFileNameA(Library->LibEAY::Handle(), n, sizeof(n))) { sprintf_s(s, sizeof(s), "Using '%s'", n); OnInformation(s); } } #endif } else if (caps) { caps->NeedsCapability("openssl", ErrMsg); } else { OnError(0, "Can't load or find OpenSSL library."); } } SslSocket::~SslSocket() { Close(); DeleteObj(d); } GStreamI *SslSocket::Clone() { return new SslSocket(d->Logger, d->Caps, true); } int SslSocket::GetTimeout() { return d->Timeout; } void SslSocket::SetTimeout(int ms) { d->Timeout = ms; } +void SslSocket::PrintInfo(const char *Fmt, ...) +{ + if (!DebugLogging) + return; + + char Buf[512]; + va_list Arg; + va_start(Arg, Fmt); + int Ch = vsprintf_s(Buf, sizeof(Buf), Fmt, Arg); + va_end(Arg); + if (Ch > 0) + { + OnInformation(Buf); + } +} + void SslSocket::SetLogger(GStreamI *logger) { d->Logger = logger; } void SslSocket::SetSslOnConnect(bool b) { d->SslOnConnect = b; } GStream *SslSocket::GetLogStream() { if (!d->LogStream && d->LogFile) { if (!d->LogStream.Reset(new GFile)) return NULL; if (!d->LogStream->Open(d->LogFile, O_WRITE)) return NULL; // Seek to the end d->LogStream->SetPos(d->LogStream->GetSize()); } return d->LogStream; } bool SslSocket::GetVariant(const char *Name, GVariant &Val, char *Arr) { if (!Name) return false; if (!_stricmp(Name, "isSsl")) { Val = true; return true; } return false; } void SslSocket::Log(const char *Str, SocketMsgType Type) { if (d->Logger && ValidStr(Str)) { d->Logger->Write(Str, strlen(Str), Type); } } void SslSocket::Error(const char *file, int line, const char *Msg) { char *Part = strrchr((char*)file, DIR_CHAR); #ifndef WIN32 printf("%s:%i - %s\n", file, line, Msg); #endif char Buf[512]; sprintf_s(Buf, sizeof(Buf), "Error: %s:%i - %s\n", Part ? Part + 1 : file, line, Msg); Log(Buf, SocketMsgError); } OsSocket SslSocket::Handle(OsSocket Set) { OsSocket h = INVALID_SOCKET; if (Bio) { Library->BIO_get_fd(Bio, &h); #ifdef _WIN64 #pragma message(__LOC__"Hack to get OpenSSL working on _WIN64") h &= 0xffffffff; #endif } return h; } bool SslSocket::IsOpen() { return Bio != 0; } int SslSocket::Open(const char *HostAddr, int Port) { bool Status = false; GMutex::Auto Lck(&Lock, _FL); DebugTrace("%s:%i - SslSocket::Open(%s,%i)\n", _FL, HostAddr, Port); if (Library && Library->IsOk() && HostAddr) { char h[256]; sprintf_s(h, sizeof(h), "%s:%i", HostAddr, Port); // Do SSL handshake? if (d->SslOnConnect) { // SSL connection.. d->IsSSL = true; if (Library->Ctx) { const char *CertDir = "/u/matthew/cert"; int r = Library->SSL_CTX_load_verify_locations(Library->Ctx, 0, CertDir); DebugTrace("%s:%i - SSL_CTX_load_verify_locations=%i\n", _FL, r); if (r > 0) { Bio = Library->BIO_new_ssl_connect(Library->Ctx); DebugTrace("%s:%i - BIO_new_ssl_connect=%p\n", _FL, Bio); if (Bio) { Library->BIO_get_ssl(Bio, &Ssl); DebugTrace("%s:%i - BIO_get_ssl=%p\n", _FL, Ssl); if (Ssl) { // Library->SSL_CTX_set_timeout() Library->BIO_set_conn_hostname(Bio, h); Library->BIO_set_conn_int_port(Bio, &Port); // Do non-block connect uint64 Start = LgiCurrentTime(); int To = GetTimeout(); d->Opening = true; Library->BIO_set_nbio(Bio, true); r = Library->BIO_do_connect(Bio); DebugTrace("%s:%i - initial BIO_do_connect=%i\n", _FL, r); while (r != 1 && d->Opening) { /* int retry = Library->BIO_should_retry(Bio); if (!retry) { DebugTrace("%s:%i - BIO_should_retry=%i\n", _FL, retry); break; } */ LgiSleep(50); r = Library->BIO_do_connect(Bio); DebugTrace("%s:%i - BIO_do_connect=%i (%i of %i ms)\n", _FL, r, (int)(LgiCurrentTime() - Start), (int)To); if (!HasntTimedOut()) { DebugTrace("%s:%i - SSL connect timeout, to=%i\n", _FL, To); OnError(0, "Connection timeout."); break; } } DebugTrace("%s:%i - open loop finished, r=%i, Opening=%i\n", _FL, r, d->Opening); d->Opening = false; if (r == 1) { Library->SSL_set_mode(Ssl, SSL_MODE_AUTO_RETRY); Status = true; char m[256]; sprintf_s(m, sizeof(m), "Connected to '%s' using SSL", h); OnInformation(m); } else Error(_FL, "BIO_do_connect failed."); } else Error(_FL, "BIO_get_ssl failed."); } else Error(_FL, "BIO_new_ssl_connect failed."); } else Error(_FL, "SSL_CTX_load_verify_locations failed."); } else Error(_FL, "No Ctx."); } else { Bio = Library->BIO_new_connect(h); DebugTrace("%s:%i - BIO_new_connect=%p\n", _FL, Bio); if (Bio) { // Non SSL... go into non-blocking mode so that if ::Close() is called we // can quit out of the connect loop. Library->BIO_set_nbio(Bio, true); d->Opening = true; uint64 Start = LgiCurrentTime(); int To = GetTimeout(); int r = Library->BIO_do_connect(Bio); DebugTrace("%s:%i - BIO_do_connect=%i\n", _FL, r); while (r != 1 && d->Opening) { if (!Library->BIO_should_retry(Bio)) { break; } LgiSleep(50); r = Library->BIO_do_connect(Bio); DebugTrace("%s:%i - BIO_do_connect=%i\n", _FL, r); if (!HasntTimedOut()) { DebugTrace("%s:%i - open timeout, to=%i\n", _FL, To); OnError(0, "Connection timeout."); break; } } d->Opening = false; DebugTrace("%s:%i - open loop finished=%i\n", _FL, r); if (r == 1) { Status = true; char m[256]; sprintf_s(m, sizeof(m), "Connected to '%s'", h); OnInformation(m); } else Error(_FL, "BIO_do_connect failed"); } else Error(_FL, "BIO_new_connect failed"); } } if (!Status) { Close(); } DebugTrace("%s:%i - SslSocket::Open status=%i\n", _FL, Status); return Status; } bool SslSocket::SetVariant(const char *Name, GVariant &Value, char *Arr) { bool Status = false; if (!Library || !Name) return false; if (!_stricmp(Name, SslSocket_LogFile)) { d->LogFile.Reset(Value.ReleaseStr()); } else if (!_stricmp(Name, SslSocket_LogFormat)) { d->LogFormat = Value.CastInt32(); } else if (!_stricmp(Name, GSocket_Protocol)) { char *v = Value.CastString(); if (v && stristr(v, "SSL")) { if (!Bio) { d->SslOnConnect = true; } else { if (!Library->Ctx) { Error(_FL, "Library->Ctx is null."); } else { Ssl = Library->SSL_new(Library->Ctx); DebugTrace("%s:%i - SSL_new=%p\n", _FL, Ssl); if (!Ssl) { Error(_FL, "SSL_new failed."); } else { int r = Library->SSL_set_bio(Ssl, Bio, Bio); DebugTrace("%s:%i - SSL_set_bio=%i\n", _FL, r); uint64 Start = LgiCurrentTime(); int To = GetTimeout(); while (HasntTimedOut()) { r = Library->SSL_connect(Ssl); DebugTrace("%s:%i - SSL_connect=%i\n", _FL, r); if (r < 0) LgiSleep(100); else break; } if (r > 0) { Status = d->UseSSLrw = d->IsSSL = true; OnInformation("Session is now using SSL"); X509 *ServerCert = Library->SSL_get_peer_certificate(Ssl); DebugTrace("%s:%i - SSL_get_peer_certificate=%p\n", _FL, ServerCert); if (ServerCert) { char Txt[256] = ""; Library->X509_NAME_oneline(Library->X509_get_subject_name(ServerCert), Txt, sizeof(Txt)); DebugTrace("%s:%i - X509_NAME_oneline=%s\n", _FL, Txt); OnInformation(Txt); } // SSL_get_verify_result } else { Error(_FL, "SSL_connect failed."); r = Library->SSL_get_error(Ssl, r); char *Msg = Library->ERR_error_string(r, 0); if (Msg) { OnError(r, Msg); } } } } } } } return Status; } int SslSocket::Close() { d->Opening = false; GMutex::Auto Lck(&Lock, _FL); if (Library) { if (Ssl) { DebugTrace("%s:%i - SSL_shutdown\n", _FL); int r = 0; if ((r = Library->SSL_shutdown(Ssl)) >= 0) { #ifdef WIN32 closesocket #else close #endif (Library->SSL_get_fd(Ssl)); } Library->SSL_free(Ssl); OnInformation("SSL connection closed."); // I think the Ssl object "owns" the Bio object... // So assume it gets fread by SSL_shutdown } else if (Bio) { DebugTrace("%s:%i - BIO_free\n", _FL); Library->BIO_free(Bio); OnInformation("Connection closed."); } Ssl = 0; Bio = 0; } else return false; return true; } bool SslSocket::Listen(int Port) { return false; } bool SslSocket::IsReadable(int TimeoutMs) { // Assign to local var to avoid a thread changing it // on us between the validity check and the select. // Which is important because a socket value of -1 // (ie invalid) will crash the FD_SET macro. OsSocket s = Handle(); if (ValidSocket(s)) { struct timeval t = {TimeoutMs / 1000, (TimeoutMs % 1000) * 1000}; fd_set r; FD_ZERO(&r); FD_SET(s, &r); int v = select(s+1, &r, 0, 0, &t); if (v > 0 && FD_ISSET(s, &r)) { return true; } else if (v < 0) { // Error(); } } else LgiTrace("%s:%i - Not a valid socket.\n", _FL); return false; } bool SslSocket::IsWritable(int TimeoutMs) { // Assign to local var to avoid a thread changing it // on us between the validity check and the select. // Which is important because a socket value of -1 // (ie invalid) will crash the FD_SET macro. OsSocket s = Handle(); if (ValidSocket(s)) { struct timeval t = {TimeoutMs / 1000, (TimeoutMs % 1000) * 1000}; fd_set w; FD_ZERO(&w); FD_SET(s, &w); int v = select(s+1, &w, 0, 0, &t); if (v > 0 && FD_ISSET(s, &w)) { return true; } else if (v < 0) { // Error(); } } else LgiTrace("%s:%i - Not a valid socket.\n", _FL); return false; } void SslSocket::OnWrite(const char *Data, int Len) { #ifdef _DEBUG if (d->RawLFCheck) { const char *End = Data + Len; while (Data < End) { LgiAssert(*Data != '\n' || d->LastWasCR); d->LastWasCR = *Data == '\r'; Data++; } } #endif } void SslSocket::OnRead(char *Data, int Len) { #ifdef _DEBUG if (d->RawLFCheck) { const char *End = Data + Len; while (Data < End) { LgiAssert(*Data != '\n' || d->LastWasCR); d->LastWasCR = *Data == '\r'; Data++; } } #endif } int SslSocket::Write(const void *Data, int Len, int Flags) { GMutex::Auto Lck(&Lock, _FL); if (!Library) { DebugTrace("%s:%i - Library is NULL\n", _FL); return -1; } if (!Bio) { DebugTrace("%s:%i - BIO is NULL\n", _FL); return -1; } int r = 0; if (d->UseSSLrw) { if (Ssl) { uint64 Start = LgiCurrentTime(); int To = GetTimeout(); while (HasntTimedOut()) { r = Library->SSL_write(Ssl, Data, Len); if (r < 0) { LgiSleep(10); } else { DebugTrace("%s:%i - SSL_write(%p,%i)=%i\n", _FL, Data, Len, r); OnWrite((const char*)Data, r); break; } } if (r < 0) { DebugTrace("%s:%i - SSL_write failed (timeout=%i, %ims)\n", _FL, To, (int) (LgiCurrentTime() - Start)); } } else { r = -1; DebugTrace("%s:%i - No SSL\n", _FL); } } else { uint64 Start = LgiCurrentTime(); int To = GetTimeout(); while (HasntTimedOut()) { if (!Library) break; r = Library->BIO_write(Bio, Data, Len); DebugTrace("%s:%i - BIO_write(%p,%i)=%i\n", _FL, Data, Len, r); if (r < 0) { LgiSleep(10); } else { OnWrite((const char*)Data, r); break; } } if (r < 0) { DebugTrace("%s:%i - BIO_write failed (timeout=%i, %ims)\n", _FL, To, (int) (LgiCurrentTime() - Start)); } } if (r > 0) { GStream *l = GetLogStream(); if (l) l->Write(Data, r); } if (Ssl) { if (r < 0) { int Err = Library->SSL_get_error(Ssl, r); char Buf[256] = ""; char *e = Library->ERR_error_string(Err, Buf); DebugTrace("%s:%i - ::Write error %i, %s\n", _FL, Err, e); if (e) { OnError(Err, e); } } if (r <= 0) { DebugTrace("%s:%i - ::Write closing %i\n", _FL, r); Close(); } } return r; } int SslSocket::Read(void *Data, int Len, int Flags) { GMutex::Auto Lck(&Lock, _FL); if (!Library) return -1; if (Bio) { int r = 0; if (d->UseSSLrw) { if (Ssl) { uint64 Start = LgiCurrentTime(); int To = GetTimeout(); while (HasntTimedOut()) { r = Library->SSL_read(Ssl, Data, Len); DebugTrace("%s:%i - SSL_read(%p,%i)=%i\n", _FL, Data, Len, r); if (r < 0) LgiSleep(10); else { OnRead((char*)Data, r); break; } } } else { r = -1; } } else { uint64 Start = LgiCurrentTime(); int To = GetTimeout(); while (HasntTimedOut()) { r = Library->BIO_read(Bio, Data, Len); if (r < 0) LgiSleep(10); else { DebugTrace("%s:%i - BIO_read(%p,%i)=%i\n", _FL, Data, Len, r); OnRead((char*)Data, r); break; } } } if (r > 0) { GStream *l = GetLogStream(); if (l) l->Write(Data, r); } if (Ssl) { if (r < 0) { int Err = Library->SSL_get_error(Ssl, r); char Buf[256]; char *e = Library->ERR_error_string(Err, Buf); if (e) { OnError(Err, e); } Close(); } if (r <= 0) { Close(); } } return r; } return -1; } void SslSocket::OnError(int ErrorCode, const char *ErrorDescription) { char s[MAX_PATH]; DebugTrace("%s:%i - OnError=%i,%s\n", _FL, ErrorCode, ErrorDescription); sprintf_s(s, sizeof(s), "Error %i: %s\n", ErrorCode, ErrorDescription); Log(s, SocketMsgError); } void SslSocket::OnInformation(const char *Str) { char s[MAX_PATH]; strcpy_s(s, sizeof(s), Str); char *e = s + strlen(s); while (e > s && strchr("\r\n", e[-1])) e--; *e++ = '\n'; *e++ = 0; Log(s, SocketMsgInfo); - -DebugTrace("%s:%i - OnInfo=%s", _FL, s); }