diff --git a/src/openssl.hxx b/src/openssl.hxx index 5cbc1e0..6f00f80 100644 --- a/src/openssl.hxx +++ b/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(hostPort.c_str()))) + if (!(_bio=BIO_new_connect(const_cast(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(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; }; //@}