*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:
cxl 2012-05-17 07:54:18 +00:00
parent fb364d3b13
commit 3dbd0cc741
4 changed files with 228 additions and 215 deletions

View file

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

View file

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

View file

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

View file

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