xref: /libevent-2.1.12/sample/https-client.c (revision c5887f73)
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 
3964d9f161SPatrick Pelletier #include "openssl_hostname_validation.h"
4064d9f161SPatrick Pelletier 
41be46c99bSCatalin Patulea static struct event_base *base;
42*c5887f73SAlexey Ozeritsky static int ignore_cert = 0;
43be46c99bSCatalin Patulea 
44be46c99bSCatalin Patulea static void
45be46c99bSCatalin Patulea http_request_done(struct evhttp_request *req, void *ctx)
46be46c99bSCatalin Patulea {
47be46c99bSCatalin Patulea 	char buffer[256];
48be46c99bSCatalin Patulea 	int nread;
49be46c99bSCatalin Patulea 
50be46c99bSCatalin Patulea 	if (req == NULL) {
515754d96aSPatrick Pelletier 		/* If req is NULL, it means an error occurred, but
525754d96aSPatrick Pelletier 		 * sadly we are mostly left guessing what the error
535754d96aSPatrick Pelletier 		 * might have been.  We'll do our best... */
545754d96aSPatrick Pelletier 		struct bufferevent *bev = (struct bufferevent *) ctx;
555754d96aSPatrick Pelletier 		unsigned long oslerr;
565754d96aSPatrick Pelletier 		int printed_err = 0;
575754d96aSPatrick Pelletier 		int errcode = EVUTIL_SOCKET_ERROR();
58be46c99bSCatalin Patulea 		fprintf(stderr, "some request failed - no idea which one though!\n");
595754d96aSPatrick Pelletier 		/* Print out the OpenSSL error queue that libevent
605754d96aSPatrick Pelletier 		 * squirreled away for us, if any. */
615754d96aSPatrick Pelletier 		while ((oslerr = bufferevent_get_openssl_error(bev))) {
625754d96aSPatrick Pelletier 			ERR_error_string_n(oslerr, buffer, sizeof(buffer));
635754d96aSPatrick Pelletier 			fprintf(stderr, "%s\n", buffer);
645754d96aSPatrick Pelletier 			printed_err = 1;
655754d96aSPatrick Pelletier 		}
665754d96aSPatrick Pelletier 		/* If the OpenSSL error queue was empty, maybe it was a
675754d96aSPatrick Pelletier 		 * socket error; let's try printing that. */
685754d96aSPatrick Pelletier 		if (! printed_err)
695754d96aSPatrick Pelletier 			fprintf(stderr, "socket error = %s (%d)\n",
705754d96aSPatrick Pelletier 				evutil_socket_error_to_string(errcode),
715754d96aSPatrick Pelletier 				errcode);
72be46c99bSCatalin Patulea 		return;
73be46c99bSCatalin Patulea 	}
74be46c99bSCatalin Patulea 
75be46c99bSCatalin Patulea 	fprintf(stderr, "Response line: %d %s\n",
76be46c99bSCatalin Patulea 		req->response_code, req->response_code_line);
77be46c99bSCatalin Patulea 
78be46c99bSCatalin Patulea 	while ((nread = evbuffer_remove(req->input_buffer, buffer, sizeof(buffer)))
79be46c99bSCatalin Patulea 	       > 0) {
8042d7441aSPatrick Pelletier 		/* These are just arbitrary chunks of 256 bytes.
8142d7441aSPatrick Pelletier 		 * They are not lines, so we can't treat them as such. */
82be46c99bSCatalin Patulea 		fwrite(buffer, nread, 1, stdout);
83be46c99bSCatalin Patulea 	}
84be46c99bSCatalin Patulea }
85be46c99bSCatalin Patulea 
86be46c99bSCatalin Patulea static void
87be46c99bSCatalin Patulea syntax(void)
88be46c99bSCatalin Patulea {
89be46c99bSCatalin Patulea 	fputs("Syntax:\n", stderr);
90*c5887f73SAlexey Ozeritsky 	fputs("   https-client -url <https-url> [-data data-file.bin] [-ignore-cert]\n", stderr);
91be46c99bSCatalin Patulea 	fputs("Example:\n", stderr);
92*c5887f73SAlexey Ozeritsky 	fputs("   https-client -url https://ip.appspot.com/\n", stderr);
93be46c99bSCatalin Patulea 
94be46c99bSCatalin Patulea 	exit(1);
95be46c99bSCatalin Patulea }
96be46c99bSCatalin Patulea 
97be46c99bSCatalin Patulea static void
98be46c99bSCatalin Patulea die(const char *msg)
99be46c99bSCatalin Patulea {
100be46c99bSCatalin Patulea 	fputs(msg, stderr);
101be46c99bSCatalin Patulea 	exit(1);
102be46c99bSCatalin Patulea }
103be46c99bSCatalin Patulea 
1045754d96aSPatrick Pelletier static void
1055754d96aSPatrick Pelletier die_openssl(const char *func)
1065754d96aSPatrick Pelletier {
1075754d96aSPatrick Pelletier 	fprintf (stderr, "%s failed:\n", func);
1085754d96aSPatrick Pelletier 
1095754d96aSPatrick Pelletier 	/* This is the OpenSSL function that prints the contents of the
1105754d96aSPatrick Pelletier 	 * error stack to the specified file handle. */
1115754d96aSPatrick Pelletier 	ERR_print_errors_fp (stderr);
1125754d96aSPatrick Pelletier 
1135754d96aSPatrick Pelletier 	exit(1);
1145754d96aSPatrick Pelletier }
1155754d96aSPatrick Pelletier 
11664d9f161SPatrick Pelletier /* See http://archives.seul.org/libevent/users/Jan-2013/msg00039.html */
11764d9f161SPatrick Pelletier static int cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg)
11864d9f161SPatrick Pelletier {
119*c5887f73SAlexey Ozeritsky 	if (ignore_cert) {
120*c5887f73SAlexey Ozeritsky 		return 1;
121*c5887f73SAlexey Ozeritsky 	}
122*c5887f73SAlexey Ozeritsky 
12364d9f161SPatrick Pelletier 	char cert_str[256];
12464d9f161SPatrick Pelletier 	const char *host = (const char *) arg;
12564d9f161SPatrick Pelletier 	const char *res_str = "X509_verify_cert failed";
12664d9f161SPatrick Pelletier 	HostnameValidationResult res = Error;
12764d9f161SPatrick Pelletier 
12864d9f161SPatrick Pelletier 	/* This is the function that OpenSSL would call if we hadn't called
12964d9f161SPatrick Pelletier 	 * SSL_CTX_set_cert_verify_callback().  Therefore, we are "wrapping"
13064d9f161SPatrick Pelletier 	 * the default functionality, rather than replacing it. */
13164d9f161SPatrick Pelletier 	int ok_so_far = X509_verify_cert(x509_ctx);
13264d9f161SPatrick Pelletier 
13364d9f161SPatrick Pelletier 	X509 *server_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
13464d9f161SPatrick Pelletier 
13564d9f161SPatrick Pelletier 	if (ok_so_far) {
13664d9f161SPatrick Pelletier 		res = validate_hostname(host, server_cert);
13764d9f161SPatrick Pelletier 
13864d9f161SPatrick Pelletier 		switch (res) {
13964d9f161SPatrick Pelletier 		case MatchFound:
14064d9f161SPatrick Pelletier 			res_str = "MatchFound";
14164d9f161SPatrick Pelletier 			break;
14264d9f161SPatrick Pelletier 		case MatchNotFound:
14364d9f161SPatrick Pelletier 			res_str = "MatchNotFound";
14464d9f161SPatrick Pelletier 			break;
14564d9f161SPatrick Pelletier 		case NoSANPresent:
14664d9f161SPatrick Pelletier 			res_str = "NoSANPresent";
14764d9f161SPatrick Pelletier 			break;
14864d9f161SPatrick Pelletier 		case MalformedCertificate:
14964d9f161SPatrick Pelletier 			res_str = "MalformedCertificate";
15064d9f161SPatrick Pelletier 			break;
15164d9f161SPatrick Pelletier 		case Error:
15264d9f161SPatrick Pelletier 			res_str = "Error";
15364d9f161SPatrick Pelletier 			break;
15464d9f161SPatrick Pelletier 		default:
15564d9f161SPatrick Pelletier 			res_str = "WTF!";
15664d9f161SPatrick Pelletier 			break;
15764d9f161SPatrick Pelletier 		}
15864d9f161SPatrick Pelletier 	}
15964d9f161SPatrick Pelletier 
16064d9f161SPatrick Pelletier 	X509_NAME_oneline(X509_get_subject_name (server_cert),
16164d9f161SPatrick Pelletier 			  cert_str, sizeof (cert_str));
16264d9f161SPatrick Pelletier 
16364d9f161SPatrick Pelletier 	if (res == MatchFound) {
16464d9f161SPatrick Pelletier 		printf("https server '%s' has this certificate, "
16564d9f161SPatrick Pelletier 		       "which looks good to me:\n%s\n",
16664d9f161SPatrick Pelletier 		       host, cert_str);
16764d9f161SPatrick Pelletier 		return 1;
16864d9f161SPatrick Pelletier 	} else {
16964d9f161SPatrick Pelletier 		printf("Got '%s' for hostname '%s' and certificate:\n%s\n",
17064d9f161SPatrick Pelletier 		       res_str, host, cert_str);
17164d9f161SPatrick Pelletier 		return 0;
17264d9f161SPatrick Pelletier 	}
17364d9f161SPatrick Pelletier }
17464d9f161SPatrick Pelletier 
175be46c99bSCatalin Patulea int
176be46c99bSCatalin Patulea main(int argc, char **argv)
177be46c99bSCatalin Patulea {
178be46c99bSCatalin Patulea 	int r;
179be46c99bSCatalin Patulea 
180be46c99bSCatalin Patulea 	struct evhttp_uri *http_uri;
181*c5887f73SAlexey Ozeritsky 	const char *url = 0, *scheme, *host, *path, *query, *data_file = 0;
182be46c99bSCatalin Patulea 	char uri[256];
183be46c99bSCatalin Patulea 	int port;
184be46c99bSCatalin Patulea 
185be46c99bSCatalin Patulea 	SSL_CTX *ssl_ctx;
186be46c99bSCatalin Patulea 	SSL *ssl;
187be46c99bSCatalin Patulea 	struct bufferevent *bev;
188be46c99bSCatalin Patulea 	struct evhttp_connection *evcon;
189be46c99bSCatalin Patulea 	struct evhttp_request *req;
190be46c99bSCatalin Patulea 
191*c5887f73SAlexey Ozeritsky 	int i;
192be46c99bSCatalin Patulea 
193*c5887f73SAlexey Ozeritsky 	for (i = 1; i < argc; i++) {
194*c5887f73SAlexey Ozeritsky 		if (!strcmp("-url", argv[i])) {
195*c5887f73SAlexey Ozeritsky 			if (i < argc - 1) {
196*c5887f73SAlexey Ozeritsky 				url = argv[i + 1];
197*c5887f73SAlexey Ozeritsky 			} else {
198*c5887f73SAlexey Ozeritsky 				syntax();
199*c5887f73SAlexey Ozeritsky 			}
200*c5887f73SAlexey Ozeritsky 		} else if (!strcmp("-ignore-cert", argv[i])) {
201*c5887f73SAlexey Ozeritsky 			ignore_cert = 1;
202*c5887f73SAlexey Ozeritsky 		} else if (!strcmp("-data", argv[i])) {
203*c5887f73SAlexey Ozeritsky 			if (i < argc - 1) {
204*c5887f73SAlexey Ozeritsky 				data_file = argv[i + 1];
205*c5887f73SAlexey Ozeritsky 			} else {
206*c5887f73SAlexey Ozeritsky 				syntax();
207*c5887f73SAlexey Ozeritsky 			}
208*c5887f73SAlexey Ozeritsky 		} else if (!strcmp("-help", argv[i])) {
209*c5887f73SAlexey Ozeritsky 			syntax();
210*c5887f73SAlexey Ozeritsky 		}
211*c5887f73SAlexey Ozeritsky 	}
212*c5887f73SAlexey Ozeritsky 
213*c5887f73SAlexey Ozeritsky 	if (!url) {
214*c5887f73SAlexey Ozeritsky 		syntax();
215*c5887f73SAlexey Ozeritsky 	}
216*c5887f73SAlexey Ozeritsky 
217be46c99bSCatalin Patulea 	http_uri = evhttp_uri_parse(url);
218be46c99bSCatalin Patulea 	if (http_uri == NULL) {
219be46c99bSCatalin Patulea 		die("malformed url");
220be46c99bSCatalin Patulea 	}
221be46c99bSCatalin Patulea 
222be46c99bSCatalin Patulea 	scheme = evhttp_uri_get_scheme(http_uri);
223be46c99bSCatalin Patulea 	if (scheme == NULL || (strcasecmp(scheme, "https") != 0 &&
224be46c99bSCatalin Patulea 	                       strcasecmp(scheme, "http") != 0)) {
225be46c99bSCatalin Patulea 		die("url must be http or https");
226be46c99bSCatalin Patulea 	}
227be46c99bSCatalin Patulea 
228be46c99bSCatalin Patulea 	host = evhttp_uri_get_host(http_uri);
229be46c99bSCatalin Patulea 	if (host == NULL) {
230be46c99bSCatalin Patulea 		die("url must have a host");
231be46c99bSCatalin Patulea 	}
232be46c99bSCatalin Patulea 
233be46c99bSCatalin Patulea 	port = evhttp_uri_get_port(http_uri);
234be46c99bSCatalin Patulea 	if (port == -1) {
235be46c99bSCatalin Patulea 		port = (strcasecmp(scheme, "http") == 0) ? 80 : 443;
236be46c99bSCatalin Patulea 	}
237be46c99bSCatalin Patulea 
238be46c99bSCatalin Patulea 	path = evhttp_uri_get_path(http_uri);
239be46c99bSCatalin Patulea 	if (path == NULL) {
240be46c99bSCatalin Patulea 		path = "/";
241be46c99bSCatalin Patulea 	}
242be46c99bSCatalin Patulea 
243be46c99bSCatalin Patulea 	query = evhttp_uri_get_query(http_uri);
244be46c99bSCatalin Patulea 	if (query == NULL) {
245be46c99bSCatalin Patulea 		snprintf(uri, sizeof(uri) - 1, "%s", path);
246be46c99bSCatalin Patulea 	} else {
247be46c99bSCatalin Patulea 		snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query);
248be46c99bSCatalin Patulea 	}
249be46c99bSCatalin Patulea 	uri[sizeof(uri) - 1] = '\0';
250be46c99bSCatalin Patulea 
251be46c99bSCatalin Patulea 	// Initialize OpenSSL
252be46c99bSCatalin Patulea 	SSL_library_init();
253be46c99bSCatalin Patulea 	ERR_load_crypto_strings();
254be46c99bSCatalin Patulea 	SSL_load_error_strings();
255be46c99bSCatalin Patulea 	OpenSSL_add_all_algorithms();
2565754d96aSPatrick Pelletier 
2575754d96aSPatrick Pelletier 	/* This isn't strictly necessary... OpenSSL performs RAND_poll
2585754d96aSPatrick Pelletier 	 * automatically on first use of random number generator. */
259be46c99bSCatalin Patulea 	r = RAND_poll();
260be46c99bSCatalin Patulea 	if (r == 0) {
2615754d96aSPatrick Pelletier 		die_openssl("RAND_poll");
262be46c99bSCatalin Patulea 	}
2635754d96aSPatrick Pelletier 
2645754d96aSPatrick Pelletier 	/* Create a new OpenSSL context */
265be46c99bSCatalin Patulea 	ssl_ctx = SSL_CTX_new(SSLv23_method());
2665754d96aSPatrick Pelletier 	if (!ssl_ctx)
2675754d96aSPatrick Pelletier 		die_openssl("SSL_CTX_new");
268be46c99bSCatalin Patulea 
269aacd674cSPatrick Pelletier 	/* Attempt to use the system's trusted root certificates.
270aacd674cSPatrick Pelletier 	 * (This path is only valid for Debian-based systems.) */
271aacd674cSPatrick Pelletier 	if (1 != SSL_CTX_load_verify_locations(ssl_ctx,
272aacd674cSPatrick Pelletier 					       "/etc/ssl/certs/ca-certificates.crt",
273aacd674cSPatrick Pelletier 					       NULL))
274aacd674cSPatrick Pelletier 		die_openssl("SSL_CTX_load_verify_locations");
27564d9f161SPatrick Pelletier 	/* Ask OpenSSL to verify the server certificate.  Note that this
27664d9f161SPatrick Pelletier 	 * does NOT include verifying that the hostname is correct.
27764d9f161SPatrick Pelletier 	 * So, by itself, this means anyone with any legitimate
27864d9f161SPatrick Pelletier 	 * CA-issued certificate for any website, can impersonate any
27964d9f161SPatrick Pelletier 	 * other website in the world.  This is not good.  See "The
28064d9f161SPatrick Pelletier 	 * Most Dangerous Code in the World" article at
28164d9f161SPatrick Pelletier 	 * https://crypto.stanford.edu/~dabo/pubs/abstracts/ssl-client-bugs.html
28264d9f161SPatrick Pelletier 	 */
283aacd674cSPatrick Pelletier 	SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
28464d9f161SPatrick Pelletier 	/* This is how we solve the problem mentioned in the previous
28564d9f161SPatrick Pelletier 	 * comment.  We "wrap" OpenSSL's validation routine in our
28664d9f161SPatrick Pelletier 	 * own routine, which also validates the hostname by calling
28764d9f161SPatrick Pelletier 	 * the code provided by iSECPartners.  Note that even though
28864d9f161SPatrick Pelletier 	 * the "Everything You've Always Wanted to Know About
28964d9f161SPatrick Pelletier 	 * Certificate Validation With OpenSSL (But Were Afraid to
29064d9f161SPatrick Pelletier 	 * Ask)" paper from iSECPartners says very explicitly not to
29164d9f161SPatrick Pelletier 	 * call SSL_CTX_set_cert_verify_callback (at the bottom of
29264d9f161SPatrick Pelletier 	 * page 2), what we're doing here is safe because our
29364d9f161SPatrick Pelletier 	 * cert_verify_callback() calls X509_verify_cert(), which is
29464d9f161SPatrick Pelletier 	 * OpenSSL's built-in routine which would have been called if
29564d9f161SPatrick Pelletier 	 * we hadn't set the callback.  Therefore, we're just
29664d9f161SPatrick Pelletier 	 * "wrapping" OpenSSL's routine, not replacing it. */
29764d9f161SPatrick Pelletier 	SSL_CTX_set_cert_verify_callback (ssl_ctx, cert_verify_callback,
29864d9f161SPatrick Pelletier 					  (void *) host);
299aacd674cSPatrick Pelletier 
300be46c99bSCatalin Patulea 	// Create event base
301be46c99bSCatalin Patulea 	base = event_base_new();
302be46c99bSCatalin Patulea 	if (!base) {
303be46c99bSCatalin Patulea 		perror("event_base_new()");
304be46c99bSCatalin Patulea 		return 1;
305be46c99bSCatalin Patulea 	}
306be46c99bSCatalin Patulea 
307be46c99bSCatalin Patulea 	// Create OpenSSL bufferevent and stack evhttp on top of it
308be46c99bSCatalin Patulea 	ssl = SSL_new(ssl_ctx);
309be46c99bSCatalin Patulea 	if (ssl == NULL) {
3105754d96aSPatrick Pelletier 		die_openssl("SSL_new()");
311be46c99bSCatalin Patulea 	}
312be46c99bSCatalin Patulea 
313be46c99bSCatalin Patulea 	if (strcasecmp(scheme, "http") == 0) {
314be46c99bSCatalin Patulea 		bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
315be46c99bSCatalin Patulea 	} else {
316be46c99bSCatalin Patulea 		bev = bufferevent_openssl_socket_new(base, -1, ssl,
317be46c99bSCatalin Patulea 			BUFFEREVENT_SSL_CONNECTING,
318be46c99bSCatalin Patulea 			BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
319be46c99bSCatalin Patulea 	}
320be46c99bSCatalin Patulea 
321be46c99bSCatalin Patulea 	if (bev == NULL) {
322be46c99bSCatalin Patulea 		fprintf(stderr, "bufferevent_openssl_socket_new() failed\n");
323be46c99bSCatalin Patulea 		return 1;
324be46c99bSCatalin Patulea 	}
325be46c99bSCatalin Patulea 
326be46c99bSCatalin Patulea 	bufferevent_openssl_set_allow_dirty_shutdown(bev, 1);
327be46c99bSCatalin Patulea 
328be46c99bSCatalin Patulea 	// For simplicity, we let DNS resolution block. Everything else should be
329be46c99bSCatalin Patulea 	// asynchronous though.
330be46c99bSCatalin Patulea 	evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev,
331be46c99bSCatalin Patulea 		host, port);
332be46c99bSCatalin Patulea 	if (evcon == NULL) {
333be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n");
334be46c99bSCatalin Patulea 		return 1;
335be46c99bSCatalin Patulea 	}
336be46c99bSCatalin Patulea 
337be46c99bSCatalin Patulea 	// Fire off the request
3385754d96aSPatrick Pelletier 	req = evhttp_request_new(http_request_done, bev);
339be46c99bSCatalin Patulea 	if (req == NULL) {
340be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_request_new() failed\n");
341be46c99bSCatalin Patulea 		return 1;
342be46c99bSCatalin Patulea 	}
343be46c99bSCatalin Patulea 
344be46c99bSCatalin Patulea 	evhttp_add_header(req->output_headers, "Host", host);
345be46c99bSCatalin Patulea 	evhttp_add_header(req->output_headers, "Connection", "close");
346be46c99bSCatalin Patulea 
347*c5887f73SAlexey Ozeritsky 	if (data_file) {
348*c5887f73SAlexey Ozeritsky 		FILE * f = fopen(data_file, "rb");
349*c5887f73SAlexey Ozeritsky 		char buf[1024];
350*c5887f73SAlexey Ozeritsky 		ssize_t s;
351*c5887f73SAlexey Ozeritsky 		size_t bytes = 0;
352*c5887f73SAlexey Ozeritsky 
353*c5887f73SAlexey Ozeritsky 		if (!f) {
354*c5887f73SAlexey Ozeritsky 			syntax();
355*c5887f73SAlexey Ozeritsky 		}
356*c5887f73SAlexey Ozeritsky 
357*c5887f73SAlexey Ozeritsky 		while ((s = fread(buf, 1, sizeof(buf), f)) > 0) {
358*c5887f73SAlexey Ozeritsky 			evbuffer_add(req->output_buffer, buf, s);
359*c5887f73SAlexey Ozeritsky 			bytes += s;
360*c5887f73SAlexey Ozeritsky 		}
361*c5887f73SAlexey Ozeritsky 		snprintf(buf, sizeof(buf)-1, "%lu", bytes);
362*c5887f73SAlexey Ozeritsky 		evhttp_add_header(req->output_headers, "Content-Length", buf);
363*c5887f73SAlexey Ozeritsky 		fclose(f);
364*c5887f73SAlexey Ozeritsky 	}
365*c5887f73SAlexey Ozeritsky 
366*c5887f73SAlexey Ozeritsky 	r = evhttp_make_request(evcon, req, data_file ? EVHTTP_REQ_POST : EVHTTP_REQ_GET, uri);
367be46c99bSCatalin Patulea 	if (r != 0) {
368be46c99bSCatalin Patulea 		fprintf(stderr, "evhttp_make_request() failed\n");
369be46c99bSCatalin Patulea 		return 1;
370be46c99bSCatalin Patulea 	}
371be46c99bSCatalin Patulea 
372be46c99bSCatalin Patulea 	event_base_dispatch(base);
373be46c99bSCatalin Patulea 
374be46c99bSCatalin Patulea 	evhttp_connection_free(evcon);
375be46c99bSCatalin Patulea 	event_base_free(base);
376be46c99bSCatalin Patulea 
377be46c99bSCatalin Patulea 	return 0;
378be46c99bSCatalin Patulea }
379