1cbc620a4SEd Maste /*
2cbc620a4SEd Maste This is an example of how to hook up evhttp with bufferevent_ssl
3cbc620a4SEd Maste
4cbc620a4SEd Maste It just GETs an https URL given on the command-line and prints the response
5cbc620a4SEd Maste body to stdout.
6cbc620a4SEd Maste
7cbc620a4SEd Maste Actually, it also accepts plain http URLs to make it easy to compare http vs
8cbc620a4SEd Maste https code paths.
9cbc620a4SEd Maste
10cbc620a4SEd Maste Loosely based on le-proxy.c.
11cbc620a4SEd Maste */
12cbc620a4SEd Maste
13cbc620a4SEd Maste // Get rid of OSX 10.7 and greater deprecation warnings.
14cbc620a4SEd Maste #if defined(__APPLE__) && defined(__clang__)
15cbc620a4SEd Maste #pragma clang diagnostic ignored "-Wdeprecated-declarations"
16cbc620a4SEd Maste #endif
17cbc620a4SEd Maste
18cbc620a4SEd Maste #include <stdio.h>
19cbc620a4SEd Maste #include <assert.h>
20cbc620a4SEd Maste #include <stdlib.h>
21cbc620a4SEd Maste #include <string.h>
22cbc620a4SEd Maste #include <errno.h>
23cbc620a4SEd Maste
24cbc620a4SEd Maste #ifdef _WIN32
25cbc620a4SEd Maste #include <winsock2.h>
26cbc620a4SEd Maste #include <ws2tcpip.h>
27cbc620a4SEd Maste
28cbc620a4SEd Maste #define snprintf _snprintf
29cbc620a4SEd Maste #define strcasecmp _stricmp
30cbc620a4SEd Maste #else
31cbc620a4SEd Maste #include <sys/socket.h>
32cbc620a4SEd Maste #include <netinet/in.h>
33cbc620a4SEd Maste #endif
34cbc620a4SEd Maste
35cbc620a4SEd Maste #include <event2/bufferevent_ssl.h>
36cbc620a4SEd Maste #include <event2/bufferevent.h>
37cbc620a4SEd Maste #include <event2/buffer.h>
38cbc620a4SEd Maste #include <event2/listener.h>
39cbc620a4SEd Maste #include <event2/util.h>
40cbc620a4SEd Maste #include <event2/http.h>
41cbc620a4SEd Maste
42cbc620a4SEd Maste #include <openssl/ssl.h>
43cbc620a4SEd Maste #include <openssl/err.h>
44cbc620a4SEd Maste #include <openssl/rand.h>
45cbc620a4SEd Maste
46cbc620a4SEd Maste #include "openssl_hostname_validation.h"
47cbc620a4SEd Maste
48cbc620a4SEd Maste static int ignore_cert = 0;
49cbc620a4SEd Maste
50cbc620a4SEd Maste static void
http_request_done(struct evhttp_request * req,void * ctx)51cbc620a4SEd Maste http_request_done(struct evhttp_request *req, void *ctx)
52cbc620a4SEd Maste {
53cbc620a4SEd Maste char buffer[256];
54cbc620a4SEd Maste int nread;
55cbc620a4SEd Maste
565223d1d9SCy Schubert if (!req || !evhttp_request_get_response_code(req)) {
57cbc620a4SEd Maste /* If req is NULL, it means an error occurred, but
58cbc620a4SEd Maste * sadly we are mostly left guessing what the error
59cbc620a4SEd Maste * might have been. We'll do our best... */
60cbc620a4SEd Maste struct bufferevent *bev = (struct bufferevent *) ctx;
61cbc620a4SEd Maste unsigned long oslerr;
62cbc620a4SEd Maste int printed_err = 0;
63cbc620a4SEd Maste int errcode = EVUTIL_SOCKET_ERROR();
64cbc620a4SEd Maste fprintf(stderr, "some request failed - no idea which one though!\n");
65cbc620a4SEd Maste /* Print out the OpenSSL error queue that libevent
66cbc620a4SEd Maste * squirreled away for us, if any. */
67cbc620a4SEd Maste while ((oslerr = bufferevent_get_openssl_error(bev))) {
68cbc620a4SEd Maste ERR_error_string_n(oslerr, buffer, sizeof(buffer));
69cbc620a4SEd Maste fprintf(stderr, "%s\n", buffer);
70cbc620a4SEd Maste printed_err = 1;
71cbc620a4SEd Maste }
72cbc620a4SEd Maste /* If the OpenSSL error queue was empty, maybe it was a
73cbc620a4SEd Maste * socket error; let's try printing that. */
74cbc620a4SEd Maste if (! printed_err)
75cbc620a4SEd Maste fprintf(stderr, "socket error = %s (%d)\n",
76cbc620a4SEd Maste evutil_socket_error_to_string(errcode),
77cbc620a4SEd Maste errcode);
78cbc620a4SEd Maste return;
79cbc620a4SEd Maste }
80cbc620a4SEd Maste
81cbc620a4SEd Maste fprintf(stderr, "Response line: %d %s\n",
82cbc620a4SEd Maste evhttp_request_get_response_code(req),
83cbc620a4SEd Maste evhttp_request_get_response_code_line(req));
84cbc620a4SEd Maste
85cbc620a4SEd Maste while ((nread = evbuffer_remove(evhttp_request_get_input_buffer(req),
86cbc620a4SEd Maste buffer, sizeof(buffer)))
87cbc620a4SEd Maste > 0) {
88cbc620a4SEd Maste /* These are just arbitrary chunks of 256 bytes.
89cbc620a4SEd Maste * They are not lines, so we can't treat them as such. */
90cbc620a4SEd Maste fwrite(buffer, nread, 1, stdout);
91cbc620a4SEd Maste }
92cbc620a4SEd Maste }
93cbc620a4SEd Maste
94cbc620a4SEd Maste static void
syntax(void)95cbc620a4SEd Maste syntax(void)
96cbc620a4SEd Maste {
97cbc620a4SEd Maste fputs("Syntax:\n", stderr);
98cbc620a4SEd Maste fputs(" https-client -url <https-url> [-data data-file.bin] [-ignore-cert] [-retries num] [-timeout sec] [-crt crt]\n", stderr);
99cbc620a4SEd Maste fputs("Example:\n", stderr);
100cbc620a4SEd Maste fputs(" https-client -url https://ip.appspot.com/\n", stderr);
101cbc620a4SEd Maste }
102cbc620a4SEd Maste
103cbc620a4SEd Maste static void
err(const char * msg)104cbc620a4SEd Maste err(const char *msg)
105cbc620a4SEd Maste {
106cbc620a4SEd Maste fputs(msg, stderr);
107cbc620a4SEd Maste }
108cbc620a4SEd Maste
109cbc620a4SEd Maste static void
err_openssl(const char * func)110cbc620a4SEd Maste err_openssl(const char *func)
111cbc620a4SEd Maste {
112cbc620a4SEd Maste fprintf (stderr, "%s failed:\n", func);
113cbc620a4SEd Maste
114cbc620a4SEd Maste /* This is the OpenSSL function that prints the contents of the
115cbc620a4SEd Maste * error stack to the specified file handle. */
116cbc620a4SEd Maste ERR_print_errors_fp (stderr);
117cbc620a4SEd Maste
118cbc620a4SEd Maste exit(1);
119cbc620a4SEd Maste }
120cbc620a4SEd Maste
121cbc620a4SEd Maste /* See http://archives.seul.org/libevent/users/Jan-2013/msg00039.html */
cert_verify_callback(X509_STORE_CTX * x509_ctx,void * arg)122cbc620a4SEd Maste static int cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg)
123cbc620a4SEd Maste {
124cbc620a4SEd Maste char cert_str[256];
125cbc620a4SEd Maste const char *host = (const char *) arg;
126cbc620a4SEd Maste const char *res_str = "X509_verify_cert failed";
127cbc620a4SEd Maste HostnameValidationResult res = Error;
128cbc620a4SEd Maste
129cbc620a4SEd Maste /* This is the function that OpenSSL would call if we hadn't called
130cbc620a4SEd Maste * SSL_CTX_set_cert_verify_callback(). Therefore, we are "wrapping"
131cbc620a4SEd Maste * the default functionality, rather than replacing it. */
132cbc620a4SEd Maste int ok_so_far = 0;
133cbc620a4SEd Maste
134cbc620a4SEd Maste X509 *server_cert = NULL;
135cbc620a4SEd Maste
136cbc620a4SEd Maste if (ignore_cert) {
137cbc620a4SEd Maste return 1;
138cbc620a4SEd Maste }
139cbc620a4SEd Maste
140cbc620a4SEd Maste ok_so_far = X509_verify_cert(x509_ctx);
141cbc620a4SEd Maste
142cbc620a4SEd Maste server_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
143cbc620a4SEd Maste
144cbc620a4SEd Maste if (ok_so_far) {
145cbc620a4SEd Maste res = validate_hostname(host, server_cert);
146cbc620a4SEd Maste
147cbc620a4SEd Maste switch (res) {
148cbc620a4SEd Maste case MatchFound:
149cbc620a4SEd Maste res_str = "MatchFound";
150cbc620a4SEd Maste break;
151cbc620a4SEd Maste case MatchNotFound:
152cbc620a4SEd Maste res_str = "MatchNotFound";
153cbc620a4SEd Maste break;
154cbc620a4SEd Maste case NoSANPresent:
155cbc620a4SEd Maste res_str = "NoSANPresent";
156cbc620a4SEd Maste break;
157cbc620a4SEd Maste case MalformedCertificate:
158cbc620a4SEd Maste res_str = "MalformedCertificate";
159cbc620a4SEd Maste break;
160cbc620a4SEd Maste case Error:
161cbc620a4SEd Maste res_str = "Error";
162cbc620a4SEd Maste break;
163cbc620a4SEd Maste default:
164cbc620a4SEd Maste res_str = "WTF!";
165cbc620a4SEd Maste break;
166cbc620a4SEd Maste }
167cbc620a4SEd Maste }
168cbc620a4SEd Maste
169cbc620a4SEd Maste X509_NAME_oneline(X509_get_subject_name (server_cert),
170cbc620a4SEd Maste cert_str, sizeof (cert_str));
171cbc620a4SEd Maste
172cbc620a4SEd Maste if (res == MatchFound) {
173cbc620a4SEd Maste printf("https server '%s' has this certificate, "
174cbc620a4SEd Maste "which looks good to me:\n%s\n",
175cbc620a4SEd Maste host, cert_str);
176cbc620a4SEd Maste return 1;
177cbc620a4SEd Maste } else {
178cbc620a4SEd Maste printf("Got '%s' for hostname '%s' and certificate:\n%s\n",
179cbc620a4SEd Maste res_str, host, cert_str);
180cbc620a4SEd Maste return 0;
181cbc620a4SEd Maste }
182cbc620a4SEd Maste }
1835223d1d9SCy Schubert
1845223d1d9SCy Schubert #ifdef _WIN32
1855223d1d9SCy Schubert static int
add_cert_for_store(X509_STORE * store,const char * name)1865223d1d9SCy Schubert add_cert_for_store(X509_STORE *store, const char *name)
1875223d1d9SCy Schubert {
1885223d1d9SCy Schubert HCERTSTORE sys_store = NULL;
1895223d1d9SCy Schubert PCCERT_CONTEXT ctx = NULL;
1905223d1d9SCy Schubert int r = 0;
1915223d1d9SCy Schubert
1925223d1d9SCy Schubert sys_store = CertOpenSystemStore(0, name);
1935223d1d9SCy Schubert if (!sys_store) {
1945223d1d9SCy Schubert err("failed to open system certificate store");
1955223d1d9SCy Schubert return -1;
1965223d1d9SCy Schubert }
1975223d1d9SCy Schubert while ((ctx = CertEnumCertificatesInStore(sys_store, ctx))) {
1985223d1d9SCy Schubert X509 *x509 = d2i_X509(NULL, (unsigned char const **)&ctx->pbCertEncoded,
1995223d1d9SCy Schubert ctx->cbCertEncoded);
2005223d1d9SCy Schubert if (x509) {
2015223d1d9SCy Schubert X509_STORE_add_cert(store, x509);
2025223d1d9SCy Schubert X509_free(x509);
2035223d1d9SCy Schubert } else {
2045223d1d9SCy Schubert r = -1;
2055223d1d9SCy Schubert err_openssl("d2i_X509");
2065223d1d9SCy Schubert break;
2075223d1d9SCy Schubert }
2085223d1d9SCy Schubert }
2095223d1d9SCy Schubert CertCloseStore(sys_store, 0);
2105223d1d9SCy Schubert return r;
2115223d1d9SCy Schubert }
212cbc620a4SEd Maste #endif
213cbc620a4SEd Maste
214cbc620a4SEd Maste int
main(int argc,char ** argv)215cbc620a4SEd Maste main(int argc, char **argv)
216cbc620a4SEd Maste {
217cbc620a4SEd Maste int r;
2185223d1d9SCy Schubert struct event_base *base = NULL;
219cbc620a4SEd Maste struct evhttp_uri *http_uri = NULL;
220cbc620a4SEd Maste const char *url = NULL, *data_file = NULL;
2215223d1d9SCy Schubert const char *crt = NULL;
222cbc620a4SEd Maste const char *scheme, *host, *path, *query;
223cbc620a4SEd Maste char uri[256];
224cbc620a4SEd Maste int port;
225cbc620a4SEd Maste int retries = 0;
226cbc620a4SEd Maste int timeout = -1;
227cbc620a4SEd Maste
228cbc620a4SEd Maste SSL_CTX *ssl_ctx = NULL;
229cbc620a4SEd Maste SSL *ssl = NULL;
230cbc620a4SEd Maste struct bufferevent *bev;
231cbc620a4SEd Maste struct evhttp_connection *evcon = NULL;
232cbc620a4SEd Maste struct evhttp_request *req;
233cbc620a4SEd Maste struct evkeyvalq *output_headers;
234cbc620a4SEd Maste struct evbuffer *output_buffer;
235cbc620a4SEd Maste
236cbc620a4SEd Maste int i;
237cbc620a4SEd Maste int ret = 0;
238cbc620a4SEd Maste enum { HTTP, HTTPS } type = HTTP;
239cbc620a4SEd Maste
240cbc620a4SEd Maste for (i = 1; i < argc; i++) {
241cbc620a4SEd Maste if (!strcmp("-url", argv[i])) {
242cbc620a4SEd Maste if (i < argc - 1) {
243cbc620a4SEd Maste url = argv[i + 1];
244cbc620a4SEd Maste } else {
245cbc620a4SEd Maste syntax();
246cbc620a4SEd Maste goto error;
247cbc620a4SEd Maste }
248cbc620a4SEd Maste } else if (!strcmp("-crt", argv[i])) {
249cbc620a4SEd Maste if (i < argc - 1) {
250cbc620a4SEd Maste crt = argv[i + 1];
251cbc620a4SEd Maste } else {
252cbc620a4SEd Maste syntax();
253cbc620a4SEd Maste goto error;
254cbc620a4SEd Maste }
255cbc620a4SEd Maste } else if (!strcmp("-ignore-cert", argv[i])) {
256cbc620a4SEd Maste ignore_cert = 1;
257cbc620a4SEd Maste } else if (!strcmp("-data", argv[i])) {
258cbc620a4SEd Maste if (i < argc - 1) {
259cbc620a4SEd Maste data_file = argv[i + 1];
260cbc620a4SEd Maste } else {
261cbc620a4SEd Maste syntax();
262cbc620a4SEd Maste goto error;
263cbc620a4SEd Maste }
264cbc620a4SEd Maste } else if (!strcmp("-retries", argv[i])) {
265cbc620a4SEd Maste if (i < argc - 1) {
266cbc620a4SEd Maste retries = atoi(argv[i + 1]);
267cbc620a4SEd Maste } else {
268cbc620a4SEd Maste syntax();
269cbc620a4SEd Maste goto error;
270cbc620a4SEd Maste }
271cbc620a4SEd Maste } else if (!strcmp("-timeout", argv[i])) {
272cbc620a4SEd Maste if (i < argc - 1) {
273cbc620a4SEd Maste timeout = atoi(argv[i + 1]);
274cbc620a4SEd Maste } else {
275cbc620a4SEd Maste syntax();
276cbc620a4SEd Maste goto error;
277cbc620a4SEd Maste }
278cbc620a4SEd Maste } else if (!strcmp("-help", argv[i])) {
279cbc620a4SEd Maste syntax();
280cbc620a4SEd Maste goto error;
281cbc620a4SEd Maste }
282cbc620a4SEd Maste }
283cbc620a4SEd Maste
284cbc620a4SEd Maste if (!url) {
285cbc620a4SEd Maste syntax();
286cbc620a4SEd Maste goto error;
287cbc620a4SEd Maste }
288cbc620a4SEd Maste
289cbc620a4SEd Maste #ifdef _WIN32
290cbc620a4SEd Maste {
291cbc620a4SEd Maste WORD wVersionRequested;
292cbc620a4SEd Maste WSADATA wsaData;
293cbc620a4SEd Maste int err;
294cbc620a4SEd Maste
295cbc620a4SEd Maste wVersionRequested = MAKEWORD(2, 2);
296cbc620a4SEd Maste
297cbc620a4SEd Maste err = WSAStartup(wVersionRequested, &wsaData);
298cbc620a4SEd Maste if (err != 0) {
299cbc620a4SEd Maste printf("WSAStartup failed with error: %d\n", err);
300cbc620a4SEd Maste goto error;
301cbc620a4SEd Maste }
302cbc620a4SEd Maste }
303cbc620a4SEd Maste #endif // _WIN32
304cbc620a4SEd Maste
305cbc620a4SEd Maste http_uri = evhttp_uri_parse(url);
306cbc620a4SEd Maste if (http_uri == NULL) {
307cbc620a4SEd Maste err("malformed url");
308cbc620a4SEd Maste goto error;
309cbc620a4SEd Maste }
310cbc620a4SEd Maste
311cbc620a4SEd Maste scheme = evhttp_uri_get_scheme(http_uri);
312cbc620a4SEd Maste if (scheme == NULL || (strcasecmp(scheme, "https") != 0 &&
313cbc620a4SEd Maste strcasecmp(scheme, "http") != 0)) {
314cbc620a4SEd Maste err("url must be http or https");
315cbc620a4SEd Maste goto error;
316cbc620a4SEd Maste }
317cbc620a4SEd Maste
318cbc620a4SEd Maste host = evhttp_uri_get_host(http_uri);
319cbc620a4SEd Maste if (host == NULL) {
320cbc620a4SEd Maste err("url must have a host");
321cbc620a4SEd Maste goto error;
322cbc620a4SEd Maste }
323cbc620a4SEd Maste
324cbc620a4SEd Maste port = evhttp_uri_get_port(http_uri);
325cbc620a4SEd Maste if (port == -1) {
326cbc620a4SEd Maste port = (strcasecmp(scheme, "http") == 0) ? 80 : 443;
327cbc620a4SEd Maste }
328cbc620a4SEd Maste
329cbc620a4SEd Maste path = evhttp_uri_get_path(http_uri);
330cbc620a4SEd Maste if (strlen(path) == 0) {
331cbc620a4SEd Maste path = "/";
332cbc620a4SEd Maste }
333cbc620a4SEd Maste
334cbc620a4SEd Maste query = evhttp_uri_get_query(http_uri);
335cbc620a4SEd Maste if (query == NULL) {
336cbc620a4SEd Maste snprintf(uri, sizeof(uri) - 1, "%s", path);
337cbc620a4SEd Maste } else {
338cbc620a4SEd Maste snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query);
339cbc620a4SEd Maste }
340cbc620a4SEd Maste uri[sizeof(uri) - 1] = '\0';
341cbc620a4SEd Maste
3425223d1d9SCy Schubert #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
3435223d1d9SCy Schubert (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
344cbc620a4SEd Maste // Initialize OpenSSL
345cbc620a4SEd Maste SSL_library_init();
346cbc620a4SEd Maste ERR_load_crypto_strings();
347cbc620a4SEd Maste SSL_load_error_strings();
348cbc620a4SEd Maste OpenSSL_add_all_algorithms();
349cbc620a4SEd Maste #endif
350cbc620a4SEd Maste
351cbc620a4SEd Maste /* This isn't strictly necessary... OpenSSL performs RAND_poll
352cbc620a4SEd Maste * automatically on first use of random number generator. */
353cbc620a4SEd Maste r = RAND_poll();
354cbc620a4SEd Maste if (r == 0) {
355cbc620a4SEd Maste err_openssl("RAND_poll");
356cbc620a4SEd Maste goto error;
357cbc620a4SEd Maste }
358cbc620a4SEd Maste
359cbc620a4SEd Maste /* Create a new OpenSSL context */
360cbc620a4SEd Maste ssl_ctx = SSL_CTX_new(SSLv23_method());
361cbc620a4SEd Maste if (!ssl_ctx) {
362cbc620a4SEd Maste err_openssl("SSL_CTX_new");
363cbc620a4SEd Maste goto error;
364cbc620a4SEd Maste }
365cbc620a4SEd Maste
3665223d1d9SCy Schubert if (crt == NULL) {
3675223d1d9SCy Schubert X509_STORE *store;
3685223d1d9SCy Schubert /* Attempt to use the system's trusted root certificates. */
3695223d1d9SCy Schubert store = SSL_CTX_get_cert_store(ssl_ctx);
3705223d1d9SCy Schubert #ifdef _WIN32
3715223d1d9SCy Schubert if (add_cert_for_store(store, "CA") < 0 ||
3725223d1d9SCy Schubert add_cert_for_store(store, "AuthRoot") < 0 ||
3735223d1d9SCy Schubert add_cert_for_store(store, "ROOT") < 0) {
3745223d1d9SCy Schubert goto error;
3755223d1d9SCy Schubert }
3765223d1d9SCy Schubert #else // _WIN32
3775223d1d9SCy Schubert if (X509_STORE_set_default_paths(store) != 1) {
3785223d1d9SCy Schubert err_openssl("X509_STORE_set_default_paths");
3795223d1d9SCy Schubert goto error;
3805223d1d9SCy Schubert }
3815223d1d9SCy Schubert #endif // _WIN32
3825223d1d9SCy Schubert } else {
3835223d1d9SCy Schubert if (SSL_CTX_load_verify_locations(ssl_ctx, crt, NULL) != 1) {
384cbc620a4SEd Maste err_openssl("SSL_CTX_load_verify_locations");
385cbc620a4SEd Maste goto error;
386cbc620a4SEd Maste }
3875223d1d9SCy Schubert }
388cbc620a4SEd Maste /* Ask OpenSSL to verify the server certificate. Note that this
389cbc620a4SEd Maste * does NOT include verifying that the hostname is correct.
390cbc620a4SEd Maste * So, by itself, this means anyone with any legitimate
391cbc620a4SEd Maste * CA-issued certificate for any website, can impersonate any
392cbc620a4SEd Maste * other website in the world. This is not good. See "The
393cbc620a4SEd Maste * Most Dangerous Code in the World" article at
394cbc620a4SEd Maste * https://crypto.stanford.edu/~dabo/pubs/abstracts/ssl-client-bugs.html
395cbc620a4SEd Maste */
396cbc620a4SEd Maste SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
397cbc620a4SEd Maste /* This is how we solve the problem mentioned in the previous
398cbc620a4SEd Maste * comment. We "wrap" OpenSSL's validation routine in our
399cbc620a4SEd Maste * own routine, which also validates the hostname by calling
400cbc620a4SEd Maste * the code provided by iSECPartners. Note that even though
401cbc620a4SEd Maste * the "Everything You've Always Wanted to Know About
402cbc620a4SEd Maste * Certificate Validation With OpenSSL (But Were Afraid to
403cbc620a4SEd Maste * Ask)" paper from iSECPartners says very explicitly not to
404cbc620a4SEd Maste * call SSL_CTX_set_cert_verify_callback (at the bottom of
405cbc620a4SEd Maste * page 2), what we're doing here is safe because our
406cbc620a4SEd Maste * cert_verify_callback() calls X509_verify_cert(), which is
407cbc620a4SEd Maste * OpenSSL's built-in routine which would have been called if
408cbc620a4SEd Maste * we hadn't set the callback. Therefore, we're just
409cbc620a4SEd Maste * "wrapping" OpenSSL's routine, not replacing it. */
410cbc620a4SEd Maste SSL_CTX_set_cert_verify_callback(ssl_ctx, cert_verify_callback,
411cbc620a4SEd Maste (void *) host);
412cbc620a4SEd Maste
413cbc620a4SEd Maste // Create event base
414cbc620a4SEd Maste base = event_base_new();
415cbc620a4SEd Maste if (!base) {
416cbc620a4SEd Maste perror("event_base_new()");
417cbc620a4SEd Maste goto error;
418cbc620a4SEd Maste }
419cbc620a4SEd Maste
420cbc620a4SEd Maste // Create OpenSSL bufferevent and stack evhttp on top of it
421cbc620a4SEd Maste ssl = SSL_new(ssl_ctx);
422cbc620a4SEd Maste if (ssl == NULL) {
423cbc620a4SEd Maste err_openssl("SSL_new()");
424cbc620a4SEd Maste goto error;
425cbc620a4SEd Maste }
426cbc620a4SEd Maste
427cbc620a4SEd Maste #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
428cbc620a4SEd Maste // Set hostname for SNI extension
429cbc620a4SEd Maste SSL_set_tlsext_host_name(ssl, host);
430cbc620a4SEd Maste #endif
431cbc620a4SEd Maste
432cbc620a4SEd Maste if (strcasecmp(scheme, "http") == 0) {
433cbc620a4SEd Maste bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
434cbc620a4SEd Maste } else {
435cbc620a4SEd Maste type = HTTPS;
436cbc620a4SEd Maste bev = bufferevent_openssl_socket_new(base, -1, ssl,
437cbc620a4SEd Maste BUFFEREVENT_SSL_CONNECTING,
438cbc620a4SEd Maste BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS);
439cbc620a4SEd Maste }
440cbc620a4SEd Maste
441cbc620a4SEd Maste if (bev == NULL) {
442cbc620a4SEd Maste fprintf(stderr, "bufferevent_openssl_socket_new() failed\n");
443cbc620a4SEd Maste goto error;
444cbc620a4SEd Maste }
445cbc620a4SEd Maste
446cbc620a4SEd Maste bufferevent_openssl_set_allow_dirty_shutdown(bev, 1);
447cbc620a4SEd Maste
448cbc620a4SEd Maste // For simplicity, we let DNS resolution block. Everything else should be
449cbc620a4SEd Maste // asynchronous though.
450cbc620a4SEd Maste evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev,
451cbc620a4SEd Maste host, port);
452cbc620a4SEd Maste if (evcon == NULL) {
453cbc620a4SEd Maste fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n");
454cbc620a4SEd Maste goto error;
455cbc620a4SEd Maste }
456cbc620a4SEd Maste
457cbc620a4SEd Maste if (retries > 0) {
458cbc620a4SEd Maste evhttp_connection_set_retries(evcon, retries);
459cbc620a4SEd Maste }
460cbc620a4SEd Maste if (timeout >= 0) {
461cbc620a4SEd Maste evhttp_connection_set_timeout(evcon, timeout);
462cbc620a4SEd Maste }
463cbc620a4SEd Maste
464cbc620a4SEd Maste // Fire off the request
465cbc620a4SEd Maste req = evhttp_request_new(http_request_done, bev);
466cbc620a4SEd Maste if (req == NULL) {
467cbc620a4SEd Maste fprintf(stderr, "evhttp_request_new() failed\n");
468cbc620a4SEd Maste goto error;
469cbc620a4SEd Maste }
470cbc620a4SEd Maste
471cbc620a4SEd Maste output_headers = evhttp_request_get_output_headers(req);
472cbc620a4SEd Maste evhttp_add_header(output_headers, "Host", host);
473cbc620a4SEd Maste evhttp_add_header(output_headers, "Connection", "close");
474cbc620a4SEd Maste
475cbc620a4SEd Maste if (data_file) {
476cbc620a4SEd Maste /* NOTE: In production code, you'd probably want to use
477cbc620a4SEd Maste * evbuffer_add_file() or evbuffer_add_file_segment(), to
478cbc620a4SEd Maste * avoid needless copying. */
479cbc620a4SEd Maste FILE * f = fopen(data_file, "rb");
480cbc620a4SEd Maste char buf[1024];
481cbc620a4SEd Maste size_t s;
482cbc620a4SEd Maste size_t bytes = 0;
483cbc620a4SEd Maste
484cbc620a4SEd Maste if (!f) {
485cbc620a4SEd Maste syntax();
486cbc620a4SEd Maste goto error;
487cbc620a4SEd Maste }
488cbc620a4SEd Maste
489cbc620a4SEd Maste output_buffer = evhttp_request_get_output_buffer(req);
490cbc620a4SEd Maste while ((s = fread(buf, 1, sizeof(buf), f)) > 0) {
491cbc620a4SEd Maste evbuffer_add(output_buffer, buf, s);
492cbc620a4SEd Maste bytes += s;
493cbc620a4SEd Maste }
494cbc620a4SEd Maste evutil_snprintf(buf, sizeof(buf)-1, "%lu", (unsigned long)bytes);
495cbc620a4SEd Maste evhttp_add_header(output_headers, "Content-Length", buf);
496cbc620a4SEd Maste fclose(f);
497cbc620a4SEd Maste }
498cbc620a4SEd Maste
499cbc620a4SEd Maste r = evhttp_make_request(evcon, req, data_file ? EVHTTP_REQ_POST : EVHTTP_REQ_GET, uri);
500cbc620a4SEd Maste if (r != 0) {
501cbc620a4SEd Maste fprintf(stderr, "evhttp_make_request() failed\n");
502cbc620a4SEd Maste goto error;
503cbc620a4SEd Maste }
504cbc620a4SEd Maste
505cbc620a4SEd Maste event_base_dispatch(base);
506cbc620a4SEd Maste goto cleanup;
507cbc620a4SEd Maste
508cbc620a4SEd Maste error:
509cbc620a4SEd Maste ret = 1;
510cbc620a4SEd Maste cleanup:
511cbc620a4SEd Maste if (evcon)
512cbc620a4SEd Maste evhttp_connection_free(evcon);
513cbc620a4SEd Maste if (http_uri)
514cbc620a4SEd Maste evhttp_uri_free(http_uri);
5155223d1d9SCy Schubert if (base)
516cbc620a4SEd Maste event_base_free(base);
517cbc620a4SEd Maste
518cbc620a4SEd Maste if (ssl_ctx)
519cbc620a4SEd Maste SSL_CTX_free(ssl_ctx);
520cbc620a4SEd Maste if (type == HTTP && ssl)
521cbc620a4SEd Maste SSL_free(ssl);
5225223d1d9SCy Schubert #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
5235223d1d9SCy Schubert (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
524cbc620a4SEd Maste EVP_cleanup();
525cbc620a4SEd Maste ERR_free_strings();
526cbc620a4SEd Maste
5275223d1d9SCy Schubert #if OPENSSL_VERSION_NUMBER < 0x10000000L
528cbc620a4SEd Maste ERR_remove_state(0);
5295223d1d9SCy Schubert #else
5305223d1d9SCy Schubert ERR_remove_thread_state(NULL);
531cbc620a4SEd Maste #endif
5325223d1d9SCy Schubert
533cbc620a4SEd Maste CRYPTO_cleanup_all_ex_data();
534cbc620a4SEd Maste
535cbc620a4SEd Maste sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
5365223d1d9SCy Schubert #endif /* (OPENSSL_VERSION_NUMBER < 0x10100000L) || \
5375223d1d9SCy Schubert (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L) */
538cbc620a4SEd Maste
539cbc620a4SEd Maste #ifdef _WIN32
540cbc620a4SEd Maste WSACleanup();
541cbc620a4SEd Maste #endif
542cbc620a4SEd Maste
543cbc620a4SEd Maste return ret;
544cbc620a4SEd Maste }
545