|
|
|
@ -71,6 +71,13 @@ namespace openssl { |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
class ssl_error: public openssl_error { |
|
|
|
|
public: |
|
|
|
|
ssl_error(const std::string& reason) throw(): |
|
|
|
|
openssl_error("ssl: "+reason) { |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
class allocation_failed: public x509_error { |
|
|
|
|
public: |
|
|
|
|
allocation_failed() throw(): |
|
|
|
@ -136,6 +143,40 @@ namespace openssl { |
|
|
|
|
bio_error("connection failed to: "+hostPort) { |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
class bio_closed_connection: public bio_error { |
|
|
|
|
public: |
|
|
|
|
bio_closed_connection() throw(): bio_error("closed connection") {} |
|
|
|
|
}; |
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
class bio_read_error: public bio_error { |
|
|
|
|
public: |
|
|
|
|
bio_read_error() throw(): bio_error("read error") {} |
|
|
|
|
}; |
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
class bio_write_error: public bio_error { |
|
|
|
|
public: |
|
|
|
|
bio_write_error() throw(): bio_error("write error") {} |
|
|
|
|
}; |
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
class ssl_cannot_create_context: public ssl_error { |
|
|
|
|
public: |
|
|
|
|
ssl_cannot_create_context() throw(): ssl_error("cannot create context") {} |
|
|
|
|
}; |
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
class ssl_certificate_file_not_loaded: public ssl_error { |
|
|
|
|
public: |
|
|
|
|
ssl_certificate_file_not_loaded(const std::string& file) throw(): |
|
|
|
|
ssl_error("certificate file not loaded: "+file) { |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
class ssl_certificate_folder_not_loaded: public ssl_error { |
|
|
|
|
public: |
|
|
|
|
ssl_certificate_folder_not_loaded(const std::string& path) throw(): |
|
|
|
|
ssl_error("certificate folder not loaded: "+path) { |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
//@}
|
|
|
|
|
|
|
|
|
|
//! @addtogroup openssllib
|
|
|
|
@ -266,9 +307,18 @@ namespace openssl { |
|
|
|
|
X509List _ca; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
class BIO { |
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
|
|
|
|
|
BIO(const BIO&); |
|
|
|
|
BIO& operator=(const BIO&); |
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
BIO() {} |
|
|
|
|
|
|
|
|
|
BIO(): _bio(0) {} |
|
|
|
|
|
|
|
|
|
~BIO() { |
|
|
|
|
try { |
|
|
|
|
close(); |
|
|
|
@ -276,17 +326,132 @@ namespace openssl { |
|
|
|
|
if (!std::uncaught_exception()) throw; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
void connect(const std::string& hostPort) { |
|
|
|
|
|
|
|
|
|
BIO& connect(const std::string& hostPort) { |
|
|
|
|
close(); |
|
|
|
|
if (_bio = BIO_new_connect(const_cast<char*>(hostPort.c_str()))) |
|
|
|
|
if (!(_bio=BIO_new_connect(const_cast<char*>(hostPort.c_str()))) || |
|
|
|
|
BIO_do_connect(_bio)<=0) |
|
|
|
|
throw bio_connection_failed(hostPort); |
|
|
|
|
// ...
|
|
|
|
|
return *this; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
BIO& operator>>(std::string& s) { |
|
|
|
|
s += read(); |
|
|
|
|
return *this; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
BIO& operator<<(const std::string& s) { |
|
|
|
|
return write(s); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*! @todo How can I find out, whether there's still data
|
|
|
|
|
available, or whether a response has been finished and |
|
|
|
|
server is waiting for next request, but connection is still |
|
|
|
|
open? */ |
|
|
|
|
std::string read() { |
|
|
|
|
if (!_bio) throw bio_closed_connection(); |
|
|
|
|
const int BUFF_SZ(1024); |
|
|
|
|
char buff[BUFF_SZ]; |
|
|
|
|
int x(BIO_read(_bio, buff, BUFF_SZ)); |
|
|
|
|
if (x<=0) |
|
|
|
|
if (BIO_should_retry(_bio)) return read(); |
|
|
|
|
else throw bio_read_error(); |
|
|
|
|
return std::string(buff, x); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
BIO& write(const std::string& s) { |
|
|
|
|
int x(BIO_write(_bio, s.begin().operator->(), s.size())); |
|
|
|
|
if (x<=0) |
|
|
|
|
if (BIO_should_retry(_bio)) return write(s); |
|
|
|
|
else throw bio_write_error(); |
|
|
|
|
else |
|
|
|
|
if (x<s.size()) return write(s.substr(x)); |
|
|
|
|
return *this; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void close() { |
|
|
|
|
//! @todo tbd
|
|
|
|
|
BIO_free_all(_bio); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
|
|
|
|
|
friend class SSL; |
|
|
|
|
::BIO* _bio; |
|
|
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
//! Read authorized certificate from a single pem file.
|
|
|
|
|
class TrustStore { |
|
|
|
|
public: |
|
|
|
|
TrustStore(const std::string& pathToPemFile): |
|
|
|
|
_file(pathToPemFile) { |
|
|
|
|
} |
|
|
|
|
const std::string& file() const { |
|
|
|
|
return _file; |
|
|
|
|
} |
|
|
|
|
private: |
|
|
|
|
std::string _file; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
class CertificateFolder { |
|
|
|
|
public: |
|
|
|
|
CertificateFolder(const std::string& certificateFolder): |
|
|
|
|
_path(certificateFolder) { |
|
|
|
|
} |
|
|
|
|
const std::string& path() const { |
|
|
|
|
return _path; |
|
|
|
|
} |
|
|
|
|
private: |
|
|
|
|
std::string _path; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
class SSL { |
|
|
|
|
private: |
|
|
|
|
SSL(); |
|
|
|
|
SSL(const SSL&); |
|
|
|
|
SSL& operator=(const SSL&); |
|
|
|
|
public: |
|
|
|
|
SSL(const TrustStore& file): |
|
|
|
|
_ctx(SSL_CTX_new(SSLv23_client_method())), |
|
|
|
|
_ssl(0) { |
|
|
|
|
if (!_ctx) throw ssl_cannot_create_context(); |
|
|
|
|
if (!SSL_CTX_load_verify_locations(_ctx, file.file().c_str(), 0)) |
|
|
|
|
throw ssl_certificate_file_not_loaded(file.file()); |
|
|
|
|
} |
|
|
|
|
SSL(const CertificateFolder& folder): |
|
|
|
|
_ctx(SSL_CTX_new(SSLv23_client_method())), |
|
|
|
|
_ssl(0) { |
|
|
|
|
if (!_ctx) throw ssl_cannot_create_context(); |
|
|
|
|
if (!SSL_CTX_load_verify_locations(_ctx, 0, folder.path().c_str())) |
|
|
|
|
throw ssl_certificate_folder_not_loaded(folder.path()); |
|
|
|
|
} |
|
|
|
|
~SSL() { |
|
|
|
|
close(); |
|
|
|
|
SSL_CTX_free(_ctx); |
|
|
|
|
} |
|
|
|
|
BIO& connect(const std::string& hostPort) { |
|
|
|
|
close(); |
|
|
|
|
if (!(_bio._bio=BIO_new_ssl_connect(_ctx))) |
|
|
|
|
throw bio_connection_failed(hostPort); |
|
|
|
|
BIO_get_ssl(_bio._bio, &_ssl); |
|
|
|
|
if (!_ssl)
|
|
|
|
|
SSL_set_mode(_ssl, SSL_MODE_AUTO_RETRY); |
|
|
|
|
BIO_set_conn_hostname(_bio._bio, const_cast<char*>(hostPort.c_str())); |
|
|
|
|
if (BIO_do_connect(_bio._bio)<=0) throw bio_connection_failed(hostPort); |
|
|
|
|
return _bio; |
|
|
|
|
} |
|
|
|
|
SSL& close() { |
|
|
|
|
_bio.close(); |
|
|
|
|
_ssl = 0; //! @todo is this correct? <--
|
|
|
|
|
return *this; |
|
|
|
|
} |
|
|
|
|
bool verifyResult() { |
|
|
|
|
return _ssl && SSL_get_verify_result(_ssl)==X509_V_OK; |
|
|
|
|
} |
|
|
|
|
private: |
|
|
|
|
SSL_CTX *_ctx; |
|
|
|
|
::SSL *_ssl; |
|
|
|
|
BIO _bio; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
//@}
|
|
|
|
|