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 {
|
class allocation_failed: public x509_error {
|
||||||
public:
|
public:
|
||||||
allocation_failed() throw():
|
allocation_failed() throw():
|
||||||
@@ -136,6 +143,40 @@ namespace openssl {
|
|||||||
bio_error("connection failed to: "+hostPort) {
|
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
|
//! @addtogroup openssllib
|
||||||
@@ -266,9 +307,18 @@ namespace openssl {
|
|||||||
X509List _ca;
|
X509List _ca;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
class BIO {
|
class BIO {
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
BIO(const BIO&);
|
||||||
|
BIO& operator=(const BIO&);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BIO() {}
|
|
||||||
|
BIO(): _bio(0) {}
|
||||||
|
|
||||||
~BIO() {
|
~BIO() {
|
||||||
try {
|
try {
|
||||||
close();
|
close();
|
||||||
@@ -276,17 +326,132 @@ namespace openssl {
|
|||||||
if (!std::uncaught_exception()) throw;
|
if (!std::uncaught_exception()) throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void connect(const std::string& hostPort) {
|
|
||||||
|
BIO& connect(const std::string& hostPort) {
|
||||||
close();
|
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);
|
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() {
|
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:
|
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