1 /*
2  * server_settings.hpp
3  *
4  *  Created on: 15 févr. 2016
5  *      Author: gaudryc
6  */
7 #pragma once
8 #ifndef WEBSERVER_SERVER_SETTINGS_HPP_
9 #define WEBSERVER_SERVER_SETTINGS_HPP_
10 
11 #include <string>
12 #include <boost/asio.hpp>
13 #include <boost/asio/ssl.hpp>
14 #include <boost/algorithm/string.hpp>
15 
16 namespace http {
17 namespace server {
18 
19 struct server_settings {
20 public:
server_settingshttp::server::server_settings21 	server_settings() :
22 		is_secure_(false) {}
server_settingshttp::server::server_settings23 	server_settings(const server_settings & s) :
24 		www_root(s.www_root),
25 		listening_address(s.listening_address),
26 		listening_port(s.listening_port),
27 		php_cgi_path(s.php_cgi_path),
28 		is_secure_(s.is_secure_)
29 		{}
~server_settingshttp::server::server_settings30 	virtual ~server_settings() {}
operator =http::server::server_settings31 	server_settings & operator=(const server_settings & s) {
32 		www_root = s.www_root;
33 		listening_address = s.listening_address;
34 		listening_port = s.listening_port;
35 		php_cgi_path = s.php_cgi_path;
36 		is_secure_ = s.is_secure_;
37 		return *this;
38 	}
is_securehttp::server::server_settings39 	bool is_secure() const {
40 		return is_secure_;
41 	}
is_enabledhttp::server::server_settings42 	bool is_enabled() const {
43 		return ((listening_port != "0") && (listening_port != ""));
44 	}
is_php_enabledhttp::server::server_settings45 	bool is_php_enabled() const {
46 		return !php_cgi_path.empty();
47 	}
48 	/**
49 	 * Set relevant values
50 	 */
sethttp::server::server_settings51 	virtual void set(const server_settings & settings) {
52 		www_root = get_valid_value(listening_address, settings.www_root);
53 		listening_address = get_valid_value(listening_address, settings.listening_address);
54 		listening_port = get_valid_value(listening_port, settings.listening_port);
55 		php_cgi_path = get_valid_value(php_cgi_path, settings.php_cgi_path);
56 		if (listening_port == "0") {
57 			listening_port.clear();// server NOT enabled
58 		}
59 	}
60 
to_stringhttp::server::server_settings61 	virtual std::string to_string() const {
62 		return std::string("'server_settings[is_secure_=") + (is_secure_ == true ? "true" : "false") +
63 			", www_root='" + www_root + "'" +
64 			", listening_address='" + listening_address + "'" +
65 			", listening_port='" + listening_port + "'" +
66 			", php_cgi_path='" + php_cgi_path + "'" +
67 			"]'";
68 	}
69 
70 protected:
server_settingshttp::server::server_settings71 	explicit server_settings(bool is_secure) :
72 		is_secure_(is_secure) {}
get_valid_valuehttp::server::server_settings73 	std::string get_valid_value(const std::string & old_value, const std::string & new_value) {
74 		if ((!new_value.empty()) && (new_value.compare(old_value) != 0)) {
75 			return new_value;
76 		}
77 		return old_value;
78 	}
79 public:
80 	std::string www_root;
81 	std::string listening_address;
82 	std::string listening_port;
83 
84 	std::string php_cgi_path; //if not empty, php files are handled
85 	//feature
86 	//std::string fastcgi_php_server; (like nginx)
87 private:
88 	bool is_secure_;
89 };
90 
91 #ifdef WWW_ENABLE_SSL
92 
93 struct ssl_server_settings : public server_settings {
94 public:
95 	std::string ssl_method;
96 	std::string certificate_chain_file_path;
97 	std::string ca_cert_file_path;
98 	std::string cert_file_path;
99 
100 	std::string private_key_file_path;
101 	std::string private_key_pass_phrase;
102 
103 	std::string ssl_options;
104 	std::string tmp_dh_file_path;
105 
106 	bool verify_peer;
107 	bool verify_fail_if_no_peer_cert;
108 	std::string verify_file_path;
109 
ssl_server_settingshttp::server::ssl_server_settings110 	ssl_server_settings() :
111 			server_settings(true),
112 			verify_peer(false),
113 			verify_fail_if_no_peer_cert(false) {}
ssl_server_settingshttp::server::ssl_server_settings114 	ssl_server_settings(const ssl_server_settings & s) :
115 		server_settings::server_settings(s),
116 		ssl_method(s.ssl_method),
117 		certificate_chain_file_path(s.certificate_chain_file_path),
118 		ca_cert_file_path(s.ca_cert_file_path),
119 		cert_file_path(s.cert_file_path),
120 		private_key_file_path(s.private_key_file_path),
121 		private_key_pass_phrase(s.private_key_pass_phrase),
122 		ssl_options(s.ssl_options),
123 		tmp_dh_file_path(s.tmp_dh_file_path),
124 		verify_peer(s.verify_peer),
125 		verify_fail_if_no_peer_cert(s.verify_fail_if_no_peer_cert),
126 		verify_file_path(s.verify_file_path) {}
~ssl_server_settingshttp::server::ssl_server_settings127 	virtual ~ssl_server_settings() {}
operator =http::server::ssl_server_settings128 	ssl_server_settings & operator=(const ssl_server_settings & s) {
129 		server_settings::operator=(s);
130 		ssl_method = s.ssl_method;
131 		certificate_chain_file_path = s.certificate_chain_file_path;
132 		ca_cert_file_path = s.ca_cert_file_path;
133 		cert_file_path = s.cert_file_path;
134 		private_key_file_path = s.private_key_file_path;
135 		private_key_pass_phrase = s.private_key_pass_phrase;
136 		ssl_options = s.ssl_options;
137 		tmp_dh_file_path = s.tmp_dh_file_path;
138 		verify_peer = s.verify_peer;
139 		verify_fail_if_no_peer_cert = s.verify_fail_if_no_peer_cert;
140 		verify_file_path = s.verify_file_path;
141 		return *this;
142 	}
143 
get_ssl_methodhttp::server::ssl_server_settings144 	boost::asio::ssl::context::method get_ssl_method() const {
145 		boost::asio::ssl::context::method method;
146 		if (ssl_method.compare("tlsv1") == 0) {
147 			method = boost::asio::ssl::context::tlsv1;
148 		} else if (ssl_method.compare("tlsv1_server") == 0) {
149 			method = boost::asio::ssl::context::tlsv1_server;
150 		} else if (ssl_method.compare("sslv23") == 0) {
151 			method = boost::asio::ssl::context::sslv23;
152 		} else if (ssl_method.compare("sslv23_server") == 0) {
153 			method = boost::asio::ssl::context::sslv23_server;
154 		} else if (ssl_method.compare("tlsv11") == 0) {
155 			method = boost::asio::ssl::context::tlsv11;
156 		} else if (ssl_method.compare("tlsv11_server") == 0) {
157 			method = boost::asio::ssl::context::tlsv11_server;
158 		} else if (ssl_method.compare("tlsv12") == 0) {
159 			method = boost::asio::ssl::context::tlsv12;
160 		} else if (ssl_method.compare("tlsv12_server") == 0) {
161 			method = boost::asio::ssl::context::tlsv12_server;
162 		} else {
163 			std::string error_message("invalid SSL method ");
164 			error_message.append("'").append(ssl_method).append("'");
165 			throw std::invalid_argument(error_message);
166 		}
167 		return method;
168 	}
169 
get_ssl_optionshttp::server::ssl_server_settings170 	boost::asio::ssl::context::options get_ssl_options() const {
171 		boost::asio::ssl::context::options opts(0x0L);
172 
173 		std::string error_message("");
174 
175 		std::vector<std::string> options_array;
176 		boost::split(options_array, ssl_options, boost::is_any_of(","), boost::token_compress_on);
177 		std::vector<std::string>::iterator itt;
178 		for (itt = options_array.begin(); itt != options_array.end(); ++itt) {
179 			std::string option = *itt;
180 			if (option.compare("default_workarounds") == 0) {
181 				update_options(opts, boost::asio::ssl::context::default_workarounds);
182 			} else if (option.compare("single_dh_use") == 0) {
183 				update_options(opts, boost::asio::ssl::context::single_dh_use);
184 			} else if (option.compare("no_sslv2") == 0) {
185 				update_options(opts, boost::asio::ssl::context::no_sslv2);
186 			} else if (option.compare("no_sslv3") == 0) {
187 				update_options(opts, boost::asio::ssl::context::no_sslv3);
188 			} else if (option.compare("no_tlsv1") == 0) {
189 				update_options(opts, boost::asio::ssl::context::no_tlsv1);
190 			} else if (option.compare("no_tlsv1_1") == 0) {
191 				update_options(opts, boost::asio::ssl::context::no_tlsv1_1);
192 			} else if (option.compare("no_tlsv1_2") == 0) {
193 				update_options(opts, boost::asio::ssl::context::no_tlsv1_2);
194 			} else if (option.compare("no_compression") == 0) {
195 				update_options(opts, boost::asio::ssl::context::no_compression);
196 			} else {
197 				if (error_message.empty()) {
198 					error_message.append("unknown SSL option(s) : ");
199 				}
200 				if (error_message.find("'") != std::string::npos) {
201 					error_message.append(", ");
202 				}
203 				error_message.append("'").append(option).append("'");
204 			}
205 		}
206 		if (!error_message.empty()) {
207 			throw std::invalid_argument(error_message);
208 		}
209 		return opts;
210 	}
211 
212 	/**
213 	 * Set relevant values
214 	 */
sethttp::server::ssl_server_settings215 	virtual void set(const ssl_server_settings & ssl_settings) {
216 		server_settings::set(ssl_settings);
217 
218 		ssl_method = server_settings::get_valid_value(ssl_method, ssl_settings.ssl_method);
219 
220 		std::string path = server_settings::get_valid_value(cert_file_path, ssl_settings.cert_file_path);
221 		bool update_cert = path.compare(ssl_settings.cert_file_path) == 0;
222 		if (update_cert) {
223 			cert_file_path = ssl_settings.cert_file_path;
224 			// use certificate file for all usage by default
225 			certificate_chain_file_path = ssl_settings.cert_file_path;
226 			ca_cert_file_path = ssl_settings.cert_file_path;
227 			private_key_file_path = ssl_settings.private_key_file_path;
228 			tmp_dh_file_path = ssl_settings.cert_file_path;
229 			verify_file_path = ssl_settings.cert_file_path;
230 		}
231 
232 		certificate_chain_file_path = server_settings::get_valid_value(certificate_chain_file_path, ssl_settings.certificate_chain_file_path);
233 		ca_cert_file_path = server_settings::get_valid_value(ca_cert_file_path, ssl_settings.ca_cert_file_path);
234 		private_key_file_path = server_settings::get_valid_value(private_key_file_path, ssl_settings.private_key_file_path);
235 		private_key_pass_phrase = server_settings::get_valid_value(private_key_pass_phrase, ssl_settings.private_key_pass_phrase);
236 
237 		ssl_options = server_settings::get_valid_value(ssl_options, ssl_settings.ssl_options);
238 		tmp_dh_file_path = server_settings::get_valid_value(tmp_dh_file_path, ssl_settings.tmp_dh_file_path);
239 
240 		verify_peer = ssl_settings.verify_peer;
241 		verify_fail_if_no_peer_cert = ssl_settings.verify_fail_if_no_peer_cert;
242 		verify_file_path = server_settings::get_valid_value(verify_file_path, ssl_settings.verify_file_path);
243 	}
244 
to_stringhttp::server::ssl_server_settings245 	virtual std::string to_string() const  override {
246 		return std::string("ssl_server_settings[") + server_settings::to_string() +
247 				", ssl_method='" + ssl_method + "'" +
248 				", certificate_chain_file_path='" + certificate_chain_file_path + "'" +
249 				", ca_cert_file_path='" + ca_cert_file_path + "'" +
250 				", cert_file_path=" + cert_file_path + "'" +
251 				", private_key_file_path='" + private_key_file_path + "'" +
252 				", private_key_pass_phrase='" + private_key_pass_phrase + "'" +
253 				", ssl_options='" + ssl_options + "'" +
254 				", tmp_dh_file_path='" + tmp_dh_file_path + "'" +
255 				", verify_peer=" + (verify_peer == true ? "true" : "false") +
256 				", verify_fail_if_no_peer_cert=" + (verify_fail_if_no_peer_cert == true ? "true" : "false") +
257 				", verify_file_path='" + verify_file_path + "'" +
258 				"]";
259 	}
260 
261 protected:
update_optionshttp::server::ssl_server_settings262 	void update_options(boost::asio::ssl::context::options & opts, boost::asio::ssl::context::options option) const {
263 		if (opts != 0x0L) {
264 			opts |= option;
265 		} else {
266 			opts = option;
267 		}
268 	}
269 };
270 
271 #endif //#ifdef WWW_ENABLE_SSL
272 
273 } // namespace server
274 } // namespace http
275 
276 #endif /* WEBSERVER_SERVER_SETTINGS_HPP_ */
277