diff --git a/headers/os/net/HttpRequest.h b/headers/os/net/HttpRequest.h index 6bac0639308..bc9d0b05757 100644 --- a/headers/os/net/HttpRequest.h +++ b/headers/os/net/HttpRequest.h @@ -16,6 +16,12 @@ #include +namespace BPrivate { + class CheckedSecureSocket; + class CheckedProxySecureSocket; +}; + + class BHttpRequest : public BNetworkRequest { public: BHttpRequest(const BUrl& url, @@ -76,7 +82,8 @@ class BHttpRequest : public BNetworkRequest { BString& _ResultStatusText(); // SSL failure management - friend class CheckedSecureSocket; + friend class BPrivate::CheckedSecureSocket; + friend class BPrivate::CheckedProxySecureSocket; bool _CertificateVerificationFailed( BCertificate& certificate, const char* message); diff --git a/headers/os/net/ProxySecureSocket.h b/headers/os/net/ProxySecureSocket.h new file mode 100644 index 00000000000..42b445e5ff4 --- /dev/null +++ b/headers/os/net/ProxySecureSocket.h @@ -0,0 +1,32 @@ +/* + * Copyright 2015, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _PROXY_SECURE_SOCKET_H +#define _PROXY_SECURE_SOCKET_H + + +#include + + +class BProxySecureSocket : public BSecureSocket { +public: + BProxySecureSocket(const BNetworkAddress& proxy); + BProxySecureSocket(const BNetworkAddress& proxy, + const BNetworkAddress& peer, + bigtime_t timeout = B_INFINITE_TIMEOUT); + BProxySecureSocket(const BProxySecureSocket& other); + virtual ~BProxySecureSocket(); + + // BSocket implementation + + virtual status_t Connect(const BNetworkAddress& peer, + bigtime_t timeout = B_INFINITE_TIMEOUT); + +private: + const BNetworkAddress fProxyAddress; +}; + + +#endif // _PROXY_SECURE_SOCKET_H + diff --git a/headers/os/net/SecureSocket.h b/headers/os/net/SecureSocket.h index 09d3c300ade..8e13d20836c 100644 --- a/headers/os/net/SecureSocket.h +++ b/headers/os/net/SecureSocket.h @@ -1,5 +1,5 @@ /* - * Copyright 2011, Haiku, Inc. All Rights Reserved. + * Copyright 2011-2015, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. */ #ifndef _SECURE_SOCKET_H @@ -23,6 +23,8 @@ class BSecureSocket : public BSocket { virtual bool CertificateVerificationFailed(BCertificate& certificate, const char* message); + status_t InitCheck(); + // BSocket implementation virtual status_t Connect(const BNetworkAddress& peer, @@ -37,6 +39,9 @@ class BSecureSocket : public BSocket { virtual ssize_t Read(void* buffer, size_t size); virtual ssize_t Write(const void* buffer, size_t size); +protected: + status_t _Setup(); + private: friend class BCertificate; class Private; diff --git a/src/kits/network/libnetapi/HttpRequest.cpp b/src/kits/network/libnetapi/HttpRequest.cpp index 3c3625007f9..f1e090bd0ee 100644 --- a/src/kits/network/libnetapi/HttpRequest.cpp +++ b/src/kits/network/libnetapi/HttpRequest.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -32,33 +33,66 @@ static const int32 kHttpBufferSize = 4096; -class CheckedSecureSocket: public BSecureSocket -{ -public: - CheckedSecureSocket(BHttpRequest* request); +namespace BPrivate { - bool CertificateVerificationFailed(BCertificate& certificate, - const char* message); + class CheckedSecureSocket: public BSecureSocket + { + public: + CheckedSecureSocket(BHttpRequest* request); -private: - BHttpRequest* fRequest; -}; + bool CertificateVerificationFailed(BCertificate& certificate, + const char* message); + private: + BHttpRequest* fRequest; + }; -CheckedSecureSocket::CheckedSecureSocket(BHttpRequest* request) - : - BSecureSocket(), - fRequest(request) -{ -} + CheckedSecureSocket::CheckedSecureSocket(BHttpRequest* request) + : + BSecureSocket(), + fRequest(request) + { + } + + + bool + CheckedSecureSocket::CertificateVerificationFailed(BCertificate& certificate, + const char* message) + { + return fRequest->_CertificateVerificationFailed(certificate, message); + } -bool -CheckedSecureSocket::CertificateVerificationFailed(BCertificate& certificate, - const char* message) -{ - return fRequest->_CertificateVerificationFailed(certificate, message); -} + + class CheckedProxySecureSocket: public BProxySecureSocket + { + public: + CheckedProxySecureSocket(const BNetworkAddress& proxy, BHttpRequest* request); + + bool CertificateVerificationFailed(BCertificate& certificate, + const char* message); + + private: + BHttpRequest* fRequest; + }; + + + CheckedProxySecureSocket::CheckedProxySecureSocket(const BNetworkAddress& proxy, + BHttpRequest* request) + : + BProxySecureSocket(proxy), + fRequest(request) + { + } + + + bool + CheckedProxySecureSocket::CertificateVerificationFailed(BCertificate& certificate, + const char* message) + { + return fRequest->_CertificateVerificationFailed(certificate, message); + } +}; BHttpRequest::BHttpRequest(const BUrl& url, bool ssl, const char* protocolName, @@ -488,9 +522,13 @@ BHttpRequest::_MakeRequest() { delete fSocket; - if (fSSL) - fSocket = new(std::nothrow) CheckedSecureSocket(this); - else + if (fSSL) { + if (fContext->UseProxy()) { + BNetworkAddress proxy(fContext->GetProxyHost(), fContext->GetProxyPort()); + fSocket = new(std::nothrow) BPrivate::CheckedProxySecureSocket(proxy, this); + } else + fSocket = new(std::nothrow) BPrivate::CheckedSecureSocket(this); + } else fSocket = new(std::nothrow) BSocket(); if (fSocket == NULL) diff --git a/src/kits/network/libnetapi/Jamfile b/src/kits/network/libnetapi/Jamfile index a0d606c171b..3220ec6ed09 100644 --- a/src/kits/network/libnetapi/Jamfile +++ b/src/kits/network/libnetapi/Jamfile @@ -66,6 +66,7 @@ for architectureObject in [ MultiArchSubDirSetup ] { DatagramSocket.cpp Socket.cpp SecureSocket.cpp + ProxySecureSocket.cpp # TODO: another add-on for file:// (a much simpler one) FileRequest.cpp diff --git a/src/kits/network/libnetapi/ProxySecureSocket.cpp b/src/kits/network/libnetapi/ProxySecureSocket.cpp new file mode 100644 index 00000000000..1ad8a234b07 --- /dev/null +++ b/src/kits/network/libnetapi/ProxySecureSocket.cpp @@ -0,0 +1,76 @@ +/* + * Copyright 2015 Haiku, Inc. + * Distributed under the terms of the MIT License. + */ + + +#include + +#include + + +BProxySecureSocket::BProxySecureSocket(const BNetworkAddress& proxy) + : + BSecureSocket(), + fProxyAddress(proxy) +{ +} + + +BProxySecureSocket::BProxySecureSocket(const BNetworkAddress& proxy, const BNetworkAddress& peer, + bigtime_t timeout) + : + BSecureSocket(), + fProxyAddress(proxy) +{ + Connect(peer, timeout); +} + + +BProxySecureSocket::BProxySecureSocket(const BProxySecureSocket& other) + : + BSecureSocket(other), + fProxyAddress(other.fProxyAddress) +{ +} + + +BProxySecureSocket::~BProxySecureSocket() +{ +} + + +status_t +BProxySecureSocket::Connect(const BNetworkAddress& peer, bigtime_t timeout) +{ + status_t status = InitCheck(); + if (status != B_OK) + return status; + + BSocket::Connect(fProxyAddress, timeout); + if (status != B_OK) + return status; + + BString connectRequest; + connectRequest.SetToFormat("CONNECT %s:%d HTTP/1.0\r\n\r\n", + peer.HostName().String(), peer.Port()); + BSocket::Write(connectRequest.String(), connectRequest.Length()); + + char buffer[256]; + ssize_t length = BSocket::Read(buffer, sizeof(buffer) - 1); + if (length <= 0) + return length; + + buffer[length] = '\0'; + int httpStatus = 0; + int matches = scanf(buffer, "HTTP/1.0 %d %*[^\r\n]\r\n\r\n", httpStatus); + if (matches != 2) + return B_BAD_DATA; + + if (httpStatus < 200 || httpStatus > 299) + return B_BAD_VALUE; + + return _Setup(); +} + + diff --git a/src/kits/network/libnetapi/SecureSocket.cpp b/src/kits/network/libnetapi/SecureSocket.cpp index 1e3beaae5a6..c763e631c1c 100644 --- a/src/kits/network/libnetapi/SecureSocket.cpp +++ b/src/kits/network/libnetapi/SecureSocket.cpp @@ -256,42 +256,15 @@ BSecureSocket::~BSecureSocket() status_t BSecureSocket::Connect(const BNetworkAddress& peer, bigtime_t timeout) { - if (fPrivate == NULL) - return B_NO_MEMORY; - - status_t state = fPrivate->InitCheck(); - if (state != B_OK) - return state; - - status_t status = BSocket::Connect(peer, timeout); + status_t status = InitCheck(); if (status != B_OK) return status; - // Do this only after BSocket::Connect has checked wether we're already - // connected. We don't want to kill an existing SSL session, as that would - // likely crash the protocol loop for it. - if (fPrivate->fSSL != NULL) { - SSL_free(fPrivate->fSSL); - } - - fPrivate->fSSL = SSL_new(BSecureSocket::Private::Context()); - if (fPrivate->fSSL == NULL) { - BSocket::Disconnect(); - return B_NO_MEMORY; - } - - BIO_set_fd(fPrivate->fBIO, fSocket, BIO_NOCLOSE); - SSL_set_bio(fPrivate->fSSL, fPrivate->fBIO, fPrivate->fBIO); - SSL_set_ex_data(fPrivate->fSSL, Private::sDataIndex, this); - - int returnValue = SSL_connect(fPrivate->fSSL); - if (returnValue <= 0) { - TRACE("SSLConnection can't connect\n"); - BSocket::Disconnect(); - return fPrivate->ErrorCode(returnValue); - } + status = BSocket::Connect(peer, timeout); + if (status != B_OK) + return status; - return B_OK; + return _Setup(); } @@ -322,6 +295,17 @@ BSecureSocket::WaitForReadable(bigtime_t timeout) const } +status_t +BSecureSocket::InitCheck() +{ + if (fPrivate == NULL) + return B_NO_MEMORY; + + status_t state = fPrivate->InitCheck(); + return state; +} + + bool BSecureSocket::CertificateVerificationFailed(BCertificate&, const char*) { @@ -363,6 +347,37 @@ BSecureSocket::Write(const void* buffer, size_t size) } +status_t +BSecureSocket::_Setup() +{ + // Do this only after BSocket::Connect has checked wether we're already + // connected. We don't want to kill an existing SSL session, as that would + // likely crash the protocol loop for it. + if (fPrivate->fSSL != NULL) { + SSL_free(fPrivate->fSSL); + } + + fPrivate->fSSL = SSL_new(BSecureSocket::Private::Context()); + if (fPrivate->fSSL == NULL) { + BSocket::Disconnect(); + return B_NO_MEMORY; + } + + BIO_set_fd(fPrivate->fBIO, fSocket, BIO_NOCLOSE); + SSL_set_bio(fPrivate->fSSL, fPrivate->fBIO, fPrivate->fBIO); + SSL_set_ex_data(fPrivate->fSSL, Private::sDataIndex, this); + + int returnValue = SSL_connect(fPrivate->fSSL); + if (returnValue <= 0) { + TRACE("SSLConnection can't connect\n"); + BSocket::Disconnect(); + return fPrivate->ErrorCode(returnValue); + } + + return B_OK; +} + + #else // OPENSSL_ENABLED