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