Core/SSL: SSL support for TcpSocket and HttpRequest finally working...

git-svn-id: svn://ultimatepp.org/upp/trunk@4783 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
cxl 2012-04-14 17:07:15 +00:00
parent 295811f28e
commit 8aace300c4
9 changed files with 2119 additions and 2212 deletions

View file

@ -319,7 +319,7 @@ NAMESPACE_UPP
#include "LocalProcess.h"
#include "Web.h"
#include "Inet.h"
#include "Win32Util.h"

View file

@ -146,8 +146,8 @@ file
MD5.cpp,
SHA1.cpp,
Web readonly separator,
Web.h,
WebUtil.cpp,
Inet.h,
InetUtil.cpp,
Socket.cpp,
Http.cpp,
"Runtime linking" readonly separator,

File diff suppressed because it is too large Load diff

View file

@ -1,376 +1,407 @@
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,
DEFAULT_HTTPS_PORT = 443
};
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 CopyCookies();
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& Cookie(const String& cookie) { return Header("Cookie", cookie); }
HttpRequest& StdHeaders(bool sh) { std_headers = sh; return *this; }
HttpRequest& NoStdHeaders() { return StdHeaders(false); }
HttpRequest& Accept(const String& a) { accept = a; return *this; }
HttpRequest& UserAgent(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 };
struct SSLInfo {
String cipher;
bool cert_avail;
bool cert_verified; // Peer verification not yet working - this is always false
String cert_subject;
String cert_issuer;
Date cert_notbefore;
Date cert_notafter;
int cert_version;
String cert_serial;
};
class TcpSocket {
enum { BUFFERSIZE = 512 };
enum { NONE, CONNECT, ACCEPT, SSL_CONNECTED };
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 dword Handshake() = 0;
virtual ~SSL() {}
};
One<SSL> ssl;
One<SSLInfo> sslinfo;
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 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; }
bool SSLHandshake();
const SSLInfo *GetSSLInfo() const { return ~sslinfo; }
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,
DEFAULT_HTTPS_PORT = 443
};
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 ssl_proxy_host;
int ssl_proxy_port;
String ssl_proxy_username;
String ssl_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 StartConnect();
void ProcessSSLProxyResponse();
void AfterConnect();
void StartRequest();
bool SendingData();
bool ReadingHeader();
void StartBody();
bool ReadingBody();
void ReadingChunkHeader();
void Finish();
void CopyCookies();
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& Cookie(const String& cookie) { return Header("Cookie", cookie); }
HttpRequest& StdHeaders(bool sh) { std_headers = sh; return *this; }
HttpRequest& NoStdHeaders() { return StdHeaders(false); }
HttpRequest& Accept(const String& a) { accept = a; return *this; }
HttpRequest& UserAgent(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 *p);
HttpRequest& ProxyAuth(const String& u, const String& p) { proxy_username = u; proxy_password = p; return *this; }
HttpRequest& SSLProxy(const String& host, int port) { ssl_proxy_host = host; ssl_proxy_port = port; return *this; }
HttpRequest& SSLProxy(const char *p);
HttpRequest& SSLProxyAuth(const String& u, const String& p) { ssl_proxy_username = u; ssl_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,
SSLPROXYREQUEST, SSLPROXYRESPONSE, SSLHANDSHAKE,
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);
};

View file

@ -71,6 +71,8 @@ EXITBLOCK
ERR_free_strings();
}
#ifdef _MULTITHREADED
static thread__ bool sThreadInit;
static thread__ void (*sPrevExit)();
@ -89,4 +91,10 @@ void SslInitThread()
sPrevExit = Thread::AtExit(sslExitThread);
}
#else
void SslInitThread() {}
#endif
END_UPP_NAMESPACE

View file

