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