1712b2f30Ssthen /* 2712b2f30Ssthen * petal.c - https daemon that is small and beautiful. 3712b2f30Ssthen * 4712b2f30Ssthen * Copyright (c) 2010, NLnet Labs. All rights reserved. 5712b2f30Ssthen * 6712b2f30Ssthen * This software is open source. 7712b2f30Ssthen * 8712b2f30Ssthen * Redistribution and use in source and binary forms, with or without 9712b2f30Ssthen * modification, are permitted provided that the following conditions 10712b2f30Ssthen * are met: 11712b2f30Ssthen * 12712b2f30Ssthen * Redistributions of source code must retain the above copyright notice, 13712b2f30Ssthen * this list of conditions and the following disclaimer. 14712b2f30Ssthen * 15712b2f30Ssthen * Redistributions in binary form must reproduce the above copyright notice, 16712b2f30Ssthen * this list of conditions and the following disclaimer in the documentation 17712b2f30Ssthen * and/or other materials provided with the distribution. 18712b2f30Ssthen * 19712b2f30Ssthen * Neither the name of the NLNET LABS nor the names of its contributors may 20712b2f30Ssthen * be used to endorse or promote products derived from this software without 21712b2f30Ssthen * specific prior written permission. 22712b2f30Ssthen * 23712b2f30Ssthen * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24712b2f30Ssthen * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25712b2f30Ssthen * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26712b2f30Ssthen * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27712b2f30Ssthen * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28712b2f30Ssthen * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29712b2f30Ssthen * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30712b2f30Ssthen * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31712b2f30Ssthen * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32712b2f30Ssthen * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33712b2f30Ssthen * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34712b2f30Ssthen */ 35712b2f30Ssthen 36712b2f30Ssthen /** 37712b2f30Ssthen * \file 38712b2f30Ssthen * 39712b2f30Ssthen * HTTP1.1/SSL server. 40712b2f30Ssthen */ 41712b2f30Ssthen 42712b2f30Ssthen #include "config.h" 43712b2f30Ssthen #ifdef HAVE_GETOPT_H 44712b2f30Ssthen #include <getopt.h> 45712b2f30Ssthen #endif 46712b2f30Ssthen #ifdef HAVE_OPENSSL_SSL_H 47712b2f30Ssthen #include <openssl/ssl.h> 48712b2f30Ssthen #endif 49712b2f30Ssthen #ifdef HAVE_OPENSSL_ERR_H 50712b2f30Ssthen #include <openssl/err.h> 51712b2f30Ssthen #endif 52712b2f30Ssthen #ifdef HAVE_OPENSSL_RAND_H 53712b2f30Ssthen #include <openssl/rand.h> 54712b2f30Ssthen #endif 55712b2f30Ssthen #include <openssl/x509.h> 56712b2f30Ssthen #include <openssl/pem.h> 57712b2f30Ssthen #include <ctype.h> 58712b2f30Ssthen #include <signal.h> 59712b2f30Ssthen #if defined(UNBOUND_ALLOC_LITE) || defined(UNBOUND_ALLOC_STATS) 60712b2f30Ssthen #ifdef malloc 61712b2f30Ssthen #undef malloc 62712b2f30Ssthen #endif 63712b2f30Ssthen #ifdef free 64712b2f30Ssthen #undef free 65712b2f30Ssthen #endif 66712b2f30Ssthen #endif /* alloc lite or alloc stats */ 67712b2f30Ssthen 68712b2f30Ssthen /** verbosity for this application */ 69712b2f30Ssthen static int verb = 0; 70712b2f30Ssthen 71712b2f30Ssthen /** Give petal usage, and exit (1). */ 72712b2f30Ssthen static void 73712b2f30Ssthen usage(void) 74712b2f30Ssthen { 75712b2f30Ssthen printf("Usage: petal [opts]\n"); 76712b2f30Ssthen printf(" https daemon serves files from ./'host'/filename\n"); 77712b2f30Ssthen printf(" (no hostname: from the 'default' directory)\n"); 78712b2f30Ssthen printf("-a addr bind to this address, 127.0.0.1\n"); 79712b2f30Ssthen printf("-p port port number, default 443\n"); 80712b2f30Ssthen printf("-k keyfile SSL private key file (PEM), petal.key\n"); 81712b2f30Ssthen printf("-c certfile SSL certificate file (PEM), petal.pem\n"); 82712b2f30Ssthen printf("-v more verbose\n"); 83712b2f30Ssthen printf("-h show this usage help\n"); 84712b2f30Ssthen printf("Version %s\n", PACKAGE_VERSION); 85712b2f30Ssthen printf("BSD licensed, see LICENSE in source package for details.\n"); 86712b2f30Ssthen printf("Report bugs to %s\n", PACKAGE_BUGREPORT); 87712b2f30Ssthen exit(1); 88712b2f30Ssthen } 89712b2f30Ssthen 90712b2f30Ssthen /** fatal exit */ 91712b2f30Ssthen static void print_exit(const char* str) {printf("error %s\n", str); exit(1);} 92712b2f30Ssthen /** print errno */ 93712b2f30Ssthen static void log_errno(const char* str) 94712b2f30Ssthen {printf("error %s: %s\n", str, strerror(errno));} 95712b2f30Ssthen 96712b2f30Ssthen /** parse a text IP address into a sockaddr */ 97712b2f30Ssthen static int 98712b2f30Ssthen parse_ip_addr(char* str, int port, struct sockaddr_storage* ret, socklen_t* l) 99712b2f30Ssthen { 100712b2f30Ssthen socklen_t len = 0; 101712b2f30Ssthen struct sockaddr_storage* addr = NULL; 102712b2f30Ssthen struct sockaddr_in6 a6; 103712b2f30Ssthen struct sockaddr_in a; 104712b2f30Ssthen uint16_t p = (uint16_t)port; 105712b2f30Ssthen int fam = 0; 106712b2f30Ssthen memset(&a6, 0, sizeof(a6)); 107712b2f30Ssthen memset(&a, 0, sizeof(a)); 108712b2f30Ssthen 109712b2f30Ssthen if(inet_pton(AF_INET6, str, &a6.sin6_addr) > 0) { 110712b2f30Ssthen /* it is an IPv6 */ 111712b2f30Ssthen fam = AF_INET6; 112712b2f30Ssthen a6.sin6_family = AF_INET6; 113712b2f30Ssthen a6.sin6_port = (in_port_t)htons(p); 114712b2f30Ssthen addr = (struct sockaddr_storage*)&a6; 115712b2f30Ssthen len = (socklen_t)sizeof(struct sockaddr_in6); 116712b2f30Ssthen } 117712b2f30Ssthen if(inet_pton(AF_INET, str, &a.sin_addr) > 0) { 118712b2f30Ssthen /* it is an IPv4 */ 119712b2f30Ssthen fam = AF_INET; 120712b2f30Ssthen a.sin_family = AF_INET; 121712b2f30Ssthen a.sin_port = (in_port_t)htons(p); 122712b2f30Ssthen addr = (struct sockaddr_storage*)&a; 123712b2f30Ssthen len = (socklen_t)sizeof(struct sockaddr_in); 124712b2f30Ssthen } 125712b2f30Ssthen if(!len) print_exit("cannot parse addr"); 126712b2f30Ssthen *l = len; 127712b2f30Ssthen memmove(ret, addr, len); 128712b2f30Ssthen return fam; 129712b2f30Ssthen } 130712b2f30Ssthen 131712b2f30Ssthen /** close the fd */ 132712b2f30Ssthen static void 133712b2f30Ssthen fd_close(int fd) 134712b2f30Ssthen { 135712b2f30Ssthen #ifndef USE_WINSOCK 136712b2f30Ssthen close(fd); 137712b2f30Ssthen #else 138712b2f30Ssthen closesocket(fd); 139712b2f30Ssthen #endif 140712b2f30Ssthen } 141712b2f30Ssthen 142712b2f30Ssthen /** 143712b2f30Ssthen * Read one line from SSL 144712b2f30Ssthen * zero terminates. 145712b2f30Ssthen * skips "\r\n" (but not copied to buf). 146712b2f30Ssthen * @param ssl: the SSL connection to read from (blocking). 147712b2f30Ssthen * @param buf: buffer to return line in. 148712b2f30Ssthen * @param len: size of the buffer. 149712b2f30Ssthen * @return 0 on error, 1 on success. 150712b2f30Ssthen */ 151712b2f30Ssthen static int 152712b2f30Ssthen read_ssl_line(SSL* ssl, char* buf, size_t len) 153712b2f30Ssthen { 154712b2f30Ssthen size_t n = 0; 155712b2f30Ssthen int r; 156712b2f30Ssthen int endnl = 0; 157712b2f30Ssthen while(1) { 158712b2f30Ssthen if(n >= len) { 159712b2f30Ssthen if(verb) printf("line too long\n"); 160712b2f30Ssthen return 0; 161712b2f30Ssthen } 162712b2f30Ssthen if((r = SSL_read(ssl, buf+n, 1)) <= 0) { 163712b2f30Ssthen if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) { 164712b2f30Ssthen /* EOF */ 165712b2f30Ssthen break; 166712b2f30Ssthen } 167712b2f30Ssthen if(verb) printf("could not SSL_read\n"); 168712b2f30Ssthen return 0; 169712b2f30Ssthen } 170712b2f30Ssthen if(endnl && buf[n] == '\n') { 171712b2f30Ssthen break; 172712b2f30Ssthen } else if(endnl) { 173712b2f30Ssthen /* bad data */ 174712b2f30Ssthen if(verb) printf("error: stray linefeeds\n"); 175712b2f30Ssthen return 0; 176712b2f30Ssthen } else if(buf[n] == '\r') { 177712b2f30Ssthen /* skip \r, and also \n on the wire */ 178712b2f30Ssthen endnl = 1; 179712b2f30Ssthen continue; 180712b2f30Ssthen } else if(buf[n] == '\n') { 181712b2f30Ssthen /* skip the \n, we are done */ 182712b2f30Ssthen break; 183712b2f30Ssthen } else n++; 184712b2f30Ssthen } 185712b2f30Ssthen buf[n] = 0; 186712b2f30Ssthen return 1; 187712b2f30Ssthen } 188712b2f30Ssthen 189712b2f30Ssthen /** process one http header */ 190712b2f30Ssthen static int 191712b2f30Ssthen process_one_header(char* buf, char* file, size_t flen, char* host, size_t hlen, 192712b2f30Ssthen int* vs) 193712b2f30Ssthen { 194712b2f30Ssthen if(strncasecmp(buf, "GET ", 4) == 0) { 195712b2f30Ssthen char* e = strstr(buf, " HTTP/1.1"); 196712b2f30Ssthen if(!e) e = strstr(buf, " http/1.1"); 197712b2f30Ssthen if(!e) { 198712b2f30Ssthen e = strstr(buf, " HTTP/1.0"); 199712b2f30Ssthen if(!e) e = strstr(buf, " http/1.0"); 200712b2f30Ssthen if(!e) e = strrchr(buf, ' '); 201712b2f30Ssthen if(!e) e = strrchr(buf, '\t'); 202712b2f30Ssthen if(e) *vs = 10; 203712b2f30Ssthen } 204712b2f30Ssthen if(e) *e = 0; 205712b2f30Ssthen if(strlen(buf) < 4) return 0; 206712b2f30Ssthen (void)strlcpy(file, buf+4, flen); 207712b2f30Ssthen } else if(strncasecmp(buf, "Host: ", 6) == 0) { 208712b2f30Ssthen (void)strlcpy(host, buf+6, hlen); 209712b2f30Ssthen } 210712b2f30Ssthen return 1; 211712b2f30Ssthen } 212712b2f30Ssthen 213712b2f30Ssthen /** read http headers and process them */ 214712b2f30Ssthen static int 215712b2f30Ssthen read_http_headers(SSL* ssl, char* file, size_t flen, char* host, size_t hlen, 216712b2f30Ssthen int* vs) 217712b2f30Ssthen { 218712b2f30Ssthen char buf[1024]; 219712b2f30Ssthen file[0] = 0; 220712b2f30Ssthen host[0] = 0; 221712b2f30Ssthen while(read_ssl_line(ssl, buf, sizeof(buf))) { 222712b2f30Ssthen if(verb>=2) printf("read: %s\n", buf); 22383152a15Ssthen if(buf[0] == 0) { 22483152a15Ssthen int e = ERR_peek_error(); 22583152a15Ssthen printf("error string: %s\n", ERR_reason_error_string(e)); 226712b2f30Ssthen return 1; 22783152a15Ssthen } 228712b2f30Ssthen if(!process_one_header(buf, file, flen, host, hlen, vs)) 229712b2f30Ssthen return 0; 230712b2f30Ssthen } 231712b2f30Ssthen return 0; 232712b2f30Ssthen } 233712b2f30Ssthen 234712b2f30Ssthen /** setup SSL context */ 235712b2f30Ssthen static SSL_CTX* 236712b2f30Ssthen setup_ctx(char* key, char* cert) 237712b2f30Ssthen { 238712b2f30Ssthen SSL_CTX* ctx = SSL_CTX_new(SSLv23_server_method()); 239712b2f30Ssthen if(!ctx) print_exit("out of memory"); 240d5d2b63dSsthen #if SSL_OP_NO_SSLv2 != 0 241712b2f30Ssthen (void)SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2); 242d5d2b63dSsthen #endif 243712b2f30Ssthen (void)SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3); 244a6cc1574Ssthen #ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL 245a6cc1574Ssthen SSL_CTX_set_security_level(ctx, 0); /* for keys in tests */ 246a6cc1574Ssthen #endif 24783152a15Ssthen if(!SSL_CTX_use_certificate_chain_file(ctx, cert)) { 24883152a15Ssthen int e = ERR_peek_error(); 24983152a15Ssthen printf("error string: %s\n", ERR_reason_error_string(e)); 250712b2f30Ssthen print_exit("cannot read cert"); 25183152a15Ssthen } 252712b2f30Ssthen if(!SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM)) 253712b2f30Ssthen print_exit("cannot read key"); 254712b2f30Ssthen if(!SSL_CTX_check_private_key(ctx)) 255712b2f30Ssthen print_exit("private key is not correct"); 256712b2f30Ssthen #if HAVE_DECL_SSL_CTX_SET_ECDH_AUTO 257712b2f30Ssthen if (!SSL_CTX_set_ecdh_auto(ctx,1)) 258712b2f30Ssthen if(verb>=1) printf("failed to set_ecdh_auto, not enabling ECDHE\n"); 259*a43524d9Ssthen #elif defined(USE_ECDSA) && defined(HAVE_SSL_CTX_SET_TMP_ECDH) 260712b2f30Ssthen if(1) { 261712b2f30Ssthen EC_KEY *ecdh = EC_KEY_new_by_curve_name (NID_X9_62_prime256v1); 262712b2f30Ssthen if (!ecdh) { 263712b2f30Ssthen if(verb>=1) printf("could not find p256, not enabling ECDHE\n"); 264712b2f30Ssthen } else { 265712b2f30Ssthen if (1 != SSL_CTX_set_tmp_ecdh (ctx, ecdh)) { 266712b2f30Ssthen if(verb>=1) printf("Error in SSL_CTX_set_tmp_ecdh, not enabling ECDHE\n"); 267712b2f30Ssthen } 268712b2f30Ssthen EC_KEY_free(ecdh); 269712b2f30Ssthen } 270712b2f30Ssthen } 271712b2f30Ssthen #endif 272712b2f30Ssthen if(!SSL_CTX_load_verify_locations(ctx, cert, NULL)) 273712b2f30Ssthen print_exit("cannot load cert verify locations"); 274712b2f30Ssthen return ctx; 275712b2f30Ssthen } 276712b2f30Ssthen 277712b2f30Ssthen /** setup listening TCP */ 278712b2f30Ssthen static int 279712b2f30Ssthen setup_fd(char* addr, int port) 280712b2f30Ssthen { 281712b2f30Ssthen struct sockaddr_storage ad; 282712b2f30Ssthen socklen_t len; 283712b2f30Ssthen int fd; 284712b2f30Ssthen int c = 1; 285712b2f30Ssthen int fam = parse_ip_addr(addr, port, &ad, &len); 286712b2f30Ssthen fd = socket(fam, SOCK_STREAM, 0); 287712b2f30Ssthen if(fd == -1) { 288712b2f30Ssthen log_errno("socket"); 289712b2f30Ssthen return -1; 290712b2f30Ssthen } 291712b2f30Ssthen if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, 292712b2f30Ssthen (void*)&c, (socklen_t) sizeof(int)) < 0) { 293712b2f30Ssthen log_errno("setsockopt(SOL_SOCKET, SO_REUSEADDR)"); 294712b2f30Ssthen } 295712b2f30Ssthen if(bind(fd, (struct sockaddr*)&ad, len) == -1) { 296712b2f30Ssthen log_errno("bind"); 297712b2f30Ssthen fd_close(fd); 298712b2f30Ssthen return -1; 299712b2f30Ssthen } 300712b2f30Ssthen if(listen(fd, 5) == -1) { 301712b2f30Ssthen log_errno("listen"); 302712b2f30Ssthen fd_close(fd); 303712b2f30Ssthen return -1; 304712b2f30Ssthen } 305712b2f30Ssthen return fd; 306712b2f30Ssthen } 307712b2f30Ssthen 308712b2f30Ssthen /** setup SSL connection to the client */ 309712b2f30Ssthen static SSL* 310712b2f30Ssthen setup_ssl(int s, SSL_CTX* ctx) 311712b2f30Ssthen { 312712b2f30Ssthen SSL* ssl = SSL_new(ctx); 313712b2f30Ssthen if(!ssl) return NULL; 314712b2f30Ssthen SSL_set_accept_state(ssl); 315b0dfc31bSsthen (void)SSL_set_mode(ssl, (long)SSL_MODE_AUTO_RETRY); 316712b2f30Ssthen if(!SSL_set_fd(ssl, s)) { 317712b2f30Ssthen SSL_free(ssl); 318712b2f30Ssthen return NULL; 319712b2f30Ssthen } 320712b2f30Ssthen return ssl; 321712b2f30Ssthen } 322712b2f30Ssthen 323712b2f30Ssthen /** check a file name for safety */ 324712b2f30Ssthen static int 325712b2f30Ssthen file_name_is_safe(char* s) 326712b2f30Ssthen { 327712b2f30Ssthen size_t l = strlen(s); 328712b2f30Ssthen if(s[0] != '/') 329712b2f30Ssthen return 0; /* must start with / */ 330712b2f30Ssthen if(strstr(s, "/../")) 331712b2f30Ssthen return 0; /* no updirs in URL */ 332712b2f30Ssthen if(l>=3 && s[l-1]=='.' && s[l-2]=='.' && s[l-3]=='/') 333712b2f30Ssthen return 0; /* ends with /.. */ 334712b2f30Ssthen return 1; 335712b2f30Ssthen } 336712b2f30Ssthen 33766a34dc2Ssthen /** adjust host */ 338712b2f30Ssthen static void 33966a34dc2Ssthen adjust_host(char* host) 340712b2f30Ssthen { 341712b2f30Ssthen size_t i, len; 342712b2f30Ssthen /* remove a port number if present */ 343712b2f30Ssthen if(strrchr(host, ':')) 344712b2f30Ssthen *strrchr(host, ':') = 0; 345712b2f30Ssthen /* lowercase */ 346712b2f30Ssthen len = strlen(host); 347712b2f30Ssthen for(i=0; i<len; i++) 348712b2f30Ssthen host[i] = tolower((unsigned char)host[i]); 34966a34dc2Ssthen } 35066a34dc2Ssthen 35166a34dc2Ssthen /** adjust filename */ 35266a34dc2Ssthen static void 35366a34dc2Ssthen adjust_file(char* file) 35466a34dc2Ssthen { 35566a34dc2Ssthen size_t i, len; 356712b2f30Ssthen len = strlen(file); 357712b2f30Ssthen for(i=0; i<len; i++) 358712b2f30Ssthen file[i] = tolower((unsigned char)file[i]); 359712b2f30Ssthen } 360712b2f30Ssthen 361712b2f30Ssthen /** check a host name for safety */ 362712b2f30Ssthen static int 363712b2f30Ssthen host_name_is_safe(char* s) 364712b2f30Ssthen { 365712b2f30Ssthen if(strchr(s, '/')) 366712b2f30Ssthen return 0; 367712b2f30Ssthen if(strcmp(s, "..") == 0) 368712b2f30Ssthen return 0; 369712b2f30Ssthen if(strcmp(s, ".") == 0) 370712b2f30Ssthen return 0; 371712b2f30Ssthen return 1; 372712b2f30Ssthen } 373712b2f30Ssthen 374712b2f30Ssthen /** provide file in whole transfer */ 375712b2f30Ssthen static void 376712b2f30Ssthen provide_file_10(SSL* ssl, char* fname) 377712b2f30Ssthen { 378712b2f30Ssthen char* buf, *at; 379712b2f30Ssthen size_t len, avail, header_reserve=1024; 380712b2f30Ssthen FILE* in = fopen(fname, 381712b2f30Ssthen #ifndef USE_WINSOCK 382712b2f30Ssthen "r" 383712b2f30Ssthen #else 384712b2f30Ssthen "rb" 385712b2f30Ssthen #endif 386712b2f30Ssthen ); 387712b2f30Ssthen size_t r; 388712b2f30Ssthen const char* rcode = "200 OK"; 389712b2f30Ssthen if(!in) { 390712b2f30Ssthen char hdr[1024]; 391712b2f30Ssthen rcode = "404 File not found"; 392712b2f30Ssthen snprintf(hdr, sizeof(hdr), "HTTP/1.1 %s\r\n\r\n", rcode); 393712b2f30Ssthen r = strlen(hdr); 394712b2f30Ssthen if(SSL_write(ssl, hdr, (int)r) <= 0) { 395712b2f30Ssthen /* write failure */ 396712b2f30Ssthen } 397712b2f30Ssthen return; 398712b2f30Ssthen } 399712b2f30Ssthen fseek(in, 0, SEEK_END); 400712b2f30Ssthen len = (size_t)ftell(in); 401712b2f30Ssthen fseek(in, 0, SEEK_SET); 402712b2f30Ssthen /* plus some space for the header */ 403712b2f30Ssthen buf = (char*)malloc(len+header_reserve); 404712b2f30Ssthen if(!buf) { 405712b2f30Ssthen fclose(in); 406712b2f30Ssthen return; 407712b2f30Ssthen } 408712b2f30Ssthen avail = len+header_reserve; 409712b2f30Ssthen at = buf; 410712b2f30Ssthen snprintf(at, avail, "HTTP/1.1 %s\r\n", rcode); 411712b2f30Ssthen r = strlen(at); 412712b2f30Ssthen at += r; 413712b2f30Ssthen avail -= r; 414712b2f30Ssthen snprintf(at, avail, "Server: petal/%s\r\n", PACKAGE_VERSION); 415712b2f30Ssthen r = strlen(at); 416712b2f30Ssthen at += r; 417712b2f30Ssthen avail -= r; 418712b2f30Ssthen snprintf(at, avail, "Content-Length: %u\r\n", (unsigned)len); 419712b2f30Ssthen r = strlen(at); 420712b2f30Ssthen at += r; 421712b2f30Ssthen avail -= r; 422712b2f30Ssthen snprintf(at, avail, "\r\n"); 423712b2f30Ssthen r = strlen(at); 424712b2f30Ssthen at += r; 425712b2f30Ssthen avail -= r; 426712b2f30Ssthen if(avail < len) { /* robust */ 427712b2f30Ssthen free(buf); 428712b2f30Ssthen fclose(in); 429712b2f30Ssthen return; 430712b2f30Ssthen } 431712b2f30Ssthen if(fread(at, 1, len, in) != len) { 432712b2f30Ssthen free(buf); 433712b2f30Ssthen fclose(in); 434712b2f30Ssthen return; 435712b2f30Ssthen } 436712b2f30Ssthen fclose(in); 437712b2f30Ssthen at += len; 4388771e50fSsthen /* avail -= len; unused */ 439712b2f30Ssthen if(SSL_write(ssl, buf, at-buf) <= 0) { 440712b2f30Ssthen /* write failure */ 441712b2f30Ssthen } 442712b2f30Ssthen free(buf); 443712b2f30Ssthen } 444712b2f30Ssthen 445712b2f30Ssthen /** provide file over SSL, chunked encoding */ 446712b2f30Ssthen static void 447712b2f30Ssthen provide_file_chunked(SSL* ssl, char* fname) 448712b2f30Ssthen { 449712b2f30Ssthen char buf[16384]; 450712b2f30Ssthen char* tmpbuf = NULL; 451712b2f30Ssthen char* at = buf; 452712b2f30Ssthen size_t avail = sizeof(buf); 453712b2f30Ssthen size_t r; 454712b2f30Ssthen FILE* in = fopen(fname, 455712b2f30Ssthen #ifndef USE_WINSOCK 456712b2f30Ssthen "r" 457712b2f30Ssthen #else 458712b2f30Ssthen "rb" 459712b2f30Ssthen #endif 460712b2f30Ssthen ); 461712b2f30Ssthen const char* rcode = "200 OK"; 462712b2f30Ssthen if(!in) { 463712b2f30Ssthen rcode = "404 File not found"; 464712b2f30Ssthen } 465712b2f30Ssthen 466712b2f30Ssthen /* print headers */ 467712b2f30Ssthen snprintf(at, avail, "HTTP/1.1 %s\r\n", rcode); 468712b2f30Ssthen r = strlen(at); 469712b2f30Ssthen at += r; 470712b2f30Ssthen avail -= r; 471712b2f30Ssthen snprintf(at, avail, "Server: petal/%s\r\n", PACKAGE_VERSION); 472712b2f30Ssthen r = strlen(at); 473712b2f30Ssthen at += r; 474712b2f30Ssthen avail -= r; 475712b2f30Ssthen snprintf(at, avail, "Transfer-Encoding: chunked\r\n"); 476712b2f30Ssthen r = strlen(at); 477712b2f30Ssthen at += r; 478712b2f30Ssthen avail -= r; 479712b2f30Ssthen snprintf(at, avail, "Connection: close\r\n"); 480712b2f30Ssthen r = strlen(at); 481712b2f30Ssthen at += r; 482712b2f30Ssthen avail -= r; 483712b2f30Ssthen snprintf(at, avail, "\r\n"); 484712b2f30Ssthen r = strlen(at); 485712b2f30Ssthen at += r; 486712b2f30Ssthen avail -= r; 487712b2f30Ssthen if(avail < 16) { /* robust */ 488712b2f30Ssthen if(in) fclose(in); 489712b2f30Ssthen return; 490712b2f30Ssthen } 491712b2f30Ssthen 492712b2f30Ssthen do { 493712b2f30Ssthen size_t red; 494712b2f30Ssthen free(tmpbuf); 495712b2f30Ssthen tmpbuf = malloc(avail-16); 496712b2f30Ssthen if(!tmpbuf) 497712b2f30Ssthen break; 498712b2f30Ssthen /* read chunk; space-16 for xxxxCRLF..CRLF0CRLFCRLF (3 spare)*/ 499712b2f30Ssthen red = in?fread(tmpbuf, 1, avail-16, in):0; 500712b2f30Ssthen /* prepare chunk */ 501712b2f30Ssthen snprintf(at, avail, "%x\r\n", (unsigned)red); 502712b2f30Ssthen r = strlen(at); 503712b2f30Ssthen if(verb >= 3) 504712b2f30Ssthen {printf("chunk len %x\n", (unsigned)red); fflush(stdout);} 505712b2f30Ssthen at += r; 506712b2f30Ssthen avail -= r; 507712b2f30Ssthen if(red != 0) { 508712b2f30Ssthen if(red > avail) break; /* robust */ 509712b2f30Ssthen memmove(at, tmpbuf, red); 510712b2f30Ssthen at += red; 511712b2f30Ssthen avail -= red; 512712b2f30Ssthen snprintf(at, avail, "\r\n"); 513712b2f30Ssthen r = strlen(at); 514712b2f30Ssthen at += r; 515712b2f30Ssthen avail -= r; 516712b2f30Ssthen } 517712b2f30Ssthen if(in && feof(in) && red != 0) { 518712b2f30Ssthen snprintf(at, avail, "0\r\n"); 519712b2f30Ssthen r = strlen(at); 520712b2f30Ssthen at += r; 521712b2f30Ssthen avail -= r; 522712b2f30Ssthen } 523712b2f30Ssthen if(!in || feof(in)) { 524712b2f30Ssthen snprintf(at, avail, "\r\n"); 525712b2f30Ssthen r = strlen(at); 526712b2f30Ssthen at += r; 5278771e50fSsthen /* avail -= r; unused */ 528712b2f30Ssthen } 529712b2f30Ssthen /* send chunk */ 530712b2f30Ssthen if(SSL_write(ssl, buf, at-buf) <= 0) { 531712b2f30Ssthen /* SSL error */ 532712b2f30Ssthen break; 533712b2f30Ssthen } 534712b2f30Ssthen 535712b2f30Ssthen /* setup for next chunk */ 536712b2f30Ssthen at = buf; 537712b2f30Ssthen avail = sizeof(buf); 538712b2f30Ssthen } while(in && !feof(in) && !ferror(in)); 539712b2f30Ssthen 540712b2f30Ssthen free(tmpbuf); 541712b2f30Ssthen if(in) fclose(in); 542712b2f30Ssthen } 543712b2f30Ssthen 544712b2f30Ssthen /** provide service to the ssl descriptor */ 545712b2f30Ssthen static void 546712b2f30Ssthen service_ssl(SSL* ssl, struct sockaddr_storage* from, socklen_t falen) 547712b2f30Ssthen { 548712b2f30Ssthen char file[1024]; 549712b2f30Ssthen char host[1024]; 550712b2f30Ssthen char combined[2048]; 551712b2f30Ssthen int vs = 11; 552712b2f30Ssthen if(!read_http_headers(ssl, file, sizeof(file), host, sizeof(host), 553712b2f30Ssthen &vs)) 554712b2f30Ssthen return; 55566a34dc2Ssthen if(host[0] != 0) adjust_host(host); 55666a34dc2Ssthen if(file[0] != 0) adjust_file(file); 557712b2f30Ssthen if(host[0] == 0 || !host_name_is_safe(host)) 558712b2f30Ssthen (void)strlcpy(host, "default", sizeof(host)); 559712b2f30Ssthen if(!file_name_is_safe(file)) { 560712b2f30Ssthen return; 561712b2f30Ssthen } 562712b2f30Ssthen snprintf(combined, sizeof(combined), "%s%s", host, file); 563712b2f30Ssthen if(verb) { 564712b2f30Ssthen char out[100]; 565712b2f30Ssthen void* a = &((struct sockaddr_in*)from)->sin_addr; 566712b2f30Ssthen if(falen != (socklen_t)sizeof(struct sockaddr_in)) 567712b2f30Ssthen a = &((struct sockaddr_in6*)from)->sin6_addr; 568712b2f30Ssthen out[0]=0; 569712b2f30Ssthen (void)inet_ntop((int)((struct sockaddr_in*)from)->sin_family, 570712b2f30Ssthen a, out, (socklen_t)sizeof(out)); 571712b2f30Ssthen printf("%s requests %s\n", out, combined); 572712b2f30Ssthen fflush(stdout); 573712b2f30Ssthen } 574712b2f30Ssthen if(vs == 10) 575712b2f30Ssthen provide_file_10(ssl, combined); 576712b2f30Ssthen else provide_file_chunked(ssl, combined); 577712b2f30Ssthen } 578712b2f30Ssthen 579712b2f30Ssthen /** provide ssl service */ 580712b2f30Ssthen static void 581712b2f30Ssthen do_service(char* addr, int port, char* key, char* cert) 582712b2f30Ssthen { 583712b2f30Ssthen SSL_CTX* sslctx = setup_ctx(key, cert); 584712b2f30Ssthen int fd = setup_fd(addr, port); 585712b2f30Ssthen if(fd == -1) print_exit("could not setup sockets"); 586712b2f30Ssthen if(verb) {printf("petal start\n"); fflush(stdout);} 58783152a15Ssthen while(1) { 588712b2f30Ssthen struct sockaddr_storage from; 589712b2f30Ssthen socklen_t flen = (socklen_t)sizeof(from); 5908771e50fSsthen int s; 5918771e50fSsthen memset(&from, 0, sizeof(from)); 5928771e50fSsthen s = accept(fd, (struct sockaddr*)&from, &flen); 593712b2f30Ssthen if(verb) fflush(stdout); 594712b2f30Ssthen if(s != -1) { 595712b2f30Ssthen SSL* ssl = setup_ssl(s, sslctx); 596712b2f30Ssthen if(verb) fflush(stdout); 597712b2f30Ssthen if(ssl) { 598712b2f30Ssthen service_ssl(ssl, &from, flen); 599712b2f30Ssthen if(verb) fflush(stdout); 600712b2f30Ssthen SSL_shutdown(ssl); 601712b2f30Ssthen SSL_free(ssl); 602712b2f30Ssthen } 603712b2f30Ssthen fd_close(s); 604712b2f30Ssthen } else if (verb >=2) log_errno("accept"); 605712b2f30Ssthen if(verb) fflush(stdout); 606712b2f30Ssthen } 607712b2f30Ssthen /* if we get a kill signal, the process dies and the OS reaps us */ 608712b2f30Ssthen if(verb) printf("petal end\n"); 609712b2f30Ssthen fd_close(fd); 610712b2f30Ssthen SSL_CTX_free(sslctx); 611712b2f30Ssthen } 612712b2f30Ssthen 613712b2f30Ssthen /** getopt global, in case header files fail to declare it. */ 614712b2f30Ssthen extern int optind; 615712b2f30Ssthen /** getopt global, in case header files fail to declare it. */ 616712b2f30Ssthen extern char* optarg; 617712b2f30Ssthen 618712b2f30Ssthen /** Main routine for petal */ 619712b2f30Ssthen int main(int argc, char* argv[]) 620712b2f30Ssthen { 621712b2f30Ssthen int c; 622712b2f30Ssthen int port = 443; 623712b2f30Ssthen char* addr = "127.0.0.1", *key = "petal.key", *cert = "petal.pem"; 624712b2f30Ssthen #ifdef USE_WINSOCK 625712b2f30Ssthen WSADATA wsa_data; 626712b2f30Ssthen if((c=WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0) 627712b2f30Ssthen { printf("WSAStartup failed\n"); exit(1); } 628712b2f30Ssthen atexit((void (*)(void))WSACleanup); 629712b2f30Ssthen #endif 630712b2f30Ssthen 631712b2f30Ssthen /* parse the options */ 632712b2f30Ssthen while( (c=getopt(argc, argv, "a:c:k:hp:v")) != -1) { 633712b2f30Ssthen switch(c) { 634712b2f30Ssthen case 'a': 635712b2f30Ssthen addr = optarg; 636712b2f30Ssthen break; 637712b2f30Ssthen case 'c': 638712b2f30Ssthen cert = optarg; 639712b2f30Ssthen break; 640712b2f30Ssthen case 'k': 641712b2f30Ssthen key = optarg; 642712b2f30Ssthen break; 643712b2f30Ssthen case 'p': 644712b2f30Ssthen port = atoi(optarg); 645712b2f30Ssthen break; 646712b2f30Ssthen case 'v': 647712b2f30Ssthen verb++; 648712b2f30Ssthen break; 649712b2f30Ssthen case '?': 650712b2f30Ssthen case 'h': 651712b2f30Ssthen default: 652712b2f30Ssthen usage(); 653712b2f30Ssthen } 654712b2f30Ssthen } 655712b2f30Ssthen argc -= optind; 6568771e50fSsthen /* argv += optind; not using further arguments */ 657712b2f30Ssthen if(argc != 0) 658712b2f30Ssthen usage(); 659712b2f30Ssthen 660712b2f30Ssthen #ifdef SIGPIPE 661712b2f30Ssthen (void)signal(SIGPIPE, SIG_IGN); 662712b2f30Ssthen #endif 663712b2f30Ssthen #ifdef HAVE_ERR_LOAD_CRYPTO_STRINGS 664712b2f30Ssthen ERR_load_crypto_strings(); 665712b2f30Ssthen #endif 666712b2f30Ssthen #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL) 667712b2f30Ssthen ERR_load_SSL_strings(); 668712b2f30Ssthen #endif 669712b2f30Ssthen #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO) 670b0dfc31bSsthen # ifndef S_SPLINT_S 671712b2f30Ssthen OpenSSL_add_all_algorithms(); 672b0dfc31bSsthen # endif 673712b2f30Ssthen #else 674712b2f30Ssthen OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS 675712b2f30Ssthen | OPENSSL_INIT_ADD_ALL_DIGESTS 676712b2f30Ssthen | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); 677712b2f30Ssthen #endif 678712b2f30Ssthen #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL) 679712b2f30Ssthen (void)SSL_library_init(); 680712b2f30Ssthen #else 681712b2f30Ssthen (void)OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); 682712b2f30Ssthen #endif 683712b2f30Ssthen 684712b2f30Ssthen do_service(addr, port, key, cert); 685712b2f30Ssthen 686712b2f30Ssthen #ifdef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA 687712b2f30Ssthen CRYPTO_cleanup_all_ex_data(); 688712b2f30Ssthen #endif 689712b2f30Ssthen #ifdef HAVE_ERR_FREE_STRINGS 690712b2f30Ssthen ERR_free_strings(); 691712b2f30Ssthen #endif 692712b2f30Ssthen return 0; 693712b2f30Ssthen } 694