@ -1,21 +1,24 @@
#include "SSL.h"
#define LLOG(x) DLOG(x)
#define LLOG(x) // DLOG(x)
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();
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();
virtual dword Handshake();
TcpSocket& socket;
SslContext context;
::SSL *ssl;
SslCertificate cert;
int GetErrorCode(int res);
String GetErrorText(int code);
void SetSSLError(const char *context);
void SetSSLResError(const char *context, int res);
bool IsAgain(int res) const;
@ -53,14 +56,14 @@ void TcpSocket::SSLImp::SetSSLError(const char *context)
const char *TcpSocketErrorDesc(int code);
void TcpSocket::SSLImp::SetSSLResError(const char *context, int res)
int TcpSocket::SSLImp::GetErrorCode(int res)
{
return SSL_get_error(ssl, res);
}
String TcpSocket::SSLImp::GetErrorText(int code)
{
int code = SSL_get_error(ssl, res);
String out;
if(code == SSL_ERROR_SYSCALL) {
socket.SetSockError(context);
return;
}
switch(code) {
#define SSLERR(c) case c: out = #c; break;
SSLERR(SSL_ERROR_NONE)
@ -76,7 +79,17 @@ void TcpSocket::SSLImp::SetSSLResError(const char *context, int res)
#endif
default: out = "unknown code"; break;
}
socket.SetSockError(context, code, out);
return out;
}
void TcpSocket::SSLImp::SetSSLResError(const char *context, int res)
{
int code = GetErrorCode(res);
if(code == SSL_ERROR_SYSCALL) {
socket.SetSockError(context);
return;
}
socket.SetSockError(context, code, GetErrorText(code));
}
bool TcpSocket::SSLImp::IsAgain(int res) const
@ -88,7 +101,7 @@ bool TcpSocket::SSLImp::IsAgain(int res) const
res == SSL_ERROR_WANT_ACCEPT;
}
bool TcpSocket::SSLImp::Start() // TIMEOUTS!!!
bool TcpSocket::SSLImp::Start()
{
LLOG("SSL Start");
@ -96,16 +109,7 @@ bool TcpSocket::SSLImp::Start() // TIMEOUTS!!!
SetSSLError("Start: SSL context.");
return false;
}
/*
while(!socket.Wait(WAIT_WRITE)) {
DLOG("Waiting for connect");
Sleep(1);
}
DLOG("Connected");
SSL_CTX *context = SSL_CTX_new (SSLv3_client_method());
*/
// context.VerifyPeer();
if(!(ssl = SSL_new(context))) {
SetSSLError("Start: SSL_new");
return false;
@ -114,32 +118,43 @@ bool TcpSocket::SSLImp::Start() // TIMEOUTS!!!
SetSSLError("Start: SSL_set_fd");
return false;
}
return Handshake();
}
dword TcpSocket::SSLImp::Handshake()
{
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);
for(;;) {
res = SSL_connect(ssl);
if(res > 0)
break;
DDUMP(res);
DDUMP(IsAgain(res));
if(res <= 0 && !IsAgain(res)) {
SetSSLResError("Start: SSL_connect", res);
return false;
}
Sleep(100);
}
if(socket.mode == ACCEPT)
res = SSL_accept(ssl);
else
if(socket.mode == CONNECT)
res = SSL_connect(ssl);
else
return 0;
if(res <= 0) {
int code = GetErrorCode(res);
if(code == SSL_ERROR_WANT_READ)
return WAIT_READ;
if(code == SSL_ERROR_WANT_WRITE)
return WAIT_WRITE;
SetSSLResError("SSL handshake", res);
return 0;
}
socket.mode = SSL_CONNECTED;
cert.Set(SSL_get_peer_certificate(ssl));
return true;
SSLInfo& f = socket.sslinfo.Create();
f.cipher = SSL_get_cipher(ssl);
if(!cert.IsEmpty()) {
f.cert_avail = true;
f.cert_subject = cert.GetSubjectName();
f.cert_issuer = cert.GetIssuerName();
f.cert_serial = cert.GetSerialNumber();
f.cert_notbefore = cert.GetNotBefore();
f.cert_notafter = cert.GetNotAfter();
f.cert_version = cert.GetVersion();
f.cert_verified = SSL_get_verify_result(ssl) == X509_V_OK;
}
return 0;
}
bool TcpSocket::SSLImp::Wait(dword flags)
@ -156,6 +171,9 @@ int TcpSocket::SSLImp::Send(const void *buffer, int maxlen)
int res = SSL_write(ssl, (const char *)buffer, maxlen);
if(res > 0)
return res;
if(res == 0)
socket.is_eof = true;
else
if(!IsAgain(res))
SetSSLResError("SSL_write", res);
return 0;
@ -167,9 +185,10 @@ int TcpSocket::SSLImp::Recv(void *buffer, int maxlen)
int res = SSL_read(ssl, (char *)buffer, maxlen);
if(res > 0)
return res;
socket.is_eof = true;
if(res && !IsAgain(res))
if(res == 0)
socket.is_eof = true;
else
if(!IsAgain(res))
SetSSLResError("SSL_read", res);
return 0;
}
@ -183,255 +202,4 @@ void TcpSocket::SSLImp::Close()
ssl = NULL;
}
#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

