xref: /libevent-2.1.12/sample/https-client.c (revision 19222e52)
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 
13be46c99bSCatalin Patulea #include <stdio.h>
14be46c99bSCatalin Patulea #include <assert.h>
15be46c99bSCatalin Patulea #include <stdlib.h>
16be46c99bSCatalin Patulea #include <string.h>
17be46c99bSCatalin Patulea #include <errno.h>
18be46c99bSCatalin Patulea 
19be46c99bSCatalin Patulea #ifdef WIN32
20be46c99bSCatalin Patulea #include <winsock2.h>
21be46c99bSCatalin Patulea #include <ws2tcpip.h>
22*19222e52SJoakim Soderberg 
23*19222e52SJoakim Soderberg #define snprintf _snprintf
24*19222e52SJoakim Soderberg #define strcasecmp _stricmp
25be46c99bSCatalin Patulea #else
26be46c99bSCatalin Patulea #include <sys/socket.h>
27be46c99bSCatalin Patulea #include <netinet/in.h>
28be46c99bSCatalin Patulea #endif
29be46c99bSCatalin Patulea 
30be46c99bSCatalin Patulea #include <event2/bufferevent_ssl.h>
31be46c99bSCatalin Patulea #include <event2/bufferevent.h>
32be46c99bSCatalin Patulea #include <event2/buffer.h>
33be46c99bSCatalin Patulea #include <event2/listener.h>
34be46c99bSCatalin Patulea #include <event2/util.h>
35be46c99bSCatalin Patulea #include <event2/http.h>
36be46c99bSCatalin Patulea 
37be46c99bSCatalin Patulea #include <openssl/ssl.h>
38be46c99bSCatalin Patulea #include <openssl/err.h>
39be46c99bSCatalin Patulea #include <openssl/rand.h>
40be46c99bSCatalin Patulea 
4164d9f161SPatrick Pelletier #include "openssl_hostname_validation.h"
4264d9f161SPatrick Pelletier 
43be46c99bSCatalin Patulea static struct event_base *base;
44be46c99bSCatalin Patulea 
45be46c99bSCatalin Patulea static void
46be46c99bSCatalin Patulea http_request_done(struct evhttp_request *req, void *ctx)
47be46c99bSCatalin Patulea {
48be46c99bSCatalin Patulea 	char buffer[256];
49be46c99bSCatalin Patulea 	int nread;
50be46c99bSCatalin Patulea 
51be46c99bSCatalin Patulea 	if (req == NULL) {
525754d96aSPatrick Pelletier 		/* If req is NULL, it means an error occurred, but
535754d96aSPatrick Pelletier 		 * sadly we are mostly left guessing what the error
545754d96aSPatrick Pelletier 		 * might have been.  We'll do our best... */
555754d96aSPatrick Pelletier 		struct bufferevent *bev = (struct bufferevent *) ctx;
565754d96aSPatrick Pelletier 		unsigned long oslerr;
575754d96aSPatrick Pelletier 		int printed_err = 0;
585754d96aSPatrick Pelletier 		int errcode = EVUTIL_SOCKET_ERROR();
59be46c99bSCatalin Patulea 		fprintf(stderr, "some request failed - no idea which one though!\n");
605754d96aSPatrick Pelletier 		/* Print out the OpenSSL error queue that libevent
615754d96aSPatrick Pelletier 		 * squirreled away for us, if any. */
625754d96aSPatrick Pelletier 		while ((oslerr = bufferevent_get_openssl_error(bev))) {
635754d96aSPatrick Pelletier 			ERR_error_string_n(oslerr, buffer, sizeof(buffer));
645754d96aSPatrick Pelletier 			fprintf(stderr, "%s\n", buffer);
655754d96aSPatrick Pelletier 			printed_err = 1;
665754d96aSPatrick Pelletier 		}
675754d96aSPatrick Pelletier 		/* If the OpenSSL error queue was empty, maybe it was a
685754d96aSPatrick Pelletier 		 * socket error; let's try printing that. */
695754d96aSPatrick Pelletier 		if (! printed_err)
705754d96aSPatrick Pelletier 			fprintf(stderr, "socket error = %s (%d)\n",
715754d96aSPatrick Pelletier 				evutil_socket_error_to_string(errcode),
725754d96aSPatrick Pelletier 				errcode);
73be46c99bSCatalin Patulea 		return;
74be46c99bSCatalin Patulea 	}
75be46c99bSCatalin Patulea 
76be46c99bSCatalin Patulea 	fprintf(stderr, "Response line: %d %s\n",
778a90a850SNick Mathewson 	    evhttp_request_get_response_code(req),
788a90a850SNick Mathewson 	    evhttp_request_get_response_code_line(req));
79be46c99bSCatalin Patulea 
8095acdaa3SNick Mathewson 	while ((nread = evbuffer_remove(evhttp_request_get_input_buffer(req),
8195acdaa3SNick Mathewson 		    buffer, sizeof(buffer)))
82be46c99bSCatalin Patulea 	       > 0) {
8342d7441aSPatrick Pelletier 		/* These are just arbitrary chunks of 256 bytes.
8442d7441aSPatrick Pelletier 		 * They are not lines, so we can't treat them as such. */
85be46c99bSCatalin Patulea 		fwrite(buffer, nread, 1, stdout);
86be46c99bSCatalin Patulea 	}
87be46c99bSCatalin Patulea }
88be46c99bSCatalin Patulea 
89be46c99bSCatalin Patulea static void
90be46c99bSCatalin Patulea syntax(void)
91be46c99bSCatalin Patulea {
92be46c99bSCatalin Patulea 	fputs("Syntax:\n", stderr);
93be46c99bSCatalin Patulea 	fputs("   https-client <https-url>\n", stderr);
94be46c99bSCatalin Patulea 	fputs("Example:\n", stderr);
95be46c99bSCatalin Patulea 	fputs("   https-client https://ip.appspot.com/\n", stderr);
96be46c99bSCatalin Patulea 
97be46c99bSCatalin Patulea 	exit(1);
98be46c99bSCatalin Patulea }
99be46c99bSCatalin Patulea 
100be46c99bSCatalin Patulea static void
101be46c99bSCatalin Patulea die(const char *msg)
102be46c99bSCatalin Patulea {
103be46c99bSCatalin Patulea 	fputs(msg, stderr);
104be46c99bSCatalin Patulea 	exit(1);
105be46c99bSCatalin Patulea }
106be46c99bSCatalin Patulea 
1075754d96aSPatrick Pelletier static void
1085754d96aSPatrick Pelletier die_openssl(const char *func)
1095754d96aSPatrick Pelletier {
1105754d96aSPatrick Pelletier 	fprintf (stderr, "%s failed:\n", func);
1115754d96aSPatrick Pelletier 
1125754d96aSPatrick Pelletier 	/* This is the OpenSSL function that prints the contents of the
1135754d96aSPatrick Pelletier 	 * error stack to the specified file handle. */
1145754d96aSPatrick Pelletier 	ERR_print_errors_fp (stderr);
1155754d96aSPatrick Pelletier 
1165754d96aSPatrick Pelletier 	exit(1);
1175754d96aSPatrick Pelletier }
1185754d96aSPatrick Pelletier 
11964d9f161SPatrick Pelletier /* See http://archives.seul.org/libevent/users/Jan-2013/msg00039.html */
12064d9f161SPatrick Pelletier static int cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg)
12164d9f161SPatrick Pelletier {
12264d9f161SPatrick Pelletier 	char cert_str[256];
12364d9f161SPatrick Pelletier 	const char *host = (const char *) arg;
12464d9f161SPatrick Pelletier 	const char *res_str = "X509_verify_cert failed";
12564d9f161SPatrick Pelletier 	HostnameValidationResult res = Error;
12664d9f161SPatrick Pelletier 
12764d9f161SPatrick Pelletier 	/* This is the function that OpenSSL would call if we hadn't called
12864d9f161SPatrick Pelletier 	 * SSL_CTX_set_cert_verify_callback().  Therefore, we are "wrapping"
12964d9f161SPatrick Pelletier 	 * the default functionality, rather than replacing it. */
13064d9f161SPatrick Pelletier 	int ok_so_far = X509_verify_cert(x509_ctx);
13164d9f161SPatrick Pelletier 
13264d9f161SPatrick Pelletier 	X509 *server_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
13364d9f161SPatrick Pelletier 
13464d9f161SPatrick Pelletier 	if (ok_so_far) {
13564d9f161SPatrick Pelletier 		res = validate_hostname(host, server_cert);
13664d9f161SPatrick Pelletier 
13764d9f161SPatrick Pelletier 		switch (res) {
13864d9f161SPatrick Pelletier 		case MatchFound:
13964d9f161SPatrick Pelletier 			res_str = "MatchFound";
14064d9f161SPatrick Pelletier 			break;
14164d9f161SPatrick Pelletier 		case MatchNotFound:
14264d9f161SPatrick Pelletier 			res_str = "MatchNotFound";
14364d9f161SPatrick Pelletier 			break;
14464d9f161SPatrick Pelletier 		case NoSANPresent:
14564d9f161SPatrick Pelletier 			res_str = "NoSANPresent";
14664d9f161SPatrick Pelletier 			break;
14764d9f161SPatrick Pelletier 		case MalformedCertificate:
14864d9f161SPatrick Pelletier 			res_str = "MalformedCertificate";
14964d9f161SPatrick Pelletier 			break;
15064d9f161SPatrick Pelletier 		case Error:
15164d9f161SPatrick Pelletier 			res_str = "Error";
15264d9f161SPatrick Pelletier 			break;
15364d9f161SPatrick Pelletier 		default:
15464d9f161SPatrick Pelletier 			res_str = "WTF!";
15564d9f161SPatrick Pelletier 			break;
15664d9f161SPatrick Pelletier 		}
15764d9f161SPatrick Pelletier 	}
15864d9f161SPatrick Pelletier 
15964d9f161SPatrick Pelletier 	X509_NAME_oneline(X509_get_subject_name (server_cert),
16064d9f161SPatrick Pelletier 			  cert_str, sizeof (cert_str));
16164d9f161SPatrick Pelletier 
16264d9f161SPatrick Pelletier 	if (res == MatchFound) {
16364d9f161SPatrick Pelletier 		printf("https server '%s' has this certificate, "
16464d9f161SPatrick Pelletier 		       "which looks good to me:\n%s\n",
16564d9f161SPatrick Pelletier 		       host, cert_str);
16664d9f161SPatrick Pelletier 		return 1;
16764d9f161SPatrick Pelletier 	} else {
16864d9f161SPatrick Pelletier 		printf("Got '%s' for hostname '%s' and certificate:\n%s\n",
16964d9f161SPatrick Pelletier 		       res_str, host, cert_str);
17064d9f161SPatrick Pelletier 		return 0;
17164d9f161SPatrick Pelletier 	}
17264d9f161SPatrick Pelletier }
17364d9f161SPatrick Pelletier 
174be46c99bSCatalin Patulea int
175be46c99bSCatalin Patulea main(int argc, char **argv)
176be46c99bSCatalin Patulea {
177be46c99bSCatalin Patulea 	int r;
178be46c99bSCatalin Patulea 
179be46c99bSCatalin Patulea 	struct evhttp_uri *http_uri;
180be46c99bSCatalin Patulea 	const char *url, *scheme, *host, *path, *query;
181be46c99bSCatalin Patulea 	char uri[256];
182be46c99bSCatalin Patulea 	int port;
183be46c99bSCatalin Patulea 
184be46c99bSCatalin Patulea 	SSL_CTX *ssl_ctx;
185be46c99bSCatalin Patulea 	SSL *ssl;
186be46c99bSCatalin Patulea 	struct bufferevent *bev;
187be46c99bSCatalin Patulea 	struct evhttp_connection *evcon;
188be46c99bSCatalin Patulea 	struct evhttp_request *req;
1898a90a850SNick Mathewson 	struct evkeyvalq *output_headers;
190be46c99bSCatalin Patulea 
191be46c99bSCatalin Patulea 	if (argc != 2)
192be46c99bSCatalin Patulea 		syntax();
193be46c99bSCatalin Patulea 
194*19222e52SJoakim Soderberg #ifdef WIN32
195*19222e52SJoakim Soderberg 	{
196*19222e52SJoakim Soderberg 		WORD wVersionRequested;
197*19222e52SJoakim Soderberg 		WSADATA wsaData;
198*19222e52SJoakim Soderberg 		int err;
199*19222e52SJoakim Soderberg 
200*19222e52SJoakim Soderberg 		wVersionRequested = MAKEWORD(2, 2);
201*19222e52SJoakim Soderberg 
202*19222e52SJoakim Soderberg 		err = WSAStartup(wVersionRequested, &wsaData);
203*19222e52SJoakim Soderberg 		if (err != 0) {
204*19222e52SJoakim Soderberg 			printf("WSAStartup failed with error: %d\n", err);
205*19222e52SJoakim Soderberg 			return 1;
206*19222e52SJoakim Soderberg 		}
207*19222e52SJoakim Soderberg 	}
208*19222e52SJoakim Soderberg #endif
209*19222e52SJoakim Soderberg 
210be46c99bSCatalin Patulea 	url = argv[1];
211be46c99bSCatalin Patulea 	http_uri = evhttp_uri_parse(url);
212be46c99bSCatalin Patulea 	if (http_uri == NULL) {
213be46c99bSCatalin Patulea 		die("malformed url");
214be46c99bSCatalin Patulea 	}
215be46c99bSCatalin Patulea 
216be46c99bSCatalin Patulea 	scheme = evhttp_uri_get_scheme(http_uri);
217be46c99bSCatalin Patulea 	if (scheme == NULL || (strcasecmp(scheme, "https") != 0 &&
218be46c99bSCatalin Patulea 	                       strcasecmp(scheme, "http") != 0)) {
219be46c99bSCatalin Patulea 		die("url must be http or https");
220be46c99bSCatalin Patulea 	}
221be46c99bSCatalin Patulea 
222be46c99bSCatalin Patulea 	host = evhttp_uri_get_host(http_uri);
223be46c99bSCatalin Patulea 	if (host == NULL) {
224be46c99bSCatalin Patulea 		die("url must have a host");
225be46c99bSCatalin Patulea 	}
226be46c99bSCatalin Patulea 
227be46c99bSCatalin Patulea 	port = evhttp_uri_get_port(http_uri);
228be46c99bSCatalin Patulea 	if (port == -1) {
229be46c99bSCatalin Patulea 		port = (strcasecmp(scheme, "http") == 0) ? 80 : 443;
230be46c99bSCatalin Patulea 	}
231be46c99bSCatalin Patulea 
232be46c99bSCatalin Patulea 	path = evhttp_uri_get_path(http_uri);
233be46c99bSCatalin Patulea 	if (path == NULL) {
234be46c99bSCatalin Patulea 		path = "/";
235be46c99bSCatalin Patulea 	}
236be46c99bSCatalin Patulea 
237be46c99bSCatalin Patulea 	query = evhttp_uri_get_query(http_uri);
238be46c99bSCatalin Patulea 	if (query == NULL) {
239be46c99bSCatalin Patulea 		snprintf(uri, sizeof(uri) - 1, "%s", path);
240be46c99bSCatalin Patulea 	} else {
241be46c99bSCatalin Patulea 		snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query);
242be46c99bSCatalin Patulea 	}
243be46c99bSCatalin Patulea 	uri[sizeof(uri) - 1] = '\0';
244be46c99bSCatalin Patulea 
245be46c99bSCatalin Patulea 	// Initialize OpenSSL
246be46c99bSCatalin Patulea 	SSL_library_init();
247be46c99bSCatalin Patulea 	ERR_load_crypto_strings();
248be46c99bSCatalin Patulea 	SSL_load_error_strings();
249be46c99bSCatalin Patulea 	OpenSSL_add_all_algorithms();
2505754d96aSPatrick Pelletier 
2515754d96aSPatrick Pelletier 	/* This isn't strictly necessary... OpenSSL performs RAND_poll
2525754d96aSPatrick Pelletier 	 * automatically on first use of random number generator. */
253be46c99bSCatalin Patulea 	r = RAND_poll();
254be46c99bSCatalin Patulea 	if (r == 0) {
2555754d96aSPatrick Pelletier 		die_openssl("RAND_poll");
256be46c99bSCatalin Patulea 	}
2575754d96aSPatrick Pelletier 
2585754d96aSPatrick Pelletier 	/* Create a new OpenSSL context */
259be46c99bSCatalin Patulea 	ssl_ctx = SSL_CTX_new(SSLv23_method());
2605754d96aSPatrick Pelletier 	if (!ssl_ctx)
2615754d96aSPatrick Pelletier 		die_openssl("SSL_CTX_new");
262be46c99bSCatalin Patulea 
263*19222e52SJoakim Soderberg 	#ifndef WIN32
264*19222e52SJoakim Soderberg 	/* TODO: Add certificate loading on Windows as well */
265*19222e52SJoakim Soderberg 
266aacd674cSPatrick Pelletier 	/* Attempt to use the system's trusted root certificates.
267aacd674cSPatrick Pelletier 	 * (This path is only valid for Debian-based systems.) */
268aacd674cSPatrick Pelletier 	if (1 != SSL_CTX_load_verify_locations(ssl_ctx,
269aacd674cSPatrick Pelletier 					       "/etc/ssl/certs/ca-certificates.crt",
270aacd674cSPatrick Pelletier 					       NULL))
271aacd674cSPatrick Pelletier 		die_openssl("SSL_CTX_load_verify_locations");
27264d9f161SPatrick Pelletier 	/* Ask OpenSSL to verify the server certificate.  Note that this
27364d9f161SPatrick Pelletier 	 * does NOT include verifying that the hostname is correct.
27464d9f161SPatrick Pelletier 	 * So, by itself, this means anyone with any legitimate
27564d9f161SPatrick Pelletier 	 * CA-issued certificate for any website, can impersonate any
27664d9f161SPatrick Pelletier 	 * other website in the world.  This is not good.  See "The
27764d9f161SPatrick Pelletier 	 * Most Dangerous Code in the World" article at
27864d9f161SPatrick Pelletier 	 * https://crypto.stanford.edu/~dabo/pubs/abstracts/ssl-client-bugs.html
27964d9f161SPatrick Pelletier 	 */
280aacd674cSPatrick Pelletier 	SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
28164d9f161SPatrick Pelletier 	/* This is how we solve the problem mentioned in the previous
28264d9f161SPatrick Pelletier 	 * comment.  We "wrap" OpenSSL's validation routine in our
28364d9f161SPatrick Pelletier 	 * own routine, which also validates the hostname by calling
28464d9f161SPatrick Pelletier 	 * the code provided by iSECPartners.  Note that even though
28564d9f161SPatrick Pelletier 	 * the "Everything You've Always Wanted to Know About
28664d9f161SPatrick Pelletier 	 * Certificate Validation With OpenSSL (But Were Afraid to
28764d9f161SPatrick Pelletier 	 * Ask)" paper from iSECPartners says very explicitly not to
28864d9f161SPatrick Pelletier 	 * call SSL_CTX_set_cert_verify_callback (at the bottom of
28964d9f161SPatrick Pelletier 	 * page 2), what we're doing here is safe because our
29064d9f161SPatrick Pelletier 	 * cert_verify_callback() calls X509_verify_cert(), which is
29164d9f161SPatrick Pelletier 	 * OpenSSL's built-in routine which would have been called if
29264d9f161SPatrick Pelletier 	 * we hadn't set the callback.  Therefore, we're just
29364d9f161SPatrick Pelletier 	 * "wrapping" OpenSSL's routine, not replacing it. */
29464d9f161SPatrick Pelletier 	SSL_CTX_set_cert_verify_callback (ssl_ctx, cert_verify_callback,
29564d9f161SPatrick Pelletier 					  (void *) host);
296*19222e52SJoakim Soderberg 	#endif // not WIN32
297aacd674cSPatrick Pelletier 
298be46c99bSCatalin Patulea 	// Create event base
299be46c99bSCatalin Patulea 	base = event_base_new();
300be46c99bSCatalin Patulea 	if (!base) {
301be46c99bSCatalin Patulea 		perror("event_base_new()");
302be46c99bSCatalin Patulea 		return 1;
303be46c99bSCatalin Patulea 	}
304be46c99bSCatalin Patulea 
305be46c99bSCatalin Patulea 	// Create OpenSSL bufferevent and stack evhttp on top of it
306be46c99bSCatalin Patulea 	ssl = SSL_new(ssl_ctx);
307be46c99bSCatalin Patulea 	if (ssl == NULL) {
3085754d96aSPatrick Pelletier 		die_openssl("SSL_new()");
309be46c99bSCatalin Patulea 	}
310be46c99bSCatalin Patulea 
311be46c99bSCatalin Patulea 	if (strcasecmp(scheme, "http") == 0) {
312be46c99bSCatalin Patulea 		bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
313be46c99bSCatalin Patulea 	} else {
314be46c99bSCatalin Patulea 		bev = bufferevent_openssl_socket_new(base, -1, ssl,
315be46c99bSCatalin Patulea 			BUFFEREVENT_SSL_CONNECTING,
316be46c99bSCatalin Patulea 			BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
317be46c99bSCatalin Patulea 	}
318be46c99bSCatalin Patulea 
319be46c99bSCatalin Patulea 	if (bev == NULL) {
320be46c99bSCatalin Patulea 		fprintf(stderr, "bufferevent_openssl_socket_new() failed\n");
321be46c99bSCatalin Patulea 		return 1;
322be46c99bSCatalin Patulea 	}
323be46c99bSCatalin Patulea 
324be46c99bSCatalin Patulea 	bufferevent_openssl_set_allow_dirty_shutdown(bev, 1);
325be46c99bSCatalin Patulea 
326be46c99bSCatalin Patulea 	// For simplicity, we let DNS resolution block. Everything else should be
327be46c99bSCatalin Patulea 	// asynchronous though.
328be46c99bSCatalin Patulea 	evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev,
329be46c99bSCatalin Patulea 		host, port);
330be46c99bSCatalin Patulea 	if (evcon == NULL) {
331be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n");
332be46c99bSCatalin Patulea 		return 1;
333be46c99bSCatalin Patulea 	}
334be46c99bSCatalin Patulea 
335be46c99bSCatalin Patulea 	// Fire off the request
3365754d96aSPatrick Pelletier 	req = evhttp_request_new(http_request_done, bev);
337be46c99bSCatalin Patulea 	if (req == NULL) {
338be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_request_new() failed\n");
339be46c99bSCatalin Patulea 		return 1;
340be46c99bSCatalin Patulea 	}
341be46c99bSCatalin Patulea 
3428a90a850SNick Mathewson 	output_headers = evhttp_request_get_output_headers(req);
3438a90a850SNick Mathewson 	evhttp_add_header(output_headers, "Host", host);
3448a90a850SNick Mathewson 	evhttp_add_header(output_headers, "Connection", "close");
345be46c99bSCatalin Patulea 
346be46c99bSCatalin Patulea 	r = evhttp_make_request(evcon, req, EVHTTP_REQ_GET, uri);
347be46c99bSCatalin Patulea 	if (r != 0) {
348be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_make_request() failed\n");
349be46c99bSCatalin Patulea 		return 1;
350be46c99bSCatalin Patulea 	}
351be46c99bSCatalin Patulea 
352be46c99bSCatalin Patulea 	event_base_dispatch(base);
353be46c99bSCatalin Patulea 
354be46c99bSCatalin Patulea 	evhttp_connection_free(evcon);
355be46c99bSCatalin Patulea 	event_base_free(base);
356be46c99bSCatalin Patulea 
357*19222e52SJoakim Soderberg #ifdef WIN32
358*19222e52SJoakim Soderberg 	WSACleanup();
359*19222e52SJoakim Soderberg #endif
360*19222e52SJoakim Soderberg 
361be46c99bSCatalin Patulea 	return 0;
362be46c99bSCatalin Patulea }
363