xref: /libevent-2.1.12/sample/https-client.c (revision f3d7ff5d)
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);
99d9da8443SAzat Khuzhin 	fputs("   https-client -url <https-url> [-data data-file.bin] [-ignore-cert] [-retries num]\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 
12264d9f161SPatrick Pelletier /* See http://archives.seul.org/libevent/users/Jan-2013/msg00039.html */
12364d9f161SPatrick Pelletier static int cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg)
12464d9f161SPatrick Pelletier {
12564d9f161SPatrick Pelletier 	char cert_str[256];
12664d9f161SPatrick Pelletier 	const char *host = (const char *) arg;
12764d9f161SPatrick Pelletier 	const char *res_str = "X509_verify_cert failed";
12864d9f161SPatrick Pelletier 	HostnameValidationResult res = Error;
12964d9f161SPatrick Pelletier 
13064d9f161SPatrick Pelletier 	/* This is the function that OpenSSL would call if we hadn't called
13164d9f161SPatrick Pelletier 	 * SSL_CTX_set_cert_verify_callback().  Therefore, we are "wrapping"
13264d9f161SPatrick Pelletier 	 * the default functionality, rather than replacing it. */
13329af65ebSAlexey Ozeritsky 	int ok_so_far = 0;
13464d9f161SPatrick Pelletier 
13529af65ebSAlexey Ozeritsky 	X509 *server_cert = NULL;
13629af65ebSAlexey Ozeritsky 
13729af65ebSAlexey Ozeritsky 	if (ignore_cert) {
13829af65ebSAlexey Ozeritsky 		return 1;
13929af65ebSAlexey Ozeritsky 	}
14029af65ebSAlexey Ozeritsky 
14129af65ebSAlexey Ozeritsky 	ok_so_far = X509_verify_cert(x509_ctx);
14229af65ebSAlexey Ozeritsky 
14329af65ebSAlexey Ozeritsky 	server_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
14464d9f161SPatrick Pelletier 
14564d9f161SPatrick Pelletier 	if (ok_so_far) {
14664d9f161SPatrick Pelletier 		res = validate_hostname(host, server_cert);
14764d9f161SPatrick Pelletier 
14864d9f161SPatrick Pelletier 		switch (res) {
14964d9f161SPatrick Pelletier 		case MatchFound:
15064d9f161SPatrick Pelletier 			res_str = "MatchFound";
15164d9f161SPatrick Pelletier 			break;
15264d9f161SPatrick Pelletier 		case MatchNotFound:
15364d9f161SPatrick Pelletier 			res_str = "MatchNotFound";
15464d9f161SPatrick Pelletier 			break;
15564d9f161SPatrick Pelletier 		case NoSANPresent:
15664d9f161SPatrick Pelletier 			res_str = "NoSANPresent";
15764d9f161SPatrick Pelletier 			break;
15864d9f161SPatrick Pelletier 		case MalformedCertificate:
15964d9f161SPatrick Pelletier 			res_str = "MalformedCertificate";
16064d9f161SPatrick Pelletier 			break;
16164d9f161SPatrick Pelletier 		case Error:
16264d9f161SPatrick Pelletier 			res_str = "Error";
16364d9f161SPatrick Pelletier 			break;
16464d9f161SPatrick Pelletier 		default:
16564d9f161SPatrick Pelletier 			res_str = "WTF!";
16664d9f161SPatrick Pelletier 			break;
16764d9f161SPatrick Pelletier 		}
16864d9f161SPatrick Pelletier 	}
16964d9f161SPatrick Pelletier 
17064d9f161SPatrick Pelletier 	X509_NAME_oneline(X509_get_subject_name (server_cert),
17164d9f161SPatrick Pelletier 			  cert_str, sizeof (cert_str));
17264d9f161SPatrick Pelletier 
17364d9f161SPatrick Pelletier 	if (res == MatchFound) {
17464d9f161SPatrick Pelletier 		printf("https server '%s' has this certificate, "
17564d9f161SPatrick Pelletier 		       "which looks good to me:\n%s\n",
17664d9f161SPatrick Pelletier 		       host, cert_str);
17764d9f161SPatrick Pelletier 		return 1;
17864d9f161SPatrick Pelletier 	} else {
17964d9f161SPatrick Pelletier 		printf("Got '%s' for hostname '%s' and certificate:\n%s\n",
18064d9f161SPatrick Pelletier 		       res_str, host, cert_str);
18164d9f161SPatrick Pelletier 		return 0;
18264d9f161SPatrick Pelletier 	}
18364d9f161SPatrick Pelletier }
18464d9f161SPatrick Pelletier 
185be46c99bSCatalin Patulea int
186be46c99bSCatalin Patulea main(int argc, char **argv)
187be46c99bSCatalin Patulea {
188be46c99bSCatalin Patulea 	int r;
189be46c99bSCatalin Patulea 
19024a1f25aSAzat Khuzhin 	struct evhttp_uri *http_uri = NULL;
19190786eb0SNick Mathewson 	const char *url = NULL, *data_file = NULL;
19290786eb0SNick Mathewson 	const char *scheme, *host, *path, *query;
193be46c99bSCatalin Patulea 	char uri[256];
194be46c99bSCatalin Patulea 	int port;
195d9da8443SAzat Khuzhin 	int retries = 0;
196be46c99bSCatalin Patulea 
19724a1f25aSAzat Khuzhin 	SSL_CTX *ssl_ctx = NULL;
198*f3d7ff5dSAzat Khuzhin 	SSL *ssl = NULL;
199be46c99bSCatalin Patulea 	struct bufferevent *bev;
20024a1f25aSAzat Khuzhin 	struct evhttp_connection *evcon = NULL;
201be46c99bSCatalin Patulea 	struct evhttp_request *req;
2028a90a850SNick Mathewson 	struct evkeyvalq *output_headers;
20329af65ebSAlexey Ozeritsky 	struct evbuffer *output_buffer;
204be46c99bSCatalin Patulea 
205c5887f73SAlexey Ozeritsky 	int i;
20624a1f25aSAzat Khuzhin 	int ret = 0;
20724a1f25aSAzat Khuzhin 	enum { HTTP, HTTPS } type = HTTP;
208be46c99bSCatalin Patulea 
209c5887f73SAlexey Ozeritsky 	for (i = 1; i < argc; i++) {
210c5887f73SAlexey Ozeritsky 		if (!strcmp("-url", argv[i])) {
211c5887f73SAlexey Ozeritsky 			if (i < argc - 1) {
212c5887f73SAlexey Ozeritsky 				url = argv[i + 1];
213c5887f73SAlexey Ozeritsky 			} else {
214c5887f73SAlexey Ozeritsky 				syntax();
21524a1f25aSAzat Khuzhin 				goto error;
216c5887f73SAlexey Ozeritsky 			}
217c5887f73SAlexey Ozeritsky 		} else if (!strcmp("-ignore-cert", argv[i])) {
218c5887f73SAlexey Ozeritsky 			ignore_cert = 1;
219c5887f73SAlexey Ozeritsky 		} else if (!strcmp("-data", argv[i])) {
220c5887f73SAlexey Ozeritsky 			if (i < argc - 1) {
221c5887f73SAlexey Ozeritsky 				data_file = argv[i + 1];
222c5887f73SAlexey Ozeritsky 			} else {
223c5887f73SAlexey Ozeritsky 				syntax();
22424a1f25aSAzat Khuzhin 				goto error;
225c5887f73SAlexey Ozeritsky 			}
226d9da8443SAzat Khuzhin 		} else if (!strcmp("-retries", argv[i])) {
227d9da8443SAzat Khuzhin 			if (i < argc - 1) {
228d9da8443SAzat Khuzhin 				retries = atoi(argv[i + 1]);
229d9da8443SAzat Khuzhin 			} else {
230d9da8443SAzat Khuzhin 				syntax();
23124a1f25aSAzat Khuzhin 				goto error;
232d9da8443SAzat Khuzhin 			}
233c5887f73SAlexey Ozeritsky 		} else if (!strcmp("-help", argv[i])) {
234c5887f73SAlexey Ozeritsky 			syntax();
23524a1f25aSAzat Khuzhin 			goto error;
236c5887f73SAlexey Ozeritsky 		}
237c5887f73SAlexey Ozeritsky 	}
238c5887f73SAlexey Ozeritsky 
239c5887f73SAlexey Ozeritsky 	if (!url) {
240c5887f73SAlexey Ozeritsky 		syntax();
24124a1f25aSAzat Khuzhin 		goto error;
242c5887f73SAlexey Ozeritsky 	}
243c5887f73SAlexey Ozeritsky 
2444e143958SJoakim Söderberg #ifdef _WIN32
24519222e52SJoakim Soderberg 	{
24619222e52SJoakim Soderberg 		WORD wVersionRequested;
24719222e52SJoakim Soderberg 		WSADATA wsaData;
24819222e52SJoakim Soderberg 		int err;
24919222e52SJoakim Soderberg 
25019222e52SJoakim Soderberg 		wVersionRequested = MAKEWORD(2, 2);
25119222e52SJoakim Soderberg 
25219222e52SJoakim Soderberg 		err = WSAStartup(wVersionRequested, &wsaData);
25319222e52SJoakim Soderberg 		if (err != 0) {
25419222e52SJoakim Soderberg 			printf("WSAStartup failed with error: %d\n", err);
25524a1f25aSAzat Khuzhin 			goto error;
25619222e52SJoakim Soderberg 		}
25719222e52SJoakim Soderberg 	}
2584e143958SJoakim Söderberg #endif // _WIN32
25919222e52SJoakim Soderberg 
260be46c99bSCatalin Patulea 	http_uri = evhttp_uri_parse(url);
261be46c99bSCatalin Patulea 	if (http_uri == NULL) {
26224a1f25aSAzat Khuzhin 		err("malformed url");
26324a1f25aSAzat Khuzhin 		goto error;
264be46c99bSCatalin Patulea 	}
265be46c99bSCatalin Patulea 
266be46c99bSCatalin Patulea 	scheme = evhttp_uri_get_scheme(http_uri);
267be46c99bSCatalin Patulea 	if (scheme == NULL || (strcasecmp(scheme, "https") != 0 &&
268be46c99bSCatalin Patulea 	                       strcasecmp(scheme, "http") != 0)) {
26924a1f25aSAzat Khuzhin 		err("url must be http or https");
27024a1f25aSAzat Khuzhin 		goto error;
271be46c99bSCatalin Patulea 	}
272be46c99bSCatalin Patulea 
273be46c99bSCatalin Patulea 	host = evhttp_uri_get_host(http_uri);
274be46c99bSCatalin Patulea 	if (host == NULL) {
27524a1f25aSAzat Khuzhin 		err("url must have a host");
27624a1f25aSAzat Khuzhin 		goto error;
277be46c99bSCatalin Patulea 	}
278be46c99bSCatalin Patulea 
279be46c99bSCatalin Patulea 	port = evhttp_uri_get_port(http_uri);
280be46c99bSCatalin Patulea 	if (port == -1) {
281be46c99bSCatalin Patulea 		port = (strcasecmp(scheme, "http") == 0) ? 80 : 443;
282be46c99bSCatalin Patulea 	}
283be46c99bSCatalin Patulea 
284be46c99bSCatalin Patulea 	path = evhttp_uri_get_path(http_uri);
28529a04825SAndrey Skriabin 	if (strlen(path) == 0) {
286be46c99bSCatalin Patulea 		path = "/";
287be46c99bSCatalin Patulea 	}
288be46c99bSCatalin Patulea 
289be46c99bSCatalin Patulea 	query = evhttp_uri_get_query(http_uri);
290be46c99bSCatalin Patulea 	if (query == NULL) {
291be46c99bSCatalin Patulea 		snprintf(uri, sizeof(uri) - 1, "%s", path);
292be46c99bSCatalin Patulea 	} else {
293be46c99bSCatalin Patulea 		snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query);
294be46c99bSCatalin Patulea 	}
295be46c99bSCatalin Patulea 	uri[sizeof(uri) - 1] = '\0';
296be46c99bSCatalin Patulea 
297be46c99bSCatalin Patulea 	// Initialize OpenSSL
298be46c99bSCatalin Patulea 	SSL_library_init();
299be46c99bSCatalin Patulea 	ERR_load_crypto_strings();
300be46c99bSCatalin Patulea 	SSL_load_error_strings();
301be46c99bSCatalin Patulea 	OpenSSL_add_all_algorithms();
3025754d96aSPatrick Pelletier 
3035754d96aSPatrick Pelletier 	/* This isn't strictly necessary... OpenSSL performs RAND_poll
3045754d96aSPatrick Pelletier 	 * automatically on first use of random number generator. */
305be46c99bSCatalin Patulea 	r = RAND_poll();
306be46c99bSCatalin Patulea 	if (r == 0) {
30724a1f25aSAzat Khuzhin 		err_openssl("RAND_poll");
30824a1f25aSAzat Khuzhin 		goto error;
309be46c99bSCatalin Patulea 	}
3105754d96aSPatrick Pelletier 
3115754d96aSPatrick Pelletier 	/* Create a new OpenSSL context */
312be46c99bSCatalin Patulea 	ssl_ctx = SSL_CTX_new(SSLv23_method());
31324a1f25aSAzat Khuzhin 	if (!ssl_ctx) {
31424a1f25aSAzat Khuzhin 		err_openssl("SSL_CTX_new");
31524a1f25aSAzat Khuzhin 		goto error;
31624a1f25aSAzat Khuzhin 	}
317be46c99bSCatalin Patulea 
3184e143958SJoakim Söderberg #ifndef _WIN32
31919222e52SJoakim Soderberg 	/* TODO: Add certificate loading on Windows as well */
32019222e52SJoakim Soderberg 
321aacd674cSPatrick Pelletier 	/* Attempt to use the system's trusted root certificates.
322aacd674cSPatrick Pelletier 	 * (This path is only valid for Debian-based systems.) */
323aacd674cSPatrick Pelletier 	if (1 != SSL_CTX_load_verify_locations(ssl_ctx,
324aacd674cSPatrick Pelletier 					       "/etc/ssl/certs/ca-certificates.crt",
32524a1f25aSAzat Khuzhin 					       NULL)) {
32624a1f25aSAzat Khuzhin 		err_openssl("SSL_CTX_load_verify_locations");
32724a1f25aSAzat Khuzhin 		goto error;
32824a1f25aSAzat Khuzhin 	}
32964d9f161SPatrick Pelletier 	/* Ask OpenSSL to verify the server certificate.  Note that this
33064d9f161SPatrick Pelletier 	 * does NOT include verifying that the hostname is correct.
33164d9f161SPatrick Pelletier 	 * So, by itself, this means anyone with any legitimate
33264d9f161SPatrick Pelletier 	 * CA-issued certificate for any website, can impersonate any
33364d9f161SPatrick Pelletier 	 * other website in the world.  This is not good.  See "The
33464d9f161SPatrick Pelletier 	 * Most Dangerous Code in the World" article at
33564d9f161SPatrick Pelletier 	 * https://crypto.stanford.edu/~dabo/pubs/abstracts/ssl-client-bugs.html
33664d9f161SPatrick Pelletier 	 */
337aacd674cSPatrick Pelletier 	SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
33864d9f161SPatrick Pelletier 	/* This is how we solve the problem mentioned in the previous
33964d9f161SPatrick Pelletier 	 * comment.  We "wrap" OpenSSL's validation routine in our
34064d9f161SPatrick Pelletier 	 * own routine, which also validates the hostname by calling
34164d9f161SPatrick Pelletier 	 * the code provided by iSECPartners.  Note that even though
34264d9f161SPatrick Pelletier 	 * the "Everything You've Always Wanted to Know About
34364d9f161SPatrick Pelletier 	 * Certificate Validation With OpenSSL (But Were Afraid to
34464d9f161SPatrick Pelletier 	 * Ask)" paper from iSECPartners says very explicitly not to
34564d9f161SPatrick Pelletier 	 * call SSL_CTX_set_cert_verify_callback (at the bottom of
34664d9f161SPatrick Pelletier 	 * page 2), what we're doing here is safe because our
34764d9f161SPatrick Pelletier 	 * cert_verify_callback() calls X509_verify_cert(), which is
34864d9f161SPatrick Pelletier 	 * OpenSSL's built-in routine which would have been called if
34964d9f161SPatrick Pelletier 	 * we hadn't set the callback.  Therefore, we're just
35064d9f161SPatrick Pelletier 	 * "wrapping" OpenSSL's routine, not replacing it. */
35164d9f161SPatrick Pelletier 	SSL_CTX_set_cert_verify_callback(ssl_ctx, cert_verify_callback,
35264d9f161SPatrick Pelletier 					  (void *) host);
3534e143958SJoakim Söderberg #endif // not _WIN32
354aacd674cSPatrick Pelletier 
355be46c99bSCatalin Patulea 	// Create event base
356be46c99bSCatalin Patulea 	base = event_base_new();
357be46c99bSCatalin Patulea 	if (!base) {
358be46c99bSCatalin Patulea 		perror("event_base_new()");
35924a1f25aSAzat Khuzhin 		goto error;
360be46c99bSCatalin Patulea 	}
361be46c99bSCatalin Patulea 
362be46c99bSCatalin Patulea 	// Create OpenSSL bufferevent and stack evhttp on top of it
363be46c99bSCatalin Patulea 	ssl = SSL_new(ssl_ctx);
364be46c99bSCatalin Patulea 	if (ssl == NULL) {
36524a1f25aSAzat Khuzhin 		err_openssl("SSL_new()");
36624a1f25aSAzat Khuzhin 		goto error;
367be46c99bSCatalin Patulea 	}
368be46c99bSCatalin Patulea 
3695c7282f7SJoakim Soderberg 	#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
370d1976f8eSNick Mathewson 	// Set hostname for SNI extension
371d1976f8eSNick Mathewson 	SSL_set_tlsext_host_name(ssl, host);
3725c7282f7SJoakim Soderberg 	#endif
373d1976f8eSNick Mathewson 
374be46c99bSCatalin Patulea 	if (strcasecmp(scheme, "http") == 0) {
375be46c99bSCatalin Patulea 		bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
376be46c99bSCatalin Patulea 	} else {
37724a1f25aSAzat Khuzhin 		type = HTTPS;
378be46c99bSCatalin Patulea 		bev = bufferevent_openssl_socket_new(base, -1, ssl,
379be46c99bSCatalin Patulea 			BUFFEREVENT_SSL_CONNECTING,
380be46c99bSCatalin Patulea 			BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
381be46c99bSCatalin Patulea 	}
382be46c99bSCatalin Patulea 
383be46c99bSCatalin Patulea 	if (bev == NULL) {
384be46c99bSCatalin Patulea 		fprintf(stderr, "bufferevent_openssl_socket_new() failed\n");
38524a1f25aSAzat Khuzhin 		goto error;
386be46c99bSCatalin Patulea 	}
387be46c99bSCatalin Patulea 
388be46c99bSCatalin Patulea 	bufferevent_openssl_set_allow_dirty_shutdown(bev, 1);
389be46c99bSCatalin Patulea 
390be46c99bSCatalin Patulea 	// For simplicity, we let DNS resolution block. Everything else should be
391be46c99bSCatalin Patulea 	// asynchronous though.
392be46c99bSCatalin Patulea 	evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev,
393be46c99bSCatalin Patulea 		host, port);
394be46c99bSCatalin Patulea 	if (evcon == NULL) {
395be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n");
39624a1f25aSAzat Khuzhin 		goto error;
397be46c99bSCatalin Patulea 	}
398be46c99bSCatalin Patulea 
399d9da8443SAzat Khuzhin 	if (retries > 0) {
400d9da8443SAzat Khuzhin 		evhttp_connection_set_retries(evcon, retries);
401d9da8443SAzat Khuzhin 	}
402d9da8443SAzat Khuzhin 
403be46c99bSCatalin Patulea 	// Fire off the request
4045754d96aSPatrick Pelletier 	req = evhttp_request_new(http_request_done, bev);
405be46c99bSCatalin Patulea 	if (req == NULL) {
406be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_request_new() failed\n");
40724a1f25aSAzat Khuzhin 		goto error;
408be46c99bSCatalin Patulea 	}
409be46c99bSCatalin Patulea 
4108a90a850SNick Mathewson 	output_headers = evhttp_request_get_output_headers(req);
4118a90a850SNick Mathewson 	evhttp_add_header(output_headers, "Host", host);
4128a90a850SNick Mathewson 	evhttp_add_header(output_headers, "Connection", "close");
413be46c99bSCatalin Patulea 
414c5887f73SAlexey Ozeritsky 	if (data_file) {
41590786eb0SNick Mathewson 		/* NOTE: In production code, you'd probably want to use
41690786eb0SNick Mathewson 		 * evbuffer_add_file() or evbuffer_add_file_segment(), to
41790786eb0SNick Mathewson 		 * avoid needless copying. */
418c5887f73SAlexey Ozeritsky 		FILE * f = fopen(data_file, "rb");
419c5887f73SAlexey Ozeritsky 		char buf[1024];
420d7be7887SJoakim Soderberg 		size_t s;
421c5887f73SAlexey Ozeritsky 		size_t bytes = 0;
422c5887f73SAlexey Ozeritsky 
423c5887f73SAlexey Ozeritsky 		if (!f) {
424c5887f73SAlexey Ozeritsky 			syntax();
42524a1f25aSAzat Khuzhin 			goto error;
426c5887f73SAlexey Ozeritsky 		}
427c5887f73SAlexey Ozeritsky 
42829af65ebSAlexey Ozeritsky 		output_buffer = evhttp_request_get_output_buffer(req);
429c5887f73SAlexey Ozeritsky 		while ((s = fread(buf, 1, sizeof(buf), f)) > 0) {
43029af65ebSAlexey Ozeritsky 			evbuffer_add(output_buffer, buf, s);
431c5887f73SAlexey Ozeritsky 			bytes += s;
432c5887f73SAlexey Ozeritsky 		}
433462e6b60SNick Mathewson 		evutil_snprintf(buf, sizeof(buf)-1, "%lu", (unsigned long)bytes);
43429af65ebSAlexey Ozeritsky 		evhttp_add_header(output_headers, "Content-Length", buf);
435c5887f73SAlexey Ozeritsky 		fclose(f);
436c5887f73SAlexey Ozeritsky 	}
437c5887f73SAlexey Ozeritsky 
438c5887f73SAlexey Ozeritsky 	r = evhttp_make_request(evcon, req, data_file ? EVHTTP_REQ_POST : EVHTTP_REQ_GET, uri);
439be46c99bSCatalin Patulea 	if (r != 0) {
440be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_make_request() failed\n");
44124a1f25aSAzat Khuzhin 		goto error;
442be46c99bSCatalin Patulea 	}
443be46c99bSCatalin Patulea 
444be46c99bSCatalin Patulea 	event_base_dispatch(base);
44524a1f25aSAzat Khuzhin 	goto cleanup;
446be46c99bSCatalin Patulea 
44724a1f25aSAzat Khuzhin error:
44824a1f25aSAzat Khuzhin 	ret = 1;
44924a1f25aSAzat Khuzhin cleanup:
45024a1f25aSAzat Khuzhin 	if (evcon)
451be46c99bSCatalin Patulea 		evhttp_connection_free(evcon);
45224a1f25aSAzat Khuzhin 	if (http_uri)
45324a1f25aSAzat Khuzhin 		evhttp_uri_free(http_uri);
454be46c99bSCatalin Patulea 	event_base_free(base);
455be46c99bSCatalin Patulea 
45624a1f25aSAzat Khuzhin 	if (ssl_ctx)
45724a1f25aSAzat Khuzhin 		SSL_CTX_free(ssl_ctx);
458*f3d7ff5dSAzat Khuzhin 	if (type == HTTP && ssl)
45924a1f25aSAzat Khuzhin 		SSL_free(ssl);
46024a1f25aSAzat Khuzhin 	EVP_cleanup();
46124a1f25aSAzat Khuzhin 	ERR_free_strings();
46224a1f25aSAzat Khuzhin 
46324a1f25aSAzat Khuzhin 	ERR_remove_state(0);
46424a1f25aSAzat Khuzhin 	CRYPTO_cleanup_all_ex_data();
46524a1f25aSAzat Khuzhin 
46624a1f25aSAzat Khuzhin 	sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
46724a1f25aSAzat Khuzhin 
4684e143958SJoakim Söderberg #ifdef _WIN32
46919222e52SJoakim Soderberg 	WSACleanup();
47019222e52SJoakim Soderberg #endif
47119222e52SJoakim Soderberg 
47224a1f25aSAzat Khuzhin 	return ret;
473be46c99bSCatalin Patulea }
474