View file

@ -1,128 +1,128 @@
#ifndef flagNOSSL
#include "WebSSL.h"
NAMESPACE_UPP
extern bool HttpClient_Trace__;
#ifdef _DEBUG
#define LLOG(x) if(HttpClient_Trace__) RLOG(x); else;
#else
#define LLOG(x)
#endif
HttpsClient::HttpsClient()
{
secure = true;
}
bool HttpsClient::ProxyConnect()
{
if(use_proxy) {
int start_time = msecs();
int end_time = msecs() + timeout_msecs;
while(!socket.PeekWrite(1000)) {
int time = msecs();
if(time >= end_time) {
error = NFormat(t_("%s:%d: connecting to host timed out"), socket_host, socket_port);
Close();
return false;
}
}
String host_port = host;
if(port)
host_port << ':' << port;
else
host_port << ":443";
String request;
request << "CONNECT " << host_port << " HTTP/1.1\r\n"
<< "Host: " << host_port << "\r\n";
if(!IsNull(proxy_username))
request << "Proxy-Authorization: Basic "
<< Base64Encode(proxy_username + ':' + proxy_password) << "\r\n";
request << "\r\n";
LLOG(request);
int written = 0;
while(msecs() < end_time) {
int nwrite = socket.WriteWait(request.GetIter(written), min(request.GetLength() - written, 1000), 1000);
if(socket.IsError()) {
error = Socket::GetErrorText();
Close();
return false;
}
if((written += nwrite) >= request.GetLength())
break;
}
if(written < request.GetLength()) {
error = NFormat(t_("%s:%d: timed out sending request to server"), host, port);
Close();
return false;
}
String line = ReadUntilProgress('\n', start_time, end_time, false);
LLOG("P< " << line);
if(socket.IsError()) {
error = Socket::GetErrorText();
Close();
return false;
}
if(!line.StartsWith("HTTP") || line.Find(" 2") < 0) {
error = "Invalid proxy reply: " + line;
Close();
return false;
}
while(line.GetCount()) {
line = ReadUntilProgress('\n', start_time, end_time, false);
if(*line.Last() == '\r')
line.Trim(line.GetCount() - 1);
LLOG("P< " << line << " len " << line.GetCount());
if(socket.IsError()) {
error = Socket::GetErrorText();
Close();
return false;
}
}
use_proxy = false;
while(!socket.PeekWrite(1000)) {
int time = msecs();
if(time >= end_time) {
error = NFormat(t_("%s:%d: connecting to host timed out"), socket_host, socket_port);
Close();
return false;
}
}
}
return true;
}
bool HttpsClient::IsSecure()
{
return secure;
}
bool HttpsClient::CreateClientSocket()
{
if(!secure)
return HttpClient::CreateClientSocket();
if(!ssl_context) {
ssl_context = new SSLContext;
if(!ssl_context->Create(const_cast<SSL_METHOD *>(SSLv3_client_method()))) {
error = t_("Error creating SSL context.");
return false;
}
}
if(!SSLClientSocketUnsecured(socket, *ssl_context, socket_host,
socket_port ? socket_port : DEFAULT_HTTPS_PORT, true, NULL, 0, false)) {
error = Socket::GetErrorText();
return false;
}
socket.Linger(0);
if(!ProxyConnect())
return false;
SSLSecureSocket(socket);
return true;
}
END_UPP_NAMESPACE
#endif
#ifndef flagNOSSL
#include "WebSSL.h"
NAMESPACE_UPP
extern bool HttpClient_Trace__;
#ifdef _DEBUG
#define LLOG(x) if(HttpClient_Trace__) RLOG(x); else;
#else
#define LLOG(x)
#endif
HttpsClient::HttpsClient()
{
secure = true;
}
bool HttpsClient::ProxyConnect()
{
if(use_proxy) {
int start_time = msecs();
int end_time = msecs() + timeout_msecs;
while(!socket.PeekWrite(1000)) {
int time = msecs();
if(time >= end_time) {
error = NFormat(t_("%s:%d: connecting to host timed out"), socket_host, socket_port);
Close();
return false;
}
}
String host_port = host;
if(port)
host_port << ':' << port;
else
host_port << ":443";
String request;
request << "CONNECT " << host_port << " HTTP/1.1\r\n"
<< "Host: " << host_port << "\r\n";
if(!IsNull(proxy_username))
request << "Proxy-Authorization: Basic "
<< Base64Encode(proxy_username + ':' + proxy_password) << "\r\n";
request << "\r\n";
LLOG(request);
int written = 0;
while(msecs() < end_time) {
int nwrite = socket.WriteWait(request.GetIter(written), min(request.GetLength() - written, 1000), 1000);
if(socket.IsError()) {
error = Socket::GetErrorText();
Close();
return false;
}
if((written += nwrite) >= request.GetLength())
break;
}
if(written < request.GetLength()) {
error = NFormat(t_("%s:%d: timed out sending request to server"), host, port);
Close();
return false;
}
String line = ReadUntilProgress('\n', start_time, end_time, false);
LLOG("P< " << line);
if(socket.IsError()) {
error = Socket::GetErrorText();
Close();
return false;
}
if(!line.StartsWith("HTTP") || line.Find(" 2") < 0) {
error = "Invalid proxy reply: " + line;
Close();
return false;
}
while(line.GetCount()) {
line = ReadUntilProgress('\n', start_time, end_time, false);
if(*line.Last() == '\r')
line.Trim(line.GetCount() - 1);
LLOG("P< " << line << " len " << line.GetCount());
if(socket.IsError()) {
error = Socket::GetErrorText();
Close();
return false;
}
}
use_proxy = false;
while(!socket.PeekWrite(1000)) {
int time = msecs();
if(time >= end_time) {
error = NFormat(t_("%s:%d: connecting to host timed out"), socket_host, socket_port);
Close();
return false;
}
}
}
return true;
}
bool HttpsClient::IsSecure()
{
return secure;
}
bool HttpsClient::CreateClientSocket()
{
if(!secure)
return HttpClient::CreateClientSocket();
if(!ssl_context) {
ssl_context = new SSLContext;
if(!ssl_context->Create(const_cast<SSL_METHOD *>(SSLv3_client_method()))) {
error = t_("Error creating SSL context.");
return false;
}
}
if(!SSLClientSocketUnsecured(socket, *ssl_context, socket_host,
socket_port ? socket_port : DEFAULT_HTTPS_PORT, true, NULL, 0, false)) {
error = Socket::GetErrorText();
return false;
}
socket.Linger(0);
if(!ProxyConnect())
return false;
SSLSecureSocket(socket);
return true;
}
END_UPP_NAMESPACE
#endif