ssl connection first try

master
Marc Wäckerlin 15 years ago
parent d5548a3f13
commit d120572c67
  1. 175
      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:
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;
};
//@}

Loading…
Cancel
Save