mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-08-24 22:02:44 -06:00
*Core/SSL: Fixed handshake issue in Win32
git-svn-id: svn://ultimatepp.org/upp/trunk@4960 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
parent
fb364d3b13
commit
3dbd0cc741
4 changed files with 228 additions and 215 deletions
|
|
@ -23,7 +23,7 @@ void HttpRequest::Init()
|
|||
ssl_proxy_port = 0;
|
||||
max_header_size = 1000000;
|
||||
max_content_size = 10000000;
|
||||
max_redirects = 5;
|
||||
max_redirects = 10;
|
||||
max_retries = 3;
|
||||
force_digest = false;
|
||||
std_headers = true;
|
||||
|
|
@ -323,6 +323,7 @@ bool HttpRequest::Do()
|
|||
|
||||
void HttpRequest::Start()
|
||||
{
|
||||
LLOG("HTTP START");
|
||||
Close();
|
||||
ClearError();
|
||||
gzip = false;
|
||||
|
|
@ -371,6 +372,7 @@ void HttpRequest::Dns()
|
|||
|
||||
void HttpRequest::StartConnect()
|
||||
{
|
||||
LLOG("HTTP StartConnect");
|
||||
if(!Connect(addrinfo))
|
||||
return;
|
||||
if(ssl && ssl_proxy_host.GetCount()) {
|
||||
|
|
@ -408,6 +410,7 @@ void HttpRequest::ProcessSSLProxyResponse()
|
|||
|
||||
void HttpRequest::AfterConnect()
|
||||
{
|
||||
LLOG("HTTP AfterConnect");
|
||||
if(ssl && !StartSSL())
|
||||
return;
|
||||
if(ssl)
|
||||
|
|
@ -437,8 +440,11 @@ void HttpRequest::StartRequest()
|
|||
url << "http://" << host_port << Nvl(path, "/");
|
||||
if(!IsNull(proxy_host) && !ssl)
|
||||
data << url;
|
||||
else
|
||||
data << Nvl(path, "/");
|
||||
else {
|
||||
if(*path != '/')
|
||||
data << '/';
|
||||
data << path;
|
||||
}
|
||||
data << " HTTP/1.1\r\n";
|
||||
if(std_headers) {
|
||||
data << "URL: " << url << "\r\n"
|
||||
|
|
@ -665,7 +671,7 @@ void HttpRequest::Finish()
|
|||
if(status_code >= 300 && status_code < 400) {
|
||||
String url = GetRedirectUrl();
|
||||
if(url.GetCount() && redirect_count++ < max_redirects) {
|
||||
LLOG("HTTP redirect " << url);
|
||||
LLOG("--- HTTP redirect " << url);
|
||||
Url(url);
|
||||
CopyCookies();
|
||||
Start();
|
||||
|
|
@ -678,6 +684,7 @@ void HttpRequest::Finish()
|
|||
|
||||
String HttpRequest::Execute()
|
||||
{
|
||||
New();
|
||||
while(Do())
|
||||
LLOG("HTTP Execute: " << GetPhaseName());
|
||||
return IsSuccess() ? GetContent() : String::GetVoid();
|
||||
|
|
|
|||
|
|
@ -1,206 +1,212 @@
|
|||
#include "SSL.h"
|
||||
|
||||
#define LLOG(x) // DLOG(x)
|
||||
|
||||
NAMESPACE_UPP
|
||||
|
||||
struct TcpSocket::SSLImp : TcpSocket::SSL {
|
||||
virtual bool Start();
|
||||
virtual bool Wait(dword flags, int end_time);
|
||||
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;
|
||||
|
||||
SSLImp(TcpSocket& socket) : socket(socket) { ssl = NULL; }
|
||||
~SSLImp();
|
||||
};
|
||||
|
||||
TcpSocket::SSL *TcpSocket::CreateSSLImp(TcpSocket& socket)
|
||||
{
|
||||
return new TcpSocket::SSLImp(socket);
|
||||
}
|
||||
|
||||
void InitCreateSSL()
|
||||
{
|
||||
TcpSocket::CreateSSL = TcpSocket::CreateSSLImp;
|
||||
}
|
||||
|
||||
INITBLOCK {
|
||||
InitCreateSSL();
|
||||
}
|
||||
|
||||
TcpSocket::SSLImp::~SSLImp()
|
||||
{
|
||||
if(ssl)
|
||||
SSL_free(ssl);
|
||||
}
|
||||
|
||||
void TcpSocket::SSLImp::SetSSLError(const char *context)
|
||||
{
|
||||
int code;
|
||||
String text = SslGetLastError(code);
|
||||
socket.SetSockError(context, code, text);
|
||||
}
|
||||
|
||||
const char *TcpSocketErrorDesc(int code);
|
||||
|
||||
int TcpSocket::SSLImp::GetErrorCode(int res)
|
||||
{
|
||||
return SSL_get_error(ssl, res);
|
||||
}
|
||||
|
||||
String TcpSocket::SSLImp::GetErrorText(int code)
|
||||
{
|
||||
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;
|
||||
}
|
||||
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
|
||||
{
|
||||
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()
|
||||
{
|
||||
LLOG("SSL Start");
|
||||
|
||||
if(!context.Create(const_cast<SSL_METHOD *>(SSLv3_client_method()))) {
|
||||
SetSSLError("Start: SSL context.");
|
||||
return false;
|
||||
}
|
||||
if(socket.cert.GetCount())
|
||||
context.UseCertificate(socket.cert, socket.pkey, socket.asn1);
|
||||
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;
|
||||
}
|
||||
return Handshake();
|
||||
}
|
||||
|
||||
dword TcpSocket::SSLImp::Handshake()
|
||||
{
|
||||
int res;
|
||||
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));
|
||||
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, int end_time)
|
||||
{
|
||||
LLOG("SSL Wait");
|
||||
if((flags & WAIT_READ) && SSL_pending(ssl) > 0)
|
||||
return true;
|
||||
return socket.RawWait(flags, end_time);
|
||||
}
|
||||
|
||||
int TcpSocket::SSLImp::Send(const void *buffer, int maxlen)
|
||||
{
|
||||
LLOG("SSL Send " << 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;
|
||||
}
|
||||
|
||||
int TcpSocket::SSLImp::Recv(void *buffer, int maxlen)
|
||||
{
|
||||
LLOG("SSL Recv " << maxlen);
|
||||
int res = SSL_read(ssl, (char *)buffer, maxlen);
|
||||
if(res > 0)
|
||||
return res;
|
||||
if(res == 0)
|
||||
socket.is_eof = true;
|
||||
else
|
||||
if(!IsAgain(res))
|
||||
SetSSLResError("SSL_read", res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TcpSocket::SSLImp::Close()
|
||||
{
|
||||
LLOG("SSL Close");
|
||||
SSL_shutdown(ssl);
|
||||
socket.RawClose();
|
||||
SSL_free(ssl);
|
||||
ssl = NULL;
|
||||
}
|
||||
|
||||
END_UPP_NAMESPACE
|
||||
#include "SSL.h"
|
||||
|
||||
#define LLOG(x) // DLOG(x)
|
||||
|
||||
NAMESPACE_UPP
|
||||
|
||||
struct TcpSocket::SSLImp : TcpSocket::SSL {
|
||||
virtual bool Start();
|
||||
virtual bool Wait(dword flags, int end_time);
|
||||
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;
|
||||
|
||||
SSLImp(TcpSocket& socket) : socket(socket) { ssl = NULL; LLOG("SSLImp(" << socket.GetSOCKET() << ")"); }
|
||||
~SSLImp();
|
||||
};
|
||||
|
||||
TcpSocket::SSL *TcpSocket::CreateSSLImp(TcpSocket& socket)
|
||||
{
|
||||
return new TcpSocket::SSLImp(socket);
|
||||
}
|
||||
|
||||
void InitCreateSSL()
|
||||
{
|
||||
TcpSocket::CreateSSL = TcpSocket::CreateSSLImp;
|
||||
}
|
||||
|
||||
INITBLOCK {
|
||||
InitCreateSSL();
|
||||
}
|
||||
|
||||
TcpSocket::SSLImp::~SSLImp()
|
||||
{
|
||||
if(ssl)
|
||||
SSL_free(ssl);
|
||||
}
|
||||
|
||||
void TcpSocket::SSLImp::SetSSLError(const char *context)
|
||||
{
|
||||
int code;
|
||||
String text = SslGetLastError(code);
|
||||
socket.SetSockError(context, code, text);
|
||||
}
|
||||
|
||||
const char *TcpSocketErrorDesc(int code);
|
||||
|
||||
int TcpSocket::SSLImp::GetErrorCode(int res)
|
||||
{
|
||||
return SSL_get_error(ssl, res);
|
||||
}
|
||||
|
||||
String TcpSocket::SSLImp::GetErrorText(int code)
|
||||
{
|
||||
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;
|
||||
}
|
||||
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
|
||||
{
|
||||
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()
|
||||
{
|
||||
LLOG("SSL Start");
|
||||
|
||||
ERR_clear_error();
|
||||
if(!context.Create(const_cast<SSL_METHOD *>(SSLv3_client_method()))) {
|
||||
SetSSLError("Start: SSL context.");
|
||||
return false;
|
||||
}
|
||||
if(socket.cert.GetCount())
|
||||
context.UseCertificate(socket.cert, socket.pkey, socket.asn1);
|
||||
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;
|
||||
}
|
||||
return Handshake();
|
||||
}
|
||||
|
||||
dword TcpSocket::SSLImp::Handshake()
|
||||
{
|
||||
int res;
|
||||
ERR_clear_error();
|
||||
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;
|
||||
if(code == SSL_ERROR_SYSCALL && socket.GetErrorCode() == WSAENOTCONN)
|
||||
return WAIT_WRITE;
|
||||
SetSSLResError("SSL handshake", res);
|
||||
return 0;
|
||||
}
|
||||
socket.mode = SSL_CONNECTED;
|
||||
cert.Set(SSL_get_peer_certificate(ssl));
|
||||
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, int end_time)
|
||||
{
|
||||
LLOG("SSL Wait");
|
||||
if((flags & WAIT_READ) && SSL_pending(ssl) > 0)
|
||||
return true;
|
||||
return socket.RawWait(flags, end_time);
|
||||
}
|
||||
|
||||
int TcpSocket::SSLImp::Send(const void *buffer, int maxlen)
|
||||
{
|
||||
LLOG("SSL Send " << maxlen);
|
||||
ERR_clear_error();
|
||||
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;
|
||||
}
|
||||
|
||||
int TcpSocket::SSLImp::Recv(void *buffer, int maxlen)
|
||||
{
|
||||
LLOG("SSL Recv " << maxlen);
|
||||
ERR_clear_error();
|
||||
int res = SSL_read(ssl, (char *)buffer, maxlen);
|
||||
if(res > 0)
|
||||
return res;
|
||||
if(res == 0)
|
||||
socket.is_eof = true;
|
||||
else
|
||||
if(!IsAgain(res))
|
||||
SetSSLResError("SSL_read", res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TcpSocket::SSLImp::Close()
|
||||
{
|
||||
LLOG("SSL Close");
|
||||
SSL_shutdown(ssl);
|
||||
socket.RawClose();
|
||||
SSL_free(ssl);
|
||||
ssl = NULL;
|
||||
}
|
||||
|
||||
END_UPP_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -434,7 +434,7 @@ bool TcpSocket::RawConnect(addrinfo *rp)
|
|||
|
||||
bool TcpSocket::Connect(IpAddrInfo& info)
|
||||
{
|
||||
LLOG("TCP Connect addrinfo");
|
||||
LLOG("Connect addrinfo");
|
||||
Init();
|
||||
Reset();
|
||||
addrinfo *result = info.GetResult();
|
||||
|
|
@ -443,7 +443,7 @@ bool TcpSocket::Connect(IpAddrInfo& info)
|
|||
|
||||
bool TcpSocket::Connect(const char *host, int port)
|
||||
{
|
||||
LLOG("TCP Connect(" << host << ':' << port << ')');
|
||||
LLOG("Connect(" << host << ':' << port << ')');
|
||||
|
||||
Init();
|
||||
Reset();
|
||||
|
|
@ -457,7 +457,7 @@ bool TcpSocket::Connect(const char *host, int port)
|
|||
|
||||
void TcpSocket::RawClose()
|
||||
{
|
||||
LLOG("TCP close " << (int)socket);
|
||||
LLOG("close " << (int)socket);
|
||||
if(socket != INVALID_SOCKET) {
|
||||
int res;
|
||||
#if defined(PLATFORM_WIN32)
|
||||
|
|
@ -800,7 +800,7 @@ void TcpSocket::SetSockError(const char *context, int code, const char *errdesc)
|
|||
errordesc << "socket(" << (int)socket << ") / ";
|
||||
errordesc << context << ": " << errdesc;
|
||||
is_error = true;
|
||||
LLOG("TCP ERROR " << errordesc);
|
||||
LLOG("ERROR " << errordesc);
|
||||
}
|
||||
|
||||
void TcpSocket::SetSockError(const char *context, const char *errdesc)
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ axContentSize]([@(0.0.255) int]_[*@3 m])&]
|
|||
[s5;:HttpRequest`:`:MaxRedirect`(int`): [_^HttpRequest^ HttpRequest][@(0.0.255) `&]_[* MaxR
|
||||
edirect]([@(0.0.255) int]_[*@3 n])&]
|
||||
[s2;%% Specifies the maximum number of redirections (code 3xx) (default
|
||||
is 5). Returns `*this.&]
|
||||
is 10). Returns `*this.&]
|
||||
[s3;%% &]
|
||||
[s4;%% &]
|
||||
[s5;:HttpRequest`:`:MaxRetries`(int`): [_^HttpRequest^ HttpRequest][@(0.0.255) `&]_[* MaxRe
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue