2 * petal.c - https daemon that is small and beautiful.
4 * Copyright (c) 2010, NLnet Labs. All rights reserved.
6 * This software is open source.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
15 * Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
19 * Neither the name of the NLNET LABS nor the names of its contributors may
20 * be used to endorse or promote products derived from this software without
21 * specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
46 #ifdef HAVE_OPENSSL_SSL_H
47 #include <openssl/ssl.h>
49 #ifdef HAVE_OPENSSL_ERR_H
50 #include <openssl/err.h>
52 #ifdef HAVE_OPENSSL_RAND_H
53 #include <openssl/rand.h>
55 #include <openssl/x509.h>
56 #include <openssl/pem.h>
59 #if defined(UNBOUND_ALLOC_LITE) || defined(UNBOUND_ALLOC_STATS)
66 #endif /* alloc lite or alloc stats */
68 /** verbosity for this application */
71 /** Give petal usage, and exit (1). */
75 printf("Usage: petal [opts]\n");
76 printf(" https daemon serves files from ./'host'/filename\n");
77 printf(" (no hostname: from the 'default' directory)\n");
78 printf("-a addr bind to this address, 127.0.0.1\n");
79 printf("-p port port number, default 443\n");
80 printf("-k keyfile SSL private key file (PEM), petal.key\n");
81 printf("-c certfile SSL certificate file (PEM), petal.pem\n");
82 printf("-v more verbose\n");
83 printf("-h show this usage help\n");
84 printf("Version %s\n", PACKAGE_VERSION);
85 printf("BSD licensed, see LICENSE in source package for details.\n");
86 printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
91 static void print_exit(const char* str) {printf("error %s\n", str); exit(1);}
93 static void log_errno(const char* str)
94 {printf("error %s: %s\n", str, strerror(errno));}
96 /** parse a text IP address into a sockaddr */
98 parse_ip_addr(char* str, int port, struct sockaddr_storage* ret, socklen_t* l)
101 struct sockaddr_storage* addr = NULL;
102 struct sockaddr_in6 a6;
103 struct sockaddr_in a;
104 uint16_t p = (uint16_t)port;
106 memset(&a6, 0, sizeof(a6));
107 memset(&a, 0, sizeof(a));
109 if(inet_pton(AF_INET6, str, &a6.sin6_addr) > 0) {
112 a6.sin6_family = AF_INET6;
113 a6.sin6_port = (in_port_t)htons(p);
114 addr = (struct sockaddr_storage*)&a6;
115 len = (socklen_t)sizeof(struct sockaddr_in6);
117 if(inet_pton(AF_INET, str, &a.sin_addr) > 0) {
120 a.sin_family = AF_INET;
121 a.sin_port = (in_port_t)htons(p);
122 addr = (struct sockaddr_storage*)&a;
123 len = (socklen_t)sizeof(struct sockaddr_in);
125 if(!len) print_exit("cannot parse addr");
127 memmove(ret, addr, len);
143 * Read one line from SSL
145 * skips "\r\n" (but not copied to buf).
146 * @param ssl: the SSL connection to read from (blocking).
147 * @param buf: buffer to return line in.
148 * @param len: size of the buffer.
149 * @return 0 on error, 1 on success.
152 read_ssl_line(SSL* ssl, char* buf, size_t len)
159 if(verb) printf("line too long\n");
162 if((r = SSL_read(ssl, buf+n, 1)) <= 0) {
163 if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) {
167 if(verb) printf("could not SSL_read\n");
170 if(endnl && buf[n] == '\n') {
174 if(verb) printf("error: stray linefeeds\n");
176 } else if(buf[n] == '\r') {
177 /* skip \r, and also \n on the wire */
180 } else if(buf[n] == '\n') {
181 /* skip the \n, we are done */
189 /** process one http header */
191 process_one_header(char* buf, char* file, size_t flen, char* host, size_t hlen,
194 if(strncasecmp(buf, "GET ", 4) == 0) {
195 char* e = strstr(buf, " HTTP/1.1");
196 if(!e) e = strstr(buf, " http/1.1");
198 e = strstr(buf, " HTTP/1.0");
199 if(!e) e = strstr(buf, " http/1.0");
200 if(!e) e = strrchr(buf, ' ');
201 if(!e) e = strrchr(buf, '\t');
205 if(strlen(buf) < 4) return 0;
206 (void)strlcpy(file, buf+4, flen);
207 } else if(strncasecmp(buf, "Host: ", 6) == 0) {
208 (void)strlcpy(host, buf+6, hlen);
213 /** read http headers and process them */
215 read_http_headers(SSL* ssl, char* file, size_t flen, char* host, size_t hlen,
221 while(read_ssl_line(ssl, buf, sizeof(buf))) {
222 if(verb>=2) printf("read: %s\n", buf);
225 if(!process_one_header(buf, file, flen, host, hlen, vs))
231 /** setup SSL context */
233 setup_ctx(char* key, char* cert)
235 SSL_CTX* ctx = SSL_CTX_new(SSLv23_server_method());
236 if(!ctx) print_exit("out of memory");
237 (void)SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
238 (void)SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
239 if(!SSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_PEM))
240 print_exit("cannot read cert");
241 if(!SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM))
242 print_exit("cannot read key");
243 if(!SSL_CTX_check_private_key(ctx))
244 print_exit("private key is not correct");
245 if(!SSL_CTX_load_verify_locations(ctx, cert, NULL))
246 print_exit("cannot load cert verify locations");
250 /** setup listening TCP */
252 setup_fd(char* addr, int port)
254 struct sockaddr_storage ad;
258 int fam = parse_ip_addr(addr, port, &ad, &len);
259 fd = socket(fam, SOCK_STREAM, 0);
264 if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
265 (void*)&c, (socklen_t) sizeof(int)) < 0) {
266 log_errno("setsockopt(SOL_SOCKET, SO_REUSEADDR)");
268 if(bind(fd, (struct sockaddr*)&ad, len) == -1) {
273 if(listen(fd, 5) == -1) {
281 /** setup SSL connection to the client */
283 setup_ssl(int s, SSL_CTX* ctx)
285 SSL* ssl = SSL_new(ctx);
286 if(!ssl) return NULL;
287 SSL_set_accept_state(ssl);
288 (void)SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
289 if(!SSL_set_fd(ssl, s)) {
296 /** check a file name for safety */
298 file_name_is_safe(char* s)
300 size_t l = strlen(s);
302 return 0; /* must start with / */
303 if(strstr(s, "/../"))
304 return 0; /* no updirs in URL */
305 if(l>=3 && s[l-1]=='.' && s[l-2]=='.' && s[l-3]=='/')
306 return 0; /* ends with /.. */
310 /** adjust host and filename */
312 adjust_host_file(char* host, char* file)
315 /* remove a port number if present */
316 if(strrchr(host, ':'))
317 *strrchr(host, ':') = 0;
321 host[i] = tolower((unsigned char)host[i]);
324 file[i] = tolower((unsigned char)file[i]);
327 /** check a host name for safety */
329 host_name_is_safe(char* s)
333 if(strcmp(s, "..") == 0)
335 if(strcmp(s, ".") == 0)
340 /** provide file in whole transfer */
342 provide_file_10(SSL* ssl, char* fname)
345 size_t len, avail, header_reserve=1024;
346 FILE* in = fopen(fname,
354 const char* rcode = "200 OK";
357 rcode = "404 File not found";
358 snprintf(hdr, sizeof(hdr), "HTTP/1.1 %s\r\n\r\n", rcode);
360 if(SSL_write(ssl, hdr, (int)r) <= 0) {
365 fseek(in, 0, SEEK_END);
366 len = (size_t)ftell(in);
367 fseek(in, 0, SEEK_SET);
368 /* plus some space for the header */
369 buf = (char*)malloc(len+header_reserve);
374 avail = len+header_reserve;
376 snprintf(at, avail, "HTTP/1.1 %s\r\n", rcode);
380 snprintf(at, avail, "Server: petal/%s\r\n", PACKAGE_VERSION);
384 snprintf(at, avail, "Content-Length: %u\r\n", (unsigned)len);
388 snprintf(at, avail, "\r\n");
392 if(avail < len) { /* robust */
397 if(fread(at, 1, len, in) != len) {
405 if(SSL_write(ssl, buf, at-buf) <= 0) {
411 /** provide file over SSL, chunked encoding */
413 provide_file_chunked(SSL* ssl, char* fname)
417 size_t avail = sizeof(buf);
419 FILE* in = fopen(fname,
426 const char* rcode = "200 OK";
428 rcode = "404 File not found";
432 snprintf(at, avail, "HTTP/1.1 %s\r\n", rcode);
436 snprintf(at, avail, "Server: petal/%s\r\n", PACKAGE_VERSION);
440 snprintf(at, avail, "Transfer-Encoding: chunked\r\n");
444 snprintf(at, avail, "Connection: close\r\n");
448 snprintf(at, avail, "\r\n");
452 if(avail < 16) { /* robust */
458 char tmpbuf[sizeof(buf)];
459 /* read chunk; space-16 for xxxxCRLF..CRLF0CRLFCRLF (3 spare)*/
460 size_t red = in?fread(tmpbuf, 1, avail-16, in):0;
462 snprintf(at, avail, "%x\r\n", (unsigned)red);
465 {printf("chunk len %x\n", (unsigned)red); fflush(stdout);}
469 if(red > avail) break; /* robust */
470 memmove(at, tmpbuf, red);
473 snprintf(at, avail, "\r\n");
478 if(in && feof(in) && red != 0) {
479 snprintf(at, avail, "0\r\n");
484 if(!in || feof(in)) {
485 snprintf(at, avail, "\r\n");
491 if(SSL_write(ssl, buf, at-buf) <= 0) {
496 /* setup for next chunk */
499 } while(in && !feof(in) && !ferror(in));
504 /** provide service to the ssl descriptor */
506 service_ssl(SSL* ssl, struct sockaddr_storage* from, socklen_t falen)
512 if(!read_http_headers(ssl, file, sizeof(file), host, sizeof(host),
515 adjust_host_file(host, file);
516 if(host[0] == 0 || !host_name_is_safe(host))
517 (void)strlcpy(host, "default", sizeof(host));
518 if(!file_name_is_safe(file)) {
521 snprintf(combined, sizeof(combined), "%s%s", host, file);
524 void* a = &((struct sockaddr_in*)from)->sin_addr;
525 if(falen != (socklen_t)sizeof(struct sockaddr_in))
526 a = &((struct sockaddr_in6*)from)->sin6_addr;
528 (void)inet_ntop((int)((struct sockaddr_in*)from)->sin_family,
529 a, out, (socklen_t)sizeof(out));
530 printf("%s requests %s\n", out, combined);
534 provide_file_10(ssl, combined);
535 else provide_file_chunked(ssl, combined);
538 /** provide ssl service */
540 do_service(char* addr, int port, char* key, char* cert)
542 SSL_CTX* sslctx = setup_ctx(key, cert);
543 int fd = setup_fd(addr, port);
545 if(fd == -1) print_exit("could not setup sockets");
546 if(verb) {printf("petal start\n"); fflush(stdout);}
548 struct sockaddr_storage from;
549 socklen_t flen = (socklen_t)sizeof(from);
550 int s = accept(fd, (struct sockaddr*)&from, &flen);
551 if(verb) fflush(stdout);
553 SSL* ssl = setup_ssl(s, sslctx);
554 if(verb) fflush(stdout);
556 service_ssl(ssl, &from, flen);
557 if(verb) fflush(stdout);
562 } else if (verb >=2) log_errno("accept");
563 if(verb) fflush(stdout);
565 /* if we get a kill signal, the process dies and the OS reaps us */
566 if(verb) printf("petal end\n");
568 SSL_CTX_free(sslctx);
571 /** getopt global, in case header files fail to declare it. */
573 /** getopt global, in case header files fail to declare it. */
576 /** Main routine for petal */
577 int main(int argc, char* argv[])
581 char* addr = "127.0.0.1", *key = "petal.key", *cert = "petal.pem";
584 if((c=WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0)
585 { printf("WSAStartup failed\n"); exit(1); }
586 atexit((void (*)(void))WSACleanup);
589 /* parse the options */
590 while( (c=getopt(argc, argv, "a:c:k:hp:v")) != -1) {
619 (void)signal(SIGPIPE, SIG_IGN);
621 ERR_load_crypto_strings();
622 ERR_load_SSL_strings();
623 OpenSSL_add_all_algorithms();
624 (void)SSL_library_init();
626 do_service(addr, port, key, cert);
628 CRYPTO_cleanup_all_ex_data();