xref: /libevent-2.1.12/sample/https-client.c (revision aacd674c)
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>
22be46c99bSCatalin Patulea #else
23be46c99bSCatalin Patulea #include <sys/socket.h>
24be46c99bSCatalin Patulea #include <netinet/in.h>
25be46c99bSCatalin Patulea #endif
26be46c99bSCatalin Patulea 
27be46c99bSCatalin Patulea #include <event2/bufferevent_ssl.h>
28be46c99bSCatalin Patulea #include <event2/bufferevent.h>
29be46c99bSCatalin Patulea #include <event2/buffer.h>
30be46c99bSCatalin Patulea #include <event2/listener.h>
31be46c99bSCatalin Patulea #include <event2/util.h>
32be46c99bSCatalin Patulea #include <event2/http.h>
33be46c99bSCatalin Patulea #include <event2/http_struct.h>
34be46c99bSCatalin Patulea 
35be46c99bSCatalin Patulea #include <openssl/ssl.h>
36be46c99bSCatalin Patulea #include <openssl/err.h>
37be46c99bSCatalin Patulea #include <openssl/rand.h>
38be46c99bSCatalin Patulea 
39be46c99bSCatalin Patulea static struct event_base *base;
40be46c99bSCatalin Patulea 
41be46c99bSCatalin Patulea static void
42be46c99bSCatalin Patulea http_request_done(struct evhttp_request *req, void *ctx)
43be46c99bSCatalin Patulea {
44be46c99bSCatalin Patulea 	char buffer[256];
45be46c99bSCatalin Patulea 	int nread;
46be46c99bSCatalin Patulea 
47be46c99bSCatalin Patulea 	if (req == NULL) {
485754d96aSPatrick Pelletier 		/* If req is NULL, it means an error occurred, but
495754d96aSPatrick Pelletier 		 * sadly we are mostly left guessing what the error
505754d96aSPatrick Pelletier 		 * might have been.  We'll do our best... */
515754d96aSPatrick Pelletier 		struct bufferevent *bev = (struct bufferevent *) ctx;
525754d96aSPatrick Pelletier 		unsigned long oslerr;
535754d96aSPatrick Pelletier 		int printed_err = 0;
545754d96aSPatrick Pelletier 		int errcode = EVUTIL_SOCKET_ERROR();
55be46c99bSCatalin Patulea 		fprintf(stderr, "some request failed - no idea which one though!\n");
565754d96aSPatrick Pelletier 		/* Print out the OpenSSL error queue that libevent
575754d96aSPatrick Pelletier 		 * squirreled away for us, if any. */
585754d96aSPatrick Pelletier 		while ((oslerr = bufferevent_get_openssl_error(bev))) {
595754d96aSPatrick Pelletier 			ERR_error_string_n(oslerr, buffer, sizeof(buffer));
605754d96aSPatrick Pelletier 			fprintf(stderr, "%s\n", buffer);
615754d96aSPatrick Pelletier 			printed_err = 1;
625754d96aSPatrick Pelletier 		}
635754d96aSPatrick Pelletier 		/* If the OpenSSL error queue was empty, maybe it was a
645754d96aSPatrick Pelletier 		 * socket error; let's try printing that. */
655754d96aSPatrick Pelletier 		if (! printed_err)
665754d96aSPatrick Pelletier 			fprintf(stderr, "socket error = %s (%d)\n",
675754d96aSPatrick Pelletier 				evutil_socket_error_to_string(errcode),
685754d96aSPatrick Pelletier 				errcode);
69be46c99bSCatalin Patulea 		return;
70be46c99bSCatalin Patulea 	}
71be46c99bSCatalin Patulea 
72be46c99bSCatalin Patulea 	fprintf(stderr, "Response line: %d %s\n",
73be46c99bSCatalin Patulea 		req->response_code, req->response_code_line);
74be46c99bSCatalin Patulea 
75be46c99bSCatalin Patulea 	while ((nread = evbuffer_remove(req->input_buffer, buffer, sizeof(buffer)))
76be46c99bSCatalin Patulea 	       > 0) {
7742d7441aSPatrick Pelletier 		/* These are just arbitrary chunks of 256 bytes.
7842d7441aSPatrick Pelletier 		 * They are not lines, so we can't treat them as such. */
79be46c99bSCatalin Patulea 		fwrite(buffer, nread, 1, stdout);
80be46c99bSCatalin Patulea 	}
81be46c99bSCatalin Patulea }
82be46c99bSCatalin Patulea 
83be46c99bSCatalin Patulea static void
84be46c99bSCatalin Patulea syntax(void)
85be46c99bSCatalin Patulea {
86be46c99bSCatalin Patulea 	fputs("Syntax:\n", stderr);
87be46c99bSCatalin Patulea 	fputs("   https-client <https-url>\n", stderr);
88be46c99bSCatalin Patulea 	fputs("Example:\n", stderr);
89be46c99bSCatalin Patulea 	fputs("   https-client https://ip.appspot.com/\n", stderr);
90be46c99bSCatalin Patulea 
91be46c99bSCatalin Patulea 	exit(1);
92be46c99bSCatalin Patulea }
93be46c99bSCatalin Patulea 
94be46c99bSCatalin Patulea static void
95be46c99bSCatalin Patulea die(const char *msg)
96be46c99bSCatalin Patulea {
97be46c99bSCatalin Patulea 	fputs(msg, stderr);
98be46c99bSCatalin Patulea 	exit(1);
99be46c99bSCatalin Patulea }
100be46c99bSCatalin Patulea 
1015754d96aSPatrick Pelletier static void
1025754d96aSPatrick Pelletier die_openssl(const char *func)
1035754d96aSPatrick Pelletier {
1045754d96aSPatrick Pelletier 	fprintf (stderr, "%s failed:\n", func);
1055754d96aSPatrick Pelletier 
1065754d96aSPatrick Pelletier 	/* This is the OpenSSL function that prints the contents of the
1075754d96aSPatrick Pelletier 	 * error stack to the specified file handle. */
1085754d96aSPatrick Pelletier 	ERR_print_errors_fp (stderr);
1095754d96aSPatrick Pelletier 
1105754d96aSPatrick Pelletier 	exit(1);
1115754d96aSPatrick Pelletier }
1125754d96aSPatrick Pelletier 
113be46c99bSCatalin Patulea int
114be46c99bSCatalin Patulea main(int argc, char **argv)
115be46c99bSCatalin Patulea {
116be46c99bSCatalin Patulea 	int r;
117be46c99bSCatalin Patulea 
118be46c99bSCatalin Patulea 	struct evhttp_uri *http_uri;
119be46c99bSCatalin Patulea 	const char *url, *scheme, *host, *path, *query;
120be46c99bSCatalin Patulea 	char uri[256];
121be46c99bSCatalin Patulea 	int port;
122be46c99bSCatalin Patulea 
123be46c99bSCatalin Patulea 	SSL_CTX *ssl_ctx;
124be46c99bSCatalin Patulea 	SSL *ssl;
125be46c99bSCatalin Patulea 	struct bufferevent *bev;
126be46c99bSCatalin Patulea 	struct evhttp_connection *evcon;
127be46c99bSCatalin Patulea 	struct evhttp_request *req;
128be46c99bSCatalin Patulea 
129be46c99bSCatalin Patulea 	if (argc != 2)
130be46c99bSCatalin Patulea 		syntax();
131be46c99bSCatalin Patulea 
132be46c99bSCatalin Patulea 	url = argv[1];
133be46c99bSCatalin Patulea 	http_uri = evhttp_uri_parse(url);
134be46c99bSCatalin Patulea 	if (http_uri == NULL) {
135be46c99bSCatalin Patulea 		die("malformed url");
136be46c99bSCatalin Patulea 	}
137be46c99bSCatalin Patulea 
138be46c99bSCatalin Patulea 	scheme = evhttp_uri_get_scheme(http_uri);
139be46c99bSCatalin Patulea 	if (scheme == NULL || (strcasecmp(scheme, "https") != 0 &&
140be46c99bSCatalin Patulea 	                       strcasecmp(scheme, "http") != 0)) {
141be46c99bSCatalin Patulea 		die("url must be http or https");
142be46c99bSCatalin Patulea 	}
143be46c99bSCatalin Patulea 
144be46c99bSCatalin Patulea 	host = evhttp_uri_get_host(http_uri);
145be46c99bSCatalin Patulea 	if (host == NULL) {
146be46c99bSCatalin Patulea 		die("url must have a host");
147be46c99bSCatalin Patulea 	}
148be46c99bSCatalin Patulea 
149be46c99bSCatalin Patulea 	port = evhttp_uri_get_port(http_uri);
150be46c99bSCatalin Patulea 	if (port == -1) {
151be46c99bSCatalin Patulea 		port = (strcasecmp(scheme, "http") == 0) ? 80 : 443;
152be46c99bSCatalin Patulea 	}
153be46c99bSCatalin Patulea 
154be46c99bSCatalin Patulea 	path = evhttp_uri_get_path(http_uri);
155be46c99bSCatalin Patulea 	if (path == NULL) {
156be46c99bSCatalin Patulea 		path = "/";
157be46c99bSCatalin Patulea 	}
158be46c99bSCatalin Patulea 
159be46c99bSCatalin Patulea 	query = evhttp_uri_get_query(http_uri);
160be46c99bSCatalin Patulea 	if (query == NULL) {
161be46c99bSCatalin Patulea 		snprintf(uri, sizeof(uri) - 1, "%s", path);
162be46c99bSCatalin Patulea 	} else {
163be46c99bSCatalin Patulea 		snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query);
164be46c99bSCatalin Patulea 	}
165be46c99bSCatalin Patulea 	uri[sizeof(uri) - 1] = '\0';
166be46c99bSCatalin Patulea 
167be46c99bSCatalin Patulea 	// Initialize OpenSSL
168be46c99bSCatalin Patulea 	SSL_library_init();
169be46c99bSCatalin Patulea 	ERR_load_crypto_strings();
170be46c99bSCatalin Patulea 	SSL_load_error_strings();
171be46c99bSCatalin Patulea 	OpenSSL_add_all_algorithms();
1725754d96aSPatrick Pelletier 
1735754d96aSPatrick Pelletier 	/* This isn't strictly necessary... OpenSSL performs RAND_poll
1745754d96aSPatrick Pelletier 	 * automatically on first use of random number generator. */
175be46c99bSCatalin Patulea 	r = RAND_poll();
176be46c99bSCatalin Patulea 	if (r == 0) {
1775754d96aSPatrick Pelletier 		die_openssl("RAND_poll");
178be46c99bSCatalin Patulea 	}
1795754d96aSPatrick Pelletier 
1805754d96aSPatrick Pelletier 	/* Create a new OpenSSL context */
181be46c99bSCatalin Patulea 	ssl_ctx = SSL_CTX_new(SSLv23_method());
1825754d96aSPatrick Pelletier 	if (!ssl_ctx)
1835754d96aSPatrick Pelletier 		die_openssl("SSL_CTX_new");
184be46c99bSCatalin Patulea 
185*aacd674cSPatrick Pelletier 	/* Attempt to use the system's trusted root certificates.
186*aacd674cSPatrick Pelletier 	 * (This path is only valid for Debian-based systems.) */
187*aacd674cSPatrick Pelletier 	if (1 != SSL_CTX_load_verify_locations(ssl_ctx,
188*aacd674cSPatrick Pelletier 					       "/etc/ssl/certs/ca-certificates.crt",
189*aacd674cSPatrick Pelletier 					       NULL))
190*aacd674cSPatrick Pelletier 		die_openssl("SSL_CTX_load_verify_locations");
191*aacd674cSPatrick Pelletier 	SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
192*aacd674cSPatrick Pelletier 
193be46c99bSCatalin Patulea 	// Create event base
194be46c99bSCatalin Patulea 	base = event_base_new();
195be46c99bSCatalin Patulea 	if (!base) {
196be46c99bSCatalin Patulea 		perror("event_base_new()");
197be46c99bSCatalin Patulea 		return 1;
198be46c99bSCatalin Patulea 	}
199be46c99bSCatalin Patulea 
200be46c99bSCatalin Patulea 	// Create OpenSSL bufferevent and stack evhttp on top of it
201be46c99bSCatalin Patulea 	ssl = SSL_new(ssl_ctx);
202be46c99bSCatalin Patulea 	if (ssl == NULL) {
2035754d96aSPatrick Pelletier 		die_openssl("SSL_new()");
204be46c99bSCatalin Patulea 	}
205be46c99bSCatalin Patulea 
206be46c99bSCatalin Patulea 	if (strcasecmp(scheme, "http") == 0) {
207be46c99bSCatalin Patulea 		bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
208be46c99bSCatalin Patulea 	} else {
209be46c99bSCatalin Patulea 		bev = bufferevent_openssl_socket_new(base, -1, ssl,
210be46c99bSCatalin Patulea 			BUFFEREVENT_SSL_CONNECTING,
211be46c99bSCatalin Patulea 			BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
212be46c99bSCatalin Patulea 	}
213be46c99bSCatalin Patulea 
214be46c99bSCatalin Patulea 	if (bev == NULL) {
215be46c99bSCatalin Patulea 		fprintf(stderr, "bufferevent_openssl_socket_new() failed\n");
216be46c99bSCatalin Patulea 		return 1;
217be46c99bSCatalin Patulea 	}
218be46c99bSCatalin Patulea 
219be46c99bSCatalin Patulea 	bufferevent_openssl_set_allow_dirty_shutdown(bev, 1);
220be46c99bSCatalin Patulea 
221be46c99bSCatalin Patulea 	// For simplicity, we let DNS resolution block. Everything else should be
222be46c99bSCatalin Patulea 	// asynchronous though.
223be46c99bSCatalin Patulea 	evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev,
224be46c99bSCatalin Patulea 		host, port);
225be46c99bSCatalin Patulea 	if (evcon == NULL) {
226be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n");
227be46c99bSCatalin Patulea 		return 1;
228be46c99bSCatalin Patulea 	}
229be46c99bSCatalin Patulea 
230be46c99bSCatalin Patulea 	// Fire off the request
2315754d96aSPatrick Pelletier 	req = evhttp_request_new(http_request_done, bev);
232be46c99bSCatalin Patulea 	if (req == NULL) {
233be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_request_new() failed\n");
234be46c99bSCatalin Patulea 		return 1;
235be46c99bSCatalin Patulea 	}
236be46c99bSCatalin Patulea 
237be46c99bSCatalin Patulea 	evhttp_add_header(req->output_headers, "Host", host);
238be46c99bSCatalin Patulea 	evhttp_add_header(req->output_headers, "Connection", "close");
239be46c99bSCatalin Patulea 
240be46c99bSCatalin Patulea 	r = evhttp_make_request(evcon, req, EVHTTP_REQ_GET, uri);
241be46c99bSCatalin Patulea 	if (r != 0) {
242be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_make_request() failed\n");
243be46c99bSCatalin Patulea 		return 1;
244be46c99bSCatalin Patulea 	}
245be46c99bSCatalin Patulea 
246be46c99bSCatalin Patulea 	event_base_dispatch(base);
247be46c99bSCatalin Patulea 
248be46c99bSCatalin Patulea 	evhttp_connection_free(evcon);
249be46c99bSCatalin Patulea 	event_base_free(base);
250be46c99bSCatalin Patulea 
251be46c99bSCatalin Patulea 	return 0;
252be46c99bSCatalin Patulea }
253