xref: /libevent-2.1.12/sample/https-client.c (revision 4e143958)
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.
140ef1d04eSJoakim Soderberg #ifdef __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 
24*4e143958SJoakim Söderberg #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;
49be46c99bSCatalin Patulea 
50be46c99bSCatalin Patulea static void
51be46c99bSCatalin Patulea http_request_done(struct evhttp_request *req, void *ctx)
52be46c99bSCatalin Patulea {
53be46c99bSCatalin Patulea 	char buffer[256];
54be46c99bSCatalin Patulea 	int nread;
55be46c99bSCatalin Patulea 
56be46c99bSCatalin Patulea 	if (req == NULL) {
575754d96aSPatrick Pelletier 		/* If req is NULL, it means an error occurred, but
585754d96aSPatrick Pelletier 		 * sadly we are mostly left guessing what the error
595754d96aSPatrick Pelletier 		 * might have been.  We'll do our best... */
605754d96aSPatrick Pelletier 		struct bufferevent *bev = (struct bufferevent *) ctx;
615754d96aSPatrick Pelletier 		unsigned long oslerr;
625754d96aSPatrick Pelletier 		int printed_err = 0;
635754d96aSPatrick Pelletier 		int errcode = EVUTIL_SOCKET_ERROR();
64be46c99bSCatalin Patulea 		fprintf(stderr, "some request failed - no idea which one though!\n");
655754d96aSPatrick Pelletier 		/* Print out the OpenSSL error queue that libevent
665754d96aSPatrick Pelletier 		 * squirreled away for us, if any. */
675754d96aSPatrick Pelletier 		while ((oslerr = bufferevent_get_openssl_error(bev))) {
685754d96aSPatrick Pelletier 			ERR_error_string_n(oslerr, buffer, sizeof(buffer));
695754d96aSPatrick Pelletier 			fprintf(stderr, "%s\n", buffer);
705754d96aSPatrick Pelletier 			printed_err = 1;
715754d96aSPatrick Pelletier 		}
725754d96aSPatrick Pelletier 		/* If the OpenSSL error queue was empty, maybe it was a
735754d96aSPatrick Pelletier 		 * socket error; let's try printing that. */
745754d96aSPatrick Pelletier 		if (! printed_err)
755754d96aSPatrick Pelletier 			fprintf(stderr, "socket error = %s (%d)\n",
765754d96aSPatrick Pelletier 				evutil_socket_error_to_string(errcode),
775754d96aSPatrick Pelletier 				errcode);
78be46c99bSCatalin Patulea 		return;
79be46c99bSCatalin Patulea 	}
80be46c99bSCatalin Patulea 
81be46c99bSCatalin Patulea 	fprintf(stderr, "Response line: %d %s\n",
828a90a850SNick Mathewson 	    evhttp_request_get_response_code(req),
838a90a850SNick Mathewson 	    evhttp_request_get_response_code_line(req));
84be46c99bSCatalin Patulea 
8595acdaa3SNick Mathewson 	while ((nread = evbuffer_remove(evhttp_request_get_input_buffer(req),
8695acdaa3SNick Mathewson 		    buffer, sizeof(buffer)))
87be46c99bSCatalin Patulea 	       > 0) {
8842d7441aSPatrick Pelletier 		/* These are just arbitrary chunks of 256 bytes.
8942d7441aSPatrick Pelletier 		 * They are not lines, so we can't treat them as such. */
90be46c99bSCatalin Patulea 		fwrite(buffer, nread, 1, stdout);
91be46c99bSCatalin Patulea 	}
92be46c99bSCatalin Patulea }
93be46c99bSCatalin Patulea 
94be46c99bSCatalin Patulea static void
95be46c99bSCatalin Patulea syntax(void)
96be46c99bSCatalin Patulea {
97be46c99bSCatalin Patulea 	fputs("Syntax:\n", stderr);
98be46c99bSCatalin Patulea 	fputs("   https-client <https-url>\n", stderr);
99be46c99bSCatalin Patulea 	fputs("Example:\n", stderr);
100be46c99bSCatalin Patulea 	fputs("   https-client https://ip.appspot.com/\n", stderr);
101be46c99bSCatalin Patulea 
102be46c99bSCatalin Patulea 	exit(1);
103be46c99bSCatalin Patulea }
104be46c99bSCatalin Patulea 
105be46c99bSCatalin Patulea static void
106be46c99bSCatalin Patulea die(const char *msg)
107be46c99bSCatalin Patulea {
108be46c99bSCatalin Patulea 	fputs(msg, stderr);
109be46c99bSCatalin Patulea 	exit(1);
110be46c99bSCatalin Patulea }
111be46c99bSCatalin Patulea 
1125754d96aSPatrick Pelletier static void
1135754d96aSPatrick Pelletier die_openssl(const char *func)
1145754d96aSPatrick Pelletier {
1155754d96aSPatrick Pelletier 	fprintf (stderr, "%s failed:\n", func);
1165754d96aSPatrick Pelletier 
1175754d96aSPatrick Pelletier 	/* This is the OpenSSL function that prints the contents of the
1185754d96aSPatrick Pelletier 	 * error stack to the specified file handle. */
1195754d96aSPatrick Pelletier 	ERR_print_errors_fp (stderr);
1205754d96aSPatrick Pelletier 
1215754d96aSPatrick Pelletier 	exit(1);
1225754d96aSPatrick Pelletier }
1235754d96aSPatrick Pelletier 
12464d9f161SPatrick Pelletier /* See http://archives.seul.org/libevent/users/Jan-2013/msg00039.html */
12564d9f161SPatrick Pelletier static int cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg)
12664d9f161SPatrick Pelletier {
12764d9f161SPatrick Pelletier 	char cert_str[256];
12864d9f161SPatrick Pelletier 	const char *host = (const char *) arg;
12964d9f161SPatrick Pelletier 	const char *res_str = "X509_verify_cert failed";
13064d9f161SPatrick Pelletier 	HostnameValidationResult res = Error;
13164d9f161SPatrick Pelletier 
13264d9f161SPatrick Pelletier 	/* This is the function that OpenSSL would call if we hadn't called
13364d9f161SPatrick Pelletier 	 * SSL_CTX_set_cert_verify_callback().  Therefore, we are "wrapping"
13464d9f161SPatrick Pelletier 	 * the default functionality, rather than replacing it. */
13564d9f161SPatrick Pelletier 	int ok_so_far = X509_verify_cert(x509_ctx);
13664d9f161SPatrick Pelletier 
13764d9f161SPatrick Pelletier 	X509 *server_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
13864d9f161SPatrick Pelletier 
13964d9f161SPatrick Pelletier 	if (ok_so_far) {
14064d9f161SPatrick Pelletier 		res = validate_hostname(host, server_cert);
14164d9f161SPatrick Pelletier 
14264d9f161SPatrick Pelletier 		switch (res) {
14364d9f161SPatrick Pelletier 		case MatchFound:
14464d9f161SPatrick Pelletier 			res_str = "MatchFound";
14564d9f161SPatrick Pelletier 			break;
14664d9f161SPatrick Pelletier 		case MatchNotFound:
14764d9f161SPatrick Pelletier 			res_str = "MatchNotFound";
14864d9f161SPatrick Pelletier 			break;
14964d9f161SPatrick Pelletier 		case NoSANPresent:
15064d9f161SPatrick Pelletier 			res_str = "NoSANPresent";
15164d9f161SPatrick Pelletier 			break;
15264d9f161SPatrick Pelletier 		case MalformedCertificate:
15364d9f161SPatrick Pelletier 			res_str = "MalformedCertificate";
15464d9f161SPatrick Pelletier 			break;
15564d9f161SPatrick Pelletier 		case Error:
15664d9f161SPatrick Pelletier 			res_str = "Error";
15764d9f161SPatrick Pelletier 			break;
15864d9f161SPatrick Pelletier 		default:
15964d9f161SPatrick Pelletier 			res_str = "WTF!";
16064d9f161SPatrick Pelletier 			break;
16164d9f161SPatrick Pelletier 		}
16264d9f161SPatrick Pelletier 	}
16364d9f161SPatrick Pelletier 
16464d9f161SPatrick Pelletier 	X509_NAME_oneline(X509_get_subject_name (server_cert),
16564d9f161SPatrick Pelletier 			  cert_str, sizeof (cert_str));
16664d9f161SPatrick Pelletier 
16764d9f161SPatrick Pelletier 	if (res == MatchFound) {
16864d9f161SPatrick Pelletier 		printf("https server '%s' has this certificate, "
16964d9f161SPatrick Pelletier 		       "which looks good to me:\n%s\n",
17064d9f161SPatrick Pelletier 		       host, cert_str);
17164d9f161SPatrick Pelletier 		return 1;
17264d9f161SPatrick Pelletier 	} else {
17364d9f161SPatrick Pelletier 		printf("Got '%s' for hostname '%s' and certificate:\n%s\n",
17464d9f161SPatrick Pelletier 		       res_str, host, cert_str);
17564d9f161SPatrick Pelletier 		return 0;
17664d9f161SPatrick Pelletier 	}
17764d9f161SPatrick Pelletier }
17864d9f161SPatrick Pelletier 
179be46c99bSCatalin Patulea int
180be46c99bSCatalin Patulea main(int argc, char **argv)
181be46c99bSCatalin Patulea {
182be46c99bSCatalin Patulea 	int r;
183be46c99bSCatalin Patulea 
184be46c99bSCatalin Patulea 	struct evhttp_uri *http_uri;
185be46c99bSCatalin Patulea 	const char *url, *scheme, *host, *path, *query;
186be46c99bSCatalin Patulea 	char uri[256];
187be46c99bSCatalin Patulea 	int port;
188be46c99bSCatalin Patulea 
189be46c99bSCatalin Patulea 	SSL_CTX *ssl_ctx;
190be46c99bSCatalin Patulea 	SSL *ssl;
191be46c99bSCatalin Patulea 	struct bufferevent *bev;
192be46c99bSCatalin Patulea 	struct evhttp_connection *evcon;
193be46c99bSCatalin Patulea 	struct evhttp_request *req;
1948a90a850SNick Mathewson 	struct evkeyvalq *output_headers;
195be46c99bSCatalin Patulea 
196be46c99bSCatalin Patulea 	if (argc != 2)
197be46c99bSCatalin Patulea 		syntax();
198be46c99bSCatalin Patulea 
199*4e143958SJoakim Söderberg #ifdef _WIN32
20019222e52SJoakim Soderberg 	{
20119222e52SJoakim Soderberg 		WORD wVersionRequested;
20219222e52SJoakim Soderberg 		WSADATA wsaData;
20319222e52SJoakim Soderberg 		int err;
20419222e52SJoakim Soderberg 
20519222e52SJoakim Soderberg 		wVersionRequested = MAKEWORD(2, 2);
20619222e52SJoakim Soderberg 
20719222e52SJoakim Soderberg 		err = WSAStartup(wVersionRequested, &wsaData);
20819222e52SJoakim Soderberg 		if (err != 0) {
20919222e52SJoakim Soderberg 			printf("WSAStartup failed with error: %d\n", err);
21019222e52SJoakim Soderberg 			return 1;
21119222e52SJoakim Soderberg 		}
21219222e52SJoakim Soderberg 	}
213*4e143958SJoakim Söderberg #endif // _WIN32
21419222e52SJoakim Soderberg 
215be46c99bSCatalin Patulea 	url = argv[1];
216be46c99bSCatalin Patulea 	http_uri = evhttp_uri_parse(url);
217be46c99bSCatalin Patulea 	if (http_uri == NULL) {
218be46c99bSCatalin Patulea 		die("malformed url");
219be46c99bSCatalin Patulea 	}
220be46c99bSCatalin Patulea 
221be46c99bSCatalin Patulea 	scheme = evhttp_uri_get_scheme(http_uri);
222be46c99bSCatalin Patulea 	if (scheme == NULL || (strcasecmp(scheme, "https") != 0 &&
223be46c99bSCatalin Patulea 	                       strcasecmp(scheme, "http") != 0)) {
224be46c99bSCatalin Patulea 		die("url must be http or https");
225be46c99bSCatalin Patulea 	}
226be46c99bSCatalin Patulea 
227be46c99bSCatalin Patulea 	host = evhttp_uri_get_host(http_uri);
228be46c99bSCatalin Patulea 	if (host == NULL) {
229be46c99bSCatalin Patulea 		die("url must have a host");
230be46c99bSCatalin Patulea 	}
231be46c99bSCatalin Patulea 
232be46c99bSCatalin Patulea 	port = evhttp_uri_get_port(http_uri);
233be46c99bSCatalin Patulea 	if (port == -1) {
234be46c99bSCatalin Patulea 		port = (strcasecmp(scheme, "http") == 0) ? 80 : 443;
235be46c99bSCatalin Patulea 	}
236be46c99bSCatalin Patulea 
237be46c99bSCatalin Patulea 	path = evhttp_uri_get_path(http_uri);
238be46c99bSCatalin Patulea 	if (path == NULL) {
239be46c99bSCatalin Patulea 		path = "/";
240be46c99bSCatalin Patulea 	}
241be46c99bSCatalin Patulea 
242be46c99bSCatalin Patulea 	query = evhttp_uri_get_query(http_uri);
243be46c99bSCatalin Patulea 	if (query == NULL) {
244be46c99bSCatalin Patulea 		snprintf(uri, sizeof(uri) - 1, "%s", path);
245be46c99bSCatalin Patulea 	} else {
246be46c99bSCatalin Patulea 		snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query);
247be46c99bSCatalin Patulea 	}
248be46c99bSCatalin Patulea 	uri[sizeof(uri) - 1] = '\0';
249be46c99bSCatalin Patulea 
250be46c99bSCatalin Patulea 	// Initialize OpenSSL
251be46c99bSCatalin Patulea 	SSL_library_init();
252be46c99bSCatalin Patulea 	ERR_load_crypto_strings();
253be46c99bSCatalin Patulea 	SSL_load_error_strings();
254be46c99bSCatalin Patulea 	OpenSSL_add_all_algorithms();
2555754d96aSPatrick Pelletier 
2565754d96aSPatrick Pelletier 	/* This isn't strictly necessary... OpenSSL performs RAND_poll
2575754d96aSPatrick Pelletier 	 * automatically on first use of random number generator. */
258be46c99bSCatalin Patulea 	r = RAND_poll();
259be46c99bSCatalin Patulea 	if (r == 0) {
2605754d96aSPatrick Pelletier 		die_openssl("RAND_poll");
261be46c99bSCatalin Patulea 	}
2625754d96aSPatrick Pelletier 
2635754d96aSPatrick Pelletier 	/* Create a new OpenSSL context */
264be46c99bSCatalin Patulea 	ssl_ctx = SSL_CTX_new(SSLv23_method());
2655754d96aSPatrick Pelletier 	if (!ssl_ctx)
2665754d96aSPatrick Pelletier 		die_openssl("SSL_CTX_new");
267be46c99bSCatalin Patulea 
268*4e143958SJoakim Söderberg 	#ifndef _WIN32
26919222e52SJoakim Soderberg 	/* TODO: Add certificate loading on Windows as well */
27019222e52SJoakim Soderberg 
271aacd674cSPatrick Pelletier 	/* Attempt to use the system's trusted root certificates.
272aacd674cSPatrick Pelletier 	 * (This path is only valid for Debian-based systems.) */
273aacd674cSPatrick Pelletier 	if (1 != SSL_CTX_load_verify_locations(ssl_ctx,
274aacd674cSPatrick Pelletier 					       "/etc/ssl/certs/ca-certificates.crt",
275aacd674cSPatrick Pelletier 					       NULL))
276aacd674cSPatrick Pelletier 		die_openssl("SSL_CTX_load_verify_locations");
27764d9f161SPatrick Pelletier 	/* Ask OpenSSL to verify the server certificate.  Note that this
27864d9f161SPatrick Pelletier 	 * does NOT include verifying that the hostname is correct.
27964d9f161SPatrick Pelletier 	 * So, by itself, this means anyone with any legitimate
28064d9f161SPatrick Pelletier 	 * CA-issued certificate for any website, can impersonate any
28164d9f161SPatrick Pelletier 	 * other website in the world.  This is not good.  See "The
28264d9f161SPatrick Pelletier 	 * Most Dangerous Code in the World" article at
28364d9f161SPatrick Pelletier 	 * https://crypto.stanford.edu/~dabo/pubs/abstracts/ssl-client-bugs.html
28464d9f161SPatrick Pelletier 	 */
285aacd674cSPatrick Pelletier 	SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
28664d9f161SPatrick Pelletier 	/* This is how we solve the problem mentioned in the previous
28764d9f161SPatrick Pelletier 	 * comment.  We "wrap" OpenSSL's validation routine in our
28864d9f161SPatrick Pelletier 	 * own routine, which also validates the hostname by calling
28964d9f161SPatrick Pelletier 	 * the code provided by iSECPartners.  Note that even though
29064d9f161SPatrick Pelletier 	 * the "Everything You've Always Wanted to Know About
29164d9f161SPatrick Pelletier 	 * Certificate Validation With OpenSSL (But Were Afraid to
29264d9f161SPatrick Pelletier 	 * Ask)" paper from iSECPartners says very explicitly not to
29364d9f161SPatrick Pelletier 	 * call SSL_CTX_set_cert_verify_callback (at the bottom of
29464d9f161SPatrick Pelletier 	 * page 2), what we're doing here is safe because our
29564d9f161SPatrick Pelletier 	 * cert_verify_callback() calls X509_verify_cert(), which is
29664d9f161SPatrick Pelletier 	 * OpenSSL's built-in routine which would have been called if
29764d9f161SPatrick Pelletier 	 * we hadn't set the callback.  Therefore, we're just
29864d9f161SPatrick Pelletier 	 * "wrapping" OpenSSL's routine, not replacing it. */
29964d9f161SPatrick Pelletier 	SSL_CTX_set_cert_verify_callback (ssl_ctx, cert_verify_callback,
30064d9f161SPatrick Pelletier 					  (void *) host);
301*4e143958SJoakim Söderberg 	#endif // not _WIN32
302aacd674cSPatrick Pelletier 
303be46c99bSCatalin Patulea 	// Create event base
304be46c99bSCatalin Patulea 	base = event_base_new();
305be46c99bSCatalin Patulea 	if (!base) {
306be46c99bSCatalin Patulea 		perror("event_base_new()");
307be46c99bSCatalin Patulea 		return 1;
308be46c99bSCatalin Patulea 	}
309be46c99bSCatalin Patulea 
310be46c99bSCatalin Patulea 	// Create OpenSSL bufferevent and stack evhttp on top of it
311be46c99bSCatalin Patulea 	ssl = SSL_new(ssl_ctx);
312be46c99bSCatalin Patulea 	if (ssl == NULL) {
3135754d96aSPatrick Pelletier 		die_openssl("SSL_new()");
314be46c99bSCatalin Patulea 	}
315be46c99bSCatalin Patulea 
316be46c99bSCatalin Patulea 	if (strcasecmp(scheme, "http") == 0) {
317be46c99bSCatalin Patulea 		bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
318be46c99bSCatalin Patulea 	} else {
319be46c99bSCatalin Patulea 		bev = bufferevent_openssl_socket_new(base, -1, ssl,
320be46c99bSCatalin Patulea 			BUFFEREVENT_SSL_CONNECTING,
321be46c99bSCatalin Patulea 			BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
322be46c99bSCatalin Patulea 	}
323be46c99bSCatalin Patulea 
324be46c99bSCatalin Patulea 	if (bev == NULL) {
325be46c99bSCatalin Patulea 		fprintf(stderr, "bufferevent_openssl_socket_new() failed\n");
326be46c99bSCatalin Patulea 		return 1;
327be46c99bSCatalin Patulea 	}
328be46c99bSCatalin Patulea 
329be46c99bSCatalin Patulea 	bufferevent_openssl_set_allow_dirty_shutdown(bev, 1);
330be46c99bSCatalin Patulea 
331be46c99bSCatalin Patulea 	// For simplicity, we let DNS resolution block. Everything else should be
332be46c99bSCatalin Patulea 	// asynchronous though.
333be46c99bSCatalin Patulea 	evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev,
334be46c99bSCatalin Patulea 		host, port);
335be46c99bSCatalin Patulea 	if (evcon == NULL) {
336be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n");
337be46c99bSCatalin Patulea 		return 1;
338be46c99bSCatalin Patulea 	}
339be46c99bSCatalin Patulea 
340be46c99bSCatalin Patulea 	// Fire off the request
3415754d96aSPatrick Pelletier 	req = evhttp_request_new(http_request_done, bev);
342be46c99bSCatalin Patulea 	if (req == NULL) {
343be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_request_new() failed\n");
344be46c99bSCatalin Patulea 		return 1;
345be46c99bSCatalin Patulea 	}
346be46c99bSCatalin Patulea 
3478a90a850SNick Mathewson 	output_headers = evhttp_request_get_output_headers(req);
3488a90a850SNick Mathewson 	evhttp_add_header(output_headers, "Host", host);
3498a90a850SNick Mathewson 	evhttp_add_header(output_headers, "Connection", "close");
350be46c99bSCatalin Patulea 
351be46c99bSCatalin Patulea 	r = evhttp_make_request(evcon, req, EVHTTP_REQ_GET, uri);
352be46c99bSCatalin Patulea 	if (r != 0) {
353be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_make_request() failed\n");
354be46c99bSCatalin Patulea 		return 1;
355be46c99bSCatalin Patulea 	}
356be46c99bSCatalin Patulea 
357be46c99bSCatalin Patulea 	event_base_dispatch(base);
358be46c99bSCatalin Patulea 
359be46c99bSCatalin Patulea 	evhttp_connection_free(evcon);
360be46c99bSCatalin Patulea 	event_base_free(base);
361be46c99bSCatalin Patulea 
362*4e143958SJoakim Söderberg #ifdef _WIN32
36319222e52SJoakim Soderberg 	WSACleanup();
36419222e52SJoakim Soderberg #endif
36519222e52SJoakim Soderberg 
366be46c99bSCatalin Patulea 	return 0;
367be46c99bSCatalin Patulea }
368