mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-05-28 17:16:29 +03:00
Added support for SSL and HTTPS requests using OpenSSL.
This commit is contained in:
@@ -87,6 +87,7 @@
|
||||
// Network
|
||||
#include <eepp/network.hpp>
|
||||
using namespace EE::Network;
|
||||
using namespace EE::Network::SSL;
|
||||
|
||||
// UI
|
||||
#include <eepp/ui.hpp>
|
||||
|
||||
@@ -11,5 +11,6 @@
|
||||
#include <eepp/network/ctcplistener.hpp>
|
||||
#include <eepp/network/ctcpsocket.hpp>
|
||||
#include <eepp/network/cudpsocket.hpp>
|
||||
#include <eepp/network/ssl/csslsocket.hpp>
|
||||
|
||||
#endif
|
||||
|
||||
@@ -39,8 +39,10 @@ class EE_API cHttp : NonCopyable {
|
||||
** URI ("/") and an empty body.
|
||||
** @param uri Target URI
|
||||
** @param method Method to use for the request
|
||||
** @param body Content of the request's body */
|
||||
Request(const std::string& uri = "/", Method method = Get, const std::string& body = "");
|
||||
** @param body Content of the request's body
|
||||
** @param validateCertificate Enables certificate validation for https request
|
||||
** @param validateHostname Enables hostname validation for https request */
|
||||
Request(const std::string& uri = "/", Method method = Get, const std::string& body = "", bool validateCertificate = false, bool validateHostname = false );
|
||||
|
||||
/** @brief Set the value of a field
|
||||
** The field is created if it doesn't exist. The name of
|
||||
@@ -48,7 +50,6 @@ class EE_API cHttp : NonCopyable {
|
||||
** By default, a request doesn't contain any field (but the
|
||||
** mandatory fields are added later by the HTTP client when
|
||||
** sending the request).
|
||||
///
|
||||
** @param field Name of the field to set
|
||||
** @param value Value of the field */
|
||||
void SetField(const std::string& field, const std::string& value);
|
||||
@@ -82,6 +83,18 @@ class EE_API cHttp : NonCopyable {
|
||||
|
||||
/** @return The request Uri */
|
||||
const std::string& GetUri() const;
|
||||
|
||||
/** @return If SSL certificate validation is enabled */
|
||||
const bool& ValidateCertificate() const;
|
||||
|
||||
/** Enable/disable SSL certificate validation */
|
||||
void ValidateCertificate( bool enable );
|
||||
|
||||
/** @return If SSL hostname validation is enabled */
|
||||
const bool& ValidateHostname() const;
|
||||
|
||||
/** Enable/disable SSL hostname validation */
|
||||
void ValidateHostname( bool enable );
|
||||
private:
|
||||
friend class cHttp;
|
||||
|
||||
@@ -101,12 +114,14 @@ class EE_API cHttp : NonCopyable {
|
||||
typedef std::map<std::string, std::string> FieldTable;
|
||||
|
||||
// Member data
|
||||
FieldTable mFields; ///< Fields of the header associated to their value
|
||||
Method mMethod; ///< Method to use for the request
|
||||
std::string mUri; ///< Target URI of the request
|
||||
unsigned int mMajorVersion; ///< Major HTTP version
|
||||
unsigned int mMinorVersion; ///< Minor HTTP version
|
||||
std::string mBody; ///< Body of the request
|
||||
FieldTable mFields; ///< Fields of the header associated to their value
|
||||
Method mMethod; ///< Method to use for the request
|
||||
std::string mUri; ///< Target URI of the request
|
||||
unsigned int mMajorVersion; ///< Major HTTP version
|
||||
unsigned int mMinorVersion; ///< Minor HTTP version
|
||||
std::string mBody; ///< Body of the request
|
||||
bool mValidateCertificate; ///< Validates the SSL certificate in case of an HTTPS request
|
||||
bool mValidateHostname; ///< Validates the hostname in case of an HTTPS request
|
||||
};
|
||||
|
||||
/** @brief Define a HTTP response */
|
||||
@@ -292,6 +307,7 @@ class EE_API cHttp : NonCopyable {
|
||||
unsigned short mPort; ///< Port used for connection with host
|
||||
std::list<cAsyncRequest*> mThreads;
|
||||
cMutex mThreadsMutex;
|
||||
bool mIsSSL;
|
||||
|
||||
void RemoveOldThreads();
|
||||
};
|
||||
|
||||
@@ -79,7 +79,7 @@ protected :
|
||||
/** @brief Close the socket gracefully
|
||||
** This function can only be accessed by derived classes. */
|
||||
void Close();
|
||||
private :
|
||||
protected :
|
||||
friend class cSocketSelector;
|
||||
// Member data
|
||||
Type mType; ///< Type of the socket (TCP or UDP)
|
||||
|
||||
@@ -48,13 +48,13 @@ class EE_API cTcpSocket : public cSocket {
|
||||
** @param timeout Optional maximum time to wait
|
||||
** @return Status code
|
||||
** @see Disconnect */
|
||||
Status Connect(const cIpAddress& remoteAddress, unsigned short remotePort, cTime timeout = cTime::Zero);
|
||||
virtual Status Connect(const cIpAddress& remoteAddress, unsigned short remotePort, cTime timeout = cTime::Zero);
|
||||
|
||||
/** @brief Disconnect the socket from its remote peer
|
||||
** This function gracefully closes the connection. If the
|
||||
** socket is not connected, this function has no effect.
|
||||
** @see Connect */
|
||||
void Disconnect();
|
||||
virtual void Disconnect();
|
||||
|
||||
/** @brief Send raw data to the remote peer
|
||||
** This function will fail if the socket is not connected.
|
||||
@@ -62,7 +62,7 @@ class EE_API cTcpSocket : public cSocket {
|
||||
** @param size Number of bytes to send
|
||||
** @return Status code
|
||||
** @see Receive */
|
||||
Status Send(const void* data, std::size_t size);
|
||||
virtual Status Send(const void* data, std::size_t size);
|
||||
|
||||
/** @brief Receive raw data from the remote peer
|
||||
** In blocking mode, this function will wait until some
|
||||
@@ -73,14 +73,14 @@ class EE_API cTcpSocket : public cSocket {
|
||||
** @param received This variable is filled with the actual number of bytes received
|
||||
** @return Status code
|
||||
** @see Send */
|
||||
Status Receive(void* data, std::size_t size, std::size_t& received);
|
||||
virtual Status Receive(void* data, std::size_t size, std::size_t& received);
|
||||
|
||||
/** @brief Send a formatted packet of data to the remote peer
|
||||
** This function will fail if the socket is not connected.
|
||||
** @param packet cPacket to send
|
||||
** @return Status code
|
||||
** @see Receive */
|
||||
Status Send(cPacket& packet);
|
||||
virtual Status Send(cPacket& packet);
|
||||
|
||||
/** @brief Receive a formatted packet of data from the remote peer
|
||||
** In blocking mode, this function will wait until the whole packet
|
||||
@@ -89,7 +89,7 @@ class EE_API cTcpSocket : public cSocket {
|
||||
** @param packet cPacket to fill with the received data
|
||||
** @return Status code
|
||||
** @see Send */
|
||||
Status Receive(cPacket& packet);
|
||||
virtual Status Receive(cPacket& packet);
|
||||
|
||||
private:
|
||||
|
||||
@@ -100,7 +100,7 @@ class EE_API cTcpSocket : public cSocket {
|
||||
PendingPacket();
|
||||
|
||||
Uint32 Size; ///< Data of packet size
|
||||
std::size_t SizeReceived; ///< Number of size bytes received so far
|
||||
std::size_t SizeReceived; ///< Number of size bytes received so far
|
||||
std::vector<char> Data; ///< Data of the packet
|
||||
};
|
||||
|
||||
|
||||
49
include/eepp/network/ssl/csslsocket.hpp
Normal file
49
include/eepp/network/ssl/csslsocket.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef EE_NETWORKCSSLSOCKET_HPP
|
||||
#define EE_NETWORKCSSLSOCKET_HPP
|
||||
|
||||
#include <eepp/network/ctcpsocket.hpp>
|
||||
|
||||
namespace EE { namespace Network { namespace SSL {
|
||||
|
||||
class cSSLSocketImpl;
|
||||
|
||||
class EE_API cSSLSocket : public cTcpSocket {
|
||||
public:
|
||||
static std::string CertificatesPath;
|
||||
|
||||
static bool Init();
|
||||
|
||||
static bool End();
|
||||
|
||||
cSSLSocket( std::string hostname, bool validateCertificate, bool validateHostname );
|
||||
|
||||
virtual ~cSSLSocket();
|
||||
|
||||
Status Connect(const cIpAddress& remoteAddress, unsigned short remotePort, cTime timeout = cTime::Zero);
|
||||
|
||||
void Disconnect();
|
||||
|
||||
Status Send(const void* data, std::size_t size);
|
||||
|
||||
Status Receive(void* data, std::size_t size, std::size_t& received);
|
||||
|
||||
Status Send(cPacket& packet);
|
||||
|
||||
Status Receive(cPacket& packet);
|
||||
protected:
|
||||
friend class cSSLSocketImpl;
|
||||
friend class cOpenSSLSocket;
|
||||
|
||||
cSSLSocketImpl * mImpl;
|
||||
std::string mHostName;
|
||||
bool mValidateCertificate;
|
||||
bool mValidateHostname;
|
||||
|
||||
Status TcpSend(const void* data, std::size_t size);
|
||||
|
||||
Status TcpReceive(void* data, std::size_t size, std::size_t& received);
|
||||
};
|
||||
|
||||
}}}
|
||||
|
||||
#endif
|
||||
31
premake4.lua
31
premake4.lua
@@ -53,6 +53,17 @@ newplatform {
|
||||
}
|
||||
}
|
||||
|
||||
newplatform {
|
||||
name = "clang-static-analyze",
|
||||
description = "Clang static analysis build",
|
||||
gcc = {
|
||||
cc = "clang --analyze",
|
||||
cxx = "clang++ --analyze",
|
||||
ar = "ar",
|
||||
cppflags = "-MMD"
|
||||
}
|
||||
}
|
||||
|
||||
newplatform {
|
||||
name = "emscripten",
|
||||
description = "Emscripten",
|
||||
@@ -126,6 +137,7 @@ if _OPTIONS.platform then
|
||||
premake.gcc.platforms['Native'] = premake.gcc.platforms[_OPTIONS.platform]
|
||||
end
|
||||
|
||||
newoption { trigger = "with-ssl", description = "Enables SSL support for the Network module ( requires OpenSSL )." }
|
||||
newoption { trigger = "with-libsndfile", description = "Build with libsndfile support." }
|
||||
newoption { trigger = "with-static-freetype", description = "Build freetype as a static library." }
|
||||
newoption { trigger = "with-static-eepp", description = "Force to build the demos and tests with eepp compiled statically" }
|
||||
@@ -617,6 +629,22 @@ function select_backend()
|
||||
end
|
||||
end
|
||||
|
||||
function check_ssl_support()
|
||||
if _OPTIONS["with-ssl"] then
|
||||
if os.is("windows") then
|
||||
table.insert( link_list, get_backend_link_name( "libssl" ) )
|
||||
table.insert( link_list, get_backend_link_name( "libcrypto" ) )
|
||||
else
|
||||
table.insert( link_list, get_backend_link_name( "ssl" ) )
|
||||
table.insert( link_list, get_backend_link_name( "crypto" ) )
|
||||
end
|
||||
|
||||
files { "src/eepp/network/ssl/backend/openssl/*.cpp" }
|
||||
|
||||
defines { "EE_SSL_SUPPORT", "EE_OPENSSL" }
|
||||
end
|
||||
end
|
||||
|
||||
function build_eepp( build_name )
|
||||
includedirs { "include", "src", "src/eepp/helper/freetype2/include", "src/eepp/helper/zlib" }
|
||||
|
||||
@@ -645,6 +673,7 @@ function build_eepp( build_name )
|
||||
"src/eepp/window/*.cpp",
|
||||
"src/eepp/window/platform/null/*.cpp",
|
||||
"src/eepp/network/*.cpp",
|
||||
"src/eepp/network/ssl/*.cpp",
|
||||
"src/eepp/ui/*.cpp",
|
||||
"src/eepp/ui/tools/*.cpp",
|
||||
"src/eepp/physics/*.cpp",
|
||||
@@ -653,6 +682,8 @@ function build_eepp( build_name )
|
||||
"src/eepp/gaming/mapeditor/*.cpp"
|
||||
}
|
||||
|
||||
check_ssl_support()
|
||||
|
||||
select_backend()
|
||||
|
||||
if not _OPTIONS["with-static-freetype"] and os_findlib("freetype") then
|
||||
|
||||
@@ -11,3 +11,5 @@
|
||||
#define EE_GL3_ENABLED 1
|
||||
#define EE_SHADERS_SUPPORTED
|
||||
#define EE_GLEW_AVAILABLE
|
||||
#define EE_SSL_SUPPORT
|
||||
#define EE_OPENSSL
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 3.0.0, 2014-04-12T15:44:53. -->
|
||||
<!-- Written by QtCreator 3.0.0, 2014-06-01T17:00:18. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.ActiveTarget</variable>
|
||||
@@ -56,13 +56,13 @@
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{388e5431-b31b-42b3-b9ad-9002d279d75d}</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">11</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">../../make/linux</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProcessStep.Arguments">--with-static-backend gmake</value>
|
||||
<value type="QString" key="ProjectExplorer.ProcessStep.Arguments">--with-static-backend --with-ssl gmake</value>
|
||||
<value type="QString" key="ProjectExplorer.ProcessStep.Command">premake4</value>
|
||||
<value type="QString" key="ProjectExplorer.ProcessStep.WorkingDirectory">%{buildDir}../../../</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Custom Process Step</value>
|
||||
@@ -151,7 +151,7 @@
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProcessStep.Arguments">--with-static-backend gmake</value>
|
||||
<value type="QString" key="ProjectExplorer.ProcessStep.Arguments">--with-static-backend --with-ssl gmake</value>
|
||||
<value type="QString" key="ProjectExplorer.ProcessStep.Command">premake4</value>
|
||||
<value type="QString" key="ProjectExplorer.ProcessStep.WorkingDirectory">%{buildDir}../../../</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Custom Process Step</value>
|
||||
@@ -200,7 +200,7 @@
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProcessStep.Arguments">--with-static-backend gmake</value>
|
||||
<value type="QString" key="ProjectExplorer.ProcessStep.Arguments">--with-static-backend --with-ssl gmake</value>
|
||||
<value type="QString" key="ProjectExplorer.ProcessStep.Command">premake4</value>
|
||||
<value type="QString" key="ProjectExplorer.ProcessStep.WorkingDirectory">%{buildDir}../../../</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Custom Process Step</value>
|
||||
@@ -609,7 +609,7 @@
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProcessStep.Arguments">--with-static-backend gmake</value>
|
||||
<value type="QString" key="ProjectExplorer.ProcessStep.Arguments">--with-static-backend --with-ssl gmake</value>
|
||||
<value type="QString" key="ProjectExplorer.ProcessStep.Command">premake4</value>
|
||||
<value type="QString" key="ProjectExplorer.ProcessStep.WorkingDirectory">%{buildDir}../../../</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Custom Process Step</value>
|
||||
@@ -778,7 +778,7 @@
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProcessStep.Arguments">--with-static-backend gmake</value>
|
||||
<value type="QString" key="ProjectExplorer.ProcessStep.Arguments">--with-static-backend --with-ssl gmake</value>
|
||||
<value type="QString" key="ProjectExplorer.ProcessStep.Command">premake4</value>
|
||||
<value type="QString" key="ProjectExplorer.ProcessStep.WorkingDirectory">%{buildDir}../../../</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Custom Process Step</value>
|
||||
|
||||
@@ -667,3 +667,10 @@
|
||||
../../src/eepp/graphics/renderer/shaders/basegl3cp.frag
|
||||
../../src/eepp/window/backend/SDL/cbackendsdl.cpp
|
||||
../../bin/assets/ee.ini
|
||||
../../src/eepp/network/ssl/csslsocket.cpp
|
||||
../../src/eepp/network/ssl/csslsocketimpl.hpp
|
||||
../../src/eepp/network/ssl/backend/openssl/copensslsocket.hpp
|
||||
../../src/eepp/network/ssl/backend/openssl/copensslsocket.cpp
|
||||
../../include/eepp/network/ssl/csslsocket.hpp
|
||||
../../src/eepp/network/ssl/backend/openssl/curl_hostcheck.cpp
|
||||
../../src/eepp/network/ssl/backend/openssl/curl_hostcheck.h
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
cd $(dirname "$0")
|
||||
premake4 --file=../../premake4.lua --os=windows --platform=mingw32 --with-static-freetype gmake
|
||||
premake4 --file=../../premake4.lua --os=windows --platform=mingw32 --with-static-freetype --with-ssl gmake
|
||||
cd ../../make/mingw32/
|
||||
make $@
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
#include <eepp/network/chttp.hpp>
|
||||
#include <eepp/network/ssl/csslsocket.hpp>
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <limits>
|
||||
|
||||
using namespace EE::Network::SSL;
|
||||
|
||||
namespace {
|
||||
// Convert a string to lower case
|
||||
std::string toLower(std::string str) {
|
||||
@@ -16,7 +19,10 @@ namespace {
|
||||
|
||||
namespace EE { namespace Network {
|
||||
|
||||
cHttp::Request::Request(const std::string& uri, Method method, const std::string& body) {
|
||||
cHttp::Request::Request(const std::string& uri, Method method, const std::string& body, bool validateCertificate, bool validateHostname ) :
|
||||
mValidateCertificate( validateCertificate ),
|
||||
mValidateHostname( validateHostname )
|
||||
{
|
||||
SetMethod(method);
|
||||
SetUri(uri);
|
||||
SetHttpVersion(1, 0);
|
||||
@@ -52,6 +58,22 @@ const std::string &cHttp::Request::GetUri() const {
|
||||
return mUri;
|
||||
}
|
||||
|
||||
const bool& cHttp::Request::ValidateCertificate() const {
|
||||
return mValidateCertificate;
|
||||
}
|
||||
|
||||
void cHttp::Request::ValidateCertificate(bool enable) {
|
||||
mValidateCertificate = enable;
|
||||
}
|
||||
|
||||
const bool &cHttp::Request::ValidateHostname() const {
|
||||
return mValidateHostname;
|
||||
}
|
||||
|
||||
void cHttp::Request::ValidateHostname(bool enable) {
|
||||
mValidateHostname = enable;
|
||||
}
|
||||
|
||||
std::string cHttp::Request::Prepare() const {
|
||||
std::ostringstream out;
|
||||
|
||||
@@ -211,12 +233,14 @@ void cHttp::Response::ParseFields(std::istream &in) {
|
||||
cHttp::cHttp() :
|
||||
mConnection( NULL ),
|
||||
mHost(),
|
||||
mPort(0)
|
||||
mPort(0),
|
||||
mIsSSL( false )
|
||||
{
|
||||
}
|
||||
|
||||
cHttp::cHttp(const std::string& host, unsigned short port) :
|
||||
mConnection( NULL )
|
||||
mConnection( NULL ),
|
||||
mIsSSL( false )
|
||||
{
|
||||
SetHost(host, port);
|
||||
}
|
||||
@@ -246,10 +270,15 @@ void cHttp::SetHost(const std::string& host, unsigned short port) {
|
||||
mHostName = host.substr(7);
|
||||
mPort = (port != 0 ? port : 80);
|
||||
} else if (toLower(host.substr(0, 8)) == "https://") {
|
||||
// HTTPS protocol -- unsupported (requires encryption and certificates and stuff...)
|
||||
eePRINTL( "HTTPS protocol is not supported by cHttp" );
|
||||
mHostName = "";
|
||||
mPort = 0;
|
||||
// HTTPS protocol
|
||||
#ifdef EE_SSL_SUPPORT
|
||||
mIsSSL = true;
|
||||
mHostName = host.substr(8);
|
||||
mPort = (port != 0 ? port : 443);
|
||||
#else
|
||||
mHostName = "";
|
||||
mPort = 0;
|
||||
#endif
|
||||
} else {
|
||||
// Undefined protocol - use HTTP
|
||||
mHostName = host;
|
||||
@@ -265,7 +294,7 @@ void cHttp::SetHost(const std::string& host, unsigned short port) {
|
||||
|
||||
cHttp::Response cHttp::SendRequest(const cHttp::Request& request, cTime timeout) {
|
||||
if ( NULL == mConnection ) {
|
||||
cTcpSocket * Conn = eeNew( cTcpSocket, () );
|
||||
cTcpSocket * Conn = mIsSSL ? eeNew( cSSLSocket, ( mHostName, request.ValidateCertificate(), request.ValidateHostname() ) ) : eeNew( cTcpSocket, () );
|
||||
mConnection = Conn;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ cSocket::~cSocket() {
|
||||
Close();
|
||||
}
|
||||
|
||||
|
||||
void cSocket::SetBlocking(bool blocking) {
|
||||
// Apply if the socket is already created
|
||||
if (mSocket != Private::cSocketImpl::InvalidSocket())
|
||||
|
||||
@@ -227,8 +227,7 @@ cSocket::Status cTcpSocket::Send(cPacket& packet) {
|
||||
return Send(&blockToSend[0], blockToSend.size());
|
||||
}
|
||||
|
||||
cSocket::Status cTcpSocket::Receive(cPacket& packet)
|
||||
{
|
||||
cSocket::Status cTcpSocket::Receive(cPacket& packet) {
|
||||
// First clear the variables to fill
|
||||
packet.Clear();
|
||||
|
||||
|
||||
391
src/eepp/network/ssl/backend/openssl/copensslsocket.cpp
Normal file
391
src/eepp/network/ssl/backend/openssl/copensslsocket.cpp
Normal file
@@ -0,0 +1,391 @@
|
||||
#include <eepp/network/ssl/backend/openssl/copensslsocket.hpp>
|
||||
|
||||
/** This implementation is based on the Godo Game Engine implementation ( https://github.com/okamstudio/godot ), MIT licensed. */
|
||||
|
||||
#ifdef EE_OPENSSL
|
||||
|
||||
#include <eepp/network/cpacket.hpp>
|
||||
#include <eepp/network/ssl/backend/openssl/curl_hostcheck.h>
|
||||
#include <eepp/system/filesystem.hpp>
|
||||
|
||||
namespace EE { namespace Network { namespace SSL {
|
||||
|
||||
static std::vector<X509*> sCerts;
|
||||
|
||||
bool cOpenSSLSocket::MatchHostname( const char * name, const char * hostname ) {
|
||||
return Tool_Curl_cert_hostcheck( name, hostname )==CURL_HOST_MATCH;
|
||||
}
|
||||
|
||||
bool cOpenSSLSocket::MatchCommonName( const char * hostname, const X509 * server_cert ) {
|
||||
int common_name_loc = -1;
|
||||
X509_NAME_ENTRY *common_name_entry = NULL;
|
||||
ASN1_STRING *common_name_asn1 = NULL;
|
||||
char *common_name_str = NULL;
|
||||
|
||||
// Find the position of the CN field in the Subject field of the certificate
|
||||
common_name_loc = X509_NAME_get_index_by_NID( X509_get_subject_name( (X509 *) server_cert ), NID_commonName, -1 );
|
||||
|
||||
// Extract the CN field
|
||||
common_name_entry = X509_NAME_get_entry( X509_get_subject_name( (X509 *) server_cert ), common_name_loc );
|
||||
|
||||
// Convert the CN field to a C string
|
||||
common_name_asn1 = X509_NAME_ENTRY_get_data( common_name_entry );
|
||||
|
||||
common_name_str = (char *) ASN1_STRING_data( common_name_asn1 );
|
||||
|
||||
// Make sure there isn't an embedded NUL character in the CN
|
||||
bool malformed_certificate = (size_t)ASN1_STRING_length( common_name_asn1 ) != strlen( common_name_str );
|
||||
|
||||
if ( malformed_certificate ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compare expected hostname with the CN
|
||||
return MatchHostname(common_name_str,hostname);
|
||||
}
|
||||
|
||||
/** Tries to find a match for hostname in the certificate's Subject Alternative Name extension. */
|
||||
bool cOpenSSLSocket::MatchSubjectAlternativeName( const char * hostname, const X509 * server_cert ) {
|
||||
bool result = false;
|
||||
int i;
|
||||
int san_names_nb = -1;
|
||||
STACK_OF(GENERAL_NAME) *san_names = NULL;
|
||||
|
||||
// Try to extract the names within the SAN extension from the certificate
|
||||
san_names = ( STACK_OF(GENERAL_NAME) *)X509_get_ext_d2i( (X509 *) server_cert, NID_subject_alt_name, NULL, NULL );
|
||||
|
||||
if ( san_names == NULL ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
san_names_nb = sk_GENERAL_NAME_num( san_names );
|
||||
|
||||
// Check each name within the extension
|
||||
for ( i=0; i < san_names_nb; i++ ) {
|
||||
const GENERAL_NAME * current_name = sk_GENERAL_NAME_value( san_names, i );
|
||||
|
||||
if ( current_name->type == GEN_DNS ) {
|
||||
// Current name is a DNS name, let's check it
|
||||
char * dns_name = (char *) ASN1_STRING_data( current_name->d.dNSName );
|
||||
|
||||
// Make sure there isn't an embedded NUL character in the DNS name
|
||||
if ( (size_t)ASN1_STRING_length( current_name->d.dNSName ) != strlen( dns_name ) ) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
else { // Compare expected hostname with the DNS name
|
||||
if ( MatchHostname( dns_name, hostname ) ) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sk_GENERAL_NAME_pop_free( san_names, GENERAL_NAME_free );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int cOpenSSLSocket::CertVerifyCb( X509_STORE_CTX * x509_ctx, void * arg ) {
|
||||
/* This is the function that OpenSSL would call if we hadn't called
|
||||
* SSL_CTX_set_cert_verify_callback(). Therefore, we are "wrapping"
|
||||
* the default functionality, rather than replacing it. */
|
||||
bool base_cert_valid = X509_verify_cert( x509_ctx );
|
||||
|
||||
if ( !base_cert_valid ) {
|
||||
eePRINTL( "Cause: %s", ( X509_verify_cert_error_string( X509_STORE_CTX_get_error( x509_ctx ) ) ) );
|
||||
ERR_print_errors_fp( stdout );
|
||||
}
|
||||
|
||||
X509 * server_cert = X509_STORE_CTX_get_current_cert( x509_ctx );
|
||||
|
||||
char cert_str[256];
|
||||
X509_NAME_oneline( X509_get_subject_name ( server_cert ), cert_str, sizeof ( cert_str ) );
|
||||
|
||||
eePRINTL( "CERT STR: %s", ( cert_str ) );
|
||||
eePRINTL( "VALID: %s", String::ToStr( base_cert_valid ).c_str() );
|
||||
|
||||
if ( !base_cert_valid ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
cOpenSSLSocket * ssl = (cOpenSSLSocket *)arg;
|
||||
|
||||
if ( ssl->mSSLSocket->mValidateHostname ) {
|
||||
bool err = !MatchSubjectAlternativeName( ssl->mSSLSocket->mHostName.c_str(), server_cert );
|
||||
|
||||
if ( err ) {
|
||||
err = !MatchCommonName( ssl->mSSLSocket->mHostName.c_str(), server_cert );
|
||||
}
|
||||
|
||||
if ( err ) {
|
||||
ssl->mStatus = cSocket::Error;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool cOpenSSLSocket::Init() {
|
||||
CRYPTO_malloc_init(); // Initialize malloc, free, etc for OpenSSL's use
|
||||
|
||||
SSL_library_init(); // Initialize OpenSSL's SSL libraries
|
||||
|
||||
SSL_load_error_strings(); // Load SSL error strings
|
||||
|
||||
ERR_load_BIO_strings(); // Load BIO error strings
|
||||
|
||||
OpenSSL_add_all_algorithms(); // Load all available encryption algorithms
|
||||
|
||||
//! Load the certificates and config
|
||||
if ( FileSystem::FileExists( cSSLSocket::CertificatesPath ) ) {
|
||||
SafeDataPointer data;
|
||||
FileSystem::FileGet( cSSLSocket::CertificatesPath, data );
|
||||
|
||||
if ( data.DataSize > 0 ) {
|
||||
BIO* mem = BIO_new(BIO_s_mem());
|
||||
|
||||
BIO_puts( mem, (const char*) data.Data );
|
||||
|
||||
while( true ) {
|
||||
X509 * cert = PEM_read_bio_X509(mem, NULL, 0, NULL);
|
||||
|
||||
if (!cert)
|
||||
break;
|
||||
|
||||
sCerts.push_back(cert);
|
||||
}
|
||||
|
||||
BIO_free(mem);
|
||||
}
|
||||
|
||||
eePRINTL( "Loaded certs from '%s': %d", cSSLSocket::CertificatesPath.c_str(), (int)sCerts.size() );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cOpenSSLSocket::End() {
|
||||
if ( !sCerts.empty() ) {
|
||||
for( size_t i = 0; i < sCerts.size(); i++ ) {
|
||||
X509_free(sCerts[i]);
|
||||
}
|
||||
|
||||
sCerts.clear();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
cOpenSSLSocket::cOpenSSLSocket( cSSLSocket * socket ) :
|
||||
cSSLSocketImpl( socket ),
|
||||
mCTX( NULL ),
|
||||
mSSL( NULL ),
|
||||
mBIO( NULL ),
|
||||
mConnected( false ),
|
||||
mStatus( cSocket::Disconnected ),
|
||||
mMaxCertChainDepth( 9 )
|
||||
{
|
||||
mSSLSocket = socket;
|
||||
}
|
||||
|
||||
cOpenSSLSocket::~cOpenSSLSocket() {
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
cSocket::Status cOpenSSLSocket::Connect( const cIpAddress& remoteAddress, unsigned short remotePort, cTime timeout ) {
|
||||
if ( mConnected ) {
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
// Set up a SSL_CTX object, which will tell our BIO object how to do its work
|
||||
mCTX = SSL_CTX_new( SSLv23_client_method() );
|
||||
|
||||
if ( mSSLSocket->mValidateCertificate ) {
|
||||
if (!sCerts.empty()) {
|
||||
//yay for undocumented OpenSSL functions
|
||||
X509_STORE * store = SSL_CTX_get_cert_store( mCTX );
|
||||
|
||||
for( size_t i = 0; i < sCerts.size(); i++ ) {
|
||||
X509_STORE_add_cert( store, sCerts[i] );
|
||||
}
|
||||
}
|
||||
|
||||
/* Ask OpenSSL to verify the server certificate. Note that this
|
||||
* does NOT include verifying that the hostname is correct.
|
||||
* So, by itself, this means anyone with any legitimate
|
||||
* CA-issued certificate for any website, can impersonate any
|
||||
* other website in the world. This is not good. See "The
|
||||
* Most Dangerous Code in the World" article at
|
||||
* https://crypto.stanford.edu/~dabo/pubs/abstracts/ssl-client-bugs.html
|
||||
*/
|
||||
SSL_CTX_set_verify( mCTX, SSL_VERIFY_PEER, NULL );
|
||||
|
||||
/* This is how we solve the problem mentioned in the previous
|
||||
* comment. We "wrap" OpenSSL's validation routine in our
|
||||
* own routine, which also validates the hostname by calling
|
||||
* the code provided by iSECPartners. Note that even though
|
||||
* the "Everything You've Always Wanted to Know About
|
||||
* Certificate Validation With OpenSSL (But Were Afraid to
|
||||
* Ask)" paper from iSECPartners says very explicitly not to
|
||||
* call SSL_CTX_set_cert_verify_callback (at the bottom of
|
||||
* page 2), what we're doing here is safe because our
|
||||
* cert_verify_callback() calls X509_verify_cert(), which is
|
||||
* OpenSSL's built-in routine which would have been called if
|
||||
* we hadn't set the callback. Therefore, we're just
|
||||
* "wrapping" OpenSSL's routine, not replacing it. */
|
||||
SSL_CTX_set_cert_verify_callback ( mCTX, CertVerifyCb, this );
|
||||
|
||||
//Let the verify_callback catch the verify_depth error so that we get an appropriate error in the logfile. (??)
|
||||
SSL_CTX_set_verify_depth( mCTX, mMaxCertChainDepth + 1 );
|
||||
}
|
||||
|
||||
mSSL = SSL_new( mCTX );
|
||||
/**
|
||||
mBIO = BIO_new( &_bio_method );
|
||||
mBIO->ptr = this;
|
||||
|
||||
SSL_set_bio( mSSL, mBIO, mBIO );
|
||||
*/
|
||||
|
||||
SSL_set_fd( mSSL, (int)mSSLSocket->mSocket );
|
||||
|
||||
// Set the SSL to automatically retry on failure.
|
||||
SSL_set_mode( mSSL , SSL_MODE_AUTO_RETRY );
|
||||
|
||||
mStatus = cSocket::Done;
|
||||
|
||||
// Same as before, try to connect.
|
||||
int result = SSL_connect( mSSL );
|
||||
|
||||
eePRINTL( "CONNECTION RESULT: %s", String::ToStr(result).c_str() );
|
||||
|
||||
if ( result < 1 ) {
|
||||
ERR_print_errors_fp(stdout);
|
||||
|
||||
_print_error(result);
|
||||
|
||||
mStatus = cSocket::Error;
|
||||
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
X509 * peer = SSL_get_peer_certificate( mSSL );
|
||||
|
||||
if ( peer ) {
|
||||
bool cert_ok = SSL_get_verify_result(mSSL) == X509_V_OK;
|
||||
|
||||
eePRINTL( "cert_ok: %s", String::ToStr(cert_ok).c_str() );
|
||||
|
||||
mStatus = cSocket::Done;
|
||||
} else if ( mSSLSocket->mValidateCertificate ) {
|
||||
mStatus = cSocket::Error;
|
||||
}
|
||||
|
||||
if ( mStatus == cSocket::Done ) {
|
||||
mConnected = true;
|
||||
}
|
||||
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
void cOpenSSLSocket::Disconnect() {
|
||||
if (!mConnected)
|
||||
return;
|
||||
|
||||
SSL_shutdown( mSSL );
|
||||
SSL_free( mSSL );
|
||||
SSL_CTX_free( mCTX );
|
||||
|
||||
mSSL = NULL;
|
||||
mCTX = NULL;
|
||||
|
||||
mConnected = false;
|
||||
mStatus = cSocket::Disconnected;
|
||||
}
|
||||
|
||||
void cOpenSSLSocket::_print_error(int err) {
|
||||
err = SSL_get_error(mSSL,err);
|
||||
|
||||
switch(err) {
|
||||
case SSL_ERROR_NONE: eePRINTL("NO ERROR: The TLS/SSL I/O operation completed"); break;
|
||||
case SSL_ERROR_ZERO_RETURN: eePRINTL("The TLS/SSL connection has been closed.");
|
||||
case SSL_ERROR_WANT_READ:
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
eePRINTL("The operation did not complete."); break;
|
||||
case SSL_ERROR_WANT_CONNECT:
|
||||
case SSL_ERROR_WANT_ACCEPT:
|
||||
eePRINTL("The connect/accept operation did not complete"); break;
|
||||
case SSL_ERROR_WANT_X509_LOOKUP:
|
||||
eePRINTL("The operation did not complete because an application callback set by SSL_CTX_set_client_cert_cb() has asked to be called again."); break;
|
||||
case SSL_ERROR_SYSCALL:
|
||||
eePRINTL("Some I/O error occurred. The OpenSSL error queue may contain more information on the error."); break;
|
||||
case SSL_ERROR_SSL:
|
||||
eePRINTL("A failure in the SSL library occurred, usually a protocol error."); break;
|
||||
}
|
||||
}
|
||||
|
||||
cSocket::Status cOpenSSLSocket::Send( const void * data, std::size_t size ) {
|
||||
Uint8 * buf = (Uint8*)data;
|
||||
|
||||
while( size > 0 ) {
|
||||
int ret = SSL_write( mSSL, buf, size );
|
||||
|
||||
if ( ret <= 0 ) {
|
||||
_print_error(ret);
|
||||
|
||||
Disconnect();
|
||||
|
||||
return cSocket::Disconnected;
|
||||
}
|
||||
|
||||
buf+=ret;
|
||||
size-=ret;
|
||||
}
|
||||
|
||||
return cSocket::Done;
|
||||
}
|
||||
|
||||
cSocket::Status cOpenSSLSocket::Receive( void * data, std::size_t size, std::size_t& received ) {
|
||||
if ( size==0 ) {
|
||||
received = 0;
|
||||
return cSocket::Done;
|
||||
}
|
||||
|
||||
size_t iniSize = size;
|
||||
|
||||
Uint8 * buf = (Uint8*)data;
|
||||
|
||||
while( size > 0 ) {
|
||||
int ret = SSL_read( mSSL, buf, size );
|
||||
|
||||
if ( ret < 0 ) {
|
||||
_print_error(ret);
|
||||
|
||||
Disconnect();
|
||||
|
||||
return cSocket::Disconnected;
|
||||
} else if ( 0 == ret ) {
|
||||
if ( size == iniSize ) {
|
||||
return cSocket::Disconnected;
|
||||
}
|
||||
|
||||
received = iniSize - size;
|
||||
|
||||
return cSocket::Done;
|
||||
}
|
||||
|
||||
buf+=ret;
|
||||
size-=ret;
|
||||
}
|
||||
|
||||
received = iniSize;
|
||||
|
||||
return cSocket::Done;
|
||||
}
|
||||
|
||||
}}}
|
||||
|
||||
#endif
|
||||
68
src/eepp/network/ssl/backend/openssl/copensslsocket.hpp
Normal file
68
src/eepp/network/ssl/backend/openssl/copensslsocket.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef EE_NETWORKCOPENLSSLSOCKET_HPP
|
||||
#define EE_NETWORKCOPENLSSLSOCKET_HPP
|
||||
|
||||
#include <eepp/network/ssl/csslsocketimpl.hpp>
|
||||
|
||||
#ifdef EE_OPENSSL
|
||||
|
||||
extern "C" {
|
||||
#if EE_PLATFORM == EE_PLATFORM_WIN
|
||||
#undef X509_NAME
|
||||
#undef X509_EXTENSIONS
|
||||
#undef X509_CERT_PAIR
|
||||
#undef PKCS7_ISSUER_AND_SERIAL
|
||||
#undef OCSP_REQUEST
|
||||
#undef OCSP_RESPONSE
|
||||
#define NOCRYPT
|
||||
#endif
|
||||
#include <openssl/bio.h> // BIO objects for I/O
|
||||
#include <openssl/ssl.h> // SSL and SSL_CTX for SSL connections
|
||||
#include <openssl/err.h> // Error reporting
|
||||
#include <openssl/x509v3.h>
|
||||
}
|
||||
|
||||
namespace EE { namespace Network { namespace SSL {
|
||||
|
||||
class cOpenSSLSocket : public cSSLSocketImpl {
|
||||
public:
|
||||
static bool Init();
|
||||
|
||||
static bool End();
|
||||
|
||||
cOpenSSLSocket( cSSLSocket * socket );
|
||||
|
||||
~cOpenSSLSocket();
|
||||
|
||||
cSocket::Status Connect(const cIpAddress& remoteAddress, unsigned short remotePort, cTime timeout = cTime::Zero);
|
||||
|
||||
void Disconnect();
|
||||
|
||||
cSocket::Status Send(const void* data, std::size_t size);
|
||||
|
||||
cSocket::Status Receive(void* data, std::size_t size, std::size_t& received);
|
||||
protected:
|
||||
SSL_CTX * mCTX;
|
||||
::SSL * mSSL;
|
||||
BIO * mBIO;
|
||||
cSSLSocket * mSSLSocket;
|
||||
bool mConnected;
|
||||
cSocket::Status mStatus;
|
||||
int mMaxCertChainDepth;
|
||||
|
||||
private:
|
||||
static int CertVerifyCb(X509_STORE_CTX *x509_ctx, void *arg);
|
||||
|
||||
static bool MatchHostname(const char *name, const char *hostname);
|
||||
|
||||
static bool MatchCommonName(const char *hostname, const X509 *server_cert);
|
||||
|
||||
static bool MatchSubjectAlternativeName(const char *hostname, const X509 *server_cert);
|
||||
|
||||
void _print_error(int err);
|
||||
};
|
||||
|
||||
}}}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
221
src/eepp/network/ssl/backend/openssl/curl_hostcheck.cpp
Normal file
221
src/eepp/network/ssl/backend/openssl/curl_hostcheck.cpp
Normal file
@@ -0,0 +1,221 @@
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at http://curl.haxx.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
/* This file is an amalgamation of hostcheck.c and most of rawstr.c
|
||||
from cURL. The contents of the COPYING file mentioned above are:
|
||||
|
||||
COPYRIGHT AND PERMISSION NOTICE
|
||||
|
||||
Copyright (c) 1996 - 2013, Daniel Stenberg, <daniel@haxx.se>.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any purpose
|
||||
with or without fee is hereby granted, provided that the above copyright
|
||||
notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN
|
||||
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
||||
OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name of a copyright holder shall not
|
||||
be used in advertising or otherwise to promote the sale, use or other dealings
|
||||
in this Software without prior written authorization of the copyright holder.
|
||||
*/
|
||||
|
||||
#ifdef EE_OPENSSL
|
||||
|
||||
#include "curl_hostcheck.h"
|
||||
#include <string.h>
|
||||
|
||||
/* Portable, consistent toupper (remember EBCDIC). Do not use toupper() because
|
||||
its behavior is altered by the current locale. */
|
||||
static char Curl_raw_toupper(char in)
|
||||
{
|
||||
switch (in) {
|
||||
case 'a':
|
||||
return 'A';
|
||||
case 'b':
|
||||
return 'B';
|
||||
case 'c':
|
||||
return 'C';
|
||||
case 'd':
|
||||
return 'D';
|
||||
case 'e':
|
||||
return 'E';
|
||||
case 'f':
|
||||
return 'F';
|
||||
case 'g':
|
||||
return 'G';
|
||||
case 'h':
|
||||
return 'H';
|
||||
case 'i':
|
||||
return 'I';
|
||||
case 'j':
|
||||
return 'J';
|
||||
case 'k':
|
||||
return 'K';
|
||||
case 'l':
|
||||
return 'L';
|
||||
case 'm':
|
||||
return 'M';
|
||||
case 'n':
|
||||
return 'N';
|
||||
case 'o':
|
||||
return 'O';
|
||||
case 'p':
|
||||
return 'P';
|
||||
case 'q':
|
||||
return 'Q';
|
||||
case 'r':
|
||||
return 'R';
|
||||
case 's':
|
||||
return 'S';
|
||||
case 't':
|
||||
return 'T';
|
||||
case 'u':
|
||||
return 'U';
|
||||
case 'v':
|
||||
return 'V';
|
||||
case 'w':
|
||||
return 'W';
|
||||
case 'x':
|
||||
return 'X';
|
||||
case 'y':
|
||||
return 'Y';
|
||||
case 'z':
|
||||
return 'Z';
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
/*
|
||||
* Curl_raw_equal() is for doing "raw" case insensitive strings. This is meant
|
||||
* to be locale independent and only compare strings we know are safe for
|
||||
* this. See http://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for
|
||||
* some further explanation to why this function is necessary.
|
||||
*
|
||||
* The function is capable of comparing a-z case insensitively even for
|
||||
* non-ascii.
|
||||
*/
|
||||
|
||||
static int Curl_raw_equal(const char *first, const char *second)
|
||||
{
|
||||
while(*first && *second) {
|
||||
if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second))
|
||||
/* get out of the loop as soon as they don't match */
|
||||
break;
|
||||
first++;
|
||||
second++;
|
||||
}
|
||||
/* we do the comparison here (possibly again), just to make sure that if the
|
||||
loop above is skipped because one of the strings reached zero, we must not
|
||||
return this as a successful match */
|
||||
return (Curl_raw_toupper(*first) == Curl_raw_toupper(*second));
|
||||
}
|
||||
|
||||
static int Curl_raw_nequal(const char *first, const char *second, size_t max)
|
||||
{
|
||||
while(*first && *second && max) {
|
||||
if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second)) {
|
||||
break;
|
||||
}
|
||||
max--;
|
||||
first++;
|
||||
second++;
|
||||
}
|
||||
if(0 == max)
|
||||
return 1; /* they are equal this far */
|
||||
|
||||
return Curl_raw_toupper(*first) == Curl_raw_toupper(*second);
|
||||
}
|
||||
|
||||
/*
|
||||
* Match a hostname against a wildcard pattern.
|
||||
* E.g.
|
||||
* "foo.host.com" matches "*.host.com".
|
||||
*
|
||||
* We use the matching rule described in RFC6125, section 6.4.3.
|
||||
* http://tools.ietf.org/html/rfc6125#section-6.4.3
|
||||
*/
|
||||
|
||||
static int hostmatch(const char *hostname, const char *pattern)
|
||||
{
|
||||
const char *pattern_label_end, *pattern_wildcard, *hostname_label_end;
|
||||
int wildcard_enabled;
|
||||
size_t prefixlen, suffixlen;
|
||||
pattern_wildcard = strchr(pattern, '*');
|
||||
if(pattern_wildcard == NULL)
|
||||
return Curl_raw_equal(pattern, hostname) ?
|
||||
CURL_HOST_MATCH : CURL_HOST_NOMATCH;
|
||||
|
||||
/* We require at least 2 dots in pattern to avoid too wide wildcard
|
||||
match. */
|
||||
wildcard_enabled = 1;
|
||||
pattern_label_end = strchr(pattern, '.');
|
||||
if(pattern_label_end == NULL || strchr(pattern_label_end+1, '.') == NULL ||
|
||||
pattern_wildcard > pattern_label_end ||
|
||||
Curl_raw_nequal(pattern, "xn--", 4)) {
|
||||
wildcard_enabled = 0;
|
||||
}
|
||||
if(!wildcard_enabled)
|
||||
return Curl_raw_equal(pattern, hostname) ?
|
||||
CURL_HOST_MATCH : CURL_HOST_NOMATCH;
|
||||
|
||||
hostname_label_end = strchr(hostname, '.');
|
||||
if(hostname_label_end == NULL ||
|
||||
!Curl_raw_equal(pattern_label_end, hostname_label_end))
|
||||
return CURL_HOST_NOMATCH;
|
||||
|
||||
/* The wildcard must match at least one character, so the left-most
|
||||
label of the hostname is at least as large as the left-most label
|
||||
of the pattern. */
|
||||
if(hostname_label_end - hostname < pattern_label_end - pattern)
|
||||
return CURL_HOST_NOMATCH;
|
||||
|
||||
prefixlen = pattern_wildcard - pattern;
|
||||
suffixlen = pattern_label_end - (pattern_wildcard+1);
|
||||
return Curl_raw_nequal(pattern, hostname, prefixlen) &&
|
||||
Curl_raw_nequal(pattern_wildcard+1, hostname_label_end - suffixlen,
|
||||
suffixlen) ?
|
||||
CURL_HOST_MATCH : CURL_HOST_NOMATCH;
|
||||
}
|
||||
|
||||
int Tool_Curl_cert_hostcheck(const char *match_pattern, const char *hostname)
|
||||
{
|
||||
if(!match_pattern || !*match_pattern ||
|
||||
!hostname || !*hostname) /* sanity check */
|
||||
return 0;
|
||||
|
||||
if(Curl_raw_equal(hostname, match_pattern)) /* trivial case */
|
||||
return 1;
|
||||
|
||||
if(hostmatch(hostname,match_pattern) == CURL_HOST_MATCH)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
35
src/eepp/network/ssl/backend/openssl/curl_hostcheck.h
Normal file
35
src/eepp/network/ssl/backend/openssl/curl_hostcheck.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#ifndef HEADER_TOOL_CURL_HOSTCHECK_H
|
||||
#define HEADER_TOOL_CURL_HOSTCHECK_H
|
||||
|
||||
#ifdef EE_OPENSSL
|
||||
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at http://curl.haxx.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#define CURL_HOST_NOMATCH 0
|
||||
#define CURL_HOST_MATCH 1
|
||||
int Tool_Curl_cert_hostcheck(const char *match_pattern, const char *hostname);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* HEADER_CURL_HOSTCHECK_H */
|
||||
|
||||
105
src/eepp/network/ssl/csslsocket.cpp
Normal file
105
src/eepp/network/ssl/csslsocket.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
#include <eepp/network/ssl/csslsocket.hpp>
|
||||
#include <eepp/network/ssl/csslsocketimpl.hpp>
|
||||
#include <eepp/network/platform/platformimpl.hpp>
|
||||
|
||||
#ifdef EE_OPENSSL
|
||||
#include <eepp/network/ssl/backend/openssl/copensslsocket.hpp>
|
||||
#endif
|
||||
|
||||
namespace EE { namespace Network { namespace SSL {
|
||||
|
||||
static bool ssl_initialized = false;
|
||||
|
||||
std::string cSSLSocket::CertificatesPath = "";
|
||||
|
||||
bool cSSLSocket::Init() {
|
||||
bool ret = false;
|
||||
|
||||
if ( !ssl_initialized ) {
|
||||
if ( CertificatesPath.empty() ) {
|
||||
#if EE_PLATFORM == EE_PLATFORM_LINUX
|
||||
CertificatesPath = "/etc/ssl/ca-bundle.pem";
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef EE_OPENSSL
|
||||
ret = cOpenSSLSocket::Init();
|
||||
#endif
|
||||
|
||||
ssl_initialized = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool cSSLSocket::End() {
|
||||
bool ret = false;
|
||||
|
||||
if ( ssl_initialized ) {
|
||||
#ifdef EE_OPENSSL
|
||||
ret = cOpenSSLSocket::End();
|
||||
#endif
|
||||
|
||||
ssl_initialized = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
cSSLSocket::cSSLSocket( std::string hostname , bool validateCertificate, bool validateHostname ) :
|
||||
#ifdef EE_OPENSSL
|
||||
mImpl( eeNew( cOpenSSLSocket, ( this ) ) ),
|
||||
#else
|
||||
mImpl( NULL ),
|
||||
#endif
|
||||
mHostName( hostname ),
|
||||
mValidateCertificate( validateCertificate ),
|
||||
mValidateHostname( validateHostname )
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
cSSLSocket::~cSSLSocket() {
|
||||
eeSAFE_DELETE( mImpl );
|
||||
}
|
||||
|
||||
cSocket::Status cSSLSocket::Connect( const cIpAddress& remoteAddress, unsigned short remotePort, cTime timeout ) {
|
||||
Status status = cSocket::Disconnected;
|
||||
|
||||
if ( ( status = cTcpSocket::Connect( remoteAddress, remotePort, timeout ) ) == cSocket::Done ) {
|
||||
status = mImpl->Connect( remoteAddress, remotePort, timeout );
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void cSSLSocket::Disconnect() {
|
||||
mImpl->Disconnect();
|
||||
cTcpSocket::Disconnect();
|
||||
}
|
||||
|
||||
cSocket::Status cSSLSocket::Send(const void* data, std::size_t size) {
|
||||
return mImpl->Send( data, size );
|
||||
}
|
||||
|
||||
cSocket::Status cSSLSocket::Receive(void* data, std::size_t size, std::size_t& received) {
|
||||
return mImpl->Receive( data, size, received );
|
||||
}
|
||||
|
||||
cSocket::Status cSSLSocket::Send(cPacket& packet) {
|
||||
return cTcpSocket::Send( packet );
|
||||
}
|
||||
|
||||
cSocket::Status cSSLSocket::Receive(cPacket& packet) {
|
||||
return cTcpSocket::Receive( packet );
|
||||
}
|
||||
|
||||
cSocket::Status cSSLSocket::TcpSend(const void* data, std::size_t size) {
|
||||
return cTcpSocket::Send( data, size );
|
||||
}
|
||||
|
||||
cSocket::Status cSSLSocket::TcpReceive(void* data, std::size_t size, std::size_t& received) {
|
||||
return cTcpSocket::Receive( data, size, received );
|
||||
}
|
||||
|
||||
}}}
|
||||
29
src/eepp/network/ssl/csslsocketimpl.hpp
Normal file
29
src/eepp/network/ssl/csslsocketimpl.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef EE_NETWORKCSSLSOCKETIMPL_HPP
|
||||
#define EE_NETWORKCSSLSOCKETIMPL_HPP
|
||||
|
||||
#include <eepp/network/ssl/csslsocket.hpp>
|
||||
|
||||
namespace EE { namespace Network { namespace SSL {
|
||||
|
||||
class cSSLSocketImpl {
|
||||
public:
|
||||
cSSLSocketImpl( cSSLSocket * socket ) :
|
||||
mSSLSocket( socket )
|
||||
{}
|
||||
|
||||
virtual ~cSSLSocketImpl() {}
|
||||
|
||||
virtual cSocket::Status Connect(const cIpAddress& remoteAddress, unsigned short remotePort, cTime timeout = cTime::Zero) = 0;
|
||||
|
||||
virtual void Disconnect() = 0;
|
||||
|
||||
virtual cSocket::Status Send(const void* data, std::size_t size) = 0;
|
||||
|
||||
virtual cSocket::Status Receive(void* data, std::size_t size, std::size_t& received) = 0;
|
||||
protected:
|
||||
cSSLSocket * mSSLSocket;
|
||||
};
|
||||
|
||||
}}}
|
||||
|
||||
#endif
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <eepp/graphics/renderer/cgl.hpp>
|
||||
#include <eepp/helper/haikuttf/hkfontmanager.hpp>
|
||||
#include <eepp/physics/cphysicsmanager.hpp>
|
||||
|
||||
#include <eepp/network/ssl/csslsocket.hpp>
|
||||
#include <eepp/window/cbackend.hpp>
|
||||
#include <eepp/window/backend/SDL/cbackendsdl.hpp>
|
||||
#include <eepp/window/backend/SDL2/cbackendsdl2.hpp>
|
||||
@@ -75,6 +75,10 @@ cEngine::~cEngine() {
|
||||
|
||||
HaikuTTF::hkFontManager::DestroySingleton();
|
||||
|
||||
#ifdef EE_SSL_SUPPORT
|
||||
Network::SSL::cSSLSocket::End();
|
||||
#endif
|
||||
|
||||
Destroy();
|
||||
|
||||
eeSAFE_DELETE( mBackend );
|
||||
|
||||
@@ -16,7 +16,7 @@ EE_MAIN_FUNC int main (int argc, char * argv []) {
|
||||
cHttp http;
|
||||
|
||||
// We'll work on http://en.wikipedia.org
|
||||
http.SetHost("http://en.wikipedia.org");
|
||||
http.SetHost("https://en.wikipedia.org");
|
||||
|
||||
// Prepare a request to get the wikipedia main page
|
||||
cHttp::Request request("/wiki/Main_Page");
|
||||
|
||||
Reference in New Issue
Block a user