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 51be46c99bSCatalin Patulea http_request_done(struct evhttp_request *req, void *ctx) 52be46c99bSCatalin Patulea { 53be46c99bSCatalin Patulea char buffer[256]; 54be46c99bSCatalin Patulea int nread; 55be46c99bSCatalin Patulea 56*a8a04565Swenyg 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 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 10424a1f25aSAzat Khuzhin err(const char *msg) 105be46c99bSCatalin Patulea { 106be46c99bSCatalin Patulea fputs(msg, stderr); 107be46c99bSCatalin Patulea } 108be46c99bSCatalin Patulea 1095754d96aSPatrick Pelletier static void 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 12113a4acdaSAzat Khuzhin #ifndef _WIN32 12264d9f161SPatrick Pelletier /* See http://archives.seul.org/libevent/users/Jan-2013/msg00039.html */ 12364d9f161SPatrick Pelletier static int cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg) 12464d9f161SPatrick Pelletier { 12564d9f161SPatrick Pelletier char cert_str[256]; 12664d9f161SPatrick Pelletier const char *host = (const char *) arg; 12764d9f161SPatrick Pelletier const char *res_str = "X509_verify_cert failed"; 12864d9f161SPatrick Pelletier HostnameValidationResult res = Error; 12964d9f161SPatrick Pelletier 13064d9f161SPatrick Pelletier /* This is the function that OpenSSL would call if we hadn't called 13164d9f161SPatrick Pelletier * SSL_CTX_set_cert_verify_callback(). Therefore, we are "wrapping" 13264d9f161SPatrick Pelletier * the default functionality, rather than replacing it. */ 13329af65ebSAlexey Ozeritsky int ok_so_far = 0; 13464d9f161SPatrick Pelletier 13529af65ebSAlexey Ozeritsky X509 *server_cert = NULL; 13629af65ebSAlexey Ozeritsky 13729af65ebSAlexey Ozeritsky if (ignore_cert) { 13829af65ebSAlexey Ozeritsky return 1; 13929af65ebSAlexey Ozeritsky } 14029af65ebSAlexey Ozeritsky 14129af65ebSAlexey Ozeritsky ok_so_far = X509_verify_cert(x509_ctx); 14229af65ebSAlexey Ozeritsky 14329af65ebSAlexey Ozeritsky server_cert = X509_STORE_CTX_get_current_cert(x509_ctx); 14464d9f161SPatrick Pelletier 14564d9f161SPatrick Pelletier if (ok_so_far) { 14664d9f161SPatrick Pelletier res = validate_hostname(host, server_cert); 14764d9f161SPatrick Pelletier 14864d9f161SPatrick Pelletier switch (res) { 14964d9f161SPatrick Pelletier case MatchFound: 15064d9f161SPatrick Pelletier res_str = "MatchFound"; 15164d9f161SPatrick Pelletier break; 15264d9f161SPatrick Pelletier case MatchNotFound: 15364d9f161SPatrick Pelletier res_str = "MatchNotFound"; 15464d9f161SPatrick Pelletier break; 15564d9f161SPatrick Pelletier case NoSANPresent: 15664d9f161SPatrick Pelletier res_str = "NoSANPresent"; 15764d9f161SPatrick Pelletier break; 15864d9f161SPatrick Pelletier case MalformedCertificate: 15964d9f161SPatrick Pelletier res_str = "MalformedCertificate"; 16064d9f161SPatrick Pelletier break; 16164d9f161SPatrick Pelletier case Error: 16264d9f161SPatrick Pelletier res_str = "Error"; 16364d9f161SPatrick Pelletier break; 16464d9f161SPatrick Pelletier default: 16564d9f161SPatrick Pelletier res_str = "WTF!"; 16664d9f161SPatrick Pelletier break; 16764d9f161SPatrick Pelletier } 16864d9f161SPatrick Pelletier } 16964d9f161SPatrick Pelletier 17064d9f161SPatrick Pelletier X509_NAME_oneline(X509_get_subject_name (server_cert), 17164d9f161SPatrick Pelletier cert_str, sizeof (cert_str)); 17264d9f161SPatrick Pelletier 17364d9f161SPatrick Pelletier if (res == MatchFound) { 17464d9f161SPatrick Pelletier printf("https server '%s' has this certificate, " 17564d9f161SPatrick Pelletier "which looks good to me:\n%s\n", 17664d9f161SPatrick Pelletier host, cert_str); 17764d9f161SPatrick Pelletier return 1; 17864d9f161SPatrick Pelletier } else { 17964d9f161SPatrick Pelletier printf("Got '%s' for hostname '%s' and certificate:\n%s\n", 18064d9f161SPatrick Pelletier res_str, host, cert_str); 18164d9f161SPatrick Pelletier return 0; 18264d9f161SPatrick Pelletier } 18364d9f161SPatrick Pelletier } 18413a4acdaSAzat Khuzhin #endif 18564d9f161SPatrick Pelletier 186be46c99bSCatalin Patulea int 187be46c99bSCatalin Patulea main(int argc, char **argv) 188be46c99bSCatalin Patulea { 189be46c99bSCatalin Patulea int r; 190c0adad8fSAzat Khuzhin struct event_base *base = NULL; 19124a1f25aSAzat Khuzhin struct evhttp_uri *http_uri = NULL; 19290786eb0SNick Mathewson const char *url = NULL, *data_file = NULL; 1935c0132f3SDavid Disseldorp const char *crt = NULL; 19490786eb0SNick Mathewson const char *scheme, *host, *path, *query; 195be46c99bSCatalin Patulea char uri[256]; 196be46c99bSCatalin Patulea int port; 197d9da8443SAzat Khuzhin int retries = 0; 1984637aa88SAzat Khuzhin int timeout = -1; 199be46c99bSCatalin Patulea 20024a1f25aSAzat Khuzhin SSL_CTX *ssl_ctx = NULL; 201f3d7ff5dSAzat Khuzhin SSL *ssl = NULL; 202be46c99bSCatalin Patulea struct bufferevent *bev; 20324a1f25aSAzat Khuzhin struct evhttp_connection *evcon = NULL; 204be46c99bSCatalin Patulea struct evhttp_request *req; 2058a90a850SNick Mathewson struct evkeyvalq *output_headers; 20629af65ebSAlexey Ozeritsky struct evbuffer *output_buffer; 207be46c99bSCatalin Patulea 208c5887f73SAlexey Ozeritsky int i; 20924a1f25aSAzat Khuzhin int ret = 0; 21024a1f25aSAzat Khuzhin enum { HTTP, HTTPS } type = HTTP; 211be46c99bSCatalin Patulea 212c5887f73SAlexey Ozeritsky for (i = 1; i < argc; i++) { 213c5887f73SAlexey Ozeritsky if (!strcmp("-url", argv[i])) { 214c5887f73SAlexey Ozeritsky if (i < argc - 1) { 215c5887f73SAlexey Ozeritsky url = argv[i + 1]; 216c5887f73SAlexey Ozeritsky } else { 217c5887f73SAlexey Ozeritsky syntax(); 21824a1f25aSAzat Khuzhin goto error; 219c5887f73SAlexey Ozeritsky } 220fdf713a0SAzat Khuzhin } else if (!strcmp("-crt", argv[i])) { 221fdf713a0SAzat Khuzhin if (i < argc - 1) { 222fdf713a0SAzat Khuzhin crt = argv[i + 1]; 223fdf713a0SAzat Khuzhin } else { 224fdf713a0SAzat Khuzhin syntax(); 225fdf713a0SAzat Khuzhin goto error; 226fdf713a0SAzat Khuzhin } 227c5887f73SAlexey Ozeritsky } else if (!strcmp("-ignore-cert", argv[i])) { 228c5887f73SAlexey Ozeritsky ignore_cert = 1; 229c5887f73SAlexey Ozeritsky } else if (!strcmp("-data", argv[i])) { 230c5887f73SAlexey Ozeritsky if (i < argc - 1) { 231c5887f73SAlexey Ozeritsky data_file = argv[i + 1]; 232c5887f73SAlexey Ozeritsky } else { 233c5887f73SAlexey Ozeritsky syntax(); 23424a1f25aSAzat Khuzhin goto error; 235c5887f73SAlexey Ozeritsky } 236d9da8443SAzat Khuzhin } else if (!strcmp("-retries", argv[i])) { 237d9da8443SAzat Khuzhin if (i < argc - 1) { 238d9da8443SAzat Khuzhin retries = atoi(argv[i + 1]); 239d9da8443SAzat Khuzhin } else { 240d9da8443SAzat Khuzhin syntax(); 24124a1f25aSAzat Khuzhin goto error; 242d9da8443SAzat Khuzhin } 2434637aa88SAzat Khuzhin } else if (!strcmp("-timeout", argv[i])) { 2444637aa88SAzat Khuzhin if (i < argc - 1) { 2454637aa88SAzat Khuzhin timeout = atoi(argv[i + 1]); 2464637aa88SAzat Khuzhin } else { 2474637aa88SAzat Khuzhin syntax(); 2484637aa88SAzat Khuzhin goto error; 2494637aa88SAzat Khuzhin } 250c5887f73SAlexey Ozeritsky } else if (!strcmp("-help", argv[i])) { 251c5887f73SAlexey Ozeritsky syntax(); 25224a1f25aSAzat Khuzhin goto error; 253c5887f73SAlexey Ozeritsky } 254c5887f73SAlexey Ozeritsky } 255c5887f73SAlexey Ozeritsky 256c5887f73SAlexey Ozeritsky if (!url) { 257c5887f73SAlexey Ozeritsky syntax(); 25824a1f25aSAzat Khuzhin goto error; 259c5887f73SAlexey Ozeritsky } 260c5887f73SAlexey Ozeritsky 2614e143958SJoakim Söderberg #ifdef _WIN32 26219222e52SJoakim Soderberg { 26319222e52SJoakim Soderberg WORD wVersionRequested; 26419222e52SJoakim Soderberg WSADATA wsaData; 26519222e52SJoakim Soderberg int err; 26619222e52SJoakim Soderberg 26719222e52SJoakim Soderberg wVersionRequested = MAKEWORD(2, 2); 26819222e52SJoakim Soderberg 26919222e52SJoakim Soderberg err = WSAStartup(wVersionRequested, &wsaData); 27019222e52SJoakim Soderberg if (err != 0) { 27119222e52SJoakim Soderberg printf("WSAStartup failed with error: %d\n", err); 27224a1f25aSAzat Khuzhin goto error; 27319222e52SJoakim Soderberg } 27419222e52SJoakim Soderberg } 2754e143958SJoakim Söderberg #endif // _WIN32 27619222e52SJoakim Soderberg 277be46c99bSCatalin Patulea http_uri = evhttp_uri_parse(url); 278be46c99bSCatalin Patulea if (http_uri == NULL) { 27924a1f25aSAzat Khuzhin err("malformed url"); 28024a1f25aSAzat Khuzhin goto error; 281be46c99bSCatalin Patulea } 282be46c99bSCatalin Patulea 283be46c99bSCatalin Patulea scheme = evhttp_uri_get_scheme(http_uri); 284be46c99bSCatalin Patulea if (scheme == NULL || (strcasecmp(scheme, "https") != 0 && 285be46c99bSCatalin Patulea strcasecmp(scheme, "http") != 0)) { 28624a1f25aSAzat Khuzhin err("url must be http or https"); 28724a1f25aSAzat Khuzhin goto error; 288be46c99bSCatalin Patulea } 289be46c99bSCatalin Patulea 290be46c99bSCatalin Patulea host = evhttp_uri_get_host(http_uri); 291be46c99bSCatalin Patulea if (host == NULL) { 29224a1f25aSAzat Khuzhin err("url must have a host"); 29324a1f25aSAzat Khuzhin goto error; 294be46c99bSCatalin Patulea } 295be46c99bSCatalin Patulea 296be46c99bSCatalin Patulea port = evhttp_uri_get_port(http_uri); 297be46c99bSCatalin Patulea if (port == -1) { 298be46c99bSCatalin Patulea port = (strcasecmp(scheme, "http") == 0) ? 80 : 443; 299be46c99bSCatalin Patulea } 300be46c99bSCatalin Patulea 301be46c99bSCatalin Patulea path = evhttp_uri_get_path(http_uri); 30229a04825SAndrey Skriabin if (strlen(path) == 0) { 303be46c99bSCatalin Patulea path = "/"; 304be46c99bSCatalin Patulea } 305be46c99bSCatalin Patulea 306be46c99bSCatalin Patulea query = evhttp_uri_get_query(http_uri); 307be46c99bSCatalin Patulea if (query == NULL) { 308be46c99bSCatalin Patulea snprintf(uri, sizeof(uri) - 1, "%s", path); 309be46c99bSCatalin Patulea } else { 310be46c99bSCatalin Patulea snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query); 311be46c99bSCatalin Patulea } 312be46c99bSCatalin Patulea uri[sizeof(uri) - 1] = '\0'; 313be46c99bSCatalin Patulea 314894ca48aSBernard Spil #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \ 315894ca48aSBernard Spil (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L) 316be46c99bSCatalin Patulea // Initialize OpenSSL 317be46c99bSCatalin Patulea SSL_library_init(); 318be46c99bSCatalin Patulea ERR_load_crypto_strings(); 319be46c99bSCatalin Patulea SSL_load_error_strings(); 320be46c99bSCatalin Patulea OpenSSL_add_all_algorithms(); 3213e9e0a0dSKurt Roeckx #endif 3225754d96aSPatrick Pelletier 3235754d96aSPatrick Pelletier /* This isn't strictly necessary... OpenSSL performs RAND_poll 3245754d96aSPatrick Pelletier * automatically on first use of random number generator. */ 325be46c99bSCatalin Patulea r = RAND_poll(); 326be46c99bSCatalin Patulea if (r == 0) { 32724a1f25aSAzat Khuzhin err_openssl("RAND_poll"); 32824a1f25aSAzat Khuzhin goto error; 329be46c99bSCatalin Patulea } 3305754d96aSPatrick Pelletier 3315754d96aSPatrick Pelletier /* Create a new OpenSSL context */ 332be46c99bSCatalin Patulea ssl_ctx = SSL_CTX_new(SSLv23_method()); 33324a1f25aSAzat Khuzhin if (!ssl_ctx) { 33424a1f25aSAzat Khuzhin err_openssl("SSL_CTX_new"); 33524a1f25aSAzat Khuzhin goto error; 33624a1f25aSAzat Khuzhin } 337be46c99bSCatalin Patulea 3384e143958SJoakim Söderberg #ifndef _WIN32 33919222e52SJoakim Soderberg /* TODO: Add certificate loading on Windows as well */ 34019222e52SJoakim Soderberg 3415c0132f3SDavid Disseldorp if (crt == NULL) { 3425c0132f3SDavid Disseldorp X509_STORE *store; 3435c0132f3SDavid Disseldorp /* Attempt to use the system's trusted root certificates. */ 3445c0132f3SDavid Disseldorp store = SSL_CTX_get_cert_store(ssl_ctx); 3455c0132f3SDavid Disseldorp if (X509_STORE_set_default_paths(store) != 1) { 3465c0132f3SDavid Disseldorp err_openssl("X509_STORE_set_default_paths"); 3475c0132f3SDavid Disseldorp goto error; 3485c0132f3SDavid Disseldorp } 3495c0132f3SDavid Disseldorp } else { 3505c0132f3SDavid Disseldorp if (SSL_CTX_load_verify_locations(ssl_ctx, crt, NULL) != 1) { 35124a1f25aSAzat Khuzhin err_openssl("SSL_CTX_load_verify_locations"); 35224a1f25aSAzat Khuzhin goto error; 35324a1f25aSAzat Khuzhin } 3545c0132f3SDavid Disseldorp } 35564d9f161SPatrick Pelletier /* Ask OpenSSL to verify the server certificate. Note that this 35664d9f161SPatrick Pelletier * does NOT include verifying that the hostname is correct. 35764d9f161SPatrick Pelletier * So, by itself, this means anyone with any legitimate 35864d9f161SPatrick Pelletier * CA-issued certificate for any website, can impersonate any 35964d9f161SPatrick Pelletier * other website in the world. This is not good. See "The 36064d9f161SPatrick Pelletier * Most Dangerous Code in the World" article at 36164d9f161SPatrick Pelletier * https://crypto.stanford.edu/~dabo/pubs/abstracts/ssl-client-bugs.html 36264d9f161SPatrick Pelletier */ 363aacd674cSPatrick Pelletier SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); 36464d9f161SPatrick Pelletier /* This is how we solve the problem mentioned in the previous 36564d9f161SPatrick Pelletier * comment. We "wrap" OpenSSL's validation routine in our 36664d9f161SPatrick Pelletier * own routine, which also validates the hostname by calling 36764d9f161SPatrick Pelletier * the code provided by iSECPartners. Note that even though 36864d9f161SPatrick Pelletier * the "Everything You've Always Wanted to Know About 36964d9f161SPatrick Pelletier * Certificate Validation With OpenSSL (But Were Afraid to 37064d9f161SPatrick Pelletier * Ask)" paper from iSECPartners says very explicitly not to 37164d9f161SPatrick Pelletier * call SSL_CTX_set_cert_verify_callback (at the bottom of 37264d9f161SPatrick Pelletier * page 2), what we're doing here is safe because our 37364d9f161SPatrick Pelletier * cert_verify_callback() calls X509_verify_cert(), which is 37464d9f161SPatrick Pelletier * OpenSSL's built-in routine which would have been called if 37564d9f161SPatrick Pelletier * we hadn't set the callback. Therefore, we're just 37664d9f161SPatrick Pelletier * "wrapping" OpenSSL's routine, not replacing it. */ 37764d9f161SPatrick Pelletier SSL_CTX_set_cert_verify_callback(ssl_ctx, cert_verify_callback, 37864d9f161SPatrick Pelletier (void *) host); 37913a4acdaSAzat Khuzhin #else // _WIN32 38013a4acdaSAzat Khuzhin (void)crt; 38113a4acdaSAzat Khuzhin #endif // _WIN32 382aacd674cSPatrick Pelletier 383be46c99bSCatalin Patulea // Create event base 384be46c99bSCatalin Patulea base = event_base_new(); 385be46c99bSCatalin Patulea if (!base) { 386be46c99bSCatalin Patulea perror("event_base_new()"); 38724a1f25aSAzat Khuzhin goto error; 388be46c99bSCatalin Patulea } 389be46c99bSCatalin Patulea 390be46c99bSCatalin Patulea // Create OpenSSL bufferevent and stack evhttp on top of it 391be46c99bSCatalin Patulea ssl = SSL_new(ssl_ctx); 392be46c99bSCatalin Patulea if (ssl == NULL) { 39324a1f25aSAzat Khuzhin err_openssl("SSL_new()"); 39424a1f25aSAzat Khuzhin goto error; 395be46c99bSCatalin Patulea } 396be46c99bSCatalin Patulea 3975c7282f7SJoakim Soderberg #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME 398d1976f8eSNick Mathewson // Set hostname for SNI extension 399d1976f8eSNick Mathewson SSL_set_tlsext_host_name(ssl, host); 4005c7282f7SJoakim Soderberg #endif 401d1976f8eSNick Mathewson 402be46c99bSCatalin Patulea if (strcasecmp(scheme, "http") == 0) { 403be46c99bSCatalin Patulea bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); 404be46c99bSCatalin Patulea } else { 40524a1f25aSAzat Khuzhin type = HTTPS; 406be46c99bSCatalin Patulea bev = bufferevent_openssl_socket_new(base, -1, ssl, 407be46c99bSCatalin Patulea BUFFEREVENT_SSL_CONNECTING, 408be46c99bSCatalin Patulea BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); 409be46c99bSCatalin Patulea } 410be46c99bSCatalin Patulea 411be46c99bSCatalin Patulea if (bev == NULL) { 412be46c99bSCatalin Patulea fprintf(stderr, "bufferevent_openssl_socket_new() failed\n"); 41324a1f25aSAzat Khuzhin goto error; 414be46c99bSCatalin Patulea } 415be46c99bSCatalin Patulea 416be46c99bSCatalin Patulea bufferevent_openssl_set_allow_dirty_shutdown(bev, 1); 417be46c99bSCatalin Patulea 418be46c99bSCatalin Patulea // For simplicity, we let DNS resolution block. Everything else should be 419be46c99bSCatalin Patulea // asynchronous though. 420be46c99bSCatalin Patulea evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev, 421be46c99bSCatalin Patulea host, port); 422be46c99bSCatalin Patulea if (evcon == NULL) { 423be46c99bSCatalin Patulea fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n"); 42424a1f25aSAzat Khuzhin goto error; 425be46c99bSCatalin Patulea } 426be46c99bSCatalin Patulea 427d9da8443SAzat Khuzhin if (retries > 0) { 428d9da8443SAzat Khuzhin evhttp_connection_set_retries(evcon, retries); 429d9da8443SAzat Khuzhin } 4304637aa88SAzat Khuzhin if (timeout >= 0) { 4314637aa88SAzat Khuzhin evhttp_connection_set_timeout(evcon, timeout); 4324637aa88SAzat Khuzhin } 433d9da8443SAzat Khuzhin 434be46c99bSCatalin Patulea // Fire off the request 4355754d96aSPatrick Pelletier req = evhttp_request_new(http_request_done, bev); 436be46c99bSCatalin Patulea if (req == NULL) { 437be46c99bSCatalin Patulea fprintf(stderr, "evhttp_request_new() failed\n"); 43824a1f25aSAzat Khuzhin goto error; 439be46c99bSCatalin Patulea } 440be46c99bSCatalin Patulea 4418a90a850SNick Mathewson output_headers = evhttp_request_get_output_headers(req); 4428a90a850SNick Mathewson evhttp_add_header(output_headers, "Host", host); 4438a90a850SNick Mathewson evhttp_add_header(output_headers, "Connection", "close"); 444be46c99bSCatalin Patulea 445c5887f73SAlexey Ozeritsky if (data_file) { 44690786eb0SNick Mathewson /* NOTE: In production code, you'd probably want to use 44790786eb0SNick Mathewson * evbuffer_add_file() or evbuffer_add_file_segment(), to 44890786eb0SNick Mathewson * avoid needless copying. */ 449c5887f73SAlexey Ozeritsky FILE * f = fopen(data_file, "rb"); 450c5887f73SAlexey Ozeritsky char buf[1024]; 451d7be7887SJoakim Soderberg size_t s; 452c5887f73SAlexey Ozeritsky size_t bytes = 0; 453c5887f73SAlexey Ozeritsky 454c5887f73SAlexey Ozeritsky if (!f) { 455c5887f73SAlexey Ozeritsky syntax(); 45624a1f25aSAzat Khuzhin goto error; 457c5887f73SAlexey Ozeritsky } 458c5887f73SAlexey Ozeritsky 45929af65ebSAlexey Ozeritsky output_buffer = evhttp_request_get_output_buffer(req); 460c5887f73SAlexey Ozeritsky while ((s = fread(buf, 1, sizeof(buf), f)) > 0) { 46129af65ebSAlexey Ozeritsky evbuffer_add(output_buffer, buf, s); 462c5887f73SAlexey Ozeritsky bytes += s; 463c5887f73SAlexey Ozeritsky } 464462e6b60SNick Mathewson evutil_snprintf(buf, sizeof(buf)-1, "%lu", (unsigned long)bytes); 46529af65ebSAlexey Ozeritsky evhttp_add_header(output_headers, "Content-Length", buf); 466c5887f73SAlexey Ozeritsky fclose(f); 467c5887f73SAlexey Ozeritsky } 468c5887f73SAlexey Ozeritsky 469c5887f73SAlexey Ozeritsky r = evhttp_make_request(evcon, req, data_file ? EVHTTP_REQ_POST : EVHTTP_REQ_GET, uri); 470be46c99bSCatalin Patulea if (r != 0) { 471be46c99bSCatalin Patulea fprintf(stderr, "evhttp_make_request() failed\n"); 47224a1f25aSAzat Khuzhin goto error; 473be46c99bSCatalin Patulea } 474be46c99bSCatalin Patulea 475be46c99bSCatalin Patulea event_base_dispatch(base); 47624a1f25aSAzat Khuzhin goto cleanup; 477be46c99bSCatalin Patulea 47824a1f25aSAzat Khuzhin error: 47924a1f25aSAzat Khuzhin ret = 1; 48024a1f25aSAzat Khuzhin cleanup: 48124a1f25aSAzat Khuzhin if (evcon) 482be46c99bSCatalin Patulea evhttp_connection_free(evcon); 48324a1f25aSAzat Khuzhin if (http_uri) 48424a1f25aSAzat Khuzhin evhttp_uri_free(http_uri); 485c0adad8fSAzat Khuzhin if (base) 486be46c99bSCatalin Patulea event_base_free(base); 487be46c99bSCatalin Patulea 48824a1f25aSAzat Khuzhin if (ssl_ctx) 48924a1f25aSAzat Khuzhin SSL_CTX_free(ssl_ctx); 490f3d7ff5dSAzat Khuzhin if (type == HTTP && ssl) 49124a1f25aSAzat Khuzhin SSL_free(ssl); 492894ca48aSBernard Spil #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || \ 493894ca48aSBernard Spil (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L) 49424a1f25aSAzat Khuzhin EVP_cleanup(); 49524a1f25aSAzat Khuzhin ERR_free_strings(); 49624a1f25aSAzat Khuzhin 497e7bd9e03SAzat Khuzhin #if OPENSSL_VERSION_NUMBER < 0x10000000L 498c4e9d9bdSAzat Khuzhin ERR_remove_state(0); 499e7bd9e03SAzat Khuzhin #else 500e7bd9e03SAzat Khuzhin ERR_remove_thread_state(NULL); 501c4e9d9bdSAzat Khuzhin #endif 502e7bd9e03SAzat Khuzhin 50324a1f25aSAzat Khuzhin CRYPTO_cleanup_all_ex_data(); 50424a1f25aSAzat Khuzhin 50524a1f25aSAzat Khuzhin sk_SSL_COMP_free(SSL_COMP_get_compression_methods()); 506894ca48aSBernard Spil #endif /* (OPENSSL_VERSION_NUMBER < 0x10100000L) || \ 507894ca48aSBernard Spil (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L) */ 50824a1f25aSAzat Khuzhin 5094e143958SJoakim Söderberg #ifdef _WIN32 51019222e52SJoakim Soderberg WSACleanup(); 51119222e52SJoakim Soderberg #endif 51219222e52SJoakim Soderberg 51324a1f25aSAzat Khuzhin return ret; 514be46c99bSCatalin Patulea } 515