ssl connection first try
This commit is contained in:
177
src/openssl.hxx
177
src/openssl.hxx
@@ -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:
|
||||
::BIO* _bio;
|
||||
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;
|
||||
};
|
||||
|
||||
//@}
|
||||
|
Reference in New Issue
Block a user