xref: /libevent-2.1.12/sample/https-client.c (revision 894ca48a)
1be46c99bSCatalin Patulea /*
2be46c99bSCatalin Patulea   This is an example of how to hook up evhttp with bufferevent_ssl
3be46c99bSCatalin Patulea 
4be46c99bSCatalin Patulea   It just GETs an https URL given on the command-line and prints the response
5be46c99bSCatalin Patulea   body to stdout.
6be46c99bSCatalin Patulea 
7be46c99bSCatalin Patulea   Actually, it also accepts plain http URLs to make it easy to compare http vs
8be46c99bSCatalin Patulea   https code paths.
9be46c99bSCatalin Patulea 
10be46c99bSCatalin Patulea   Loosely based on le-proxy.c.
11be46c99bSCatalin Patulea  */
12be46c99bSCatalin Patulea 
1369c3516bSJoakim Söderberg // Get rid of OSX 10.7 and greater deprecation warnings.
14d7be7887SJoakim Soderberg #if defined(__APPLE__) && defined(__clang__)
1569c3516bSJoakim Söderberg #pragma clang diagnostic ignored "-Wdeprecated-declarations"
160ef1d04eSJoakim Soderberg #endif
1769c3516bSJoakim Söderberg 
18be46c99bSCatalin Patulea #include <stdio.h>
19be46c99bSCatalin Patulea #include <assert.h>
20be46c99bSCatalin Patulea #include <stdlib.h>
21be46c99bSCatalin Patulea #include <string.h>
22be46c99bSCatalin Patulea #include <errno.h>
23be46c99bSCatalin Patulea 
2488ecda3bSNick Mathewson #ifdef _WIN32
25be46c99bSCatalin Patulea #include <winsock2.h>
26be46c99bSCatalin Patulea #include <ws2tcpip.h>
2719222e52SJoakim Soderberg 
2819222e52SJoakim Soderberg #define snprintf _snprintf
2919222e52SJoakim Soderberg #define strcasecmp _stricmp
30be46c99bSCatalin Patulea #else
31be46c99bSCatalin Patulea #include <sys/socket.h>
32be46c99bSCatalin Patulea #include <netinet/in.h>
33be46c99bSCatalin Patulea #endif
34be46c99bSCatalin Patulea 
35be46c99bSCatalin Patulea #include <event2/bufferevent_ssl.h>
36be46c99bSCatalin Patulea #include <event2/bufferevent.h>
37be46c99bSCatalin Patulea #include <event2/buffer.h>
38be46c99bSCatalin Patulea #include <event2/listener.h>
39be46c99bSCatalin Patulea #include <event2/util.h>
40be46c99bSCatalin Patulea #include <event2/http.h>
41be46c99bSCatalin Patulea 
42be46c99bSCatalin Patulea #include <openssl/ssl.h>
43be46c99bSCatalin Patulea #include <openssl/err.h>
44be46c99bSCatalin Patulea #include <openssl/rand.h>
45be46c99bSCatalin Patulea 
4664d9f161SPatrick Pelletier #include "openssl_hostname_validation.h"
4764d9f161SPatrick Pelletier 
48be46c99bSCatalin Patulea static struct event_base *base;
49c5887f73SAlexey Ozeritsky static int ignore_cert = 0;
50be46c99bSCatalin Patulea 
51be46c99bSCatalin Patulea static void
52be46c99bSCatalin Patulea http_request_done(struct evhttp_request *req, void *ctx)
53be46c99bSCatalin Patulea {
54be46c99bSCatalin Patulea 	char buffer[256];
55be46c99bSCatalin Patulea 	int nread;
56be46c99bSCatalin Patulea 
57be46c99bSCatalin Patulea 	if (req == NULL) {
585754d96aSPatrick Pelletier 		/* If req is NULL, it means an error occurred, but
595754d96aSPatrick Pelletier 		 * sadly we are mostly left guessing what the error
605754d96aSPatrick Pelletier 		 * might have been.  We'll do our best... */
615754d96aSPatrick Pelletier 		struct bufferevent *bev = (struct bufferevent *) ctx;
625754d96aSPatrick Pelletier 		unsigned long oslerr;
635754d96aSPatrick Pelletier 		int printed_err = 0;
645754d96aSPatrick Pelletier 		int errcode = EVUTIL_SOCKET_ERROR();
65be46c99bSCatalin Patulea 		fprintf(stderr, "some request failed - no idea which one though!\n");
665754d96aSPatrick Pelletier 		/* Print out the OpenSSL error queue that libevent
675754d96aSPatrick Pelletier 		 * squirreled away for us, if any. */
685754d96aSPatrick Pelletier 		while ((oslerr = bufferevent_get_openssl_error(bev))) {
695754d96aSPatrick Pelletier 			ERR_error_string_n(oslerr, buffer, sizeof(buffer));
705754d96aSPatrick Pelletier 			fprintf(stderr, "%s\n", buffer);
715754d96aSPatrick Pelletier 			printed_err = 1;
725754d96aSPatrick Pelletier 		}
735754d96aSPatrick Pelletier 		/* If the OpenSSL error queue was empty, maybe it was a
745754d96aSPatrick Pelletier 		 * socket error; let's try printing that. */
755754d96aSPatrick Pelletier 		if (! printed_err)
765754d96aSPatrick Pelletier 			fprintf(stderr, "socket error = %s (%d)\n",
775754d96aSPatrick Pelletier 				evutil_socket_error_to_string(errcode),
785754d96aSPatrick Pelletier 				errcode);
79be46c99bSCatalin Patulea 		return;
80be46c99bSCatalin Patulea 	}
81be46c99bSCatalin Patulea 
82be46c99bSCatalin Patulea 	fprintf(stderr, "Response line: %d %s\n",
838a90a850SNick Mathewson 	    evhttp_request_get_response_code(req),
848a90a850SNick Mathewson 	    evhttp_request_get_response_code_line(req));
85be46c99bSCatalin Patulea 
8695acdaa3SNick Mathewson 	while ((nread = evbuffer_remove(evhttp_request_get_input_buffer(req),
8795acdaa3SNick Mathewson 		    buffer, sizeof(buffer)))
88be46c99bSCatalin Patulea 	       > 0) {
8942d7441aSPatrick Pelletier 		/* These are just arbitrary chunks of 256 bytes.
9042d7441aSPatrick Pelletier 		 * They are not lines, so we can't treat them as such. */
91be46c99bSCatalin Patulea 		fwrite(buffer, nread, 1, stdout);
92be46c99bSCatalin Patulea 	}
93be46c99bSCatalin Patulea }
94be46c99bSCatalin Patulea 
95be46c99bSCatalin Patulea static void
96be46c99bSCatalin Patulea syntax(void)
97be46c99bSCatalin Patulea {
98be46c99bSCatalin Patulea 	fputs("Syntax:\n", stderr);
99fdf713a0SAzat Khuzhin 	fputs("   https-client -url <https-url> [-data data-file.bin] [-ignore-cert] [-retries num] [-timeout sec] [-crt crt]\n", stderr);
100be46c99bSCatalin Patulea 	fputs("Example:\n", stderr);
101c5887f73SAlexey Ozeritsky 	fputs("   https-client -url https://ip.appspot.com/\n", stderr);
102be46c99bSCatalin Patulea }
103be46c99bSCatalin Patulea 
104be46c99bSCatalin Patulea static void
10524a1f25aSAzat Khuzhin err(const char *msg)
106be46c99bSCatalin Patulea {
107be46c99bSCatalin Patulea 	fputs(msg, stderr);
108be46c99bSCatalin Patulea }
109be46c99bSCatalin Patulea 
1105754d96aSPatrick Pelletier static void
11124a1f25aSAzat Khuzhin err_openssl(const char *func)
1125754d96aSPatrick Pelletier {
1135754d96aSPatrick Pelletier 	fprintf (stderr, "%s failed:\n", func);
1145754d96aSPatrick Pelletier 
1155754d96aSPatrick Pelletier 	/* This is the OpenSSL function that prints the contents of the
1165754d96aSPatrick Pelletier 	 * error stack to the specified file handle. */
1175754d96aSPatrick Pelletier 	ERR_print_errors_fp (stderr);
1185754d96aSPatrick Pelletier 
1195754d96aSPatrick Pelletier 	exit(1);
1205754d96aSPatrick Pelletier }
1215754d96aSPatrick Pelletier 
12213a4acdaSAzat Khuzhin #ifndef _WIN32
12364d9f161SPatrick Pelletier /* See http://archives.seul.org/libevent/users/Jan-2013/msg00039.html */
12464d9f161SPatrick Pelletier static int cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg)
12564d9f161SPatrick Pelletier {
12664d9f161SPatrick Pelletier 	char cert_str[256];
12764d9f161SPatrick Pelletier 	const char *host = (const char *) arg;
12864d9f161SPatrick Pelletier 	const char *res_str = "X509_verify_cert failed";
12964d9f161SPatrick Pelletier 	HostnameValidationResult res = Error;
13064d9f161SPatrick Pelletier 
13164d9f161SPatrick Pelletier 	/* This is the function that OpenSSL would call if we hadn't called
13264d9f161SPatrick Pelletier 	 * SSL_CTX_set_cert_verify_callback().  Therefore, we are "wrapping"
13364d9f161SPatrick Pelletier 	 * the default functionality, rather than replacing it. */
13429af65ebSAlexey Ozeritsky 	int ok_so_far = 0;
13564d9f161SPatrick Pelletier 
13629af65ebSAlexey Ozeritsky 	X509 *server_cert = NULL;
13729af65ebSAlexey Ozeritsky 
13829af65ebSAlexey Ozeritsky 	if (ignore_cert) {
13929af65ebSAlexey Ozeritsky 		return 1;
14029af65ebSAlexey Ozeritsky 	}
14129af65ebSAlexey Ozeritsky 
14229af65ebSAlexey Ozeritsky 	ok_so_far = X509_verify_cert(x509_ctx);
14329af65ebSAlexey Ozeritsky 
14429af65ebSAlexey Ozeritsky 	server_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
14564d9f161SPatrick Pelletier 
14664d9f161SPatrick Pelletier 	if (ok_so_far) {
14764d9f161SPatrick Pelletier 		res = validate_hostname(host, server_cert);
14864d9f161SPatrick Pelletier 
14964d9f161SPatrick Pelletier 		switch (res) {
15064d9f161SPatrick Pelletier 		case MatchFound:
15164d9f161SPatrick Pelletier 			res_str = "MatchFound";
15264d9f161SPatrick Pelletier 			break;
15364d9f161SPatrick Pelletier 		case MatchNotFound:
15464d9f161SPatrick Pelletier 			res_str = "MatchNotFound";
15564d9f161SPatrick Pelletier 			break;
15664d9f161SPatrick Pelletier 		case NoSANPresent:
15764d9f161SPatrick Pelletier 			res_str = "NoSANPresent";
15864d9f161SPatrick Pelletier 			break;
15964d9f161SPatrick Pelletier 		case MalformedCertificate:
16064d9f161SPatrick Pelletier 			res_str = "MalformedCertificate";
16164d9f161SPatrick Pelletier 			break;
16264d9f161SPatrick Pelletier 		case Error:
16364d9f161SPatrick Pelletier 			res_str = "Error";
16464d9f161SPatrick Pelletier 			break;
16564d9f161SPatrick Pelletier 		default:
16664d9f161SPatrick Pelletier 			res_str = "WTF!";
16764d9f161SPatrick Pelletier 			break;
16864d9f161SPatrick Pelletier 		}
16964d9f161SPatrick Pelletier 	}
17064d9f161SPatrick Pelletier 
17164d9f161SPatrick Pelletier 	X509_NAME_oneline(X509_get_subject_name (server_cert),
17264d9f161SPatrick Pelletier 			  cert_str, sizeof (cert_str));
17364d9f161SPatrick Pelletier 
17464d9f161SPatrick Pelletier 	if (res == MatchFound) {
17564d9f161SPatrick Pelletier 		printf("https server '%s' has this certificate, "
17664d9f161SPatrick Pelletier 		       "which looks good to me:\n%s\n",
17764d9f161SPatrick Pelletier 		       host, cert_str);
17864d9f161SPatrick Pelletier 		return 1;
17964d9f161SPatrick Pelletier 	} else {
18064d9f161SPatrick Pelletier 		printf("Got '%s' for hostname '%s' and certificate:\n%s\n",
18164d9f161SPatrick Pelletier 		       res_str, host, cert_str);
18264d9f161SPatrick Pelletier 		return 0;
18364d9f161SPatrick Pelletier 	}
18464d9f161SPatrick Pelletier }
18513a4acdaSAzat Khuzhin #endif
18664d9f161SPatrick Pelletier 
187be46c99bSCatalin Patulea int
188be46c99bSCatalin Patulea main(int argc, char **argv)
189be46c99bSCatalin Patulea {
190be46c99bSCatalin Patulea 	int r;
191be46c99bSCatalin Patulea 
19224a1f25aSAzat Khuzhin 	struct evhttp_uri *http_uri = NULL;
19390786eb0SNick Mathewson 	const char *url = NULL, *data_file = NULL;
1945c0132f3SDavid Disseldorp 	const char *crt = NULL;
19590786eb0SNick Mathewson 	const char *scheme, *host, *path, *query;
196be46c99bSCatalin Patulea 	char uri[256];
197be46c99bSCatalin Patulea 	int port;
198d9da8443SAzat Khuzhin 	int retries = 0;
1994637aa88SAzat Khuzhin 	int timeout = -1;
200be46c99bSCatalin Patulea 
20124a1f25aSAzat Khuzhin 	SSL_CTX *ssl_ctx = NULL;
202f3d7ff5dSAzat Khuzhin 	SSL *ssl = NULL;
203be46c99bSCatalin Patulea 	struct bufferevent *bev;
20424a1f25aSAzat Khuzhin 	struct evhttp_connection *evcon = NULL;
205be46c99bSCatalin Patulea 	struct evhttp_request *req;
2068a90a850SNick Mathewson 	struct evkeyvalq *output_headers;
20729af65ebSAlexey Ozeritsky 	struct evbuffer *output_buffer;
208be46c99bSCatalin Patulea 
209c5887f73SAlexey Ozeritsky 	int i;
21024a1f25aSAzat Khuzhin 	int ret = 0;
21124a1f25aSAzat Khuzhin 	enum { HTTP, HTTPS } type = HTTP;
212be46c99bSCatalin Patulea 
213c5887f73SAlexey Ozeritsky 	for (i = 1; i < argc; i++) {
214c5887f73SAlexey Ozeritsky 		if (!strcmp("-url", argv[i])) {
215c5887f73SAlexey Ozeritsky 			if (i < argc - 1) {
216c5887f73SAlexey Ozeritsky 				url = argv[i + 1];
217c5887f73SAlexey Ozeritsky 			} else {
218c5887f73SAlexey Ozeritsky 				syntax();
21924a1f25aSAzat Khuzhin 				goto error;
220c5887f73SAlexey Ozeritsky 			}
221fdf713a0SAzat Khuzhin 		} else if (!strcmp("-crt", argv[i])) {
222fdf713a0SAzat Khuzhin 			if (i < argc - 1) {
223fdf713a0SAzat Khuzhin 				crt = argv[i + 1];
224fdf713a0SAzat Khuzhin 			} else {
225fdf713a0SAzat Khuzhin 				syntax();
226fdf713a0SAzat Khuzhin 				goto error;
227fdf713a0SAzat Khuzhin 			}
228c5887f73SAlexey Ozeritsky 		} else if (!strcmp("-ignore-cert", argv[i])) {
229c5887f73SAlexey Ozeritsky 			ignore_cert = 1;
230c5887f73SAlexey Ozeritsky 		} else if (!strcmp("-data", argv[i])) {
231c5887f73SAlexey Ozeritsky 			if (i < argc - 1) {
232c5887f73SAlexey Ozeritsky 				data_file = argv[i + 1];
233c5887f73SAlexey Ozeritsky 			} else {
234c5887f73SAlexey Ozeritsky 				syntax();
23524a1f25aSAzat Khuzhin 				goto error;
236c5887f73SAlexey Ozeritsky 			}
237d9da8443SAzat Khuzhin 		} else if (!strcmp("-retries", argv[i])) {
238d9da8443SAzat Khuzhin 			if (i < argc - 1) {
239d9da8443SAzat Khuzhin 				retries = atoi(argv[i + 1]);
240d9da8443SAzat Khuzhin 			} else {
241d9da8443SAzat Khuzhin 				syntax();
24224a1f25aSAzat Khuzhin 				goto error;
243d9da8443SAzat Khuzhin 			}
2444637aa88SAzat Khuzhin 		} else if (!strcmp("-timeout", argv[i])) {
2454637aa88SAzat Khuzhin 			if (i < argc - 1) {
2464637aa88SAzat Khuzhin 				timeout = atoi(argv[i + 1]);
2474637aa88SAzat Khuzhin 			} else {
2484637aa88SAzat Khuzhin 				syntax();
2494637aa88SAzat Khuzhin 				goto error;
2504637aa88SAzat Khuzhin 			}
251c5887f73SAlexey Ozeritsky 		} else if (!strcmp("-help", argv[i])) {
252c5887f73SAlexey Ozeritsky 			syntax();
25324a1f25aSAzat Khuzhin 			goto error;
254c5887f73SAlexey Ozeritsky 		}
255c5887f73SAlexey Ozeritsky 	}
256c5887f73SAlexey Ozeritsky 
257c5887f73SAlexey Ozeritsky 	if (!url) {
258c5887f73SAlexey Ozeritsky 		syntax();
25924a1f25aSAzat Khuzhin 		goto error;
260c5887f73SAlexey Ozeritsky 	}
261c5887f73SAlexey Ozeritsky 
2624e143958SJoakim Söderberg #ifdef _WIN32
26319222e52SJoakim Soderberg 	{
26419222e52SJoakim Soderberg 		WORD wVersionRequested;
26519222e52SJoakim Soderberg 		WSADATA wsaData;
26619222e52SJoakim Soderberg 		int err;
26719222e52SJoakim Soderberg 
26819222e52SJoakim Soderberg 		wVersionRequested = MAKEWORD(2, 2);
26919222e52SJoakim Soderberg 
27019222e52SJoakim Soderberg 		err = WSAStartup(wVersionRequested, &wsaData);
27119222e52SJoakim Soderberg 		if (err != 0) {
27219222e52SJoakim Soderberg 			printf("WSAStartup failed with error: %d\n", err);
27324a1f25aSAzat Khuzhin 			goto error;
27419222e52SJoakim Soderberg 		}
27519222e52SJoakim Soderberg 	}
2764e143958SJoakim Söderberg #endif // _WIN32
27719222e52SJoakim Soderberg 
278be46c99bSCatalin Patulea 	http_uri = evhttp_uri_parse(url);
279be46c99bSCatalin Patulea 	if (http_uri == NULL) {
28024a1f25aSAzat Khuzhin 		err("malformed url");
28124a1f25aSAzat Khuzhin 		goto error;
282be46c99bSCatalin Patulea 	}
283be46c99bSCatalin Patulea 
284be46c99bSCatalin Patulea 	scheme = evhttp_uri_get_scheme(http_uri);
285be46c99bSCatalin Patulea 	if (scheme == NULL || (strcasecmp(scheme, "https") != 0 &&
286be46c99bSCatalin Patulea 	                       strcasecmp(scheme, "http") != 0)) {
28724a1f25aSAzat Khuzhin 		err("url must be http or https");
28824a1f25aSAzat Khuzhin 		goto error;
289be46c99bSCatalin Patulea 	}
290be46c99bSCatalin Patulea 
291be46c99bSCatalin Patulea 	host = evhttp_uri_get_host(http_uri);
292be46c99bSCatalin Patulea 	if (host == NULL) {
29324a1f25aSAzat Khuzhin 		err("url must have a host");
29424a1f25aSAzat Khuzhin 		goto error;
295be46c99bSCatalin Patulea 	}
296be46c99bSCatalin Patulea 
297be46c99bSCatalin Patulea 	port = evhttp_uri_get_port(http_uri);
298be46c99bSCatalin Patulea 	if (port == -1) {
299be46c99bSCatalin Patulea 		port = (strcasecmp(scheme, "http") == 0) ? 80 : 443;
300be46c99bSCatalin Patulea 	}
301be46c99bSCatalin Patulea 
302be46c99bSCatalin Patulea 	path = evhttp_uri_get_path(http_uri);
30329a04825SAndrey Skriabin 	if (strlen(path) == 0) {
304be46c99bSCatalin Patulea 		path = "/";
305be46c99bSCatalin Patulea 	}
306be46c99bSCatalin Patulea 
307be46c99bSCatalin Patulea 	query = evhttp_uri_get_query(http_uri);
308be46c99bSCatalin Patulea 	if (query == NULL) {
309be46c99bSCatalin Patulea 		snprintf(uri, sizeof(uri) - 1, "%s", path);
310be46c99bSCatalin Patulea 	} else {
311be46c99bSCatalin Patulea 		snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query);
312be46c99bSCatalin Patulea 	}
313be46c99bSCatalin Patulea 	uri[sizeof(uri) - 1] = '\0';
314be46c99bSCatalin Patulea 
315*894ca48aSBernard Spil #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
316*894ca48aSBernard Spil 	(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
317be46c99bSCatalin Patulea 	// Initialize OpenSSL
318be46c99bSCatalin Patulea 	SSL_library_init();
319be46c99bSCatalin Patulea 	ERR_load_crypto_strings();
320be46c99bSCatalin Patulea 	SSL_load_error_strings();
321be46c99bSCatalin Patulea 	OpenSSL_add_all_algorithms();
3223e9e0a0dSKurt Roeckx #endif
3235754d96aSPatrick Pelletier 
3245754d96aSPatrick Pelletier 	/* This isn't strictly necessary... OpenSSL performs RAND_poll
3255754d96aSPatrick Pelletier 	 * automatically on first use of random number generator. */
326be46c99bSCatalin Patulea 	r = RAND_poll();
327be46c99bSCatalin Patulea 	if (r == 0) {
32824a1f25aSAzat Khuzhin 		err_openssl("RAND_poll");
32924a1f25aSAzat Khuzhin 		goto error;
330be46c99bSCatalin Patulea 	}
3315754d96aSPatrick Pelletier 
3325754d96aSPatrick Pelletier 	/* Create a new OpenSSL context */
333be46c99bSCatalin Patulea 	ssl_ctx = SSL_CTX_new(SSLv23_method());
33424a1f25aSAzat Khuzhin 	if (!ssl_ctx) {
33524a1f25aSAzat Khuzhin 		err_openssl("SSL_CTX_new");
33624a1f25aSAzat Khuzhin 		goto error;
33724a1f25aSAzat Khuzhin 	}
338be46c99bSCatalin Patulea 
3394e143958SJoakim Söderberg #ifndef _WIN32
34019222e52SJoakim Soderberg 	/* TODO: Add certificate loading on Windows as well */
34119222e52SJoakim Soderberg 
3425c0132f3SDavid Disseldorp 	if (crt == NULL) {
3435c0132f3SDavid Disseldorp 		X509_STORE *store;
3445c0132f3SDavid Disseldorp 		/* Attempt to use the system's trusted root certificates. */
3455c0132f3SDavid Disseldorp 		store = SSL_CTX_get_cert_store(ssl_ctx);
3465c0132f3SDavid Disseldorp 		if (X509_STORE_set_default_paths(store) != 1) {
3475c0132f3SDavid Disseldorp 			err_openssl("X509_STORE_set_default_paths");
3485c0132f3SDavid Disseldorp 			goto error;
3495c0132f3SDavid Disseldorp 		}
3505c0132f3SDavid Disseldorp 	} else {
3515c0132f3SDavid Disseldorp 		if (SSL_CTX_load_verify_locations(ssl_ctx, crt, NULL) != 1) {
35224a1f25aSAzat Khuzhin 			err_openssl("SSL_CTX_load_verify_locations");
35324a1f25aSAzat Khuzhin 			goto error;
35424a1f25aSAzat Khuzhin 		}
3555c0132f3SDavid Disseldorp 	}
35664d9f161SPatrick Pelletier 	/* Ask OpenSSL to verify the server certificate.  Note that this
35764d9f161SPatrick Pelletier 	 * does NOT include verifying that the hostname is correct.
35864d9f161SPatrick Pelletier 	 * So, by itself, this means anyone with any legitimate
35964d9f161SPatrick Pelletier 	 * CA-issued certificate for any website, can impersonate any
36064d9f161SPatrick Pelletier 	 * other website in the world.  This is not good.  See "The
36164d9f161SPatrick Pelletier 	 * Most Dangerous Code in the World" article at
36264d9f161SPatrick Pelletier 	 * https://crypto.stanford.edu/~dabo/pubs/abstracts/ssl-client-bugs.html
36364d9f161SPatrick Pelletier 	 */
364aacd674cSPatrick Pelletier 	SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
36564d9f161SPatrick Pelletier 	/* This is how we solve the problem mentioned in the previous
36664d9f161SPatrick Pelletier 	 * comment.  We "wrap" OpenSSL's validation routine in our
36764d9f161SPatrick Pelletier 	 * own routine, which also validates the hostname by calling
36864d9f161SPatrick Pelletier 	 * the code provided by iSECPartners.  Note that even though
36964d9f161SPatrick Pelletier 	 * the "Everything You've Always Wanted to Know About
37064d9f161SPatrick Pelletier 	 * Certificate Validation With OpenSSL (But Were Afraid to
37164d9f161SPatrick Pelletier 	 * Ask)" paper from iSECPartners says very explicitly not to
37264d9f161SPatrick Pelletier 	 * call SSL_CTX_set_cert_verify_callback (at the bottom of
37364d9f161SPatrick Pelletier 	 * page 2), what we're doing here is safe because our
37464d9f161SPatrick Pelletier 	 * cert_verify_callback() calls X509_verify_cert(), which is
37564d9f161SPatrick Pelletier 	 * OpenSSL's built-in routine which would have been called if
37664d9f161SPatrick Pelletier 	 * we hadn't set the callback.  Therefore, we're just
37764d9f161SPatrick Pelletier 	 * "wrapping" OpenSSL's routine, not replacing it. */
37864d9f161SPatrick Pelletier 	SSL_CTX_set_cert_verify_callback(ssl_ctx, cert_verify_callback,
37964d9f161SPatrick Pelletier 					  (void *) host);
38013a4acdaSAzat Khuzhin #else // _WIN32
38113a4acdaSAzat Khuzhin 	(void)crt;
38213a4acdaSAzat Khuzhin #endif // _WIN32
383aacd674cSPatrick Pelletier 
384be46c99bSCatalin Patulea 	// Create event base
385be46c99bSCatalin Patulea 	base = event_base_new();
386be46c99bSCatalin Patulea 	if (!base) {
387be46c99bSCatalin Patulea 		perror("event_base_new()");
38824a1f25aSAzat Khuzhin 		goto error;
389be46c99bSCatalin Patulea 	}
390be46c99bSCatalin Patulea 
391be46c99bSCatalin Patulea 	// Create OpenSSL bufferevent and stack evhttp on top of it
392be46c99bSCatalin Patulea 	ssl = SSL_new(ssl_ctx);
393be46c99bSCatalin Patulea 	if (ssl == NULL) {
39424a1f25aSAzat Khuzhin 		err_openssl("SSL_new()");
39524a1f25aSAzat Khuzhin 		goto error;
396be46c99bSCatalin Patulea 	}
397be46c99bSCatalin Patulea 
3985c7282f7SJoakim Soderberg 	#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
399d1976f8eSNick Mathewson 	// Set hostname for SNI extension
400d1976f8eSNick Mathewson 	SSL_set_tlsext_host_name(ssl, host);
4015c7282f7SJoakim Soderberg 	#endif
402d1976f8eSNick Mathewson 
403be46c99bSCatalin Patulea 	if (strcasecmp(scheme, "http") == 0) {
404be46c99bSCatalin Patulea 		bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
405be46c99bSCatalin Patulea 	} else {
40624a1f25aSAzat Khuzhin 		type = HTTPS;
407be46c99bSCatalin Patulea 		bev = bufferevent_openssl_socket_new(base, -1, ssl,
408be46c99bSCatalin Patulea 			BUFFEREVENT_SSL_CONNECTING,
409be46c99bSCatalin Patulea 			BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
410be46c99bSCatalin Patulea 	}
411be46c99bSCatalin Patulea 
412be46c99bSCatalin Patulea 	if (bev == NULL) {
413be46c99bSCatalin Patulea 		fprintf(stderr, "bufferevent_openssl_socket_new() failed\n");
41424a1f25aSAzat Khuzhin 		goto error;
415be46c99bSCatalin Patulea 	}
416be46c99bSCatalin Patulea 
417be46c99bSCatalin Patulea 	bufferevent_openssl_set_allow_dirty_shutdown(bev, 1);
418be46c99bSCatalin Patulea 
419be46c99bSCatalin Patulea 	// For simplicity, we let DNS resolution block. Everything else should be
420be46c99bSCatalin Patulea 	// asynchronous though.
421be46c99bSCatalin Patulea 	evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev,
422be46c99bSCatalin Patulea 		host, port);
423be46c99bSCatalin Patulea 	if (evcon == NULL) {
424be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n");
42524a1f25aSAzat Khuzhin 		goto error;
426be46c99bSCatalin Patulea 	}
427be46c99bSCatalin Patulea 
428d9da8443SAzat Khuzhin 	if (retries > 0) {
429d9da8443SAzat Khuzhin 		evhttp_connection_set_retries(evcon, retries);
430d9da8443SAzat Khuzhin 	}
4314637aa88SAzat Khuzhin 	if (timeout >= 0) {
4324637aa88SAzat Khuzhin 		evhttp_connection_set_timeout(evcon, timeout);
4334637aa88SAzat Khuzhin 	}
434d9da8443SAzat Khuzhin 
435be46c99bSCatalin Patulea 	// Fire off the request
4365754d96aSPatrick Pelletier 	req = evhttp_request_new(http_request_done, bev);
437be46c99bSCatalin Patulea 	if (req == NULL) {
438be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_request_new() failed\n");
43924a1f25aSAzat Khuzhin 		goto error;
440be46c99bSCatalin Patulea 	}
441be46c99bSCatalin Patulea 
4428a90a850SNick Mathewson 	output_headers = evhttp_request_get_output_headers(req);
4438a90a850SNick Mathewson 	evhttp_add_header(output_headers, "Host", host);
4448a90a850SNick Mathewson 	evhttp_add_header(output_headers, "Connection", "close");
445be46c99bSCatalin Patulea 
446c5887f73SAlexey Ozeritsky 	if (data_file) {
44790786eb0SNick Mathewson 		/* NOTE: In production code, you'd probably want to use
44890786eb0SNick Mathewson 		 * evbuffer_add_file() or evbuffer_add_file_segment(), to
44990786eb0SNick Mathewson 		 * avoid needless copying. */
450c5887f73SAlexey Ozeritsky 		FILE * f = fopen(data_file, "rb");
451c5887f73SAlexey Ozeritsky 		char buf[1024];
452d7be7887SJoakim Soderberg 		size_t s;
453c5887f73SAlexey Ozeritsky 		size_t bytes = 0;
454c5887f73SAlexey Ozeritsky 
455c5887f73SAlexey Ozeritsky 		if (!f) {
456c5887f73SAlexey Ozeritsky 			syntax();
45724a1f25aSAzat Khuzhin 			goto error;
458c5887f73SAlexey Ozeritsky 		}
459c5887f73SAlexey Ozeritsky 
46029af65ebSAlexey Ozeritsky 		output_buffer = evhttp_request_get_output_buffer(req);
461c5887f73SAlexey Ozeritsky 		while ((s = fread(buf, 1, sizeof(buf), f)) > 0) {
46229af65ebSAlexey Ozeritsky 			evbuffer_add(output_buffer, buf, s);
463c5887f73SAlexey Ozeritsky 			bytes += s;
464c5887f73SAlexey Ozeritsky 		}
465462e6b60SNick Mathewson 		evutil_snprintf(buf, sizeof(buf)-1, "%lu", (unsigned long)bytes);
46629af65ebSAlexey Ozeritsky 		evhttp_add_header(output_headers, "Content-Length", buf);
467c5887f73SAlexey Ozeritsky 		fclose(f);
468c5887f73SAlexey Ozeritsky 	}
469c5887f73SAlexey Ozeritsky 
470c5887f73SAlexey Ozeritsky 	r = evhttp_make_request(evcon, req, data_file ? EVHTTP_REQ_POST : EVHTTP_REQ_GET, uri);
471be46c99bSCatalin Patulea 	if (r != 0) {
472be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_make_request() failed\n");
47324a1f25aSAzat Khuzhin 		goto error;
474be46c99bSCatalin Patulea 	}
475be46c99bSCatalin Patulea 
476be46c99bSCatalin Patulea 	event_base_dispatch(base);
47724a1f25aSAzat Khuzhin 	goto cleanup;
478be46c99bSCatalin Patulea 
47924a1f25aSAzat Khuzhin error:
48024a1f25aSAzat Khuzhin 	ret = 1;
48124a1f25aSAzat Khuzhin cleanup:
48224a1f25aSAzat Khuzhin 	if (evcon)
483be46c99bSCatalin Patulea 		evhttp_connection_free(evcon);
48424a1f25aSAzat Khuzhin 	if (http_uri)
48524a1f25aSAzat Khuzhin 		evhttp_uri_free(http_uri);
486be46c99bSCatalin Patulea 	event_base_free(base);
487be46c99bSCatalin Patulea 
48824a1f25aSAzat Khuzhin 	if (ssl_ctx)
48924a1f25aSAzat Khuzhin 		SSL_CTX_free(ssl_ctx);
490f3d7ff5dSAzat Khuzhin 	if (type == HTTP && ssl)
49124a1f25aSAzat Khuzhin 		SSL_free(ssl);
492*894ca48aSBernard Spil #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
493*894ca48aSBernard Spil 	(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
49424a1f25aSAzat Khuzhin 	EVP_cleanup();
49524a1f25aSAzat Khuzhin 	ERR_free_strings();
49624a1f25aSAzat Khuzhin 
497e7bd9e03SAzat Khuzhin #if OPENSSL_VERSION_NUMBER < 0x10000000L
498c4e9d9bdSAzat Khuzhin 	ERR_remove_state(0);
499e7bd9e03SAzat Khuzhin #else
500e7bd9e03SAzat Khuzhin 	ERR_remove_thread_state(NULL);
501c4e9d9bdSAzat Khuzhin #endif
502e7bd9e03SAzat Khuzhin 
50324a1f25aSAzat Khuzhin 	CRYPTO_cleanup_all_ex_data();
50424a1f25aSAzat Khuzhin 
50524a1f25aSAzat Khuzhin 	sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
506*894ca48aSBernard Spil #endif /* (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
507*894ca48aSBernard Spil 	(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L) */
50824a1f25aSAzat Khuzhin 
5094e143958SJoakim Söderberg #ifdef _WIN32
51019222e52SJoakim Soderberg 	WSACleanup();
51119222e52SJoakim Soderberg #endif
51219222e52SJoakim Soderberg 
51324a1f25aSAzat Khuzhin 	return ret;
514be46c99bSCatalin Patulea }
515