/**
* Copyright ( C ) 2013 - 2017 Jessica James .
*
* Permission to use , copy , modify , and / or 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 " AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL , DIRECT , INDIRECT , OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN ACTION
* OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
*
* Written by Jessica James < jessica . aj @ outlook . com >
*/
# include <utility> // std::move
# include <openssl/ssl.h> // OpenSSL SSL functions
# include <openssl/err.h> // OpenSSL SSL errors
# include "SecureSocket.h"
struct Jupiter : : SecureSocket : : SSLData
{
SSL * handle = nullptr ;
SSL_CTX * context = nullptr ;
const SSL_METHOD * method = nullptr ;
Jupiter : : SecureSocket : : EncryptionMethod eMethod = ANY ;
std : : string cert ;
std : : string key ;
~ SSLData ( ) ;
} ;
Jupiter : : SecureSocket : : SSLData : : ~ SSLData ( )
{
if ( Jupiter : : SecureSocket : : SSLData : : handle ! = nullptr )
{
if ( SSL_shutdown ( Jupiter : : SecureSocket : : SSLData : : handle ) = = 0 ) SSL_shutdown ( Jupiter : : SecureSocket : : SSLData : : handle ) ;
SSL_free ( Jupiter : : SecureSocket : : SSLData : : handle ) ;
}
if ( Jupiter : : SecureSocket : : SSLData : : context ! = nullptr ) SSL_CTX_free ( Jupiter : : SecureSocket : : SSLData : : context ) ;
}
Jupiter : : SecureSocket & Jupiter : : SecureSocket : : operator = ( Jupiter : : SecureSocket & & source )
{
Jupiter : : Socket : : operator = ( std : : move ( source ) ) ;
Jupiter : : SecureSocket : : SSLdata_ = source . SSLdata_ ;
source . SSLdata_ = nullptr ;
return * this ;
}
Jupiter : : SecureSocket : : SecureSocket ( ) : Jupiter : : Socket ( )
{
Jupiter : : SecureSocket : : SSLdata_ = new Jupiter : : SecureSocket : : SSLData ( ) ;
}
Jupiter : : SecureSocket : : SecureSocket ( size_t bufferSize ) : Jupiter : : Socket ( bufferSize )
{
Jupiter : : SecureSocket : : SSLdata_ = new Jupiter : : SecureSocket : : SSLData ( ) ;
}
Jupiter : : SecureSocket : : SecureSocket ( Jupiter : : Socket & & source ) : Jupiter : : Socket ( std : : move ( source ) )
{
Jupiter : : SecureSocket : : SSLdata_ = new Jupiter : : SecureSocket : : SSLData ( ) ;
}
Jupiter : : SecureSocket : : SecureSocket ( Jupiter : : SecureSocket & & source ) : Jupiter : : Socket ( std : : move ( source ) )
{
Jupiter : : SecureSocket : : SSLdata_ = source . SSLdata_ ;
source . SSLdata_ = nullptr ;
}
Jupiter : : SecureSocket : : ~ SecureSocket ( )
{
if ( Jupiter : : SecureSocket : : SSLdata_ ! = nullptr ) delete Jupiter : : SecureSocket : : SSLdata_ ;
}
Jupiter : : SecureSocket * Jupiter : : SecureSocket : : accept ( )
{
SocketType tSock = SSL_accept ( Jupiter : : SecureSocket : : SSLdata_ - > handle ) ;
if ( tSock > 0 )
{
SecureSocket * r = new SecureSocket ( Jupiter : : SecureSocket : : getBufferSize ( ) ) ;
r - > setDescriptor ( tSock ) ;
r - > setType ( this - > getType ( ) ) ;
r - > setProtocol ( this - > getProtocol ( ) ) ;
return r ;
}
return nullptr ;
}
bool Jupiter : : SecureSocket : : bind ( const char * hostname , unsigned short iPort , bool andListen )
{
return Jupiter : : Socket : : bind ( hostname , iPort , andListen ) ;
}
void Jupiter : : SecureSocket : : shutdown ( )
{
Jupiter : : Socket : : shutdown ( ) ;
if ( Jupiter : : SecureSocket : : SSLdata_ ! = nullptr & & Jupiter : : SecureSocket : : SSLdata_ - > handle ! = nullptr )
{
if ( SSL_shutdown ( Jupiter : : SecureSocket : : SSLdata_ - > handle ) = = 0 )
SSL_shutdown ( Jupiter : : SecureSocket : : SSLdata_ - > handle ) ;
}
}
void Jupiter : : SecureSocket : : close ( )
{
Jupiter : : Socket : : close ( ) ;
if ( Jupiter : : SecureSocket : : SSLdata_ ! = nullptr & & Jupiter : : SecureSocket : : SSLdata_ - > handle ! = nullptr )
{
if ( SSL_shutdown ( Jupiter : : SecureSocket : : SSLdata_ - > handle ) = = 0 )
SSL_shutdown ( Jupiter : : SecureSocket : : SSLdata_ - > handle ) ;
SSL_free ( Jupiter : : SecureSocket : : SSLdata_ - > handle ) ;
Jupiter : : SecureSocket : : SSLdata_ - > handle = nullptr ;
}
}
const SSL_METHOD * translateEncryptionMethod ( Jupiter : : SecureSocket : : EncryptionMethod method )
{
switch ( method )
{
//case Jupiter::SecureSocket::EncryptionMethod::SSL3:
// return SSLv3_method();
case Jupiter : : SecureSocket : : EncryptionMethod : : TLS1 :
return TLSv1_method ( ) ;
case Jupiter : : SecureSocket : : EncryptionMethod : : TLS1_1 :
return TLSv1_1_method ( ) ;
case Jupiter : : SecureSocket : : EncryptionMethod : : TLS1_2 :
return TLSv1_2_method ( ) ;
case Jupiter : : SecureSocket : : EncryptionMethod : : DTLS1 :
return DTLSv1_method ( ) ;
case Jupiter : : SecureSocket : : EncryptionMethod : : ANY :
return SSLv23_method ( ) ;
default :
return nullptr ;
}
}
const char * Jupiter : : SecureSocket : : getCipherName ( ) const
{
return SSL_CIPHER_get_name ( SSL_get_current_cipher ( Jupiter : : SecureSocket : : SSLdata_ - > handle ) ) ;
}
Jupiter : : SecureSocket : : EncryptionMethod Jupiter : : SecureSocket : : getMethod ( ) const
{
return Jupiter : : SecureSocket : : SSLdata_ - > eMethod ;
}
void Jupiter : : SecureSocket : : setMethod ( Jupiter : : SecureSocket : : EncryptionMethod method )
{
Jupiter : : SecureSocket : : SSLdata_ - > eMethod = method ;
}
bool loadCertificate ( SSL_CTX * context , const char * cert , const char * key )
{
if ( SSL_CTX_load_verify_locations ( context , cert , key ) ! = 1 ) ERR_print_errors_fp ( stderr ) ;
if ( SSL_CTX_set_default_verify_paths ( context ) ! = 1 ) ERR_print_errors_fp ( stderr ) ;
if ( SSL_CTX_use_certificate_file ( context , cert , SSL_FILETYPE_PEM ) < = 0 )
{
ERR_print_errors_fp ( stderr ) ;
return false ;
}
if ( SSL_CTX_use_PrivateKey_file ( context , key , SSL_FILETYPE_PEM ) < = 0 )
{
ERR_print_errors_fp ( stderr ) ;
return false ;
}
if ( ! SSL_CTX_check_private_key ( context ) ) return false ;
return true ;
}
void Jupiter : : SecureSocket : : setCertificate ( const Jupiter : : ReadableString & cert , const Jupiter : : ReadableString & key )
{
Jupiter : : SecureSocket : : SSLdata_ - > cert = static_cast < std : : string > ( cert ) ;
Jupiter : : SecureSocket : : SSLdata_ - > key = static_cast < std : : string > ( key ) ;
}
void Jupiter : : SecureSocket : : setCertificate ( const Jupiter : : ReadableString & pem )
{
Jupiter : : SecureSocket : : setCertificate ( pem , pem ) ;
}
bool Jupiter : : SecureSocket : : connect ( const char * hostname , unsigned short iPort , const char * clientAddress , unsigned short clientPort )
{
return Jupiter : : Socket : : connect ( hostname , iPort , clientAddress , clientPort ) & & this - > initSSL ( ) ;
}
int Jupiter : : SecureSocket : : peek ( )
{
if ( Jupiter : : SecureSocket : : SSLdata_ - > handle = = nullptr )
return - 1 ;
Jupiter : : Socket : : Buffer & buffer = this - > getInternalBuffer ( ) ;
buffer . erase ( ) ;
int r = SSL_peek ( Jupiter : : SecureSocket : : SSLdata_ - > handle , buffer . get_str ( ) , this - > getBufferSize ( ) ) ;
if ( r > 0 )
buffer . set_length ( r ) ;
return r ;
}
int Jupiter : : SecureSocket : : recv ( )
{
if ( Jupiter : : SecureSocket : : SSLdata_ - > handle = = nullptr )
return - 1 ;
Jupiter : : Socket : : Buffer & buffer = this - > getInternalBuffer ( ) ;
buffer . erase ( ) ;
int r = SSL_read ( Jupiter : : SecureSocket : : SSLdata_ - > handle , buffer . get_str ( ) , this - > getBufferSize ( ) ) ;
if ( r > 0 )
buffer . set_length ( r ) ;
return r ;
}
int Jupiter : : SecureSocket : : send ( const char * data , size_t datalen )
{
return SSL_write ( Jupiter : : SecureSocket : : SSLdata_ - > handle , data , datalen ) ;
}
bool Jupiter : : SecureSocket : : initSSL ( )
{
SSL_load_error_strings ( ) ;
SSL_library_init ( ) ;
if ( Jupiter : : SecureSocket : : SSLdata_ - > context = = nullptr )
{
if ( Jupiter : : SecureSocket : : SSLdata_ - > method = = nullptr )
{
Jupiter : : SecureSocket : : SSLdata_ - > method = translateEncryptionMethod ( Jupiter : : SecureSocket : : SSLdata_ - > eMethod ) ;
if ( Jupiter : : SecureSocket : : SSLdata_ - > method = = nullptr )
return false ;
}
Jupiter : : SecureSocket : : SSLdata_ - > context = SSL_CTX_new ( Jupiter : : SecureSocket : : SSLdata_ - > method ) ;
if ( Jupiter : : SecureSocket : : SSLdata_ - > context = = nullptr )
{
ERR_print_errors_fp ( stderr ) ;
return false ;
}
if ( ! Jupiter : : SecureSocket : : SSLdata_ - > cert . empty ( ) )
loadCertificate ( Jupiter : : SecureSocket : : SSLdata_ - > context , Jupiter : : SecureSocket : : SSLdata_ - > cert . c_str ( ) , Jupiter : : SecureSocket : : SSLdata_ - > key . c_str ( ) ) ;
}
Jupiter : : SecureSocket : : SSLdata_ - > handle = SSL_new ( Jupiter : : SecureSocket : : SSLdata_ - > context ) ;
if ( Jupiter : : SecureSocket : : SSLdata_ - > handle = = nullptr )
{
ERR_print_errors_fp ( stderr ) ;
return false ;
}
if ( SSL_set_fd ( Jupiter : : SecureSocket : : SSLdata_ - > handle , this - > getDescriptor ( ) ) = = 0 )
{
ERR_print_errors_fp ( stderr ) ;
return false ;
}
if ( SSL_set_tlsext_host_name ( Jupiter : : SecureSocket : : SSLdata_ - > handle , this - > getRemoteHostnameC ( ) ) ! = 1 ) // This error check is potentially redundant, but no documentation has been found.
{
ERR_print_errors_fp ( stderr ) ;
return false ;
}
int t = SSL_connect ( Jupiter : : SecureSocket : : SSLdata_ - > handle ) ;
if ( t ! = 1 )
{
ERR_print_errors_fp ( stderr ) ;
return false ;
}
return true ;
}