mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-07-11 22:03:01 -06:00
Core: TcpSocket, Thread::AtExit
git-svn-id: svn://ultimatepp.org/upp/trunk@4761 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
parent
cfd894c242
commit
b5e405587a
12 changed files with 895 additions and 17 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(); };
|
||||
|
|
|
|||
90
uppsrc/Core/SSL/InitExit.cpp
Normal file
90
uppsrc/Core/SSL/InitExit.cpp
Normal file
|
|
@ -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<int>(*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
|
||||
|
|
@ -1,4 +1,136 @@
|
|||
#ifndef _SSL_SSL_h
|
||||
#define _SSL_SSL_h
|
||||
#include <Core/Core.h>
|
||||
|
||||
#endif
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/conf.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/engine.h>
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1 +1,6 @@
|
|||
file "SSL.h";
|
||||
file
|
||||
SSL.h,
|
||||
Util.cpp,
|
||||
InitExit.cpp,
|
||||
Socket.cpp;
|
||||
|
||||
|
|
|
|||
343
uppsrc/Core/SSL/Socket.cpp
Normal file
343
uppsrc/Core/SSL/Socket.cpp
Normal file
|
|
@ -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<SSLSocketData> 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<SSLSocketData> 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<SSLSocketData> 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<SSLSocketData> 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<SSLSocketData *>(~socket.data);
|
||||
if(!sd)
|
||||
return false;
|
||||
return sd->Secure();
|
||||
}
|
||||
#endif
|
||||
|
||||
END_UPP_NAMESPACE
|
||||
208
uppsrc/Core/SSL/Util.cpp
Normal file
208
uppsrc/Core/SSL/Util.cpp
Normal file
|
|
@ -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<char *>(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
|
||||
3
uppsrc/Core/SSL/init
Normal file
3
uppsrc/Core/SSL/init
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#ifndef _Core_SSL_icpp_init_stub
|
||||
#define _Core_SSL_icpp_init_stub
|
||||
#endif
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<SSLBase> ssl;
|
||||
struct SSLImp;
|
||||
|
||||
friend struct SSLImp;
|
||||
|
||||
One<SSL> 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; }
|
||||
|
|
|
|||
|
|
@ -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)].&]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue