mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-06-03 06:12:43 -06:00
.Core/SSL
git-svn-id: svn://ultimatepp.org/upp/trunk@4766 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
parent
1cddb1e275
commit
0fac8a45ff
8 changed files with 2169 additions and 2090 deletions
1186
uppsrc/Core/Http.cpp
1186
uppsrc/Core/Http.cpp
File diff suppressed because it is too large
Load diff
|
|
@ -1,3 +1,7 @@
|
|||
description "\3770,128,128";
|
||||
|
||||
library(POSIX) "crypto ssl";
|
||||
|
||||
file
|
||||
SSL.h,
|
||||
Util.cpp,
|
||||
|
|
|
|||
|
|
@ -1,343 +1,406 @@
|
|||
#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
|
||||
#include "SSL.h"
|
||||
|
||||
NAMESPACE_UPP
|
||||
|
||||
struct TcpSocket::SSLImp : TcpSocket::SSL {
|
||||
virtual bool Start();
|
||||
virtual bool Wait(dword flags);
|
||||
virtual int Send(const void *buffer, int maxlen);
|
||||
virtual int Recv(void *buffer, int maxlen);
|
||||
virtual void Close();
|
||||
|
||||
TcpSocket& socket;
|
||||
SslContext context;
|
||||
::SSL *ssl;
|
||||
SslCertificate cert;
|
||||
|
||||
void SetSSLError(const char *context);
|
||||
void SetSSLResError(const char *context, int res);
|
||||
bool IsAgain(int res) const;
|
||||
|
||||
SSLImp(TcpSocket& socket) : socket(socket) {}
|
||||
};
|
||||
|
||||
TcpSocket::SSL *TcpSocket::CreateSSLImp(TcpSocket& socket)
|
||||
{
|
||||
return new TcpSocket::SSLImp(socket);
|
||||
}
|
||||
|
||||
void InitCreateSSL()
|
||||
{
|
||||
TcpSocket::CreateSSL = TcpSocket::CreateSSLImp;
|
||||
}
|
||||
|
||||
INITBLOCK {
|
||||
InitCreateSSL();
|
||||
}
|
||||
|
||||
void TcpSocket::SSLImp::SetSSLError(const char *context)
|
||||
{
|
||||
int code;
|
||||
String text = SslGetLastError(code);
|
||||
socket.SetSockError(context, code, text);
|
||||
}
|
||||
|
||||
void TcpSocket::SSLImp::SetSSLResError(const char *context, int res)
|
||||
{
|
||||
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;
|
||||
}
|
||||
socket.SetSockError(context, code, out);
|
||||
}
|
||||
|
||||
bool TcpSocket::SSLImp::IsAgain(int res) const
|
||||
{
|
||||
res = SSL_get_error(ssl, res);
|
||||
return res == SSL_ERROR_WANT_READ ||
|
||||
res == SSL_ERROR_WANT_WRITE ||
|
||||
res == SSL_ERROR_WANT_CONNECT ||
|
||||
res == SSL_ERROR_WANT_ACCEPT;
|
||||
}
|
||||
|
||||
bool TcpSocket::SSLImp::Start()
|
||||
{
|
||||
if(!context.Create(const_cast<SSL_METHOD *>(SSLv3_client_method()))) {
|
||||
SetSSLError("Start: SSL context.");
|
||||
return false;
|
||||
}
|
||||
if(!(ssl = SSL_new(context))) {
|
||||
SetSSLError("Start: SSL_new");
|
||||
return false;
|
||||
}
|
||||
if(!SSL_set_fd(ssl, socket.GetSOCKET())) {
|
||||
SetSSLError("Start: SSL_set_fd");
|
||||
return false;
|
||||
}
|
||||
int res;
|
||||
if(socket.mode == ACCEPT) {
|
||||
SSL_set_accept_state(ssl);
|
||||
int res = SSL_accept(ssl);
|
||||
if(res <= 0 && !IsAgain(res)) {
|
||||
SetSSLResError("Start: SSL_accept", res);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
SSL_set_connect_state(ssl);
|
||||
res = SSL_connect(ssl);
|
||||
if(res <= 0 && !IsAgain(res)) {
|
||||
SetSSLResError("Start: SSL_connect", res);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
cert.Set(SSL_get_peer_certificate(ssl));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TcpSocket::SSLImp::Wait(dword flags)
|
||||
{
|
||||
if((flags & WAIT_READ) && SSL_pending(ssl) > 0)
|
||||
return true;
|
||||
return socket.RawWait(flags);
|
||||
}
|
||||
|
||||
int TcpSocket::SSLImp::Send(const void *buffer, int maxlen)
|
||||
{
|
||||
int res = SSL_write(ssl, (const char *)buffer, maxlen);
|
||||
if(IsAgain(res))
|
||||
return 0;
|
||||
if(res <= 0) {
|
||||
SetSSLResError("SSL_write", res);
|
||||
return 0;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int TcpSocket::SSLImp::Recv(void *buffer, int maxlen)
|
||||
{
|
||||
int res = SSL_read(ssl, (char *)buffer, maxlen);
|
||||
if(IsAgain(res))
|
||||
return 0;
|
||||
if(res == 0) {
|
||||
socket.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()
|
||||
{
|
||||
SSL_shutdown(ssl);
|
||||
socket.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
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -74,7 +74,7 @@ void Stream::LoadError() {
|
|||
throw LoadingError();
|
||||
}
|
||||
|
||||
bool Stream::GetAll(void *data, dword size) {
|
||||
bool Stream::GetAll(void *data, int size) {
|
||||
if(Get(data, size) != size) {
|
||||
LoadError();
|
||||
return false;
|
||||
|
|
@ -1084,7 +1084,7 @@ void CompareStream::Seek(int64 apos) {
|
|||
ptr = buffer;
|
||||
}
|
||||
|
||||
void CompareStream::Compare(int64 pos, const void *data, dword size) {
|
||||
void CompareStream::Compare(int64 pos, const void *data, int size) {
|
||||
ASSERT(stream);
|
||||
if(!size) return;
|
||||
Buffer<byte> b(size);
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ public:
|
|||
void LoadThrowing() { style |= STRM_THROW; }
|
||||
void LoadError();
|
||||
|
||||
bool GetAll(void *data, dword size);
|
||||
bool GetAll(void *data, int size);
|
||||
|
||||
int Get8() { return ptr < rdlim ? *ptr++ : _Get8(); }
|
||||
#ifdef CPU_X86
|
||||
|
|
@ -487,7 +487,7 @@ private:
|
|||
int64 size;
|
||||
byte h[128];
|
||||
|
||||
void Compare(int64 pos, const void *data, dword size);
|
||||
void Compare(int64 pos, const void *data, int size);
|
||||
|
||||
public:
|
||||
void Open(Stream& aStream);
|
||||
|
|
|
|||
|
|
@ -1,367 +1,372 @@
|
|||
String FormatIP(dword _ip);
|
||||
|
||||
String UrlEncode(const String& s);
|
||||
String UrlEncode(const String& s, const char *specials);
|
||||
String UrlDecode(const char *b, const char *e);
|
||||
inline String UrlDecode(const String& s) { return UrlDecode(s.Begin(), s.End() ); }
|
||||
|
||||
String Base64Encode(const char *b, const char *e);
|
||||
inline String Base64Encode(const String& data) { return Base64Encode(data.Begin(), data.End()); }
|
||||
String Base64Decode(const char *b, const char *e);
|
||||
inline String Base64Decode(const String& data) { return Base64Decode(data.Begin(), data.End()); }
|
||||
|
||||
class IpAddrInfo {
|
||||
enum { COUNT = 32 };
|
||||
struct Entry {
|
||||
const char *host;
|
||||
const char *port;
|
||||
int status;
|
||||
addrinfo *addr;
|
||||
};
|
||||
static Entry pool[COUNT];
|
||||
|
||||
enum {
|
||||
EMPTY = 0, WORKING, CANCELED, RESOLVED, FAILED
|
||||
};
|
||||
|
||||
String host, port;
|
||||
Entry *entry;
|
||||
Entry exe[1];
|
||||
|
||||
static void EnterPool();
|
||||
static void LeavePool();
|
||||
static rawthread_t rawthread__ Thread(void *ptr);
|
||||
|
||||
void Start();
|
||||
|
||||
public:
|
||||
void Start(const String& host, int port);
|
||||
bool InProgress();
|
||||
bool Execute(const String& host, int port);
|
||||
addrinfo *GetResult();
|
||||
void Clear();
|
||||
|
||||
IpAddrInfo();
|
||||
~IpAddrInfo() { Clear(); }
|
||||
};
|
||||
|
||||
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;
|
||||
bool is_eof;
|
||||
bool is_error;
|
||||
bool is_abort;
|
||||
bool ipv6;
|
||||
|
||||
int timeout;
|
||||
int waitstep;
|
||||
int done;
|
||||
|
||||
int errorcode;
|
||||
String errordesc;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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 RawClose();
|
||||
|
||||
void ReadBuffer();
|
||||
int Get_();
|
||||
int Peek_();
|
||||
|
||||
void Reset();
|
||||
|
||||
void SetSockError(const char *context, const char *errdesc);
|
||||
void SetSockError(const char *context);
|
||||
|
||||
static int GetErrorCode();
|
||||
static bool WouldBlock();
|
||||
|
||||
public:
|
||||
Callback WhenWait;
|
||||
|
||||
static String GetHostName();
|
||||
|
||||
int GetDone() const { return done; }
|
||||
|
||||
static void Init();
|
||||
|
||||
bool IsOpen() const { return socket != INVALID_SOCKET; }
|
||||
bool IsEof() const { return is_eof && ptr == end; }
|
||||
|
||||
bool IsError() const { return is_error; }
|
||||
void ClearError() { is_error = false; errorcode = 0; errordesc.Clear(); }
|
||||
int GetError() const { return errorcode; }
|
||||
String GetErrorDesc() const { return errordesc; }
|
||||
|
||||
void Abort() { is_abort = true; }
|
||||
bool IsAbort() const { return is_abort; }
|
||||
void ClearAbort() { is_abort = false; }
|
||||
|
||||
SOCKET GetSOCKET() const { return socket; }
|
||||
String GetPeerAddr() const;
|
||||
|
||||
void Attach(SOCKET socket);
|
||||
bool Connect(const char *host, int port);
|
||||
bool Connect(IpAddrInfo& info);
|
||||
bool Listen(int port, int listen_count, bool ipv6 = false, bool reuse = true);
|
||||
bool Accept(TcpSocket& listen_socket);
|
||||
void Close();
|
||||
void Shutdown();
|
||||
|
||||
void NoDelay();
|
||||
void Linger(int msecs);
|
||||
void NoLinger() { Linger(Null); }
|
||||
void Reuse(bool reuse = true);
|
||||
|
||||
bool Wait(dword events);
|
||||
bool WaitRead() { return Wait(WAIT_READ); }
|
||||
bool WaitWrite() { return Wait(WAIT_WRITE); }
|
||||
|
||||
int Peek() { return ptr < end ? *ptr : Peek_(); }
|
||||
int Term() { return Peek(); }
|
||||
int Get() { return ptr < end ? *ptr++ : Get_(); }
|
||||
int Get(void *buffer, int len);
|
||||
String Get(int len);
|
||||
int GetAll(void *buffer, int len) { return Get(buffer, len) == len; }
|
||||
String GetAll(int len) { String s = Get(len); return s.GetCount() == len ? s : String::GetVoid(); }
|
||||
String GetLine(int maxlen = 2000000);
|
||||
|
||||
int Put(const char *s, int len);
|
||||
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; }
|
||||
TcpSocket& Blocking() { return Timeout(Null); }
|
||||
|
||||
TcpSocket();
|
||||
~TcpSocket() { Close(); }
|
||||
};
|
||||
|
||||
class SocketWaitEvent {
|
||||
Vector< Tuple2<int, dword> > socket;
|
||||
fd_set read[1], write[1], exception[1];
|
||||
|
||||
public:
|
||||
void Clear() { socket.Clear(); }
|
||||
void Add(SOCKET s, dword events = WAIT_ALL) { socket.Add(MakeTuple((int)s, events)); }
|
||||
void Add(TcpSocket& s, dword events = WAIT_ALL) { Add(s.GetSOCKET(), events); }
|
||||
int Wait(int timeout);
|
||||
dword Get(int i) const;
|
||||
dword operator[](int i) const { return Get(i); }
|
||||
|
||||
SocketWaitEvent();
|
||||
};
|
||||
|
||||
struct HttpHeader {
|
||||
String first_line;
|
||||
VectorMap<String, String> fields;
|
||||
|
||||
String operator[](const char *id) { return fields.Get(id, Null); }
|
||||
|
||||
bool Response(String& protocol, int& code, String& reason);
|
||||
bool Request(String& method, String& uri, String& version);
|
||||
|
||||
void Clear();
|
||||
bool Parse(const String& hdrs);
|
||||
};
|
||||
|
||||
class HttpRequest : public TcpSocket {
|
||||
int phase;
|
||||
String data;
|
||||
int count;
|
||||
|
||||
HttpHeader header;
|
||||
|
||||
String error;
|
||||
String body;
|
||||
|
||||
enum {
|
||||
DEFAULT_HTTP_PORT = 80,
|
||||
};
|
||||
|
||||
enum {
|
||||
METHOD_GET,
|
||||
METHOD_POST,
|
||||
METHOD_HEAD,
|
||||
METHOD_PUT,
|
||||
};
|
||||
|
||||
int max_header_size;
|
||||
int max_content_size;
|
||||
int max_redirects;
|
||||
int max_retries;
|
||||
int timeout;
|
||||
|
||||
String host;
|
||||
int port;
|
||||
String proxy_host;
|
||||
int proxy_port;
|
||||
String proxy_username;
|
||||
String proxy_password;
|
||||
String path;
|
||||
|
||||
int method;
|
||||
String accept;
|
||||
String agent;
|
||||
bool force_digest;
|
||||
bool is_post;
|
||||
bool std_headers;
|
||||
bool hasurlvar;
|
||||
String contenttype;
|
||||
String username;
|
||||
String password;
|
||||
String digest;
|
||||
String request_headers;
|
||||
String postdata;
|
||||
|
||||
String protocol;
|
||||
int status_code;
|
||||
String reason_phrase;
|
||||
|
||||
int start_time;
|
||||
int retry_count;
|
||||
int redirect_count;
|
||||
|
||||
int chunk;
|
||||
|
||||
IpAddrInfo addrinfo;
|
||||
int bodylen;
|
||||
bool gzip;
|
||||
Zlib z;
|
||||
|
||||
void Init();
|
||||
|
||||
void StartPhase(int s);
|
||||
void Start();
|
||||
void Dns();
|
||||
void StartRequest();
|
||||
bool SendingData();
|
||||
bool ReadingHeader();
|
||||
void StartBody();
|
||||
bool ReadingBody();
|
||||
void ReadingChunkHeader();
|
||||
void Finish();
|
||||
|
||||
void HttpError(const char *s);
|
||||
void ContentOut(const void *ptr, dword size);
|
||||
void Out(const void *ptr, dword size);
|
||||
|
||||
String CalculateDigest(const String& authenticate) const;
|
||||
|
||||
public:
|
||||
Callback2<const void *, dword> WhenContent;
|
||||
|
||||
HttpRequest& MaxHeaderSize(int m) { max_header_size = m; return *this; }
|
||||
HttpRequest& MaxContentSize(int m) { max_content_size = m; return *this; }
|
||||
HttpRequest& MaxRedirect(int n) { max_redirects = n; return *this; }
|
||||
HttpRequest& MaxRetries(int n) { max_retries = n; return *this; }
|
||||
HttpRequest& RequestTimeout(int ms) { timeout = ms; return *this; }
|
||||
HttpRequest& ChunkSize(int n) { chunk = n; return *this; }
|
||||
|
||||
HttpRequest& Method(int m) { method = m; return *this; }
|
||||
HttpRequest& GET() { return Method(METHOD_GET); }
|
||||
HttpRequest& POST() { return Method(METHOD_POST); }
|
||||
HttpRequest& HEAD() { return Method(METHOD_HEAD); }
|
||||
HttpRequest& PUT() { return Method(METHOD_PUT); }
|
||||
|
||||
HttpRequest& Host(const String& h) { host = h; return *this; }
|
||||
HttpRequest& Port(int p) { port = p; return *this; }
|
||||
HttpRequest& Path(const String& p) { path = p; return *this; }
|
||||
HttpRequest& User(const String& u, const String& p) { username = u; password = p; return *this; }
|
||||
HttpRequest& Digest() { force_digest = true; return *this; }
|
||||
HttpRequest& Digest(const String& d) { digest = d; return *this; }
|
||||
HttpRequest& Url(const char *url);
|
||||
HttpRequest& UrlVar(const char *id, const String& data);
|
||||
HttpRequest& operator()(const char *id, const String& data) { return UrlVar(id, data); }
|
||||
HttpRequest& PostData(const String& pd) { postdata = pd; return *this; }
|
||||
HttpRequest& PostUData(const String& pd) { return PostData(UrlEncode(pd)); }
|
||||
HttpRequest& Post(const String& data) { POST(); return PostData(data); }
|
||||
HttpRequest& Post(const char *id, const String& data);
|
||||
|
||||
HttpRequest& Headers(const String& h) { request_headers = h; return *this; }
|
||||
HttpRequest& ClearHeaders() { return Headers(Null); }
|
||||
HttpRequest& AddHeaders(const String& h) { request_headers.Cat(h); return *this; }
|
||||
HttpRequest& Header(const char *id, const String& data);
|
||||
|
||||
HttpRequest& StdHeaders(bool sh) { std_headers = sh; return *this; }
|
||||
HttpRequest& NoStdHeaders() { return StdHeaders(false); }
|
||||
HttpRequest& Accept(const String& a) { accept = a; return *this; }
|
||||
HttpRequest& Agent(const String& a) { agent = a; return *this; }
|
||||
HttpRequest& ContentType(const String& a) { contenttype = a; return *this; }
|
||||
|
||||
HttpRequest& Proxy(const String& host, int port) { proxy_host = host; proxy_port = port; return *this; }
|
||||
HttpRequest& Proxy(const char *url);
|
||||
HttpRequest& ProxyAuth(const String& u, const String& p) { proxy_username = u; proxy_password = p; return *this; }
|
||||
|
||||
bool IsSocketError() const { return TcpSocket::IsError(); }
|
||||
bool IsHttpError() const { return !IsNull(error) ; }
|
||||
bool IsError() const { return IsSocketError() || IsHttpError(); }
|
||||
String GetErrorDesc() const { return IsSocketError() ? TcpSocket::GetErrorDesc() : error; }
|
||||
void ClearError() { TcpSocket::ClearError(); error.Clear(); }
|
||||
|
||||
String GetHeader(const char *s) { return header[s]; }
|
||||
String operator[](const char *s) { return GetHeader(s); }
|
||||
String GetRedirectUrl();
|
||||
int GetContentLength();
|
||||
int GetStatusCode() const { return status_code; }
|
||||
String GetReasonPhrase() const { return reason_phrase; }
|
||||
|
||||
String GetContent() const { return body; }
|
||||
String operator~() const { return GetContent(); }
|
||||
operator String() const { return GetContent(); }
|
||||
void ClearContent() { body.Clear(); }
|
||||
|
||||
enum Phase {
|
||||
START, DNS, REQUEST, HEADER, BODY, CHUNK_HEADER, CHUNK_BODY, TRAILER, FINISHED, FAILED
|
||||
};
|
||||
|
||||
bool Do();
|
||||
int GetPhase() const { return phase; }
|
||||
String GetPhaseName() const;
|
||||
bool InProgress() const { return phase != FAILED && phase != FINISHED; }
|
||||
bool IsFailure() const { return phase == FAILED; }
|
||||
bool IsSuccess() const { return phase == FINISHED && status_code >= 200 && status_code < 300; }
|
||||
|
||||
String Execute();
|
||||
|
||||
HttpRequest();
|
||||
HttpRequest(const char *url);
|
||||
|
||||
static void Trace(bool b = true);
|
||||
};
|
||||
String FormatIP(dword _ip);
|
||||
|
||||
String UrlEncode(const String& s);
|
||||
String UrlEncode(const String& s, const char *specials);
|
||||
String UrlDecode(const char *b, const char *e);
|
||||
inline String UrlDecode(const String& s) { return UrlDecode(s.Begin(), s.End() ); }
|
||||
|
||||
String Base64Encode(const char *b, const char *e);
|
||||
inline String Base64Encode(const String& data) { return Base64Encode(data.Begin(), data.End()); }
|
||||
String Base64Decode(const char *b, const char *e);
|
||||
inline String Base64Decode(const String& data) { return Base64Decode(data.Begin(), data.End()); }
|
||||
|
||||
class IpAddrInfo {
|
||||
enum { COUNT = 32 };
|
||||
struct Entry {
|
||||
const char *host;
|
||||
const char *port;
|
||||
int status;
|
||||
addrinfo *addr;
|
||||
};
|
||||
static Entry pool[COUNT];
|
||||
|
||||
enum {
|
||||
EMPTY = 0, WORKING, CANCELED, RESOLVED, FAILED
|
||||
};
|
||||
|
||||
String host, port;
|
||||
Entry *entry;
|
||||
Entry exe[1];
|
||||
|
||||
static void EnterPool();
|
||||
static void LeavePool();
|
||||
static rawthread_t rawthread__ Thread(void *ptr);
|
||||
|
||||
void Start();
|
||||
|
||||
public:
|
||||
void Start(const String& host, int port);
|
||||
bool InProgress();
|
||||
bool Execute(const String& host, int port);
|
||||
addrinfo *GetResult();
|
||||
void Clear();
|
||||
|
||||
IpAddrInfo();
|
||||
~IpAddrInfo() { Clear(); }
|
||||
};
|
||||
|
||||
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;
|
||||
bool is_eof;
|
||||
bool is_error;
|
||||
bool is_abort;
|
||||
bool ipv6;
|
||||
|
||||
int timeout;
|
||||
int waitstep;
|
||||
int done;
|
||||
|
||||
int errorcode;
|
||||
String errordesc;
|
||||
|
||||
struct SSL {
|
||||
virtual bool Start() = 0;
|
||||
virtual bool Wait(dword flags) = 0;
|
||||
virtual int Send(const void *buffer, int maxlen) = 0;
|
||||
virtual int Recv(void *buffer, int maxlen) = 0;
|
||||
virtual void Close() = 0;
|
||||
|
||||
virtual ~SSL() {}
|
||||
};
|
||||
|
||||
One<SSL> ssl;
|
||||
|
||||
struct SSLImp;
|
||||
friend struct SSLImp;
|
||||
|
||||
static SSL *(*CreateSSL)(TcpSocket& socket);
|
||||
static SSL *CreateSSLImp(TcpSocket& socket);
|
||||
|
||||
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 RawClose();
|
||||
|
||||
void ReadBuffer();
|
||||
int Get_();
|
||||
int Peek_();
|
||||
|
||||
void Reset();
|
||||
|
||||
void SetSockError(const char *context, int code, const char *errdesc);
|
||||
void SetSockError(const char *context, const char *errdesc);
|
||||
void SetSockError(const char *context);
|
||||
|
||||
static int GetErrorCode();
|
||||
static bool WouldBlock();
|
||||
|
||||
public:
|
||||
Callback WhenWait;
|
||||
|
||||
static String GetHostName();
|
||||
|
||||
int GetDone() const { return done; }
|
||||
|
||||
static void Init();
|
||||
|
||||
bool IsOpen() const { return socket != INVALID_SOCKET; }
|
||||
bool IsEof() const { return is_eof && ptr == end; }
|
||||
|
||||
bool IsError() const { return is_error; }
|
||||
void ClearError() { is_error = false; errorcode = 0; errordesc.Clear(); }
|
||||
int GetError() const { return errorcode; }
|
||||
String GetErrorDesc() const { return errordesc; }
|
||||
|
||||
void Abort() { is_abort = true; }
|
||||
bool IsAbort() const { return is_abort; }
|
||||
void ClearAbort() { is_abort = false; }
|
||||
|
||||
SOCKET GetSOCKET() const { return socket; }
|
||||
String GetPeerAddr() const;
|
||||
|
||||
void Attach(SOCKET socket);
|
||||
bool Connect(const char *host, int port);
|
||||
bool Connect(IpAddrInfo& info);
|
||||
bool Listen(int port, int listen_count, bool ipv6 = false, bool reuse = true);
|
||||
bool Accept(TcpSocket& listen_socket);
|
||||
void Close();
|
||||
void Shutdown();
|
||||
|
||||
void NoDelay();
|
||||
void Linger(int msecs);
|
||||
void NoLinger() { Linger(Null); }
|
||||
void Reuse(bool reuse = true);
|
||||
|
||||
bool Wait(dword events);
|
||||
bool WaitRead() { return Wait(WAIT_READ); }
|
||||
bool WaitWrite() { return Wait(WAIT_WRITE); }
|
||||
|
||||
int Peek() { return ptr < end ? *ptr : Peek_(); }
|
||||
int Term() { return Peek(); }
|
||||
int Get() { return ptr < end ? *ptr++ : Get_(); }
|
||||
int Get(void *buffer, int len);
|
||||
String Get(int len);
|
||||
int GetAll(void *buffer, int len) { return Get(buffer, len) == len; }
|
||||
String GetAll(int len) { String s = Get(len); return s.GetCount() == len ? s : String::GetVoid(); }
|
||||
String GetLine(int maxlen = 2000000);
|
||||
|
||||
int Put(const char *s, int len);
|
||||
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();
|
||||
bool IsSSL() const { return ssl; }
|
||||
|
||||
TcpSocket& Timeout(int ms) { timeout = ms; return *this; }
|
||||
int GetTimeout() const { return timeout; }
|
||||
TcpSocket& Blocking() { return Timeout(Null); }
|
||||
|
||||
TcpSocket();
|
||||
~TcpSocket() { Close(); }
|
||||
};
|
||||
|
||||
class SocketWaitEvent {
|
||||
Vector< Tuple2<int, dword> > socket;
|
||||
fd_set read[1], write[1], exception[1];
|
||||
|
||||
public:
|
||||
void Clear() { socket.Clear(); }
|
||||
void Add(SOCKET s, dword events = WAIT_ALL) { socket.Add(MakeTuple((int)s, events)); }
|
||||
void Add(TcpSocket& s, dword events = WAIT_ALL) { Add(s.GetSOCKET(), events); }
|
||||
int Wait(int timeout);
|
||||
dword Get(int i) const;
|
||||
dword operator[](int i) const { return Get(i); }
|
||||
|
||||
SocketWaitEvent();
|
||||
};
|
||||
|
||||
struct HttpHeader {
|
||||
String first_line;
|
||||
VectorMap<String, String> fields;
|
||||
|
||||
String operator[](const char *id) { return fields.Get(id, Null); }
|
||||
|
||||
bool Response(String& protocol, int& code, String& reason);
|
||||
bool Request(String& method, String& uri, String& version);
|
||||
|
||||
void Clear();
|
||||
bool Parse(const String& hdrs);
|
||||
};
|
||||
|
||||
class HttpRequest : public TcpSocket {
|
||||
int phase;
|
||||
String data;
|
||||
int count;
|
||||
|
||||
HttpHeader header;
|
||||
|
||||
String error;
|
||||
String body;
|
||||
|
||||
enum {
|
||||
DEFAULT_HTTP_PORT = 80,
|
||||
};
|
||||
|
||||
enum {
|
||||
METHOD_GET,
|
||||
METHOD_POST,
|
||||
METHOD_HEAD,
|
||||
METHOD_PUT,
|
||||
};
|
||||
|
||||
int max_header_size;
|
||||
int max_content_size;
|
||||
int max_redirects;
|
||||
int max_retries;
|
||||
int timeout;
|
||||
|
||||
String host;
|
||||
int port;
|
||||
String proxy_host;
|
||||
int proxy_port;
|
||||
String proxy_username;
|
||||
String proxy_password;
|
||||
String path;
|
||||
bool ssl;
|
||||
|
||||
int method;
|
||||
String accept;
|
||||
String agent;
|
||||
bool force_digest;
|
||||
bool is_post;
|
||||
bool std_headers;
|
||||
bool hasurlvar;
|
||||
String contenttype;
|
||||
String username;
|
||||
String password;
|
||||
String digest;
|
||||
String request_headers;
|
||||
String postdata;
|
||||
|
||||
String protocol;
|
||||
int status_code;
|
||||
String reason_phrase;
|
||||
|
||||
int start_time;
|
||||
int retry_count;
|
||||
int redirect_count;
|
||||
|
||||
int chunk;
|
||||
|
||||
IpAddrInfo addrinfo;
|
||||
int bodylen;
|
||||
bool gzip;
|
||||
Zlib z;
|
||||
|
||||
void Init();
|
||||
|
||||
void StartPhase(int s);
|
||||
void Start();
|
||||
void Dns();
|
||||
void StartRequest();
|
||||
bool SendingData();
|
||||
bool ReadingHeader();
|
||||
void StartBody();
|
||||
bool ReadingBody();
|
||||
void ReadingChunkHeader();
|
||||
void Finish();
|
||||
|
||||
void HttpError(const char *s);
|
||||
void ContentOut(const void *ptr, dword size);
|
||||
void Out(const void *ptr, dword size);
|
||||
|
||||
String CalculateDigest(const String& authenticate) const;
|
||||
|
||||
public:
|
||||
Callback2<const void *, dword> WhenContent;
|
||||
|
||||
HttpRequest& MaxHeaderSize(int m) { max_header_size = m; return *this; }
|
||||
HttpRequest& MaxContentSize(int m) { max_content_size = m; return *this; }
|
||||
HttpRequest& MaxRedirect(int n) { max_redirects = n; return *this; }
|
||||
HttpRequest& MaxRetries(int n) { max_retries = n; return *this; }
|
||||
HttpRequest& RequestTimeout(int ms) { timeout = ms; return *this; }
|
||||
HttpRequest& ChunkSize(int n) { chunk = n; return *this; }
|
||||
|
||||
HttpRequest& Method(int m) { method = m; return *this; }
|
||||
HttpRequest& GET() { return Method(METHOD_GET); }
|
||||
HttpRequest& POST() { return Method(METHOD_POST); }
|
||||
HttpRequest& HEAD() { return Method(METHOD_HEAD); }
|
||||
HttpRequest& PUT() { return Method(METHOD_PUT); }
|
||||
|
||||
HttpRequest& Host(const String& h) { host = h; return *this; }
|
||||
HttpRequest& Port(int p) { port = p; return *this; }
|
||||
HttpRequest& SSL(bool b = true) { ssl = b; return *this; }
|
||||
HttpRequest& Path(const String& p) { path = p; return *this; }
|
||||
HttpRequest& User(const String& u, const String& p) { username = u; password = p; return *this; }
|
||||
HttpRequest& Digest() { force_digest = true; return *this; }
|
||||
HttpRequest& Digest(const String& d) { digest = d; return *this; }
|
||||
HttpRequest& Url(const char *url);
|
||||
HttpRequest& UrlVar(const char *id, const String& data);
|
||||
HttpRequest& operator()(const char *id, const String& data) { return UrlVar(id, data); }
|
||||
HttpRequest& PostData(const String& pd) { postdata = pd; return *this; }
|
||||
HttpRequest& PostUData(const String& pd) { return PostData(UrlEncode(pd)); }
|
||||
HttpRequest& Post(const String& data) { POST(); return PostData(data); }
|
||||
HttpRequest& Post(const char *id, const String& data);
|
||||
|
||||
HttpRequest& Headers(const String& h) { request_headers = h; return *this; }
|
||||
HttpRequest& ClearHeaders() { return Headers(Null); }
|
||||
HttpRequest& AddHeaders(const String& h) { request_headers.Cat(h); return *this; }
|
||||
HttpRequest& Header(const char *id, const String& data);
|
||||
|
||||
HttpRequest& StdHeaders(bool sh) { std_headers = sh; return *this; }
|
||||
HttpRequest& NoStdHeaders() { return StdHeaders(false); }
|
||||
HttpRequest& Accept(const String& a) { accept = a; return *this; }
|
||||
HttpRequest& Agent(const String& a) { agent = a; return *this; }
|
||||
HttpRequest& ContentType(const String& a) { contenttype = a; return *this; }
|
||||
|
||||
HttpRequest& Proxy(const String& host, int port) { proxy_host = host; proxy_port = port; return *this; }
|
||||
HttpRequest& Proxy(const char *url);
|
||||
HttpRequest& ProxyAuth(const String& u, const String& p) { proxy_username = u; proxy_password = p; return *this; }
|
||||
|
||||
bool IsSocketError() const { return TcpSocket::IsError(); }
|
||||
bool IsHttpError() const { return !IsNull(error) ; }
|
||||
bool IsError() const { return IsSocketError() || IsHttpError(); }
|
||||
String GetErrorDesc() const { return IsSocketError() ? TcpSocket::GetErrorDesc() : error; }
|
||||
void ClearError() { TcpSocket::ClearError(); error.Clear(); }
|
||||
|
||||
String GetHeader(const char *s) { return header[s]; }
|
||||
String operator[](const char *s) { return GetHeader(s); }
|
||||
String GetRedirectUrl();
|
||||
int GetContentLength();
|
||||
int GetStatusCode() const { return status_code; }
|
||||
String GetReasonPhrase() const { return reason_phrase; }
|
||||
|
||||
String GetContent() const { return body; }
|
||||
String operator~() const { return GetContent(); }
|
||||
operator String() const { return GetContent(); }
|
||||
void ClearContent() { body.Clear(); }
|
||||
|
||||
enum Phase {
|
||||
START, DNS, REQUEST, HEADER, BODY, CHUNK_HEADER, CHUNK_BODY, TRAILER, FINISHED, FAILED
|
||||
};
|
||||
|
||||
bool Do();
|
||||
int GetPhase() const { return phase; }
|
||||
String GetPhaseName() const;
|
||||
bool InProgress() const { return phase != FAILED && phase != FINISHED; }
|
||||
bool IsFailure() const { return phase == FAILED; }
|
||||
bool IsSuccess() const { return phase == FINISHED && status_code >= 200 && status_code < 300; }
|
||||
|
||||
String Execute();
|
||||
|
||||
HttpRequest();
|
||||
HttpRequest(const char *url);
|
||||
|
||||
static void Trace(bool b = true);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -410,6 +410,14 @@ Returns the number of bytes actually read.&]
|
|||
result as String.&]
|
||||
[s3; &]
|
||||
[s4;%- &]
|
||||
[s5;:Stream`:`:GetAll`(void`*`,int`):%- [@(0.0.255) bool]_[* GetAll]([@(0.0.255) void]_`*[*@3 d
|
||||
ata], [@(0.0.255) int]_[*@3 size])&]
|
||||
[s2; Reads [%-*@3 size] bytes from the stream to memory at [%-*@3 data].
|
||||
If there is not enough data in the stream, LoadError is invoked
|
||||
(that in turn might throw an exception). Returns true if required
|
||||
number of bytes was read.&]
|
||||
[s3; &]
|
||||
[s4;%- &]
|
||||
[s5;:Stream`:`:LoadThrowing`(`):%- [@(0.0.255) void]_[* LoadThrowing]()&]
|
||||
[s2; Sets stream into the mode that throws LoadingError exception
|
||||
when LoadError is invoked. This mode is typical for serialization
|
||||
|
|
@ -422,16 +430,6 @@ the LoadThrowing mode (by LoadThrowing() method), LoadingError
|
|||
exception is thrown.&]
|
||||
[s3; &]
|
||||
[s4;%- &]
|
||||
[s5;:Stream`:`:GetAll`(void`*`,dword`):%- [@(0.0.255) bool]_[* GetAll]([@(0.0.255) void]_`*
|
||||
[*@3 data], [_^dword^ dword]_[*@3 size])&]
|
||||
[s2; Reads a block of raw binary data from the stream. If there is
|
||||
not enough data in the stream, LoadError is invoked (that in
|
||||
turn might throw an exception).&]
|
||||
[s7; [%-*C@3 data]-|Pointer to buffer to receive the data.&]
|
||||
[s7; [%-*C@3 size]-|Number of bytes to read.&]
|
||||
[s7; [*/ Return value]-|true if required number of bytes was read.&]
|
||||
[s3; &]
|
||||
[s4;%- &]
|
||||
[s5;:Stream`:`:Get8`(`):%- [@(0.0.255) int]_[* Get8]()&]
|
||||
[s2; Reads a single byte from the stream. If there is not enough
|
||||
data in the stream, LoadError is invoked (that in turn might
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue