diff --git a/uppsrc/Core/App.cpp b/uppsrc/Core/App.cpp index 032871bc0..4fe2bb0f5 100644 --- a/uppsrc/Core/App.cpp +++ b/uppsrc/Core/App.cpp @@ -403,7 +403,7 @@ void LaunchWebBrowser(const String& url) { WString wurl = ToSystemCharsetW(url); if (int(ShellExecuteW(NULL, L"open", wurl, NULL, L".", SW_SHOWDEFAULT)) <= 32) { - int l = 2 * wurl.GetLength() + 1; + int l = sizeof(wchar) * wurl.GetLength() + 1; char *curl = (char *)malloc(l); memcpy(curl, wurl, l); StartRawThread(sShellExecuteOpen, curl); diff --git a/uppsrc/Core/Mt.cpp b/uppsrc/Core/Mt.cpp index 266acd15c..13463dc04 100644 --- a/uppsrc/Core/Mt.cpp +++ b/uppsrc/Core/Mt.cpp @@ -48,6 +48,15 @@ void Thread::Detach() static Atomic sThreadCount; +static thread__ void (*sExit)(void); + +void (*Thread::AtExit(void (*exitfn)()))() +{ + void (*prev)() = sExit; + sExit = exitfn; + return prev; +} + #if defined(PLATFORM_WIN32) || defined(PLATFORM_POSIX) static #ifdef PLATFORM_WIN32 @@ -65,6 +74,8 @@ sThreadRoutine(void *arg) (*cb)(); AtomicDec(sThreadCount); delete cb; + if(sExit) + (*sExit)(); #ifdef UPP_HEAP MemoryFreeThread(); #endif diff --git a/uppsrc/Core/Mt.h b/uppsrc/Core/Mt.h index a03029822..ada8d9b82 100644 --- a/uppsrc/Core/Mt.h +++ b/uppsrc/Core/Mt.h @@ -87,6 +87,8 @@ public: static int GetCount(); static void ShutdownThreads(); static bool IsShutdownThreads(); + static void (*AtExit(void (*exitfn)()))(); + #ifdef PLATFORM_WIN32 static Handle GetCurrentHandle() { return GetCurrentThread(); } static inline Id GetCurrentId() { return ::GetCurrentThreadId(); }; diff --git a/uppsrc/Core/SSL/InitExit.cpp b/uppsrc/Core/SSL/InitExit.cpp new file mode 100644 index 000000000..f8a321ed4 --- /dev/null +++ b/uppsrc/Core/SSL/InitExit.cpp @@ -0,0 +1,90 @@ +#include "SSL.h" + +#define LLOG(x) // DLOG(x) + +NAMESPACE_UPP + +static int UPP_SSL_alloc = 0; + +static void *SslAlloc(size_t size) +{ + size_t alloc = size + sizeof(int); + int *aptr = (int *)MemoryAllocSz(alloc); + *aptr++ = alloc; + UPP_SSL_alloc += alloc; + LLOG("UPP_SSL_MALLOC(" << (int)size << ", alloc " << alloc << ") -> " << FormatIntHex(aptr) << ", total = " << UPP_SSL_alloc); + return aptr; +} + +static void SslFree(void *ptr) +{ + if(!ptr) + return; + int *aptr = (int *)ptr - 1; + UPP_SSL_alloc -= *aptr; + LLOG("UPP_SSL_FREE(" << ptr << ", alloc " << *aptr << "), total = " << UPP_SSL_alloc); + MemoryFree(aptr); +} + +static void *SslRealloc(void *ptr, size_t size) +{ + if(!ptr) + return NULL; + int *aptr = (int *)ptr - 1; + if((int)(size + sizeof(int)) <= *aptr) { + LLOG("UPP_SSL_REALLOC(" << ptr << ", " << (int)size << ", alloc " << *aptr << ") -> keep same block"); + return ptr; + } + size_t newalloc = size + sizeof(int); + int *newaptr = (int *)MemoryAllocSz(newalloc); + if(!newaptr) { + LLOG("UPP_SSL_REALLOC(" << ptr << ", " << (int)size << ", alloc " << newalloc << ") -> fail"); + return NULL; + } + *newaptr++ = newalloc; + memcpy(newaptr, ptr, min(*aptr - sizeof(int), size)); + UPP_SSL_alloc += newalloc - *aptr; + LLOG("UPP_SSL_REALLOC(" << ptr << ", " << (int)size << ", alloc " << newalloc << ") -> " + << FormatIntHex(newaptr) << ", total = " << UPP_SSL_alloc); + MemoryFree(aptr); + return newaptr; +} + +void TcpSocketInit(); + +INITBLOCK +{ + TcpSocketInit(); + MemoryIgnoreLeaksBlock __; + CRYPTO_set_mem_functions(SslAlloc, SslRealloc, SslFree); + SSL_load_error_strings(); + SSL_library_init(); +} + +EXITBLOCK +{ + CONF_modules_unload(1); + EVP_cleanup(); + ENGINE_cleanup(); + CRYPTO_cleanup_all_ex_data(); + ERR_remove_state(0); + ERR_free_strings(); +} + +static thread__ bool sThreadInit; +static thread__ void (*sPrevExit)(); + +static void sslExitThread() +{ + ERR_remove_state(0); +} + +void SslInitThread() +{ + if(sThreadInit || Thread::IsMain()) + return; + sThreadInit = true; + Thread::AtExit(sslExitThread); +} + +END_UPP_NAMESPACE diff --git a/uppsrc/Core/SSL/SSL.h b/uppsrc/Core/SSL/SSL.h index d675f6e0a..deced43cd 100644 --- a/uppsrc/Core/SSL/SSL.h +++ b/uppsrc/Core/SSL/SSL.h @@ -1,4 +1,136 @@ -#ifndef _SSL_SSL_h -#define _SSL_SSL_h +#include -#endif +#include +#include +#include +#include + +NAMESPACE_UPP + +void SslInitThread(); + +class SslBuffer +{ +public: + SslBuffer(BUF_MEM *m = NULL) : buf_mem(m) {} + ~SslBuffer() { Clear(); } + + bool IsEmpty() const { return !buf_mem; } + + bool Set(BUF_MEM *b) { Clear(); return !!(buf_mem = b); } + bool Create() { return Set(BUF_MEM_new()); } + void Clear() { if(buf_mem) { BUF_MEM_free(buf_mem); buf_mem = NULL; } } + BUF_MEM *Detach() { BUF_MEM *b = buf_mem; buf_mem = NULL; return b; } + + bool Grow(int length); + + String Get() const; + bool Set(const String& d); + + operator BUF_MEM * () const { return buf_mem; } + +private: + BUF_MEM *buf_mem; +}; + +class SslStream +{ +public: + SslStream(BIO *b = NULL) : bio(b) {} + ~SslStream() { Clear(); } + + bool IsEmpty() const { return !bio; } + + bool Set(BIO *b) { Clear(); return !!(bio = b); } + bool Create(BIO_METHOD *meth) { return Set(BIO_new(meth)); } + void Clear() { if(bio) { BIO_free(bio); bio = NULL; } } + + bool OpenBuffer(const char *data, int length); + bool CreateBuffer(); + String GetResult() const; + + operator BIO * () const { return bio; } + +private: + BIO *bio; +}; + +class SslKey +{ +public: + SslKey(EVP_PKEY *k = NULL) : key(k) {} + ~SslKey() { Clear(); } + + bool IsEmpty() const { return !key; } + + bool Set(EVP_PKEY *k) { Clear(); return !!(key = k); } + void Clear() { if(key) { EVP_PKEY_free(key); key = NULL; } } + EVP_PKEY *Detach() { EVP_PKEY *k = key; key = NULL; return k; } + + operator EVP_PKEY * () const { return key; } + + bool Load(const String& data); + +private: + EVP_PKEY *key; +}; + +class SslCertificate +{ +public: + SslCertificate(X509 *c = NULL) : cert(c) {} + ~SslCertificate() { Clear(); } + + bool IsEmpty() const { return !cert; } + + bool Set(X509 *c) { Clear(); return !!(cert = c); } + bool Create() { return Set(X509_new()); } + void Clear() { if(cert) { X509_free(cert); cert = NULL; } } + X509 *Detach() { X509 *c = cert; cert = NULL; return c; } + + bool Load(const String& data, bool asn1 = false); + String Save(bool asn1 = false) const; + + String GetSubjectName() const; + String GetIssuerName() const; + Date GetNotBefore() const; + Date GetNotAfter() const; + int GetVersion() const; + String GetSerialNumber() const; + + operator X509 * () const { return cert; } + +private: + X509 *cert; +}; + +class SslContext +{ +public: + SslContext(SSL_CTX *c = NULL); + ~SslContext() { Clear(); } + + bool IsEmpty() const { return !ssl_ctx; } + + bool Set(SSL_CTX *c) { Clear(); return !!(ssl_ctx = c); } + bool Create(SSL_METHOD *meth) { return Set(SSL_CTX_new(meth)); } + void Clear() { if(ssl_ctx) { SSL_CTX_free(ssl_ctx); ssl_ctx = NULL; } } + SSL_CTX *Detach() { SSL_CTX *c = ssl_ctx; ssl_ctx = NULL; return c; } + + operator SSL_CTX * () const { return ssl_ctx; } + + bool CipherList(const char *list); + bool UseCertificate(String certificate, String private_key, bool cert_asn1 = false); + void VerifyPeer(bool verify = true, int depth = 2); + +private: + SSL_CTX *ssl_ctx; +}; + +String SslGetLastError(int& code); +String SslGetLastError(); +String SslToString(X509_NAME *name); +Date ASN1ToDate(ASN1_STRING *time); +String ASN1ToString(ASN1_STRING *s); + +END_UPP_NAMESPACE diff --git a/uppsrc/Core/SSL/SSL.upp b/uppsrc/Core/SSL/SSL.upp index 5a7b62260..ef48d5bf5 100644 --- a/uppsrc/Core/SSL/SSL.upp +++ b/uppsrc/Core/SSL/SSL.upp @@ -1 +1,6 @@ -file "SSL.h"; +file + SSL.h, + Util.cpp, + InitExit.cpp, + Socket.cpp; + diff --git a/uppsrc/Core/SSL/Socket.cpp b/uppsrc/Core/SSL/Socket.cpp new file mode 100644 index 000000000..83639ecab --- /dev/null +++ b/uppsrc/Core/SSL/Socket.cpp @@ -0,0 +1,343 @@ +#include "SSL.h" + +NAMESPACE_UPP + +struct TcpSocket::SSLImp : TcpSocket::SSL { + virtual bool Start(TcpSocket& s); + virtual bool Wait(TcpSocket& s, dword flags); + virtual int Send(TcpSocket& s, const void *buffer, int maxlen); + virtual int Recv(TcpSocket& s, void *buffer, int maxlen); + virtual void Close(TcpSocket& s); +}; + +TcpSocket::SSLImp *TcpSocket::CreateSSLImp() +{ + return new TcpSSLImp(); +} + +void InitCreateSSL() +{ + TcpTcpSocket::CreateSSL = sCreate(); +} + +bool TcpSocket::SSLImp::Start(TcpSocket& socket) +{ + if(!(ssl = SSL_new(ssl_context))) { + SetSSLError("OpenClient / SSL_new"); + return false; + } + if(!SSL_set_fd(ssl, socket)) { + SetSSLError("OpenClient / SSL_set_fd"); + return false; + } + int res; + if(mode == ACCEPT) { + SSL_set_accept_state(ssl); + res = SSL_accept(ssl); + } + else { + SSL_set_connect_state(ssl); + res = SSL_connect(ssl); + } + if(res <= 0) { + SetSSLResError("OpenClient / SSL_connect", res); + return false; + } + cert.Set(SSL_get_peer_certificate(ssl)); + return true; +} + +bool TcpSocket::SSLImp::Wait(TcpSocket& s, dword flags) +{ + if((flags & WAIT_READ) && SSL_pending(ssl) > 0) + return true; + return s.RawWait(flags); +} + +int TcpSocket::SSLImp::Send(TcpSocket& s, const void *buffer, int maxlen) +{ + if(!ssl) + return Data::Write(buf, amount); + int res = SSL_write(ssl, (const char *)buf, amount); + if(res <= 0) { + SetSSLResError("SSL_write", res); + return 0; + } + return res; +} + +int TcpSocket::SSLImp::Recv(TcpSocket& s, void *buffer, int maxlen) +{ + int res = SSL_read(ssl, (char *)buf, amount); + if(res == 0) { + s.is_eof = true; + if(SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN) + return 0; + } + if(res <= 0) { + SetSSLResError("SSL_read", res); + return 0; + } + return res; +} + +void TcpSocket::SSLImp::Close(TcpSocket& s) +{ + SSL_shutdown(ssl); + s.RawClose(); + SSL_free(ssl); +} + + +#if 0 +class SSLSocketData : public TcpSocket::Data +{ +public: + SSLSocketData(SslContext& context); + virtual ~SSLSocketData(); + + bool OpenClientUnsecured(const char *host, int port, bool nodelay, dword *my_addr, + int timeout, bool is_blocking); + bool Secure(); + bool OpenAccept(SOCKET connection, bool nodelay, bool blocking); + + virtual int GetKind() const { return SOCKKIND_SSL; } + virtual bool Peek(int timeout_msec, bool write); + virtual int Read(void *buf, int amount); + virtual int Write(const void *buf, int amount); + virtual bool Accept(Socket& socket, dword *ipaddr, bool nodelay, int timeout_msec); + virtual bool Close(int timeout_msec); + virtual Value GetInfo(String info) const; + + void SetSSLError(const char *context); + void SetSSLResError(const char *context, int res); + +public: + SslContext& ssl_context; + SSL *ssl; + SslCertificate cert; +}; + +SSLSocketData::SSLSocketData(SslContext& ssl_context) +: ssl_context(ssl_context) +{ + SSLInit().AddThread(); + ssl = NULL; +} + +SSLSocketData::~SSLSocketData() +{ + Close(0); +} + +void SSLSocketData::SetSSLError(const char *context) +{ + if(sock) { + int code; + String text = SSLGetLastError(code); + SetSockError(context, code, text); + } +} + +void SSLSocketData::SetSSLResError(const char *context, int res) +{ + if(sock) { + int code = SSL_get_error(ssl, res); + String out; + switch(code) { + #define SSLERR(c) case c: out = #c; break; + SSLERR(SSL_ERROR_NONE) + SSLERR(SSL_ERROR_SSL) + SSLERR(SSL_ERROR_WANT_READ) + SSLERR(SSL_ERROR_WANT_WRITE) + SSLERR(SSL_ERROR_WANT_X509_LOOKUP) + SSLERR(SSL_ERROR_SYSCALL) + SSLERR(SSL_ERROR_ZERO_RETURN) + SSLERR(SSL_ERROR_WANT_CONNECT) + #ifdef PLATFORM_WIN32 + SSLERR(SSL_ERROR_WANT_ACCEPT) + #endif + default: out = "unknown code"; break; + } + SetSockError(context, code, out); + } +} + +bool SSLSocketData::Peek(int timeout_msec, bool write) +{ + if(ssl && !write && SSL_pending(ssl) > 0) + return true; + return Data::Peek(timeout_msec, write); +} + +int SSLSocketData::Read(void *buf, int amount) +{ + if(!ssl) + return Data::Read(buf, amount); + int res = SSL_read(ssl, (char *)buf, amount); + if(res == 0) { + is_eof = true; + if(SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN) + return 0; + } + if(res <= 0) + SetSSLResError("SSL_read", res); +#ifndef NOFAKEERROR + if(fake_error && res > 0) { + if((fake_error -= res) <= 0) { + fake_error = 0; + SetSockError("SSL_read", 0, "fake error"); + return -1; + } + else + RLOG("SSLSocketData::Read: fake error after " << fake_error); + } +#endif + return res; +} + +int SSLSocketData::Write(const void *buf, int amount) +{ + if(!ssl) + return Data::Write(buf, amount); + int res = SSL_write(ssl, (const char *)buf, amount); + if(res <= 0) + SetSSLResError("SSL_write", res); + return res; +} + +bool SSLSocketData::OpenClientUnsecured(const char *host, int port, bool nodelay, dword *my_addr, + int timeout, bool blocking) +{ + return Data::OpenClient(host, port, nodelay, my_addr, timeout, /*blocking*/true); +} + +bool SSLSocketData::Secure() +{ + if(!(ssl = SSL_new(ssl_context))) + { + SetSSLError("OpenClient / SSL_new"); + return false; + } + if(!SSL_set_fd(ssl, socket)) + { + SetSSLError("OpenClient / SSL_set_fd"); + return false; + } + SSL_set_connect_state(ssl); + int res = SSL_connect(ssl); + if(res <= 0) + { + SetSSLResError("OpenClient / SSL_connect", res); + return false; + } + cert.Set(SSL_get_peer_certificate(ssl)); + return true; +} + +bool SSLSocketData::OpenAccept(SOCKET conn, bool nodelay, bool blocking) +{ + Attach(conn, nodelay, blocking); + if(!(ssl = SSL_new(ssl_context))) + { + SetSSLError("Accept / SSL_new"); + return false; + } + if(!SSL_set_fd(ssl, socket)) + { + SetSSLError("Accept / SSL_set_fd"); + return false; + } + SSL_set_accept_state(ssl); + int res = SSL_accept(ssl); + if(res <= 0) + { + SetSSLResError("Accept / SSL_accept", res); + return false; + } + cert.Set(SSL_get_peer_certificate(ssl)); + return true; +} + +bool SSLSocketData::Accept(Socket& socket, dword *ipaddr, bool nodelay, int timeout_msec) +{ + SOCKET connection = AcceptRaw(ipaddr, timeout_msec); + if(connection == INVALID_SOCKET) + return false; + One data = new SSLSocketData(ssl_context); + if(!data->OpenAccept(connection, nodelay, is_blocking)) + return false; + socket.Attach(-data); + return true; +} + +bool SSLSocketData::Close(int timeout_msec) +{ + if(ssl) + SSL_shutdown(ssl); + bool res = Data::Close(timeout_msec); + if(ssl) { + SSL_free(ssl); + ssl = NULL; + } + return res; +} + +Value SSLSocketData::GetInfo(String info) const +{ + if(info == SSLInfoCipher()) return SSL_get_cipher(ssl); + if(info == SSLInfoCertAvail()) return cert.IsEmpty() ? 0 : 1; + if(info == SSLInfoCertVerified()) return SSL_get_verify_result(ssl) == X509_V_OK ? 1 : 0; + if(info == SSLInfoCertSubjectName()) return cert.IsEmpty() ? String::GetVoid() : cert.GetSubjectName(); + if(info == SSLInfoCertIssuerName()) return cert.IsEmpty() ? String::GetVoid() : cert.GetIssuerName(); + if(info == SSLInfoCertNotBefore()) return cert.IsEmpty() ? Date(Null) : cert.GetNotBefore(); + if(info == SSLInfoCertNotAfter()) return cert.IsEmpty() ? Date(Null) : cert.GetNotAfter(); + if(info == SSLInfoCertVersion()) return cert.IsEmpty() ? int(Null) : cert.GetVersion(); + if(info == SSLInfoCertSerialNumber()) return cert.IsEmpty() ? String::GetVoid() : cert.GetSerialNumber(); + + return Data::GetInfo(info); +} + +bool SSLServerSocket(Socket& socket, SslContext& ssl_context, int port, bool nodelay, int listen_count, bool blocking) +{ + One data = new SSLSocketData(ssl_context); + if(!data->OpenServer(port, nodelay, listen_count, blocking)) + return false; + socket.Attach(-data); + return true; +} + +bool SSLClientSocket(Socket& socket, SslContext& ssl_context, const char *host, int port, bool nodelay, + dword *my_addr, int timeout, bool blocking) +{ + One data = new SSLSocketData(ssl_context); + if(!data->OpenClient(host, port, nodelay, my_addr, timeout, blocking)) + return false; + if(!data->Secure()) + return false; + socket.Attach(-data); + return true; +} + +bool SSLClientSocketUnsecured(Socket& socket, SslContext& ssl_context, const char *host, + int port, bool nodelay, dword *my_addr, int timeout, + bool is_blocking) +{ + One data = new SSLSocketData(ssl_context); + if(data->OpenClientUnsecured(host, port, nodelay, my_addr, timeout, is_blocking)) { + socket.Attach(-data); + return true; + } + return false; +} + +bool SSLSecureSocket(Socket& socket) +{ + SSLSocketData *sd = dynamic_cast(~socket.data); + if(!sd) + return false; + return sd->Secure(); +} +#endif + +END_UPP_NAMESPACE diff --git a/uppsrc/Core/SSL/Util.cpp b/uppsrc/Core/SSL/Util.cpp new file mode 100644 index 000000000..8e8582d05 --- /dev/null +++ b/uppsrc/Core/SSL/Util.cpp @@ -0,0 +1,208 @@ +#include "SSL.h" + +NAMESPACE_UPP + +String SslBuffer::Get() const +{ + if(IsEmpty()) + return String::GetVoid(); + return String(buf_mem->data, buf_mem->length); +} + +bool SslBuffer::Grow(int length) +{ + return !IsEmpty() && BUF_MEM_grow(buf_mem, length); +} + +bool SslBuffer::Set(const String& d) +{ + if(!buf_mem && !Create()) + return false; + int len = d.GetLength(); + if((int)buf_mem->max < len && !Grow(len)) + return false; + ASSERT((int)buf_mem->max >= len); + buf_mem->length = len; + memcpy(buf_mem, d, len); + return true; +} + +bool SslStream::OpenBuffer(const char *data, int length) +{ + return Set(BIO_new_mem_buf(const_cast(data), length)); +} + +bool SslStream::CreateBuffer() +{ + Clear(); + SslBuffer buf; + if(!buf.Create() || !Create(BIO_s_mem())) + return false; + BIO_set_mem_buf(bio, buf.Detach(), BIO_CLOSE); + return true; +} + +String SslStream::GetResult() const +{ + if(IsEmpty()) + return String::GetVoid(); + BUF_MEM *bm = NULL; + BIO_get_mem_ptr(bio, &bm); + if(!bm) + return String::GetVoid(); + return String(bm->data, bm->length); +} + +bool SslKey::Load(const String& data) +{ + Clear(); + SslStream strm; + if(!strm.OpenBuffer(data.Begin(), data.GetLength())) + return false; + return Set(PEM_read_bio_PrivateKey(strm, NULL, NULL, NULL)); +} + +bool SslCertificate::Load(const String& data, bool asn1) +{ + Clear(); + SslStream in, pem, *sio = ∈ + if(!in.OpenBuffer(data, data.GetLength())) + return false; + if(!asn1) + { + if(!pem.Create(BIO_f_base64())) + return false; + BIO_push(pem, in); + sio = &pem; + } + return Set(d2i_X509_bio(*sio, NULL)); +} + +String SslCertificate::Save(bool asn1) const +{ + if(IsEmpty()) + return String::GetVoid(); + SslStream out, pem, *sio = &out; + if(!out.CreateBuffer()) + return String::GetVoid(); + if(!asn1) + { + if(!pem.Create(BIO_f_base64())) + return String::GetVoid(); + BIO_push(pem, out); + sio = &pem; + } + i2d_X509_bio(*sio, cert); + return out.GetResult(); +} + +String SslCertificate::GetSubjectName() const +{ + ASSERT(!IsEmpty()); + return SslToString(X509_get_subject_name(cert)); +} + +String SslCertificate::GetIssuerName() const +{ + ASSERT(!IsEmpty()); + return SslToString(X509_get_issuer_name(cert)); +} + +Date SslCertificate::GetNotBefore() const +{ + ASSERT(!IsEmpty()); + return ASN1ToDate(X509_get_notBefore(cert)); +} + +Date SslCertificate::GetNotAfter() const +{ + ASSERT(!IsEmpty()); + return ASN1ToDate(X509_get_notAfter(cert)); +} + +int SslCertificate::GetVersion() const +{ + ASSERT(!IsEmpty()); + return X509_get_version(cert); +} + +String SslCertificate::GetSerialNumber() const +{ + ASSERT(!IsEmpty()); + return ASN1ToString(X509_get_serialNumber(cert)); +} + +SslContext::SslContext(SSL_CTX *c) +: ssl_ctx(c) +{ + SslInitThread(); +} + +bool SslContext::CipherList(const char *list) +{ + ASSERT(ssl_ctx); + return SSL_CTX_set_cipher_list(ssl_ctx, list); +} + +bool SslContext::UseCertificate(String certdata, String pkeydata, bool cert_asn1) +{ + ASSERT(ssl_ctx); + if(IsNull(certdata) || IsNull(pkeydata)) + return false; + SslCertificate cert; + SslKey pkey; + if(!cert.Load(certdata, cert_asn1) || !pkey.Load(pkeydata)) + return false; + if(!SSL_CTX_use_certificate(ssl_ctx, cert) || !SSL_CTX_use_PrivateKey(ssl_ctx, pkey)) + return false; + if(!SSL_CTX_check_private_key(ssl_ctx)) + return false; + return true; +} + +void SslContext::VerifyPeer(bool verify, int depth) +{ + ASSERT(ssl_ctx); + SSL_CTX_set_verify(ssl_ctx, verify ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); + SSL_CTX_set_verify_depth(ssl_ctx, depth); +} + +String SslGetLastError(int& code) +{ + char errbuf[150]; + ERR_error_string(code = ERR_get_error(), errbuf); + return errbuf; +} + +String SslGetLastError() +{ + int dummy; + return SslGetLastError(dummy); +} + +String SslToString(X509_NAME *name) +{ + char buffer[500]; + return X509_NAME_oneline(name, buffer, sizeof(buffer)); +} + +Date ASN1ToDate(ASN1_STRING *time) +{ + if(!time) return Null; + int digit = 0; + while(digit < time->length && IsDigit(time->data[digit])) + digit++; + if(digit < 6) + return Null; + int year2 = time->data[0] * 10 + time->data[1] - 11 * '0'; + int month = time->data[2] * 10 + time->data[3] - 11 * '0'; + int day = time->data[4] * 10 + time->data[5] - 11 * '0'; + return Date(year2 + (year2 < 90 ? 2000 : 1900), month, day); +} + +String ASN1ToString(ASN1_STRING *s) +{ + return String(s->data, s->length); +} + +END_UPP_NAMESPACE diff --git a/uppsrc/Core/SSL/init b/uppsrc/Core/SSL/init new file mode 100644 index 000000000..5d16396f3 --- /dev/null +++ b/uppsrc/Core/SSL/init @@ -0,0 +1,3 @@ +#ifndef _Core_SSL_icpp_init_stub +#define _Core_SSL_icpp_init_stub +#endif diff --git a/uppsrc/Core/Socket.cpp b/uppsrc/Core/Socket.cpp index 0802fc9e2..c08019e38 100644 --- a/uppsrc/Core/Socket.cpp +++ b/uppsrc/Core/Socket.cpp @@ -235,8 +235,7 @@ int TcpSocket::GetErrorCode() #endif - -void TcpSocket::Init() +void TcpSocketInit() { #if defined(PLATFORM_WIN32) ONCELOCK { @@ -246,6 +245,11 @@ void TcpSocket::Init() #endif } +void TcpSocket::Init() +{ + TcpSocketInit(); +} + void TcpSocket::Reset() { is_eof = false; @@ -254,6 +258,8 @@ void TcpSocket::Reset() ptr = end = buffer; is_error = false; is_abort = false; + mode = NONE; + ssl.Clear(); } TcpSocket::TcpSocket() @@ -338,6 +344,7 @@ bool TcpSocket::Accept(TcpSocket& ls) SetSockError("accept"); return false; } + mode = ACCEPT; return true; } @@ -400,6 +407,7 @@ bool TcpSocket::RawConnect(addrinfo *rp) return false; } } + mode = CONNECT; return true; } @@ -425,7 +433,7 @@ bool TcpSocket::Connect(const char *host, int port) return Connect(info); } -void TcpSocket::Close() +void TcpSocket::RawClose() { LLOG("TCP close " << (int)socket); if(socket != INVALID_SOCKET) { @@ -443,6 +451,15 @@ void TcpSocket::Close() } } +void TcpSocket::Close() +{ + if(ssl) + ssl->Close(*this); + else + RawClose(); + ssl.Clear(); +} + bool TcpSocket::WouldBlock() { int c = GetErrorCode(); @@ -454,7 +471,7 @@ bool TcpSocket::WouldBlock() #endif } -int TcpSocket::Send(const void *buf, int amount) +int TcpSocket::RawSend(const void *buf, int amount) { int res = send(socket, (const char *)buf, amount, 0); if(res < 0 && WouldBlock()) @@ -465,6 +482,11 @@ int TcpSocket::Send(const void *buf, int amount) return res; } +int TcpSocket::Send(const void *buf, int amount) +{ + return ssl ? ssl->Send(*this, buf, amount) : RawSend(buf, amount); +} + void TcpSocket::Shutdown() { ASSERT(IsOpen()); @@ -480,7 +502,7 @@ String TcpSocket::GetHostName() return buffer; } -bool TcpSocket::Wait(dword flags) +bool TcpSocket::RawWait(dword flags) { LLOG("Wait(" << timeout << ", " << flags << ")"); if((flags & WAIT_READ) && ptr != end) @@ -525,6 +547,11 @@ bool TcpSocket::Wait(dword flags) } } +bool TcpSocket::Wait(dword flags) +{ + return ssl ? ssl->Wait(*this, flags) : RawWait(flags); +} + int TcpSocket::Put(const char *s, int length) { LLOG("Put " << socket << ": " << length); @@ -551,7 +578,7 @@ int TcpSocket::Put(const char *s, int length) return done; } -int TcpSocket::Recv(void *buf, int amount) +int TcpSocket::RawRecv(void *buf, int amount) { int res = recv(socket, (char *)buf, amount, 0); if(res == 0) @@ -568,6 +595,11 @@ int TcpSocket::Recv(void *buf, int amount) return res; } +int TcpSocket::Recv(void *buffer, int maxlen) +{ + return ssl ? ssl->Recv(*this, buffer, maxlen) : RawRecv(buffer, maxlen); +} + void TcpSocket::ReadBuffer() { ptr = end = buffer; @@ -667,6 +699,28 @@ void TcpSocket::SetSockError(const char *context) SetSockError(context, TcpSocketErrorDesc(GetErrorCode())); } +TcpSocket::SSL *(*TcpSocket::CreateSSL)(); + +bool TcpSocket::StartSSL() +{ + if(!CreateSSL) { + errorcode = -1; + errordesc = "Missing SSL support (Core/SSL)"; + return false; + } + if(!IsOpen() || mode == NONE) { + errorcode = -1; + errordesc = "Socket not open or listening"; + return false; + } + ssl = (*CreateSSL)(); + if(!ssl->Start(*this)) { + ssl.Clear(); + return false; + } + return true; +} + int SocketWaitEvent::Wait(int timeout) { FD_ZERO(read); diff --git a/uppsrc/Core/Web.h b/uppsrc/Core/Web.h index 1b5e2b3bd..d84d4947a 100644 --- a/uppsrc/Core/Web.h +++ b/uppsrc/Core/Web.h @@ -49,7 +49,9 @@ enum { WAIT_READ = 1, WAIT_WRITE = 2, WAIT_EXCEPTION = 4, WAIT_ALL = 7 }; class TcpSocket { enum { BUFFERSIZE = 512 }; + enum { NONE, CONNECT, ACCEPT }; SOCKET socket; + int mode; char buffer[BUFFERSIZE]; char *ptr; char *end; @@ -65,20 +67,34 @@ class TcpSocket { int errorcode; String errordesc; - struct SSLBase { - virtual void Secure(TcpSocket& s) = 0; - virtual void Send(TcpSocket& s) = 0; - virtual void Recv(TcpSocket& s) = 0; + struct SSL { + virtual bool Start(TcpSocket& s) = 0; + virtual bool Wait(TcpSocket& s, dword flags) = 0; + virtual int Send(TcpSocket& s, const void *buffer, int maxlen) = 0; + virtual int Recv(TcpSocket& s, void *buffer, int maxlen) = 0; + virtual void Close(TcpSocket& s) = 0; }; - One ssl; + struct SSLImp; + + friend struct SSLImp; + + One ssl; + + static SSL *(*CreateSSL)(); + SSLImp *CreateSSLImp(); + friend void InitCreateSSL(); + + bool RawWait(dword flags); SOCKET AcceptRaw(dword *ipaddr, int timeout_msec); bool Open(int family, int type, int protocol); + int RawRecv(void *buffer, int maxlen); int Recv(void *buffer, int maxlen); + int RawSend(const void *buffer, int maxlen); int Send(const void *buffer, int maxlen); bool RawConnect(addrinfo *info); - void CreateSSL(); + void RawClose(); void ReadBuffer(); int Get_(); @@ -146,6 +162,8 @@ public: int Put(const String& s) { return Put(s.Begin(), s.GetLength()); } bool PutAll(const char *s, int len) { return Put(s, len) == len; } bool PutAll(const String& s) { return Put(s) == s.GetCount(); } + + bool StartSSL(); TcpSocket& Timeout(int ms) { timeout = ms; return *this; } int GetTimeout() const { return timeout; } diff --git a/uppsrc/Core/src.tpp/Thread$en-us.tpp b/uppsrc/Core/src.tpp/Thread$en-us.tpp index e391feb0a..fa817fbbf 100644 --- a/uppsrc/Core/src.tpp/Thread$en-us.tpp +++ b/uppsrc/Core/src.tpp/Thread$en-us.tpp @@ -71,6 +71,10 @@ the thread to finish in this case).&] is running so far).&] [s3; &] [s4; &] +[s5;:Thread`:`:IsMain`(`): [@(0.0.255) static] [@(0.0.255) bool]_[* IsMain]()&] +[s2;%% Returns true if current thread is main.&] +[s3; &] +[s4; &] [s5;:Thread`:`:GetCount`(`): [@(0.0.255) static] [@(0.0.255) int]_[* GetCount]()&] [s2;%% Number of running threads.&] [s3; &] @@ -85,6 +89,14 @@ reads]()&] [s2;%% True if ShutdownThreads was called.&] [s3; &] [s4; &] +[s5;:Thread`:`:AtExit: [@(0.0.255) static]_[@(0.0.255) void]_(`*[* AtExit]([@(0.0.255) void]_ +(`*[*@3 exitfn])()))()&] +[s2;%% Allow to install a function [%-*@3 exitfn] to be called at thread +exit. Returns the pointer to function already installed or NULL, +client code should call the already installed function (return +value is not null)&] +[s3; &] +[s4; &] [s5;:Thread`:`:Priority`(int`): [@(0.0.255) void]_[* Priority]([@(0.0.255) int]_[*@3 percent]) &] [s2;%% Sets the treads priority to [%-*@3 percent ][%- (0 to 100)].&]