xref: /libevent-2.1.12/sample/https-client.c (revision e9478640)
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 
48c5887f73SAlexey Ozeritsky static int ignore_cert = 0;
49be46c99bSCatalin Patulea 
50be46c99bSCatalin Patulea static void
http_request_done(struct evhttp_request * req,void * ctx)51be46c99bSCatalin Patulea http_request_done(struct evhttp_request *req, void *ctx)
52be46c99bSCatalin Patulea {
53be46c99bSCatalin Patulea 	char buffer[256];
54be46c99bSCatalin Patulea 	int nread;
55be46c99bSCatalin Patulea 
56a8a04565Swenyg 	if (!req || !evhttp_request_get_response_code(req)) {
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
syntax(void)95be46c99bSCatalin Patulea syntax(void)
96be46c99bSCatalin Patulea {
97be46c99bSCatalin Patulea 	fputs("Syntax:\n", stderr);
98fdf713a0SAzat Khuzhin 	fputs("   https-client -url <https-url> [-data data-file.bin] [-ignore-cert] [-retries num] [-timeout sec] [-crt crt]\n", stderr);
99be46c99bSCatalin Patulea 	fputs("Example:\n", stderr);
100c5887f73SAlexey Ozeritsky 	fputs("   https-client -url https://ip.appspot.com/\n", stderr);
101be46c99bSCatalin Patulea }
102be46c99bSCatalin Patulea 
103be46c99bSCatalin Patulea static void
err(const char * msg)10424a1f25aSAzat Khuzhin err(const char *msg)
105be46c99bSCatalin Patulea {
106be46c99bSCatalin Patulea 	fputs(msg, stderr);
107be46c99bSCatalin Patulea }
108be46c99bSCatalin Patulea 
1095754d96aSPatrick Pelletier static void
err_openssl(const char * func)11024a1f25aSAzat Khuzhin err_openssl(const char *func)
1115754d96aSPatrick Pelletier {
1125754d96aSPatrick Pelletier 	fprintf (stderr, "%s failed:\n", func);
1135754d96aSPatrick Pelletier 
1145754d96aSPatrick Pelletier 	/* This is the OpenSSL function that prints the contents of the
1155754d96aSPatrick Pelletier 	 * error stack to the specified file handle. */
1165754d96aSPatrick Pelletier 	ERR_print_errors_fp (stderr);
1175754d96aSPatrick Pelletier 
1185754d96aSPatrick Pelletier 	exit(1);
1195754d96aSPatrick Pelletier }
1205754d96aSPatrick Pelletier 
12164d9f161SPatrick Pelletier /* See http://archives.seul.org/libevent/users/Jan-2013/msg00039.html */
cert_verify_callback(X509_STORE_CTX * x509_ctx,void * arg)12264d9f161SPatrick Pelletier static int cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg)
12364d9f161SPatrick Pelletier {
12464d9f161SPatrick Pelletier 	char cert_str[256];
12564d9f161SPatrick Pelletier 	const char *host = (const char *) arg;
12664d9f161SPatrick Pelletier 	const char *res_str = "X509_verify_cert failed";
12764d9f161SPatrick Pelletier 	HostnameValidationResult res = Error;
12864d9f161SPatrick Pelletier 
12964d9f161SPatrick Pelletier 	/* This is the function that OpenSSL would call if we hadn't called
13064d9f161SPatrick Pelletier 	 * SSL_CTX_set_cert_verify_callback().  Therefore, we are "wrapping"
13164d9f161SPatrick Pelletier 	 * the default functionality, rather than replacing it. */
13229af65ebSAlexey Ozeritsky 	int ok_so_far = 0;
13364d9f161SPatrick Pelletier 
13429af65ebSAlexey Ozeritsky 	X509 *server_cert = NULL;
13529af65ebSAlexey Ozeritsky 
13629af65ebSAlexey Ozeritsky 	if (ignore_cert) {
13729af65ebSAlexey Ozeritsky 		return 1;
13829af65ebSAlexey Ozeritsky 	}
13929af65ebSAlexey Ozeritsky 
14029af65ebSAlexey Ozeritsky 	ok_so_far = X509_verify_cert(x509_ctx);
14129af65ebSAlexey Ozeritsky 
14229af65ebSAlexey Ozeritsky 	server_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
14364d9f161SPatrick Pelletier 
14464d9f161SPatrick Pelletier 	if (ok_so_far) {
14564d9f161SPatrick Pelletier 		res = validate_hostname(host, server_cert);
14664d9f161SPatrick Pelletier 
14764d9f161SPatrick Pelletier 		switch (res) {
14864d9f161SPatrick Pelletier 		case MatchFound:
14964d9f161SPatrick Pelletier 			res_str = "MatchFound";
15064d9f161SPatrick Pelletier 			break;
15164d9f161SPatrick Pelletier 		case MatchNotFound:
15264d9f161SPatrick Pelletier 			res_str = "MatchNotFound";
15364d9f161SPatrick Pelletier 			break;
15464d9f161SPatrick Pelletier 		case NoSANPresent:
15564d9f161SPatrick Pelletier 			res_str = "NoSANPresent";
15664d9f161SPatrick Pelletier 			break;
15764d9f161SPatrick Pelletier 		case MalformedCertificate:
15864d9f161SPatrick Pelletier 			res_str = "MalformedCertificate";
15964d9f161SPatrick Pelletier 			break;
16064d9f161SPatrick Pelletier 		case Error:
16164d9f161SPatrick Pelletier 			res_str = "Error";
16264d9f161SPatrick Pelletier 			break;
16364d9f161SPatrick Pelletier 		default:
16464d9f161SPatrick Pelletier 			res_str = "WTF!";
16564d9f161SPatrick Pelletier 			break;
16664d9f161SPatrick Pelletier 		}
16764d9f161SPatrick Pelletier 	}
16864d9f161SPatrick Pelletier 
16964d9f161SPatrick Pelletier 	X509_NAME_oneline(X509_get_subject_name (server_cert),
17064d9f161SPatrick Pelletier 			  cert_str, sizeof (cert_str));
17164d9f161SPatrick Pelletier 
17264d9f161SPatrick Pelletier 	if (res == MatchFound) {
17364d9f161SPatrick Pelletier 		printf("https server '%s' has this certificate, "
17464d9f161SPatrick Pelletier 		       "which looks good to me:\n%s\n",
17564d9f161SPatrick Pelletier 		       host, cert_str);
17664d9f161SPatrick Pelletier 		return 1;
17764d9f161SPatrick Pelletier 	} else {
17864d9f161SPatrick Pelletier 		printf("Got '%s' for hostname '%s' and certificate:\n%s\n",
17964d9f161SPatrick Pelletier 		       res_str, host, cert_str);
18064d9f161SPatrick Pelletier 		return 0;
18164d9f161SPatrick Pelletier 	}
18264d9f161SPatrick Pelletier }
183*e9478640Syuangongji 
184*e9478640Syuangongji #ifdef _WIN32
185*e9478640Syuangongji static int
add_cert_for_store(X509_STORE * store,const char * name)186*e9478640Syuangongji add_cert_for_store(X509_STORE *store, const char *name)
187*e9478640Syuangongji {
188*e9478640Syuangongji 	HCERTSTORE sys_store = NULL;
189*e9478640Syuangongji 	PCCERT_CONTEXT ctx = NULL;
190*e9478640Syuangongji 	int r = 0;
191*e9478640Syuangongji 
192*e9478640Syuangongji 	sys_store = CertOpenSystemStore(0, name);
193*e9478640Syuangongji 	if (!sys_store) {
194*e9478640Syuangongji 		err("failed to open system certificate store");
195*e9478640Syuangongji 		return -1;
196*e9478640Syuangongji 	}
197*e9478640Syuangongji 	while ((ctx = CertEnumCertificatesInStore(sys_store, ctx))) {
198*e9478640Syuangongji 		X509 *x509 = d2i_X509(NULL, (unsigned char const **)&ctx->pbCertEncoded,
199*e9478640Syuangongji 			ctx->cbCertEncoded);
200*e9478640Syuangongji 		if (x509) {
201*e9478640Syuangongji 			X509_STORE_add_cert(store, x509);
202*e9478640Syuangongji 			X509_free(x509);
203*e9478640Syuangongji 		} else {
204*e9478640Syuangongji 			r = -1;
205*e9478640Syuangongji 			err_openssl("d2i_X509");
206*e9478640Syuangongji 			break;
207*e9478640Syuangongji 		}
208*e9478640Syuangongji 	}
209*e9478640Syuangongji 	CertCloseStore(sys_store, 0);
210*e9478640Syuangongji 	return r;
211*e9478640Syuangongji }
21213a4acdaSAzat Khuzhin #endif
21364d9f161SPatrick Pelletier 
214be46c99bSCatalin Patulea int
main(int argc,char ** argv)215be46c99bSCatalin Patulea main(int argc, char **argv)
216be46c99bSCatalin Patulea {
217be46c99bSCatalin Patulea 	int r;
218c0adad8fSAzat Khuzhin 	struct event_base *base = NULL;
21924a1f25aSAzat Khuzhin 	struct evhttp_uri *http_uri = NULL;
22090786eb0SNick Mathewson 	const char *url = NULL, *data_file = NULL;
2215c0132f3SDavid Disseldorp 	const char *crt = NULL;
22290786eb0SNick Mathewson 	const char *scheme, *host, *path, *query;
223be46c99bSCatalin Patulea 	char uri[256];
224be46c99bSCatalin Patulea 	int port;
225d9da8443SAzat Khuzhin 	int retries = 0;
2264637aa88SAzat Khuzhin 	int timeout = -1;
227be46c99bSCatalin Patulea 
22824a1f25aSAzat Khuzhin 	SSL_CTX *ssl_ctx = NULL;
229f3d7ff5dSAzat Khuzhin 	SSL *ssl = NULL;
230be46c99bSCatalin Patulea 	struct bufferevent *bev;
23124a1f25aSAzat Khuzhin 	struct evhttp_connection *evcon = NULL;
232be46c99bSCatalin Patulea 	struct evhttp_request *req;
2338a90a850SNick Mathewson 	struct evkeyvalq *output_headers;
23429af65ebSAlexey Ozeritsky 	struct evbuffer *output_buffer;
235be46c99bSCatalin Patulea 
236c5887f73SAlexey Ozeritsky 	int i;
23724a1f25aSAzat Khuzhin 	int ret = 0;
23824a1f25aSAzat Khuzhin 	enum { HTTP, HTTPS } type = HTTP;
239be46c99bSCatalin Patulea 
240c5887f73SAlexey Ozeritsky 	for (i = 1; i < argc; i++) {
241c5887f73SAlexey Ozeritsky 		if (!strcmp("-url", argv[i])) {
242c5887f73SAlexey Ozeritsky 			if (i < argc - 1) {
243c5887f73SAlexey Ozeritsky 				url = argv[i + 1];
244c5887f73SAlexey Ozeritsky 			} else {
245c5887f73SAlexey Ozeritsky 				syntax();
24624a1f25aSAzat Khuzhin 				goto error;
247c5887f73SAlexey Ozeritsky 			}
248fdf713a0SAzat Khuzhin 		} else if (!strcmp("-crt", argv[i])) {
249fdf713a0SAzat Khuzhin 			if (i < argc - 1) {
250fdf713a0SAzat Khuzhin 				crt = argv[i + 1];
251fdf713a0SAzat Khuzhin 			} else {
252fdf713a0SAzat Khuzhin 				syntax();
253fdf713a0SAzat Khuzhin 				goto error;
254fdf713a0SAzat Khuzhin 			}
255c5887f73SAlexey Ozeritsky 		} else if (!strcmp("-ignore-cert", argv[i])) {
256c5887f73SAlexey Ozeritsky 			ignore_cert = 1;
257c5887f73SAlexey Ozeritsky 		} else if (!strcmp("-data", argv[i])) {
258c5887f73SAlexey Ozeritsky 			if (i < argc - 1) {
259c5887f73SAlexey Ozeritsky 				data_file = argv[i + 1];
260c5887f73SAlexey Ozeritsky 			} else {
261c5887f73SAlexey Ozeritsky 				syntax();
26224a1f25aSAzat Khuzhin 				goto error;
263c5887f73SAlexey Ozeritsky 			}
264d9da8443SAzat Khuzhin 		} else if (!strcmp("-retries", argv[i])) {
265d9da8443SAzat Khuzhin 			if (i < argc - 1) {
266d9da8443SAzat Khuzhin 				retries = atoi(argv[i + 1]);
267d9da8443SAzat Khuzhin 			} else {
268d9da8443SAzat Khuzhin 				syntax();
26924a1f25aSAzat Khuzhin 				goto error;
270d9da8443SAzat Khuzhin 			}
2714637aa88SAzat Khuzhin 		} else if (!strcmp("-timeout", argv[i])) {
2724637aa88SAzat Khuzhin 			if (i < argc - 1) {
2734637aa88SAzat Khuzhin 				timeout = atoi(argv[i + 1]);
2744637aa88SAzat Khuzhin 			} else {
2754637aa88SAzat Khuzhin 				syntax();
2764637aa88SAzat Khuzhin 				goto error;
2774637aa88SAzat Khuzhin 			}
278c5887f73SAlexey Ozeritsky 		} else if (!strcmp("-help", argv[i])) {
279c5887f73SAlexey Ozeritsky 			syntax();
28024a1f25aSAzat Khuzhin 			goto error;
281c5887f73SAlexey Ozeritsky 		}
282c5887f73SAlexey Ozeritsky 	}
283c5887f73SAlexey Ozeritsky 
284c5887f73SAlexey Ozeritsky 	if (!url) {
285c5887f73SAlexey Ozeritsky 		syntax();
28624a1f25aSAzat Khuzhin 		goto error;
287c5887f73SAlexey Ozeritsky 	}
288c5887f73SAlexey Ozeritsky 
2894e143958SJoakim Söderberg #ifdef _WIN32
29019222e52SJoakim Soderberg 	{
29119222e52SJoakim Soderberg 		WORD wVersionRequested;
29219222e52SJoakim Soderberg 		WSADATA wsaData;
29319222e52SJoakim Soderberg 		int err;
29419222e52SJoakim Soderberg 
29519222e52SJoakim Soderberg 		wVersionRequested = MAKEWORD(2, 2);
29619222e52SJoakim Soderberg 
29719222e52SJoakim Soderberg 		err = WSAStartup(wVersionRequested, &wsaData);
29819222e52SJoakim Soderberg 		if (err != 0) {
29919222e52SJoakim Soderberg 			printf("WSAStartup failed with error: %d\n", err);
30024a1f25aSAzat Khuzhin 			goto error;
30119222e52SJoakim Soderberg 		}
30219222e52SJoakim Soderberg 	}
3034e143958SJoakim Söderberg #endif // _WIN32
30419222e52SJoakim Soderberg 
305be46c99bSCatalin Patulea 	http_uri = evhttp_uri_parse(url);
306be46c99bSCatalin Patulea 	if (http_uri == NULL) {
30724a1f25aSAzat Khuzhin 		err("malformed url");
30824a1f25aSAzat Khuzhin 		goto error;
309be46c99bSCatalin Patulea 	}
310be46c99bSCatalin Patulea 
311be46c99bSCatalin Patulea 	scheme = evhttp_uri_get_scheme(http_uri);
312be46c99bSCatalin Patulea 	if (scheme == NULL || (strcasecmp(scheme, "https") != 0 &&
313be46c99bSCatalin Patulea 	                       strcasecmp(scheme, "http") != 0)) {
31424a1f25aSAzat Khuzhin 		err("url must be http or https");
31524a1f25aSAzat Khuzhin 		goto error;
316be46c99bSCatalin Patulea 	}
317be46c99bSCatalin Patulea 
318be46c99bSCatalin Patulea 	host = evhttp_uri_get_host(http_uri);
319be46c99bSCatalin Patulea 	if (host == NULL) {
32024a1f25aSAzat Khuzhin 		err("url must have a host");
32124a1f25aSAzat Khuzhin 		goto error;
322be46c99bSCatalin Patulea 	}
323be46c99bSCatalin Patulea 
324be46c99bSCatalin Patulea 	port = evhttp_uri_get_port(http_uri);
325be46c99bSCatalin Patulea 	if (port == -1) {
326be46c99bSCatalin Patulea 		port = (strcasecmp(scheme, "http") == 0) ? 80 : 443;
327be46c99bSCatalin Patulea 	}
328be46c99bSCatalin Patulea 
329be46c99bSCatalin Patulea 	path = evhttp_uri_get_path(http_uri);
33029a04825SAndrey Skriabin 	if (strlen(path) == 0) {
331be46c99bSCatalin Patulea 		path = "/";
332be46c99bSCatalin Patulea 	}
333be46c99bSCatalin Patulea 
334be46c99bSCatalin Patulea 	query = evhttp_uri_get_query(http_uri);
335be46c99bSCatalin Patulea 	if (query == NULL) {
336be46c99bSCatalin Patulea 		snprintf(uri, sizeof(uri) - 1, "%s", path);
337be46c99bSCatalin Patulea 	} else {
338be46c99bSCatalin Patulea 		snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query);
339be46c99bSCatalin Patulea 	}
340be46c99bSCatalin Patulea 	uri[sizeof(uri) - 1] = '\0';
341be46c99bSCatalin Patulea 
342894ca48aSBernard Spil #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
343894ca48aSBernard Spil 	(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
344be46c99bSCatalin Patulea 	// Initialize OpenSSL
345be46c99bSCatalin Patulea 	SSL_library_init();
346be46c99bSCatalin Patulea 	ERR_load_crypto_strings();
347be46c99bSCatalin Patulea 	SSL_load_error_strings();
348be46c99bSCatalin Patulea 	OpenSSL_add_all_algorithms();
3493e9e0a0dSKurt Roeckx #endif
3505754d96aSPatrick Pelletier 
3515754d96aSPatrick Pelletier 	/* This isn't strictly necessary... OpenSSL performs RAND_poll
3525754d96aSPatrick Pelletier 	 * automatically on first use of random number generator. */
353be46c99bSCatalin Patulea 	r = RAND_poll();
354be46c99bSCatalin Patulea 	if (r == 0) {
35524a1f25aSAzat Khuzhin 		err_openssl("RAND_poll");
35624a1f25aSAzat Khuzhin 		goto error;
357be46c99bSCatalin Patulea 	}
3585754d96aSPatrick Pelletier 
3595754d96aSPatrick Pelletier 	/* Create a new OpenSSL context */
360be46c99bSCatalin Patulea 	ssl_ctx = SSL_CTX_new(SSLv23_method());
36124a1f25aSAzat Khuzhin 	if (!ssl_ctx) {
36224a1f25aSAzat Khuzhin 		err_openssl("SSL_CTX_new");
36324a1f25aSAzat Khuzhin 		goto error;
36424a1f25aSAzat Khuzhin 	}
365be46c99bSCatalin Patulea 
3665c0132f3SDavid Disseldorp 	if (crt == NULL) {
3675c0132f3SDavid Disseldorp 		X509_STORE *store;
3685c0132f3SDavid Disseldorp 		/* Attempt to use the system's trusted root certificates. */
3695c0132f3SDavid Disseldorp 		store = SSL_CTX_get_cert_store(ssl_ctx);
370*e9478640Syuangongji #ifdef _WIN32
371*e9478640Syuangongji 		if (add_cert_for_store(store, "CA") < 0 ||
372*e9478640Syuangongji 		    add_cert_for_store(store, "AuthRoot") < 0 ||
373*e9478640Syuangongji 		    add_cert_for_store(store, "ROOT") < 0) {
374*e9478640Syuangongji 			goto error;
375*e9478640Syuangongji 		}
376*e9478640Syuangongji #else // _WIN32
3775c0132f3SDavid Disseldorp 		if (X509_STORE_set_default_paths(store) != 1) {
3785c0132f3SDavid Disseldorp 			err_openssl("X509_STORE_set_default_paths");
3795c0132f3SDavid Disseldorp 			goto error;
3805c0132f3SDavid Disseldorp 		}
381*e9478640Syuangongji #endif // _WIN32
3825c0132f3SDavid Disseldorp 	} else {
3835c0132f3SDavid Disseldorp 		if (SSL_CTX_load_verify_locations(ssl_ctx, crt, NULL) != 1) {
38424a1f25aSAzat Khuzhin 			err_openssl("SSL_CTX_load_verify_locations");
38524a1f25aSAzat Khuzhin 			goto error;
38624a1f25aSAzat Khuzhin 		}
3875c0132f3SDavid Disseldorp 	}
38864d9f161SPatrick Pelletier 	/* Ask OpenSSL to verify the server certificate.  Note that this
38964d9f161SPatrick Pelletier 	 * does NOT include verifying that the hostname is correct.
39064d9f161SPatrick Pelletier 	 * So, by itself, this means anyone with any legitimate
39164d9f161SPatrick Pelletier 	 * CA-issued certificate for any website, can impersonate any
39264d9f161SPatrick Pelletier 	 * other website in the world.  This is not good.  See "The
39364d9f161SPatrick Pelletier 	 * Most Dangerous Code in the World" article at
39464d9f161SPatrick Pelletier 	 * https://crypto.stanford.edu/~dabo/pubs/abstracts/ssl-client-bugs.html
39564d9f161SPatrick Pelletier 	 */
396aacd674cSPatrick Pelletier 	SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
39764d9f161SPatrick Pelletier 	/* This is how we solve the problem mentioned in the previous
39864d9f161SPatrick Pelletier 	 * comment.  We "wrap" OpenSSL's validation routine in our
39964d9f161SPatrick Pelletier 	 * own routine, which also validates the hostname by calling
40064d9f161SPatrick Pelletier 	 * the code provided by iSECPartners.  Note that even though
40164d9f161SPatrick Pelletier 	 * the "Everything You've Always Wanted to Know About
40264d9f161SPatrick Pelletier 	 * Certificate Validation With OpenSSL (But Were Afraid to
40364d9f161SPatrick Pelletier 	 * Ask)" paper from iSECPartners says very explicitly not to
40464d9f161SPatrick Pelletier 	 * call SSL_CTX_set_cert_verify_callback (at the bottom of
40564d9f161SPatrick Pelletier 	 * page 2), what we're doing here is safe because our
40664d9f161SPatrick Pelletier 	 * cert_verify_callback() calls X509_verify_cert(), which is
40764d9f161SPatrick Pelletier 	 * OpenSSL's built-in routine which would have been called if
40864d9f161SPatrick Pelletier 	 * we hadn't set the callback.  Therefore, we're just
40964d9f161SPatrick Pelletier 	 * "wrapping" OpenSSL's routine, not replacing it. */
41064d9f161SPatrick Pelletier 	SSL_CTX_set_cert_verify_callback(ssl_ctx, cert_verify_callback,
41164d9f161SPatrick Pelletier 					  (void *) host);
412aacd674cSPatrick Pelletier 
413be46c99bSCatalin Patulea 	// Create event base
414be46c99bSCatalin Patulea 	base = event_base_new();
415be46c99bSCatalin Patulea 	if (!base) {
416be46c99bSCatalin Patulea 		perror("event_base_new()");
41724a1f25aSAzat Khuzhin 		goto error;
418be46c99bSCatalin Patulea 	}
419be46c99bSCatalin Patulea 
420be46c99bSCatalin Patulea 	// Create OpenSSL bufferevent and stack evhttp on top of it
421be46c99bSCatalin Patulea 	ssl = SSL_new(ssl_ctx);
422be46c99bSCatalin Patulea 	if (ssl == NULL) {
42324a1f25aSAzat Khuzhin 		err_openssl("SSL_new()");
42424a1f25aSAzat Khuzhin 		goto error;
425be46c99bSCatalin Patulea 	}
426be46c99bSCatalin Patulea 
4275c7282f7SJoakim Soderberg 	#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
428d1976f8eSNick Mathewson 	// Set hostname for SNI extension
429d1976f8eSNick Mathewson 	SSL_set_tlsext_host_name(ssl, host);
4305c7282f7SJoakim Soderberg 	#endif
431d1976f8eSNick Mathewson 
432be46c99bSCatalin Patulea 	if (strcasecmp(scheme, "http") == 0) {
433be46c99bSCatalin Patulea 		bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
434be46c99bSCatalin Patulea 	} else {
43524a1f25aSAzat Khuzhin 		type = HTTPS;
436be46c99bSCatalin Patulea 		bev = bufferevent_openssl_socket_new(base, -1, ssl,
437be46c99bSCatalin Patulea 			BUFFEREVENT_SSL_CONNECTING,
438be46c99bSCatalin Patulea 			BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
439be46c99bSCatalin Patulea 	}
440be46c99bSCatalin Patulea 
441be46c99bSCatalin Patulea 	if (bev == NULL) {
442be46c99bSCatalin Patulea 		fprintf(stderr, "bufferevent_openssl_socket_new() failed\n");
44324a1f25aSAzat Khuzhin 		goto error;
444be46c99bSCatalin Patulea 	}
445be46c99bSCatalin Patulea 
446be46c99bSCatalin Patulea 	bufferevent_openssl_set_allow_dirty_shutdown(bev, 1);
447be46c99bSCatalin Patulea 
448be46c99bSCatalin Patulea 	// For simplicity, we let DNS resolution block. Everything else should be
449be46c99bSCatalin Patulea 	// asynchronous though.
450be46c99bSCatalin Patulea 	evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev,
451be46c99bSCatalin Patulea 		host, port);
452be46c99bSCatalin Patulea 	if (evcon == NULL) {
453be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n");
45424a1f25aSAzat Khuzhin 		goto error;
455be46c99bSCatalin Patulea 	}
456be46c99bSCatalin Patulea 
457d9da8443SAzat Khuzhin 	if (retries > 0) {
458d9da8443SAzat Khuzhin 		evhttp_connection_set_retries(evcon, retries);
459d9da8443SAzat Khuzhin 	}
4604637aa88SAzat Khuzhin 	if (timeout >= 0) {
4614637aa88SAzat Khuzhin 		evhttp_connection_set_timeout(evcon, timeout);
4624637aa88SAzat Khuzhin 	}
463d9da8443SAzat Khuzhin 
464be46c99bSCatalin Patulea 	// Fire off the request
4655754d96aSPatrick Pelletier 	req = evhttp_request_new(http_request_done, bev);
466be46c99bSCatalin Patulea 	if (req == NULL) {
467be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_request_new() failed\n");
46824a1f25aSAzat Khuzhin 		goto error;
469be46c99bSCatalin Patulea 	}
470be46c99bSCatalin Patulea 
4718a90a850SNick Mathewson 	output_headers = evhttp_request_get_output_headers(req);
4728a90a850SNick Mathewson 	evhttp_add_header(output_headers, "Host", host);
4738a90a850SNick Mathewson 	evhttp_add_header(output_headers, "Connection", "close");
474be46c99bSCatalin Patulea 
475c5887f73SAlexey Ozeritsky 	if (data_file) {
47690786eb0SNick Mathewson 		/* NOTE: In production code, you'd probably want to use
47790786eb0SNick Mathewson 		 * evbuffer_add_file() or evbuffer_add_file_segment(), to
47890786eb0SNick Mathewson 		 * avoid needless copying. */
479c5887f73SAlexey Ozeritsky 		FILE * f = fopen(data_file, "rb");
480c5887f73SAlexey Ozeritsky 		char buf[1024];
481d7be7887SJoakim Soderberg 		size_t s;
482c5887f73SAlexey Ozeritsky 		size_t bytes = 0;
483c5887f73SAlexey Ozeritsky 
484c5887f73SAlexey Ozeritsky 		if (!f) {
485c5887f73SAlexey Ozeritsky 			syntax();
48624a1f25aSAzat Khuzhin 			goto error;
487c5887f73SAlexey Ozeritsky 		}
488c5887f73SAlexey Ozeritsky 
48929af65ebSAlexey Ozeritsky 		output_buffer = evhttp_request_get_output_buffer(req);
490c5887f73SAlexey Ozeritsky 		while ((s = fread(buf, 1, sizeof(buf), f)) > 0) {
49129af65ebSAlexey Ozeritsky 			evbuffer_add(output_buffer, buf, s);
492c5887f73SAlexey Ozeritsky 			bytes += s;
493c5887f73SAlexey Ozeritsky 		}
494462e6b60SNick Mathewson 		evutil_snprintf(buf, sizeof(buf)-1, "%lu", (unsigned long)bytes);
49529af65ebSAlexey Ozeritsky 		evhttp_add_header(output_headers, "Content-Length", buf);
496c5887f73SAlexey Ozeritsky 		fclose(f);
497c5887f73SAlexey Ozeritsky 	}
498c5887f73SAlexey Ozeritsky 
499c5887f73SAlexey Ozeritsky 	r = evhttp_make_request(evcon, req, data_file ? EVHTTP_REQ_POST : EVHTTP_REQ_GET, uri);
500be46c99bSCatalin Patulea 	if (r != 0) {
501be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_make_request() failed\n");
50224a1f25aSAzat Khuzhin 		goto error;
503be46c99bSCatalin Patulea 	}
504be46c99bSCatalin Patulea 
505be46c99bSCatalin Patulea 	event_base_dispatch(base);
50624a1f25aSAzat Khuzhin 	goto cleanup;
507be46c99bSCatalin Patulea 
50824a1f25aSAzat Khuzhin error:
50924a1f25aSAzat Khuzhin 	ret = 1;
51024a1f25aSAzat Khuzhin cleanup:
51124a1f25aSAzat Khuzhin 	if (evcon)
512be46c99bSCatalin Patulea 		evhttp_connection_free(evcon);
51324a1f25aSAzat Khuzhin 	if (http_uri)
51424a1f25aSAzat Khuzhin 		evhttp_uri_free(http_uri);
515c0adad8fSAzat Khuzhin 	if (base)
516be46c99bSCatalin Patulea 		event_base_free(base);
517be46c99bSCatalin Patulea 
51824a1f25aSAzat Khuzhin 	if (ssl_ctx)
51924a1f25aSAzat Khuzhin 		SSL_CTX_free(ssl_ctx);
520f3d7ff5dSAzat Khuzhin 	if (type == HTTP && ssl)
52124a1f25aSAzat Khuzhin 		SSL_free(ssl);
522894ca48aSBernard Spil #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
523894ca48aSBernard Spil 	(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
52424a1f25aSAzat Khuzhin 	EVP_cleanup();
52524a1f25aSAzat Khuzhin 	ERR_free_strings();
52624a1f25aSAzat Khuzhin 
527e7bd9e03SAzat Khuzhin #if OPENSSL_VERSION_NUMBER < 0x10000000L
528c4e9d9bdSAzat Khuzhin 	ERR_remove_state(0);
529e7bd9e03SAzat Khuzhin #else
530e7bd9e03SAzat Khuzhin 	ERR_remove_thread_state(NULL);
531c4e9d9bdSAzat Khuzhin #endif
532e7bd9e03SAzat Khuzhin 
53324a1f25aSAzat Khuzhin 	CRYPTO_cleanup_all_ex_data();
53424a1f25aSAzat Khuzhin 
53524a1f25aSAzat Khuzhin 	sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
536894ca48aSBernard Spil #endif /* (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
537894ca48aSBernard Spil 	(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L) */
53824a1f25aSAzat Khuzhin 
5394e143958SJoakim Söderberg #ifdef _WIN32
54019222e52SJoakim Soderberg 	WSACleanup();
54119222e52SJoakim Soderberg #endif
54219222e52SJoakim Soderberg 
54324a1f25aSAzat Khuzhin 	return ret;
544be46c99bSCatalin Patulea }
545