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) { 48*5754d96aSPatrick Pelletier /* If req is NULL, it means an error occurred, but 49*5754d96aSPatrick Pelletier * sadly we are mostly left guessing what the error 50*5754d96aSPatrick Pelletier * might have been. We'll do our best... */ 51*5754d96aSPatrick Pelletier struct bufferevent *bev = (struct bufferevent *) ctx; 52*5754d96aSPatrick Pelletier unsigned long oslerr; 53*5754d96aSPatrick Pelletier int printed_err = 0; 54*5754d96aSPatrick Pelletier int errcode = EVUTIL_SOCKET_ERROR(); 55be46c99bSCatalin Patulea fprintf(stderr, "some request failed - no idea which one though!\n"); 56*5754d96aSPatrick Pelletier /* Print out the OpenSSL error queue that libevent 57*5754d96aSPatrick Pelletier * squirreled away for us, if any. */ 58*5754d96aSPatrick Pelletier while ((oslerr = bufferevent_get_openssl_error(bev))) { 59*5754d96aSPatrick Pelletier ERR_error_string_n(oslerr, buffer, sizeof(buffer)); 60*5754d96aSPatrick Pelletier fprintf(stderr, "%s\n", buffer); 61*5754d96aSPatrick Pelletier printed_err = 1; 62*5754d96aSPatrick Pelletier } 63*5754d96aSPatrick Pelletier /* If the OpenSSL error queue was empty, maybe it was a 64*5754d96aSPatrick Pelletier * socket error; let's try printing that. */ 65*5754d96aSPatrick Pelletier if (! printed_err) 66*5754d96aSPatrick Pelletier fprintf(stderr, "socket error = %s (%d)\n", 67*5754d96aSPatrick Pelletier evutil_socket_error_to_string(errcode), 68*5754d96aSPatrick 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 101*5754d96aSPatrick Pelletier static void 102*5754d96aSPatrick Pelletier die_openssl(const char *func) 103*5754d96aSPatrick Pelletier { 104*5754d96aSPatrick Pelletier fprintf (stderr, "%s failed:\n", func); 105*5754d96aSPatrick Pelletier 106*5754d96aSPatrick Pelletier /* This is the OpenSSL function that prints the contents of the 107*5754d96aSPatrick Pelletier * error stack to the specified file handle. */ 108*5754d96aSPatrick Pelletier ERR_print_errors_fp (stderr); 109*5754d96aSPatrick Pelletier 110*5754d96aSPatrick Pelletier exit(1); 111*5754d96aSPatrick Pelletier } 112*5754d96aSPatrick 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(); 172*5754d96aSPatrick Pelletier 173*5754d96aSPatrick Pelletier /* This isn't strictly necessary... OpenSSL performs RAND_poll 174*5754d96aSPatrick Pelletier * automatically on first use of random number generator. */ 175be46c99bSCatalin Patulea r = RAND_poll(); 176be46c99bSCatalin Patulea if (r == 0) { 177*5754d96aSPatrick Pelletier die_openssl("RAND_poll"); 178be46c99bSCatalin Patulea } 179*5754d96aSPatrick Pelletier 180*5754d96aSPatrick Pelletier /* Create a new OpenSSL context */ 181be46c99bSCatalin Patulea ssl_ctx = SSL_CTX_new(SSLv23_method()); 182*5754d96aSPatrick Pelletier if (!ssl_ctx) 183*5754d96aSPatrick Pelletier die_openssl("SSL_CTX_new"); 184be46c99bSCatalin Patulea 185be46c99bSCatalin Patulea // Create event base 186be46c99bSCatalin Patulea base = event_base_new(); 187be46c99bSCatalin Patulea if (!base) { 188be46c99bSCatalin Patulea perror("event_base_new()"); 189be46c99bSCatalin Patulea return 1; 190be46c99bSCatalin Patulea } 191be46c99bSCatalin Patulea 192be46c99bSCatalin Patulea // Create OpenSSL bufferevent and stack evhttp on top of it 193be46c99bSCatalin Patulea ssl = SSL_new(ssl_ctx); 194be46c99bSCatalin Patulea if (ssl == NULL) { 195*5754d96aSPatrick Pelletier die_openssl("SSL_new()"); 196be46c99bSCatalin Patulea } 197be46c99bSCatalin Patulea 198be46c99bSCatalin Patulea if (strcasecmp(scheme, "http") == 0) { 199be46c99bSCatalin Patulea bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); 200be46c99bSCatalin Patulea } else { 201be46c99bSCatalin Patulea bev = bufferevent_openssl_socket_new(base, -1, ssl, 202be46c99bSCatalin Patulea BUFFEREVENT_SSL_CONNECTING, 203be46c99bSCatalin Patulea BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); 204be46c99bSCatalin Patulea } 205be46c99bSCatalin Patulea 206be46c99bSCatalin Patulea if (bev == NULL) { 207be46c99bSCatalin Patulea fprintf(stderr, "bufferevent_openssl_socket_new() failed\n"); 208be46c99bSCatalin Patulea return 1; 209be46c99bSCatalin Patulea } 210be46c99bSCatalin Patulea 211be46c99bSCatalin Patulea bufferevent_openssl_set_allow_dirty_shutdown(bev, 1); 212be46c99bSCatalin Patulea 213be46c99bSCatalin Patulea // For simplicity, we let DNS resolution block. Everything else should be 214be46c99bSCatalin Patulea // asynchronous though. 215be46c99bSCatalin Patulea evcon = evhttp_connection_base_bufferevent_new(base, NULL, bev, 216be46c99bSCatalin Patulea host, port); 217be46c99bSCatalin Patulea if (evcon == NULL) { 218be46c99bSCatalin Patulea fprintf(stderr, "evhttp_connection_base_bufferevent_new() failed\n"); 219be46c99bSCatalin Patulea return 1; 220be46c99bSCatalin Patulea } 221be46c99bSCatalin Patulea 222be46c99bSCatalin Patulea // Fire off the request 223*5754d96aSPatrick Pelletier req = evhttp_request_new(http_request_done, bev); 224be46c99bSCatalin Patulea if (req == NULL) { 225be46c99bSCatalin Patulea fprintf(stderr, "evhttp_request_new() failed\n"); 226be46c99bSCatalin Patulea return 1; 227be46c99bSCatalin Patulea } 228be46c99bSCatalin Patulea 229be46c99bSCatalin Patulea evhttp_add_header(req->output_headers, "Host", host); 230be46c99bSCatalin Patulea evhttp_add_header(req->output_headers, "Connection", "close"); 231be46c99bSCatalin Patulea 232be46c99bSCatalin Patulea r = evhttp_make_request(evcon, req, EVHTTP_REQ_GET, uri); 233be46c99bSCatalin Patulea if (r != 0) { 234be46c99bSCatalin Patulea fprintf(stderr, "evhttp_make_request() failed\n"); 235be46c99bSCatalin Patulea return 1; 236be46c99bSCatalin Patulea } 237be46c99bSCatalin Patulea 238be46c99bSCatalin Patulea event_base_dispatch(base); 239be46c99bSCatalin Patulea 240be46c99bSCatalin Patulea evhttp_connection_free(evcon); 241be46c99bSCatalin Patulea event_base_free(base); 242be46c99bSCatalin Patulea 243be46c99bSCatalin Patulea return 0; 244be46c99bSCatalin Patulea } 245