Core: TcpSocket, Thread::AtExit

git-svn-id: svn://ultimatepp.org/upp/trunk@4761 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
cxl 2012-04-09 13:51:08 +00:00
parent cfd894c242
commit b5e405587a
12 changed files with 895 additions and 17 deletions

View file

@ -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);

View file

@ -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

View file

@ -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(); };

View 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

View file

@ -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

View file

@ -1 +1,6 @@
file "SSL.h";
file
SSL.h,
Util.cpp,
InitExit.cpp,
Socket.cpp;

343
uppsrc/Core/SSL/Socket.cpp Normal file
View 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
View 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 = &in;
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
View file

@ -0,0 +1,3 @@
#ifndef _Core_SSL_icpp_init_stub
#define _Core_SSL_icpp_init_stub
#endif

View file

@ -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);

View file

@ -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; }

View file

@ -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)].&]