1*be46c99bSCatalin Patulea /* 2*be46c99bSCatalin Patulea This is an example of how to hook up evhttp with bufferevent_ssl 3*be46c99bSCatalin Patulea 4*be46c99bSCatalin Patulea It just GETs an https URL given on the command-line and prints the response 5*be46c99bSCatalin Patulea body to stdout. 6*be46c99bSCatalin Patulea 7*be46c99bSCatalin Patulea Actually, it also accepts plain http URLs to make it easy to compare http vs 8*be46c99bSCatalin Patulea https code paths. 9*be46c99bSCatalin Patulea 10*be46c99bSCatalin Patulea Loosely based on le-proxy.c. 11*be46c99bSCatalin Patulea */ 12*be46c99bSCatalin Patulea 13*be46c99bSCatalin Patulea #include <stdio.h> 14*be46c99bSCatalin Patulea #include <assert.h> 15*be46c99bSCatalin Patulea #include <stdlib.h> 16*be46c99bSCatalin Patulea #include <string.h> 17*be46c99bSCatalin Patulea #include <errno.h> 18*be46c99bSCatalin Patulea 19*be46c99bSCatalin Patulea #ifdef WIN32 20*be46c99bSCatalin Patulea #include <winsock2.h> 21*be46c99bSCatalin Patulea #include <ws2tcpip.h> 22*be46c99bSCatalin Patulea #else 23*be46c99bSCatalin Patulea #include <sys/socket.h> 24*be46c99bSCatalin Patulea #include <netinet/in.h> 25*be46c99bSCatalin Patulea #endif 26*be46c99bSCatalin Patulea 27*be46c99bSCatalin Patulea #include <event2/bufferevent_ssl.h> 28*be46c99bSCatalin Patulea #include <event2/bufferevent.h> 29*be46c99bSCatalin Patulea #include <event2/buffer.h> 30*be46c99bSCatalin Patulea #include <event2/listener.h> 31*be46c99bSCatalin Patulea #include <event2/util.h> 32*be46c99bSCatalin Patulea #include <event2/http.h> 33*be46c99bSCatalin Patulea #include <event2/http_struct.h> 34*be46c99bSCatalin Patulea 35*be46c99bSCatalin Patulea #include <openssl/ssl.h> 36*be46c99bSCatalin Patulea #include <openssl/err.h> 37*be46c99bSCatalin Patulea #include <openssl/rand.h> 38*be46c99bSCatalin Patulea 39*be46c99bSCatalin Patulea static struct event_base *base; 40*be46c99bSCatalin Patulea 41*be46c99bSCatalin Patulea static void 42*be46c99bSCatalin Patulea http_request_done(struct evhttp_request *req, void *ctx) 43*be46c99bSCatalin Patulea { 44*be46c99bSCatalin Patulea char buffer[256]; 45*be46c99bSCatalin Patulea int nread; 46*be46c99bSCatalin Patulea 47*be46c99bSCatalin Patulea if (req == NULL) { 48*be46c99bSCatalin Patulea fprintf(stderr, "some request failed - no idea which one though!\n"); 49*be46c99bSCatalin Patulea return; 50*be46c99bSCatalin Patulea } 51*be46c99bSCatalin Patulea 52*be46c99bSCatalin Patulea fprintf(stderr, "Response line: %d %s\n", 53*be46c99bSCatalin Patulea req->response_code, req->response_code_line); 54*be46c99bSCatalin Patulea 55*be46c99bSCatalin Patulea while ((nread = evbuffer_remove(req->input_buffer, buffer, sizeof(buffer))) 56*be46c99bSCatalin Patulea > 0) { 57*be46c99bSCatalin Patulea fwrite("> ", 2, 1, stdout); 58*be46c99bSCatalin Patulea fwrite(buffer, nread, 1, stdout); 59*be46c99bSCatalin Patulea fwrite("\n", 1, 1, stdout); 60*be46c99bSCatalin Patulea } 61*be46c99bSCatalin Patulea } 62*be46c99bSCatalin Patulea 63*be46c99bSCatalin Patulea static void 64*be46c99bSCatalin Patulea syntax(void) 65*be46c99bSCatalin Patulea { 66*be46c99bSCatalin Patulea fputs("Syntax:\n", stderr); 67*be46c99bSCatalin Patulea fputs(" https-client <https-url>\n", stderr); 68*be46c99bSCatalin Patulea fputs("Example:\n", stderr); 69*be46c99bSCatalin Patulea fputs(" https-client https://ip.appspot.com/\n", stderr); 70*be46c99bSCatalin Patulea 71*be46c99bSCatalin Patulea exit(1); 72*be46c99bSCatalin Patulea } 73*be46c99bSCatalin Patulea 74*be46c99bSCatalin Patulea static void 75*be46c99bSCatalin Patulea die(const char *msg) 76*be46c99bSCatalin Patulea { 77*be46c99bSCatalin Patulea fputs(msg, stderr); 78*be46c99bSCatalin Patulea exit(1); 79*be46c99bSCatalin Patulea } 80*be46c99bSCatalin Patulea 81*be46c99bSCatalin Patulea int 82*be46c99bSCatalin Patulea main(int argc, char **argv) 83*be46c99bSCatalin Patulea { 84*be46c99bSCatalin Patulea int r; 85*be46c99bSCatalin Patulea 86*be46c99bSCatalin Patulea struct evhttp_uri *http_uri; 87*be46c99bSCatalin Patulea const char *url, *scheme, *host, *path, *query; 88*be46c99bSCatalin Patulea char uri[256]; 89*be46c99bSCatalin Patulea int port; 90*be46c99bSCatalin Patulea 91*be46c99bSCatalin Patulea SSL_CTX *ssl_ctx; 92*be46c99bSCatalin Patulea SSL *ssl; 93*be46c99bSCatalin Patulea struct bufferevent *bev; 94*be46c99bSCatalin Patulea struct evhttp_connection *evcon; 95*be46c99bSCatalin Patulea struct evhttp_request *req; 96*be46c99bSCatalin Patulea 97*be46c99bSCatalin Patulea if (argc != 2) 98*be46c99bSCatalin Patulea syntax(); 99*be46c99bSCatalin Patulea 100*be46c99bSCatalin Patulea url = argv[1]; 101*be46c99bSCatalin Patulea http_uri = evhttp_uri_parse(url); 102*be46c99bSCatalin Patulea if (http_uri == NULL) { 103*be46c99bSCatalin Patulea die("malformed url"); 104*be46c99bSCatalin Patulea } 105*be46c99bSCatalin Patulea 106*be46c99bSCatalin Patulea scheme = evhttp_uri_get_scheme(http_uri); 107*be46c99bSCatalin Patulea if (scheme == NULL || (strcasecmp(scheme, "https") != 0 && 108*be46c99bSCatalin Patulea strcasecmp(scheme, "http") != 0)) { 109*be46c99bSCatalin Patulea die("url must be http or https"); 110*be46c99bSCatalin Patulea } 111*be46c99bSCatalin Patulea 112*be46c99bSCatalin Patulea host = evhttp_uri_get_host(http_uri); 113*be46c99bSCatalin Patulea if (host == NULL) { 114*be46c99bSCatalin Patulea die("url must have a host"); 115*be46c99bSCatalin Patulea } 116*be46c99bSCatalin Patulea 117*be46c99bSCatalin Patulea port = evhttp_uri_get_port(http_uri); 118*be46c99bSCatalin Patulea if (port == -1) { 119*be46c99bSCatalin Patulea port = (strcasecmp(scheme, "http") == 0) ? 80 : 443; 120*be46c99bSCatalin Patulea } 121*be46c99bSCatalin Patulea 122*be46c99bSCatalin Patulea path = evhttp_uri_get_path(http_uri); 123*be46c99bSCatalin Patulea if (path == NULL) { 124*be46c99bSCatalin Patulea path = "/"; 125*be46c99bSCatalin Patulea } 126*be46c99bSCatalin Patulea 127*be46c99bSCatalin Patulea query = evhttp_uri_get_query(http_uri); 128*be46c99bSCatalin Patulea if (query == NULL) { 129*be46c99bSCatalin Patulea snprintf(uri, sizeof(uri) - 1, "%s", path); 130*be46c99bSCatalin Patulea } else { 131*be46c99bSCatalin Patulea snprintf(uri, sizeof(uri) - 1, "%s?%s", path, query); 132*be46c99bSCatalin Patulea } 133*be46c99bSCatalin Patulea uri[sizeof(uri) - 1] = '\0'; 134*be46c99bSCatalin Patulea 135*be46c99bSCatalin Patulea // Initialize OpenSSL 136*be46c99bSCatalin Patulea SSL_library_init(); 137*be46c99bSCatalin Patulea ERR_load_crypto_strings(); 138*be46c99bSCatalin Patulea SSL_load_error_strings(); 139*be46c99bSCatalin Patulea OpenSSL_add_all_algorithms(); 140*be46c99bSCatalin Patulea r = RAND_poll(); 141*be46c99bSCatalin Patulea if (r == 0) { 142*be46c99bSCatalin Patulea fprintf(stderr, "RAND_poll() failed.\n"); 143*be46c99bSCatalin Patulea return 1; 144*be46c99bSCatalin Patulea } 145*be46c99bSCatalin Patulea ssl_ctx = SSL_CTX_new(SSLv23_method()); 146*be46c99bSCatalin Patulea 147*be46c99bSCatalin Patulea // Create event base 148*be46c99bSCatalin Patulea base = event_base_new(); 149*be46c99bSCatalin Patulea if (!base) { 150*be46c99bSCatalin Patulea perror("event_base_new()"); 151*be46c99bSCatalin Patulea return 1; 152*be46c99bSCatalin Patulea } 153*be46c99bSCatalin Patulea 154*be46c99bSCatalin Patulea // Create OpenSSL bufferevent and stack evhttp on top of it 155*be46c99bSCatalin Patulea ssl = SSL_new(ssl_ctx); 156*be46c99bSCatalin Patulea if (ssl == NULL) { 157*be46c99bSCatalin Patulea fprintf(stderr, "SSL_new() failed\n"); 158*be46c99bSCatalin Patulea return 1; 159*be46c99bSCatalin Patulea } 160*be46c99bSCatalin Patulea 161*be46c99bSCatalin Patulea if (strcasecmp(scheme, "http") == 0) { 162*be46c99bSCatalin Patulea bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); 163*be46c99bSCatalin Patulea } else { 164*be46c99bSCatalin Patulea bev = bufferevent_openssl_socket_new(base, -1, ssl, 165*be46c99bSCatalin Patulea BUFFEREVENT_SSL_CONNECTING, 166*be46c99bSCatalin Patulea BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); 167*be46c99bSCatalin Patulea } 168*be46c99bSCatalin Patulea 169*be46c99bSCatalin Patulea if (bev == NULL) { 170*be46c99bSCatalin Patulea fprintf(stderr, "bufferevent_openssl_socket_new() failed\n"); 171*be46c99bSCatalin Patulea return 1; 172*be46c99bSCatalin Patulea } 173*be46c99bSCatalin Patulea 174*be46c99bSCatalin Patulea bufferevent_openssl_set_allow_dirty_shutdown(bev, 1); 175*be46c99bSCatalin Patulea 176*be46c99bSCatalin Patulea // For simplicity, we let DNS resolution block. Everything else should be 177*be46c99bSCatalin Patulea // asynchronous though. 178*be46c99bSCatalin Patulea evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev, 179*be46c99bSCatalin Patulea host, port); 180*be46c99bSCatalin Patulea if (evcon == NULL) { 181*be46c99bSCatalin Patulea fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n"); 182*be46c99bSCatalin Patulea return 1; 183*be46c99bSCatalin Patulea } 184*be46c99bSCatalin Patulea 185*be46c99bSCatalin Patulea // Fire off the request 186*be46c99bSCatalin Patulea req = evhttp_request_new(http_request_done, NULL); 187*be46c99bSCatalin Patulea if (req == NULL) { 188*be46c99bSCatalin Patulea fprintf(stderr, "evhttp_request_new() failed\n"); 189*be46c99bSCatalin Patulea return 1; 190*be46c99bSCatalin Patulea } 191*be46c99bSCatalin Patulea 192*be46c99bSCatalin Patulea evhttp_add_header(req->output_headers, "Host", host); 193*be46c99bSCatalin Patulea evhttp_add_header(req->output_headers, "Connection", "close"); 194*be46c99bSCatalin Patulea 195*be46c99bSCatalin Patulea r = evhttp_make_request(evcon, req, EVHTTP_REQ_GET, uri); 196*be46c99bSCatalin Patulea if (r != 0) { 197*be46c99bSCatalin Patulea fprintf(stderr, "evhttp_make_request() failed\n"); 198*be46c99bSCatalin Patulea return 1; 199*be46c99bSCatalin Patulea } 200*be46c99bSCatalin Patulea 201*be46c99bSCatalin Patulea event_base_dispatch(base); 202*be46c99bSCatalin Patulea 203*be46c99bSCatalin Patulea evhttp_connection_free(evcon); 204*be46c99bSCatalin Patulea event_base_free(base); 205*be46c99bSCatalin Patulea 206*be46c99bSCatalin Patulea return 0; 207*be46c99bSCatalin Patulea } 208