| /* |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| |
| #include "options.hpp" |
| |
| #include <proton/connection_options.hpp> |
| #include <proton/connection.hpp> |
| #include <proton/container.hpp> |
| #include <proton/error_condition.hpp> |
| #include <proton/listen_handler.hpp> |
| #include <proton/listener.hpp> |
| #include <proton/message.hpp> |
| #include <proton/messaging_handler.hpp> |
| #include <proton/ssl.hpp> |
| #include <proton/tracker.hpp> |
| #include <proton/transport.hpp> |
| |
| #include <iostream> |
| |
| |
| using proton::connection_options; |
| using proton::ssl_client_options; |
| using proton::ssl_server_options; |
| using proton::ssl_certificate; |
| |
| // Helper functions defined below. |
| bool using_OpenSSL(); |
| std::string platform_CA(const std::string &base_name); |
| ssl_certificate platform_certificate(const std::string &base_name, const std::string &passwd); |
| std::string find_CN(const std::string &); |
| |
| namespace { |
| const std::string verify_full("full"); // Normal verification |
| const std::string verify_noname("noname"); // Skip matching host name against the certificate |
| const std::string verify_fail("fail"); // Force name mismatch failure |
| std::string verify(verify_full); // Default for example |
| std::string cert_directory; |
| } |
| |
| |
| struct server_handler : public proton::messaging_handler { |
| proton::listener listener; |
| |
| void on_connection_open(proton::connection &c) override { |
| std::cout << "Inbound server connection connected via SSL. Protocol: " << |
| c.transport().ssl().protocol() << std::endl; |
| listener.stop(); // Just expecting the one connection. |
| |
| // Go and do default inbound open stuff too |
| messaging_handler::on_connection_open(c); |
| } |
| |
| void on_transport_error(proton::transport &t) override { |
| listener.stop(); |
| } |
| |
| void on_message(proton::delivery &, proton::message &m) override { |
| std::cout << m.body() << std::endl; |
| } |
| }; |
| |
| |
| class hello_world_direct : public proton::messaging_handler { |
| private: |
| class listener_open_handler : public proton::listen_handler { |
| void on_open(proton::listener& l) override { |
| std::ostringstream url; |
| url << "//:" << l.port() << "/example"; // Connect to the actual port |
| l.container().open_sender(url.str()); |
| } |
| }; |
| |
| listener_open_handler listen_handler; |
| server_handler s_handler; |
| |
| public: |
| void on_container_start(proton::container &c) override { |
| // Configure listener. Details vary by platform. |
| ssl_certificate server_cert = platform_certificate("tserver", "tserverpw"); |
| ssl_server_options ssl_srv(server_cert); |
| connection_options server_opts; |
| server_opts.ssl_server_options(ssl_srv).handler(s_handler); |
| c.server_connection_options(server_opts); |
| |
| // Configure client with a Certificate Authority database |
| // populated with the server's self signed certificate. |
| connection_options client_opts; |
| if (verify == verify_full) { |
| ssl_client_options ssl_cli(platform_CA("tserver")); |
| client_opts.ssl_client_options(ssl_cli); |
| // The next line is optional in normal use. Since the |
| // example uses IP addresses in the connection string, use |
| // the virtual_host option to set the server host name |
| // used for certificate verification: |
| client_opts.virtual_host("test_server"); |
| } else if (verify == verify_noname) { |
| // Downgrade the verification from VERIFY_PEER_NAME to VERIFY_PEER. |
| ssl_client_options ssl_cli(platform_CA("tserver"), proton::ssl::VERIFY_PEER); |
| client_opts.ssl_client_options(ssl_cli); |
| } else if (verify == verify_fail) { |
| ssl_client_options ssl_cli(platform_CA("tserver")); |
| client_opts.ssl_client_options(ssl_cli); |
| client_opts.virtual_host("wrong_name_for_server"); // Pick any name that doesn't match. |
| } else throw std::logic_error("bad verify mode: " + verify); |
| |
| c.client_connection_options(client_opts); |
| s_handler.listener = c.listen("//:0", listen_handler); // Listen on port 0 for a dynamic port |
| } |
| |
| void on_connection_open(proton::connection &c) override { |
| std::string subject = c.transport().ssl().remote_subject(); |
| std::cout << "Outgoing client connection connected via SSL. Server certificate identity " << |
| find_CN(subject) << std::endl; |
| } |
| |
| void on_transport_error(proton::transport &t) override { |
| std::string err = t.error().what(); |
| if (verify == verify_fail && err.find("certificate") != std::string::npos) { |
| std::cout << "Expected failure of connection with wrong peer name: " << err |
| << std::endl; |
| } else { |
| std::cout << "Unexpected transport error: " << err << std::endl; |
| } |
| } |
| |
| void on_sendable(proton::sender &s) override { |
| proton::message m; |
| m.body("Hello World!"); |
| s.send(m); |
| s.close(); |
| } |
| |
| void on_tracker_accept(proton::tracker &t) override { |
| // All done. |
| t.connection().close(); |
| } |
| }; |
| |
| int main(int argc, char **argv) { |
| example::options opts(argc, argv); |
| opts.add_value(cert_directory, 'c', "cert_directory", |
| "directory containing SSL certificates and private key information", "CERTDIR"); |
| opts.add_value(verify, 'v', "verify", "verify type: \"minimum\", \"full\", \"fail\"", "VERIFY"); |
| |
| try { |
| opts.parse(); |
| |
| size_t sz = cert_directory.size(); |
| if (sz) { |
| if (cert_directory[sz -1] != '/') { |
| cert_directory.append("/"); |
| } |
| } else { |
| cert_directory = "ssl-certs/"; |
| } |
| |
| if (verify != verify_noname && verify != verify_full && verify != verify_fail) { |
| throw std::runtime_error("bad verify argument: " + verify); |
| } |
| |
| hello_world_direct hwd; |
| proton::container(hwd).run(); |
| return 0; |
| } catch (const example::bad_option& e) { |
| std::cout << opts << std::endl << e.what() << std::endl; |
| } catch (const std::exception& e) { |
| std::cerr << e.what() << std::endl; |
| } |
| return 1; |
| } |
| |
| |
| bool using_OpenSSL() { |
| // Current defaults. |
| #if defined(_WIN32) |
| return false; |
| #else |
| return true; |
| #endif |
| } |
| |
| ssl_certificate platform_certificate(const std::string &base_name, const std::string &passwd) { |
| if (using_OpenSSL()) { |
| // The first argument will be the name of the file containing the public certificate, the |
| // second argument will be the name of the file containing the private key. |
| return ssl_certificate(cert_directory + base_name + "-certificate.pem", |
| cert_directory + base_name + "-private-key.pem", passwd); |
| } |
| else { |
| // Windows SChannel |
| // The first argument will be the database or store that contains one or more complete certificates |
| // (public and private data). The second will be an optional name of the certificate in the store |
| // (not used in this example with one certificate per store). |
| return ssl_certificate(cert_directory + base_name + "-full.p12", "", passwd); |
| } |
| } |
| |
| std::string platform_CA(const std::string &base_name) { |
| if (using_OpenSSL()) { |
| // In this simple example with self-signed certificates, the peer's certificate is the CA database. |
| return cert_directory + base_name + "-certificate.pem"; |
| } |
| else { |
| // Windows SChannel. Use a pkcs#12 file with just the peer's public certificate information. |
| return cert_directory + base_name + "-certificate.p12"; |
| } |
| } |
| |
| std::string find_CN(const std::string &subject) { |
| // The subject string is returned with different whitespace and component ordering between platforms. |
| // Here we just return the common name by searching for "CN=...." in the subject, knowing that |
| // the test certificates do not contain any escaped characters. |
| size_t pos = subject.find("CN="); |
| if (pos == std::string::npos) throw std::runtime_error("No common name in certificate subject"); |
| std::string cn = subject.substr(pos); |
| pos = cn.find(','); |
| return pos == std::string::npos ? cn : cn.substr(0, pos); |
| } |