xref: /src/contrib/libevent/sample/https-client.c (revision b50261e21f39a6c7249a49e7b60aa878c98512a8)
1cbc620a4SEd Maste /*
2cbc620a4SEd Maste   This is an example of how to hook up evhttp with bufferevent_ssl
3cbc620a4SEd Maste 
4cbc620a4SEd Maste   It just GETs an https URL given on the command-line and prints the response
5cbc620a4SEd Maste   body to stdout.
6cbc620a4SEd Maste 
7cbc620a4SEd Maste   Actually, it also accepts plain http URLs to make it easy to compare http vs
8cbc620a4SEd Maste   https code paths.
9cbc620a4SEd Maste 
10cbc620a4SEd Maste   Loosely based on le-proxy.c.
11cbc620a4SEd Maste  */
12cbc620a4SEd Maste 
13cbc620a4SEd Maste // Get rid of OSX 10.7 and greater deprecation warnings.
14cbc620a4SEd Maste #if defined(__APPLE__) && defined(__clang__)
15cbc620a4SEd Maste #pragma clang diagnostic ignored "-Wdeprecated-declarations"
16cbc620a4SEd Maste #endif
17cbc620a4SEd Maste 
18cbc620a4SEd Maste #include <stdio.h>
19cbc620a4SEd Maste #include <assert.h>
20cbc620a4SEd Maste #include <stdlib.h>
21cbc620a4SEd Maste #include <string.h>
22cbc620a4SEd Maste #include <errno.h>
23cbc620a4SEd Maste 
24cbc620a4SEd Maste #ifdef _WIN32
25cbc620a4SEd Maste #include <winsock2.h>
26cbc620a4SEd Maste #include <ws2tcpip.h>
27cbc620a4SEd Maste 
28cbc620a4SEd Maste #define snprintf _snprintf
29cbc620a4SEd Maste #define strcasecmp _stricmp
30cbc620a4SEd Maste #else
31cbc620a4SEd Maste #include <sys/socket.h>
32cbc620a4SEd Maste #include <netinet/in.h>
33cbc620a4SEd Maste #endif
34cbc620a4SEd Maste 
35cbc620a4SEd Maste #include <event2/bufferevent_ssl.h>
36cbc620a4SEd Maste #include <event2/bufferevent.h>
37cbc620a4SEd Maste #include <event2/buffer.h>
38cbc620a4SEd Maste #include <event2/listener.h>
39cbc620a4SEd Maste #include <event2/util.h>
40cbc620a4SEd Maste #include <event2/http.h>
41cbc620a4SEd Maste 
42cbc620a4SEd Maste #include <openssl/ssl.h>
43cbc620a4SEd Maste #include <openssl/err.h>
44cbc620a4SEd Maste #include <openssl/rand.h>
45cbc620a4SEd Maste 
46cbc620a4SEd Maste #include "openssl_hostname_validation.h"
47cbc620a4SEd Maste 
48cbc620a4SEd Maste static int ignore_cert = 0;
49cbc620a4SEd Maste 
50cbc620a4SEd Maste static void
http_request_done(struct evhttp_request * req,void * ctx)51cbc620a4SEd Maste http_request_done(struct evhttp_request *req, void *ctx)
52cbc620a4SEd Maste {
53cbc620a4SEd Maste 	char buffer[256];
54cbc620a4SEd Maste 	int nread;
55cbc620a4SEd Maste 
565223d1d9SCy Schubert 	if (!req || !evhttp_request_get_response_code(req)) {
57cbc620a4SEd Maste 		/* If req is NULL, it means an error occurred, but
58cbc620a4SEd Maste 		 * sadly we are mostly left guessing what the error
59cbc620a4SEd Maste 		 * might have been.  We'll do our best... */
60cbc620a4SEd Maste 		struct bufferevent *bev = (struct bufferevent *) ctx;
61cbc620a4SEd Maste 		unsigned long oslerr;
62cbc620a4SEd Maste 		int printed_err = 0;
63cbc620a4SEd Maste 		int errcode = EVUTIL_SOCKET_ERROR();
64cbc620a4SEd Maste 		fprintf(stderr, "some request failed - no idea which one though!\n");
65cbc620a4SEd Maste 		/* Print out the OpenSSL error queue that libevent
66cbc620a4SEd Maste 		 * squirreled away for us, if any. */
67cbc620a4SEd Maste 		while ((oslerr = bufferevent_get_openssl_error(bev))) {
68cbc620a4SEd Maste 			ERR_error_string_n(oslerr, buffer, sizeof(buffer));
69cbc620a4SEd Maste 			fprintf(stderr, "%s\n", buffer);
70cbc620a4SEd Maste 			printed_err = 1;
71cbc620a4SEd Maste 		}
72cbc620a4SEd Maste 		/* If the OpenSSL error queue was empty, maybe it was a
73cbc620a4SEd Maste 		 * socket error; let's try printing that. */
74cbc620a4SEd Maste 		if (! printed_err)
75cbc620a4SEd Maste 			fprintf(stderr, "socket error = %s (%d)\n",
76cbc620a4SEd Maste 				evutil_socket_error_to_string(errcode),
77cbc620a4SEd Maste 				errcode);
78cbc620a4SEd Maste 		return;
79cbc620a4SEd Maste 	}
80cbc620a4SEd Maste 
81cbc620a4SEd Maste 	fprintf(stderr, "Response line: %d %s\n",
82cbc620a4SEd Maste 	    evhttp_request_get_response_code(req),
83cbc620a4SEd Maste 	    evhttp_request_get_response_code_line(req));
84cbc620a4SEd Maste 
85cbc620a4SEd Maste 	while ((nread = evbuffer_remove(evhttp_request_get_input_buffer(req),
86cbc620a4SEd Maste 		    buffer, sizeof(buffer)))
87cbc620a4SEd Maste 	       > 0) {
88cbc620a4SEd Maste 		/* These are just arbitrary chunks of 256 bytes.
89cbc620a4SEd Maste 		 * They are not lines, so we can't treat them as such. */
90cbc620a4SEd Maste 		fwrite(buffer, nread, 1, stdout);
91cbc620a4SEd Maste 	}
92cbc620a4SEd Maste }
93cbc620a4SEd Maste 
94cbc620a4SEd Maste static void
syntax(void)95cbc620a4SEd Maste syntax(void)
96cbc620a4SEd Maste {
97cbc620a4SEd Maste 	fputs("Syntax:\n", stderr);
98cbc620a4SEd Maste 	fputs("   https-client -url <https-url> [-data data-file.bin] [-ignore-cert] [-retries num] [-timeout sec] [-crt crt]\n", stderr);
99cbc620a4SEd Maste 	fputs("Example:\n", stderr);
100cbc620a4SEd Maste 	fputs("   https-client -url https://ip.appspot.com/\n", stderr);
101cbc620a4SEd Maste }
102cbc620a4SEd Maste 
103cbc620a4SEd Maste static void
err(const char * msg)104cbc620a4SEd Maste err(const char *msg)
105cbc620a4SEd Maste {
106cbc620a4SEd Maste 	fputs(msg, stderr);
107cbc620a4SEd Maste }
108cbc620a4SEd Maste 
109cbc620a4SEd Maste static void
err_openssl(const char * func)110cbc620a4SEd Maste err_openssl(const char *func)
111cbc620a4SEd Maste {
112cbc620a4SEd Maste 	fprintf (stderr, "%s failed:\n", func);
113cbc620a4SEd Maste 
114cbc620a4SEd Maste 	/* This is the OpenSSL function that prints the contents of the
115cbc620a4SEd Maste 	 * error stack to the specified file handle. */
116cbc620a4SEd Maste 	ERR_print_errors_fp (stderr);
117cbc620a4SEd Maste 
118cbc620a4SEd Maste 	exit(1);
119cbc620a4SEd Maste }
120cbc620a4SEd Maste 
121cbc620a4SEd Maste /* See http://archives.seul.org/libevent/users/Jan-2013/msg00039.html */
cert_verify_callback(X509_STORE_CTX * x509_ctx,void * arg)122cbc620a4SEd Maste static int cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg)
123cbc620a4SEd Maste {
124cbc620a4SEd Maste 	char cert_str[256];
125cbc620a4SEd Maste 	const char *host = (const char *) arg;
126cbc620a4SEd Maste 	const char *res_str = "X509_verify_cert failed";
127cbc620a4SEd Maste 	HostnameValidationResult res = Error;
128cbc620a4SEd Maste 
129cbc620a4SEd Maste 	/* This is the function that OpenSSL would call if we hadn't called
130cbc620a4SEd Maste 	 * SSL_CTX_set_cert_verify_callback().  Therefore, we are "wrapping"
131cbc620a4SEd Maste 	 * the default functionality, rather than replacing it. */
132cbc620a4SEd Maste 	int ok_so_far = 0;
133cbc620a4SEd Maste 
134cbc620a4SEd Maste 	X509 *server_cert = NULL;
135cbc620a4SEd Maste 
136cbc620a4SEd Maste 	if (ignore_cert) {
137cbc620a4SEd Maste 		return 1;
138cbc620a4SEd Maste 	}
139cbc620a4SEd Maste 
140cbc620a4SEd Maste 	ok_so_far = X509_verify_cert(x509_ctx);
141cbc620a4SEd Maste 
142cbc620a4SEd Maste 	server_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
143cbc620a4SEd Maste 
144cbc620a4SEd Maste 	if (ok_so_far) {
145cbc620a4SEd Maste 		res = validate_hostname(host, server_cert);
146cbc620a4SEd Maste 
147cbc620a4SEd Maste 		switch (res) {
148cbc620a4SEd Maste 		case MatchFound:
149cbc620a4SEd Maste 			res_str = "MatchFound";
150cbc620a4SEd Maste 			break;
151cbc620a4SEd Maste 		case MatchNotFound:
152cbc620a4SEd Maste 			res_str = "MatchNotFound";
153cbc620a4SEd Maste 			break;
154cbc620a4SEd Maste 		case NoSANPresent:
155cbc620a4SEd Maste 			res_str = "NoSANPresent";
156cbc620a4SEd Maste 			break;
157cbc620a4SEd Maste 		case MalformedCertificate:
158cbc620a4SEd Maste 			res_str = "MalformedCertificate";
159cbc620a4SEd Maste 			break;
160cbc620a4SEd Maste 		case Error:
161cbc620a4SEd Maste 			res_str = "Error";
162cbc620a4SEd Maste 			break;
163cbc620a4SEd Maste 		default:
164cbc620a4SEd Maste 			res_str = "WTF!";
165cbc620a4SEd Maste 			break;
166cbc620a4SEd Maste 		}
167cbc620a4SEd Maste 	}
168cbc620a4SEd Maste 
169cbc620a4SEd Maste 	X509_NAME_oneline(X509_get_subject_name (server_cert),
170cbc620a4SEd Maste 			  cert_str, sizeof (cert_str));
171cbc620a4SEd Maste 
172cbc620a4SEd Maste 	if (res == MatchFound) {
173cbc620a4SEd Maste 		printf("https server '%s' has this certificate, "
174cbc620a4SEd Maste 		       "which looks good to me:\n%s\n",
175cbc620a4SEd Maste 		       host, cert_str);
176cbc620a4SEd Maste 		return 1;
177cbc620a4SEd Maste 	} else {
178cbc620a4SEd Maste 		printf("Got '%s' for hostname '%s' and certificate:\n%s\n",
179cbc620a4SEd Maste 		       res_str, host, cert_str);
180cbc620a4SEd Maste 		return 0;
181cbc620a4SEd Maste 	}
182cbc620a4SEd Maste }
1835223d1d9SCy Schubert 
1845223d1d9SCy Schubert #ifdef _WIN32
1855223d1d9SCy Schubert static int
add_cert_for_store(X509_STORE * store,const char * name)1865223d1d9SCy Schubert add_cert_for_store(X509_STORE *store, const char *name)
1875223d1d9SCy Schubert {
1885223d1d9SCy Schubert 	HCERTSTORE sys_store = NULL;
1895223d1d9SCy Schubert 	PCCERT_CONTEXT ctx = NULL;
1905223d1d9SCy Schubert 	int r = 0;
1915223d1d9SCy Schubert 
1925223d1d9SCy Schubert 	sys_store = CertOpenSystemStore(0, name);
1935223d1d9SCy Schubert 	if (!sys_store) {
1945223d1d9SCy Schubert 		err("failed to open system certificate store");
1955223d1d9SCy Schubert 		return -1;
1965223d1d9SCy Schubert 	}
1975223d1d9SCy Schubert 	while ((ctx = CertEnumCertificatesInStore(sys_store, ctx))) {
1985223d1d9SCy Schubert 		X509 *x509 = d2i_X509(NULL, (unsigned char const **)&ctx->pbCertEncoded,
1995223d1d9SCy Schubert 			ctx->cbCertEncoded);
2005223d1d9SCy Schubert 		if (x509) {
2015223d1d9SCy Schubert 			X509_STORE_add_cert(store, x509);
2025223d1d9SCy Schubert 			X509_free(x509);
2035223d1d9SCy Schubert 		} else {
2045223d1d9SCy Schubert 			r = -1;
2055223d1d9SCy Schubert 			err_openssl("d2i_X509");
2065223d1d9SCy Schubert 			break;
2075223d1d9SCy Schubert 		}
2085223d1d9SCy Schubert 	}
2095223d1d9SCy Schubert 	CertCloseStore(sys_store, 0);
2105223d1d9SCy Schubert 	return r;
2115223d1d9SCy Schubert }
212cbc620a4SEd Maste #endif
213cbc620a4SEd Maste 
214cbc620a4SEd Maste int
main(int argc,char ** argv)215cbc620a4SEd Maste main(int argc, char **argv)
216cbc620a4SEd Maste {
217cbc620a4SEd Maste 	int r;
2185223d1d9SCy Schubert 	struct event_base *base = NULL;
219cbc620a4SEd Maste 	struct evhttp_uri *http_uri = NULL;
220cbc620a4SEd Maste 	const char *url = NULL, *data_file = NULL;
2215223d1d9SCy Schubert 	const char *crt = NULL;
222cbc620a4SEd Maste 	const char *scheme, *host, *path, *query;
223cbc620a4SEd Maste 	char uri[256];
224cbc620a4SEd Maste 	int port;
225cbc620a4SEd Maste 	int retries = 0;
226cbc620a4SEd Maste 	int timeout = -1;
227cbc620a4SEd Maste 
228cbc620a4SEd Maste 	SSL_CTX *ssl_ctx = NULL;
229cbc620a4SEd Maste 	SSL *ssl = NULL;
230cbc620a4SEd Maste 	struct bufferevent *bev;
231cbc620a4SEd Maste 	struct evhttp_connection *evcon = NULL;
232cbc620a4SEd Maste 	struct evhttp_request *req;
233cbc620a4SEd Maste 	struct evkeyvalq *output_headers;
234cbc620a4SEd Maste 	struct evbuffer *output_buffer;
235cbc620a4SEd Maste 
236cbc620a4SEd Maste 	int i;
237cbc620a4SEd Maste 	int ret = 0;
238cbc620a4SEd Maste 	enum { HTTP, HTTPS } type = HTTP;
239cbc620a4SEd Maste 
240cbc620a4SEd Maste 	for (i = 1; i < argc; i++) {
241cbc620a4SEd Maste 		if (!strcmp("-url", argv[i])) {
242cbc620a4SEd Maste 			if (i < argc - 1) {
243cbc620a4SEd Maste 				url = argv[i + 1];
244cbc620a4SEd Maste 			} else {
245cbc620a4SEd Maste 				syntax();
246cbc620a4SEd Maste 				goto error;
247cbc620a4SEd Maste 			}
248cbc620a4SEd Maste 		} else if (!strcmp("-crt", argv[i])) {
249cbc620a4SEd Maste 			if (i < argc - 1) {
250cbc620a4SEd Maste 				crt = argv[i + 1];
251cbc620a4SEd Maste 			} else {
252cbc620a4SEd Maste 				syntax();
253cbc620a4SEd Maste 				goto error;
254cbc620a4SEd Maste 			}
255cbc620a4SEd Maste 		} else if (!strcmp("-ignore-cert", argv[i])) {
256cbc620a4SEd Maste 			ignore_cert = 1;
257cbc620a4SEd Maste 		} else if (!strcmp("-data", argv[i])) {
258cbc620a4SEd Maste 			if (i < argc - 1) {
259cbc620a4SEd Maste 				data_file = argv[i + 1];
260cbc620a4SEd Maste 			} else {
261cbc620a4SEd Maste 				syntax();
262cbc620a4SEd Maste 				goto error;
263cbc620a4SEd Maste 			}
264cbc620a4SEd Maste 		} else if (!strcmp("-retries", argv[i])) {
265cbc620a4SEd Maste 			if (i < argc - 1) {
266cbc620a4SEd Maste 				retries = atoi(argv[i + 1]);
267cbc620a4SEd Maste 			} else {
268cbc620a4SEd Maste 				syntax();
269cbc620a4SEd Maste 				goto error;
270cbc620a4SEd Maste 			}
271cbc620a4SEd Maste 		} else if (!strcmp("-timeout", argv[i])) {
272cbc620a4SEd Maste 			if (i < argc - 1) {
273cbc620a4SEd Maste 				timeout = atoi(argv[i + 1]);
274cbc620a4SEd Maste 			} else {
275cbc620a4SEd Maste 				syntax();
276cbc620a4SEd Maste 				goto error;
277cbc620a4SEd Maste 			}
278cbc620a4SEd Maste 		} else if (!strcmp("-help", argv[i])) {
279cbc620a4SEd Maste 			syntax();
280cbc620a4SEd Maste 			goto error;
281cbc620a4SEd Maste 		}
282cbc620a4SEd Maste 	}
283cbc620a4SEd Maste 
284cbc620a4SEd Maste 	if (!url) {
285cbc620a4SEd Maste 		syntax();
286cbc620a4SEd Maste 		goto error;
287cbc620a4SEd Maste 	}
288cbc620a4SEd Maste 
289cbc620a4SEd Maste #ifdef _WIN32
290cbc620a4SEd Maste 	{
291cbc620a4SEd Maste 		WORD wVersionRequested;
292cbc620a4SEd Maste 		WSADATA wsaData;
293cbc620a4SEd Maste 		int err;
294cbc620a4SEd Maste 
295cbc620a4SEd Maste 		wVersionRequested = MAKEWORD(2, 2);
296cbc620a4SEd Maste 
297cbc620a4SEd Maste 		err = WSAStartup(wVersionRequested, &wsaData);
298cbc620a4SEd Maste 		if (err != 0) {
299cbc620a4SEd Maste 			printf("WSAStartup failed with error: %d\n", err);
300cbc620a4SEd Maste 			goto error;
301cbc620a4SEd Maste 		}
302cbc620a4SEd Maste 	}
303cbc620a4SEd Maste #endif // _WIN32
304cbc620a4SEd Maste 
305cbc620a4SEd Maste 	http_uri = evhttp_uri_parse(url);
306cbc620a4SEd Maste 	if (http_uri == NULL) {
307cbc620a4SEd Maste 		err("malformed url");
308cbc620a4SEd Maste 		goto error;
309cbc620a4SEd Maste 	}
310cbc620a4SEd Maste 
311cbc620a4SEd Maste 	scheme = evhttp_uri_get_scheme(http_uri);
312cbc620a4SEd Maste 	if (scheme == NULL || (strcasecmp(scheme, "https") != 0 &&
313cbc620a4SEd Maste 	                       strcasecmp(scheme, "http") != 0)) {
314cbc620a4SEd Maste 		err("url must be http or https");
315cbc620a4SEd Maste 		goto error;
316cbc620a4SEd Maste 	}
317cbc620a4SEd Maste 
318cbc620a4SEd Maste 	host = evhttp_uri_get_host(http_uri);
319cbc620a4SEd Maste 	if (host == NULL) {
320cbc620a4SEd Maste 		err("url must have a host");
321cbc620a4SEd Maste 		goto error;
322cbc620a4SEd Maste 	}
323cbc620a4SEd Maste 
324cbc620a4SEd Maste 	port = evhttp_uri_get_port(http_uri);
325cbc620a4SEd Maste 	if (port == -1) {
326cbc620a4SEd Maste 		port = (strcasecmp(scheme, "http") == 0) ? 80 : 443;
327cbc620a4SEd Maste 	}
328cbc620a4SEd Maste 
329cbc620a4SEd Maste 	path = evhttp_uri_get_path(http_uri);
330cbc620a4SEd Maste 	if (strlen(path) == 0) {
331cbc620a4SEd Maste 		path = "/";
332cbc620a4SEd Maste 	}
333cbc620a4SEd Maste 
334cbc620a4SEd Maste 	query = evhttp_uri_get_query(http_uri);
335cbc620a4SEd Maste 	if (query == NULL) {
336cbc620a4SEd Maste 		snprintf(uri, sizeof(uri) - 1, "%s", path);
337cbc620a4SEd Maste 	} else {
338cbc620a4SEd Maste 		snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query);
339cbc620a4SEd Maste 	}
340cbc620a4SEd Maste 	uri[sizeof(uri) - 1] = '\0';
341cbc620a4SEd Maste 
3425223d1d9SCy Schubert #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
3435223d1d9SCy Schubert 	(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
344cbc620a4SEd Maste 	// Initialize OpenSSL
345cbc620a4SEd Maste 	SSL_library_init();
346cbc620a4SEd Maste 	ERR_load_crypto_strings();
347cbc620a4SEd Maste 	SSL_load_error_strings();
348cbc620a4SEd Maste 	OpenSSL_add_all_algorithms();
349cbc620a4SEd Maste #endif
350cbc620a4SEd Maste 
351cbc620a4SEd Maste 	/* This isn't strictly necessary... OpenSSL performs RAND_poll
352cbc620a4SEd Maste 	 * automatically on first use of random number generator. */
353cbc620a4SEd Maste 	r = RAND_poll();
354cbc620a4SEd Maste 	if (r == 0) {
355cbc620a4SEd Maste 		err_openssl("RAND_poll");
356cbc620a4SEd Maste 		goto error;
357cbc620a4SEd Maste 	}
358cbc620a4SEd Maste 
359cbc620a4SEd Maste 	/* Create a new OpenSSL context */
360cbc620a4SEd Maste 	ssl_ctx = SSL_CTX_new(SSLv23_method());
361cbc620a4SEd Maste 	if (!ssl_ctx) {
362cbc620a4SEd Maste 		err_openssl("SSL_CTX_new");
363cbc620a4SEd Maste 		goto error;
364cbc620a4SEd Maste 	}
365cbc620a4SEd Maste 
3665223d1d9SCy Schubert 	if (crt == NULL) {
3675223d1d9SCy Schubert 		X509_STORE *store;
3685223d1d9SCy Schubert 		/* Attempt to use the system's trusted root certificates. */
3695223d1d9SCy Schubert 		store = SSL_CTX_get_cert_store(ssl_ctx);
3705223d1d9SCy Schubert #ifdef _WIN32
3715223d1d9SCy Schubert 		if (add_cert_for_store(store, "CA") < 0 ||
3725223d1d9SCy Schubert 		    add_cert_for_store(store, "AuthRoot") < 0 ||
3735223d1d9SCy Schubert 		    add_cert_for_store(store, "ROOT") < 0) {
3745223d1d9SCy Schubert 			goto error;
3755223d1d9SCy Schubert 		}
3765223d1d9SCy Schubert #else // _WIN32
3775223d1d9SCy Schubert 		if (X509_STORE_set_default_paths(store) != 1) {
3785223d1d9SCy Schubert 			err_openssl("X509_STORE_set_default_paths");
3795223d1d9SCy Schubert 			goto error;
3805223d1d9SCy Schubert 		}
3815223d1d9SCy Schubert #endif // _WIN32
3825223d1d9SCy Schubert 	} else {
3835223d1d9SCy Schubert 		if (SSL_CTX_load_verify_locations(ssl_ctx, crt, NULL) != 1) {
384cbc620a4SEd Maste 			err_openssl("SSL_CTX_load_verify_locations");
385cbc620a4SEd Maste 			goto error;
386cbc620a4SEd Maste 		}
3875223d1d9SCy Schubert 	}
388cbc620a4SEd Maste 	/* Ask OpenSSL to verify the server certificate.  Note that this
389cbc620a4SEd Maste 	 * does NOT include verifying that the hostname is correct.
390cbc620a4SEd Maste 	 * So, by itself, this means anyone with any legitimate
391cbc620a4SEd Maste 	 * CA-issued certificate for any website, can impersonate any
392cbc620a4SEd Maste 	 * other website in the world.  This is not good.  See "The
393cbc620a4SEd Maste 	 * Most Dangerous Code in the World" article at
394cbc620a4SEd Maste 	 * https://crypto.stanford.edu/~dabo/pubs/abstracts/ssl-client-bugs.html
395cbc620a4SEd Maste 	 */
396cbc620a4SEd Maste 	SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
397cbc620a4SEd Maste 	/* This is how we solve the problem mentioned in the previous
398cbc620a4SEd Maste 	 * comment.  We "wrap" OpenSSL's validation routine in our
399cbc620a4SEd Maste 	 * own routine, which also validates the hostname by calling
400cbc620a4SEd Maste 	 * the code provided by iSECPartners.  Note that even though
401cbc620a4SEd Maste 	 * the "Everything You've Always Wanted to Know About
402cbc620a4SEd Maste 	 * Certificate Validation With OpenSSL (But Were Afraid to
403cbc620a4SEd Maste 	 * Ask)" paper from iSECPartners says very explicitly not to
404cbc620a4SEd Maste 	 * call SSL_CTX_set_cert_verify_callback (at the bottom of
405cbc620a4SEd Maste 	 * page 2), what we're doing here is safe because our
406cbc620a4SEd Maste 	 * cert_verify_callback() calls X509_verify_cert(), which is
407cbc620a4SEd Maste 	 * OpenSSL's built-in routine which would have been called if
408cbc620a4SEd Maste 	 * we hadn't set the callback.  Therefore, we're just
409cbc620a4SEd Maste 	 * "wrapping" OpenSSL's routine, not replacing it. */
410cbc620a4SEd Maste 	SSL_CTX_set_cert_verify_callback(ssl_ctx, cert_verify_callback,
411cbc620a4SEd Maste 					  (void *) host);
412cbc620a4SEd Maste 
413cbc620a4SEd Maste 	// Create event base
414cbc620a4SEd Maste 	base = event_base_new();
415cbc620a4SEd Maste 	if (!base) {
416cbc620a4SEd Maste 		perror("event_base_new()");
417cbc620a4SEd Maste 		goto error;
418cbc620a4SEd Maste 	}
419cbc620a4SEd Maste 
420cbc620a4SEd Maste 	// Create OpenSSL bufferevent and stack evhttp on top of it
421cbc620a4SEd Maste 	ssl = SSL_new(ssl_ctx);
422cbc620a4SEd Maste 	if (ssl == NULL) {
423cbc620a4SEd Maste 		err_openssl("SSL_new()");
424cbc620a4SEd Maste 		goto error;
425cbc620a4SEd Maste 	}
426cbc620a4SEd Maste 
427cbc620a4SEd Maste 	#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
428cbc620a4SEd Maste 	// Set hostname for SNI extension
429cbc620a4SEd Maste 	SSL_set_tlsext_host_name(ssl, host);
430cbc620a4SEd Maste 	#endif
431cbc620a4SEd Maste 
432cbc620a4SEd Maste 	if (strcasecmp(scheme, "http") == 0) {
433cbc620a4SEd Maste 		bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
434cbc620a4SEd Maste 	} else {
435cbc620a4SEd Maste 		type = HTTPS;
436cbc620a4SEd Maste 		bev = bufferevent_openssl_socket_new(base, -1, ssl,
437cbc620a4SEd Maste 			BUFFEREVENT_SSL_CONNECTING,
438cbc620a4SEd Maste 			BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
439cbc620a4SEd Maste 	}
440cbc620a4SEd Maste 
441cbc620a4SEd Maste 	if (bev == NULL) {
442cbc620a4SEd Maste 		fprintf(stderr, "bufferevent_openssl_socket_new() failed\n");
443cbc620a4SEd Maste 		goto error;
444cbc620a4SEd Maste 	}
445cbc620a4SEd Maste 
446cbc620a4SEd Maste 	bufferevent_openssl_set_allow_dirty_shutdown(bev, 1);
447cbc620a4SEd Maste 
448cbc620a4SEd Maste 	// For simplicity, we let DNS resolution block. Everything else should be
449cbc620a4SEd Maste 	// asynchronous though.
450cbc620a4SEd Maste 	evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev,
451cbc620a4SEd Maste 		host, port);
452cbc620a4SEd Maste 	if (evcon == NULL) {
453cbc620a4SEd Maste 		fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n");
454cbc620a4SEd Maste 		goto error;
455cbc620a4SEd Maste 	}
456cbc620a4SEd Maste 
457cbc620a4SEd Maste 	if (retries > 0) {
458cbc620a4SEd Maste 		evhttp_connection_set_retries(evcon, retries);
459cbc620a4SEd Maste 	}
460cbc620a4SEd Maste 	if (timeout >= 0) {
461cbc620a4SEd Maste 		evhttp_connection_set_timeout(evcon, timeout);
462cbc620a4SEd Maste 	}
463cbc620a4SEd Maste 
464cbc620a4SEd Maste 	// Fire off the request
465cbc620a4SEd Maste 	req = evhttp_request_new(http_request_done, bev);
466cbc620a4SEd Maste 	if (req == NULL) {
467cbc620a4SEd Maste 		fprintf(stderr, "evhttp_request_new() failed\n");
468cbc620a4SEd Maste 		goto error;
469cbc620a4SEd Maste 	}
470cbc620a4SEd Maste 
471cbc620a4SEd Maste 	output_headers = evhttp_request_get_output_headers(req);
472cbc620a4SEd Maste 	evhttp_add_header(output_headers, "Host", host);
473cbc620a4SEd Maste 	evhttp_add_header(output_headers, "Connection", "close");
474cbc620a4SEd Maste 
475cbc620a4SEd Maste 	if (data_file) {
476cbc620a4SEd Maste 		/* NOTE: In production code, you'd probably want to use
477cbc620a4SEd Maste 		 * evbuffer_add_file() or evbuffer_add_file_segment(), to
478cbc620a4SEd Maste 		 * avoid needless copying. */
479cbc620a4SEd Maste 		FILE * f = fopen(data_file, "rb");
480cbc620a4SEd Maste 		char buf[1024];
481cbc620a4SEd Maste 		size_t s;
482cbc620a4SEd Maste 		size_t bytes = 0;
483cbc620a4SEd Maste 
484cbc620a4SEd Maste 		if (!f) {
485cbc620a4SEd Maste 			syntax();
486cbc620a4SEd Maste 			goto error;
487cbc620a4SEd Maste 		}
488cbc620a4SEd Maste 
489cbc620a4SEd Maste 		output_buffer = evhttp_request_get_output_buffer(req);
490cbc620a4SEd Maste 		while ((s = fread(buf, 1, sizeof(buf), f)) > 0) {
491cbc620a4SEd Maste 			evbuffer_add(output_buffer, buf, s);
492cbc620a4SEd Maste 			bytes += s;
493cbc620a4SEd Maste 		}
494cbc620a4SEd Maste 		evutil_snprintf(buf, sizeof(buf)-1, "%lu", (unsigned long)bytes);
495cbc620a4SEd Maste 		evhttp_add_header(output_headers, "Content-Length", buf);
496cbc620a4SEd Maste 		fclose(f);
497cbc620a4SEd Maste 	}
498cbc620a4SEd Maste 
499cbc620a4SEd Maste 	r = evhttp_make_request(evcon, req, data_file ? EVHTTP_REQ_POST : EVHTTP_REQ_GET, uri);
500cbc620a4SEd Maste 	if (r != 0) {
501cbc620a4SEd Maste 		fprintf(stderr, "evhttp_make_request() failed\n");
502cbc620a4SEd Maste 		goto error;
503cbc620a4SEd Maste 	}
504cbc620a4SEd Maste 
505cbc620a4SEd Maste 	event_base_dispatch(base);
506cbc620a4SEd Maste 	goto cleanup;
507cbc620a4SEd Maste 
508cbc620a4SEd Maste error:
509cbc620a4SEd Maste 	ret = 1;
510cbc620a4SEd Maste cleanup:
511cbc620a4SEd Maste 	if (evcon)
512cbc620a4SEd Maste 		evhttp_connection_free(evcon);
513cbc620a4SEd Maste 	if (http_uri)
514cbc620a4SEd Maste 		evhttp_uri_free(http_uri);
5155223d1d9SCy Schubert 	if (base)
516cbc620a4SEd Maste 		event_base_free(base);
517cbc620a4SEd Maste 
518cbc620a4SEd Maste 	if (ssl_ctx)
519cbc620a4SEd Maste 		SSL_CTX_free(ssl_ctx);
520cbc620a4SEd Maste 	if (type == HTTP && ssl)
521cbc620a4SEd Maste 		SSL_free(ssl);
5225223d1d9SCy Schubert #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
5235223d1d9SCy Schubert 	(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
524cbc620a4SEd Maste 	EVP_cleanup();
525cbc620a4SEd Maste 	ERR_free_strings();
526cbc620a4SEd Maste 
5275223d1d9SCy Schubert #if OPENSSL_VERSION_NUMBER < 0x10000000L
528cbc620a4SEd Maste 	ERR_remove_state(0);
5295223d1d9SCy Schubert #else
5305223d1d9SCy Schubert 	ERR_remove_thread_state(NULL);
531cbc620a4SEd Maste #endif
5325223d1d9SCy Schubert 
533cbc620a4SEd Maste 	CRYPTO_cleanup_all_ex_data();
534cbc620a4SEd Maste 
535cbc620a4SEd Maste 	sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
5365223d1d9SCy Schubert #endif /* (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
5375223d1d9SCy Schubert 	(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L) */
538cbc620a4SEd Maste 
539cbc620a4SEd Maste #ifdef _WIN32
540cbc620a4SEd Maste 	WSACleanup();
541cbc620a4SEd Maste #endif
542cbc620a4SEd Maste 
543cbc620a4SEd Maste 	return ret;
544cbc620a4SEd Maste }
545