]> git.saurik.com Git - apple/network_cmds.git/blame - unbound/testcode/petal.c
network_cmds-596.100.2.tar.gz
[apple/network_cmds.git] / unbound / testcode / petal.c
CommitLineData
89c4ed63
A
1/*
2 * petal.c - https daemon that is small and beautiful.
3 *
4 * Copyright (c) 2010, NLnet Labs. All rights reserved.
5 *
6 * This software is open source.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 *
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.
18 *
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.
22 *
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.
34 */
35
36/**
37 * \file
38 *
39 * HTTP1.1/SSL server.
40 */
41
42#include "config.h"
43#ifdef HAVE_GETOPT_H
44#include <getopt.h>
45#endif
46#ifdef HAVE_OPENSSL_SSL_H
47#include <openssl/ssl.h>
48#endif
49#ifdef HAVE_OPENSSL_ERR_H
50#include <openssl/err.h>
51#endif
52#ifdef HAVE_OPENSSL_RAND_H
53#include <openssl/rand.h>
54#endif
55#include <openssl/x509.h>
56#include <openssl/pem.h>
57#include <ctype.h>
58#include <signal.h>
59#if defined(UNBOUND_ALLOC_LITE) || defined(UNBOUND_ALLOC_STATS)
60#ifdef malloc
61#undef malloc
62#endif
63#ifdef free
64#undef free
65#endif
66#endif /* alloc lite or alloc stats */
67
68/** verbosity for this application */
69static int verb = 0;
70
71/** Give petal usage, and exit (1). */
72static void
73usage()
74{
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);
87 exit(1);
88}
89
90/** fatal exit */
91static void print_exit(const char* str) {printf("error %s\n", str); exit(1);}
92/** print errno */
93static void log_errno(const char* str)
94{printf("error %s: %s\n", str, strerror(errno));}
95
96/** parse a text IP address into a sockaddr */
97static int
98parse_ip_addr(char* str, int port, struct sockaddr_storage* ret, socklen_t* l)
99{
100 socklen_t len = 0;
101 struct sockaddr_storage* addr = NULL;
102 struct sockaddr_in6 a6;
103 struct sockaddr_in a;
104 uint16_t p = (uint16_t)port;
105 int fam = 0;
106 memset(&a6, 0, sizeof(a6));
107 memset(&a, 0, sizeof(a));
108
109 if(inet_pton(AF_INET6, str, &a6.sin6_addr) > 0) {
110 /* it is an IPv6 */
111 fam = AF_INET6;
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);
116 }
117 if(inet_pton(AF_INET, str, &a.sin_addr) > 0) {
118 /* it is an IPv4 */
119 fam = AF_INET;
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);
124 }
125 if(!len) print_exit("cannot parse addr");
126 *l = len;
127 memmove(ret, addr, len);
128 return fam;
129}
130
131/** close the fd */
132static void
133fd_close(int fd)
134{
135#ifndef USE_WINSOCK
136 close(fd);
137#else
138 closesocket(fd);
139#endif
140}
141
142/**
143 * Read one line from SSL
144 * zero terminates.
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.
150 */
151static int
152read_ssl_line(SSL* ssl, char* buf, size_t len)
153{
154 size_t n = 0;
155 int r;
156 int endnl = 0;
157 while(1) {
158 if(n >= len) {
159 if(verb) printf("line too long\n");
160 return 0;
161 }
162 if((r = SSL_read(ssl, buf+n, 1)) <= 0) {
163 if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) {
164 /* EOF */
165 break;
166 }
167 if(verb) printf("could not SSL_read\n");
168 return 0;
169 }
170 if(endnl && buf[n] == '\n') {
171 break;
172 } else if(endnl) {
173 /* bad data */
174 if(verb) printf("error: stray linefeeds\n");
175 return 0;
176 } else if(buf[n] == '\r') {
177 /* skip \r, and also \n on the wire */
178 endnl = 1;
179 continue;
180 } else if(buf[n] == '\n') {
181 /* skip the \n, we are done */
182 break;
183 } else n++;
184 }
185 buf[n] = 0;
186 return 1;
187}
188
189/** process one http header */
190static int
191process_one_header(char* buf, char* file, size_t flen, char* host, size_t hlen,
192 int* vs)
193{
194 if(strncasecmp(buf, "GET ", 4) == 0) {
195 char* e = strstr(buf, " HTTP/1.1");
196 if(!e) e = strstr(buf, " http/1.1");
197 if(!e) {
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');
202 if(e) *vs = 10;
203 }
204 if(e) *e = 0;
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);
209 }
210 return 1;
211}
212
213/** read http headers and process them */
214static int
215read_http_headers(SSL* ssl, char* file, size_t flen, char* host, size_t hlen,
216 int* vs)
217{
218 char buf[1024];
219 file[0] = 0;
220 host[0] = 0;
221 while(read_ssl_line(ssl, buf, sizeof(buf))) {
222 if(verb>=2) printf("read: %s\n", buf);
223 if(buf[0] == 0)
224 return 1;
225 if(!process_one_header(buf, file, flen, host, hlen, vs))
226 return 0;
227 }
228 return 0;
229}
230
231/** setup SSL context */
232static SSL_CTX*
233setup_ctx(char* key, char* cert)
234{
235 SSL_CTX* ctx = SSL_CTX_new(SSLv23_server_method());
236 if(!ctx) print_exit("out of memory");
89c4ed63
A
237 if(!SSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_PEM))
238 print_exit("cannot read cert");
239 if(!SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM))
240 print_exit("cannot read key");
241 if(!SSL_CTX_check_private_key(ctx))
242 print_exit("private key is not correct");
243 if(!SSL_CTX_load_verify_locations(ctx, cert, NULL))
244 print_exit("cannot load cert verify locations");
245 return ctx;
246}
247
248/** setup listening TCP */
249static int
250setup_fd(char* addr, int port)
251{
252 struct sockaddr_storage ad;
253 socklen_t len;
254 int fd;
255 int c = 1;
256 int fam = parse_ip_addr(addr, port, &ad, &len);
257 fd = socket(fam, SOCK_STREAM, 0);
258 if(fd == -1) {
259 log_errno("socket");
260 return -1;
261 }
262 if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
263 (void*)&c, (socklen_t) sizeof(int)) < 0) {
264 log_errno("setsockopt(SOL_SOCKET, SO_REUSEADDR)");
265 }
266 if(bind(fd, (struct sockaddr*)&ad, len) == -1) {
267 log_errno("bind");
268 fd_close(fd);
269 return -1;
270 }
271 if(listen(fd, 5) == -1) {
272 log_errno("listen");
273 fd_close(fd);
274 return -1;
275 }
276 return fd;
277}
278
279/** setup SSL connection to the client */
280static SSL*
281setup_ssl(int s, SSL_CTX* ctx)
282{
283 SSL* ssl = SSL_new(ctx);
284 if(!ssl) return NULL;
285 SSL_set_accept_state(ssl);
286 (void)SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
287 if(!SSL_set_fd(ssl, s)) {
288 SSL_free(ssl);
289 return NULL;
290 }
291 return ssl;
292}
293
294/** check a file name for safety */
295static int
296file_name_is_safe(char* s)
297{
298 size_t l = strlen(s);
299 if(s[0] != '/')
300 return 0; /* must start with / */
301 if(strstr(s, "/../"))
302 return 0; /* no updirs in URL */
303 if(l>=3 && s[l-1]=='.' && s[l-2]=='.' && s[l-3]=='/')
304 return 0; /* ends with /.. */
305 return 1;
306}
307
308/** adjust host and filename */
309static void
310adjust_host_file(char* host, char* file)
311{
312 size_t i, len;
313 /* remove a port number if present */
314 if(strrchr(host, ':'))
315 *strrchr(host, ':') = 0;
316 /* lowercase */
317 len = strlen(host);
318 for(i=0; i<len; i++)
319 host[i] = tolower((unsigned char)host[i]);
320 len = strlen(file);
321 for(i=0; i<len; i++)
322 file[i] = tolower((unsigned char)file[i]);
323}
324
325/** check a host name for safety */
326static int
327host_name_is_safe(char* s)
328{
329 if(strchr(s, '/'))
330 return 0;
331 if(strcmp(s, "..") == 0)
332 return 0;
333 if(strcmp(s, ".") == 0)
334 return 0;
335 return 1;
336}
337
338/** provide file in whole transfer */
339static void
340provide_file_10(SSL* ssl, char* fname)
341{
342 char* buf, *at;
343 size_t len, avail, header_reserve=1024;
344 FILE* in = fopen(fname,
345#ifndef USE_WINSOCK
346 "r"
347#else
348 "rb"
349#endif
350 );
351 size_t r;
352 const char* rcode = "200 OK";
353 if(!in) {
354 char hdr[1024];
355 rcode = "404 File not found";
356 snprintf(hdr, sizeof(hdr), "HTTP/1.1 %s\r\n\r\n", rcode);
357 r = strlen(hdr);
358 if(SSL_write(ssl, hdr, (int)r) <= 0) {
359 /* write failure */
360 }
361 return;
362 }
363 fseek(in, 0, SEEK_END);
364 len = (size_t)ftell(in);
365 fseek(in, 0, SEEK_SET);
366 /* plus some space for the header */
367 buf = (char*)malloc(len+header_reserve);
368 if(!buf) {
369 fclose(in);
370 return;
371 }
372 avail = len+header_reserve;
373 at = buf;
374 snprintf(at, avail, "HTTP/1.1 %s\r\n", rcode);
375 r = strlen(at);
376 at += r;
377 avail -= r;
378 snprintf(at, avail, "Server: petal/%s\r\n", PACKAGE_VERSION);
379 r = strlen(at);
380 at += r;
381 avail -= r;
382 snprintf(at, avail, "Content-Length: %u\r\n", (unsigned)len);
383 r = strlen(at);
384 at += r;
385 avail -= r;
386 snprintf(at, avail, "\r\n");
387 r = strlen(at);
388 at += r;
389 avail -= r;
390 if(avail < len) { /* robust */
391 free(buf);
392 fclose(in);
393 return;
394 }
395 if(fread(at, 1, len, in) != len) {
396 free(buf);
397 fclose(in);
398 return;
399 }
400 fclose(in);
401 at += len;
402 avail -= len;
403 if(SSL_write(ssl, buf, at-buf) <= 0) {
404 /* write failure */
405 }
406 free(buf);
407}
408
409/** provide file over SSL, chunked encoding */
410static void
411provide_file_chunked(SSL* ssl, char* fname)
412{
413 char buf[16384];
414 char* at = buf;
415 size_t avail = sizeof(buf);
416 size_t r;
417 FILE* in = fopen(fname,
418#ifndef USE_WINSOCK
419 "r"
420#else
421 "rb"
422#endif
423 );
424 const char* rcode = "200 OK";
425 if(!in) {
426 rcode = "404 File not found";
427 }
428
429 /* print headers */
430 snprintf(at, avail, "HTTP/1.1 %s\r\n", rcode);
431 r = strlen(at);
432 at += r;
433 avail -= r;
434 snprintf(at, avail, "Server: petal/%s\r\n", PACKAGE_VERSION);
435 r = strlen(at);
436 at += r;
437 avail -= r;
438 snprintf(at, avail, "Transfer-Encoding: chunked\r\n");
439 r = strlen(at);
440 at += r;
441 avail -= r;
442 snprintf(at, avail, "Connection: close\r\n");
443 r = strlen(at);
444 at += r;
445 avail -= r;
446 snprintf(at, avail, "\r\n");
447 r = strlen(at);
448 at += r;
449 avail -= r;
450 if(avail < 16) { /* robust */
451 if(in) fclose(in);
452 return;
453 }
454
455 do {
456 char tmpbuf[sizeof(buf)];
457 /* read chunk; space-16 for xxxxCRLF..CRLF0CRLFCRLF (3 spare)*/
458 size_t red = in?fread(tmpbuf, 1, avail-16, in):0;
459 /* prepare chunk */
460 snprintf(at, avail, "%x\r\n", (unsigned)red);
461 r = strlen(at);
462 if(verb >= 3)
463 {printf("chunk len %x\n", (unsigned)red); fflush(stdout);}
464 at += r;
465 avail -= r;
466 if(red != 0) {
467 if(red > avail) break; /* robust */
468 memmove(at, tmpbuf, red);
469 at += red;
470 avail -= red;
471 snprintf(at, avail, "\r\n");
472 r = strlen(at);
473 at += r;
474 avail -= r;
475 }
476 if(in && feof(in) && red != 0) {
477 snprintf(at, avail, "0\r\n");
478 r = strlen(at);
479 at += r;
480 avail -= r;
481 }
482 if(!in || feof(in)) {
483 snprintf(at, avail, "\r\n");
484 r = strlen(at);
485 at += r;
486 avail -= r;
487 }
488 /* send chunk */
489 if(SSL_write(ssl, buf, at-buf) <= 0) {
490 /* SSL error */
491 break;
492 }
493
494 /* setup for next chunk */
495 at = buf;
496 avail = sizeof(buf);
497 } while(in && !feof(in) && !ferror(in));
498
499 if(in) fclose(in);
500}
501
502/** provide service to the ssl descriptor */
503static void
504service_ssl(SSL* ssl, struct sockaddr_storage* from, socklen_t falen)
505{
506 char file[1024];
507 char host[1024];
508 char combined[2048];
509 int vs = 11;
510 if(!read_http_headers(ssl, file, sizeof(file), host, sizeof(host),
511 &vs))
512 return;
513 adjust_host_file(host, file);
514 if(host[0] == 0 || !host_name_is_safe(host))
515 (void)strlcpy(host, "default", sizeof(host));
516 if(!file_name_is_safe(file)) {
517 return;
518 }
519 snprintf(combined, sizeof(combined), "%s%s", host, file);
520 if(verb) {
521 char out[100];
522 void* a = &((struct sockaddr_in*)from)->sin_addr;
523 if(falen != (socklen_t)sizeof(struct sockaddr_in))
524 a = &((struct sockaddr_in6*)from)->sin6_addr;
525 out[0]=0;
526 (void)inet_ntop((int)((struct sockaddr_in*)from)->sin_family,
527 a, out, (socklen_t)sizeof(out));
528 printf("%s requests %s\n", out, combined);
529 fflush(stdout);
530 }
531 if(vs == 10)
532 provide_file_10(ssl, combined);
533 else provide_file_chunked(ssl, combined);
534}
535
536/** provide ssl service */
537static void
538do_service(char* addr, int port, char* key, char* cert)
539{
540 SSL_CTX* sslctx = setup_ctx(key, cert);
541 int fd = setup_fd(addr, port);
542 int go = 1;
543 if(fd == -1) print_exit("could not setup sockets");
544 if(verb) {printf("petal start\n"); fflush(stdout);}
545 while(go) {
546 struct sockaddr_storage from;
547 socklen_t flen = (socklen_t)sizeof(from);
548 int s = accept(fd, (struct sockaddr*)&from, &flen);
549 if(verb) fflush(stdout);
550 if(s != -1) {
551 SSL* ssl = setup_ssl(s, sslctx);
552 if(verb) fflush(stdout);
553 if(ssl) {
554 service_ssl(ssl, &from, flen);
555 if(verb) fflush(stdout);
556 SSL_shutdown(ssl);
557 SSL_free(ssl);
558 }
559 fd_close(s);
560 } else if (verb >=2) log_errno("accept");
561 if(verb) fflush(stdout);
562 }
563 /* if we get a kill signal, the process dies and the OS reaps us */
564 if(verb) printf("petal end\n");
565 fd_close(fd);
566 SSL_CTX_free(sslctx);
567}
568
569/** getopt global, in case header files fail to declare it. */
570extern int optind;
571/** getopt global, in case header files fail to declare it. */
572extern char* optarg;
573
574/** Main routine for petal */
575int main(int argc, char* argv[])
576{
577 int c;
578 int port = 443;
579 char* addr = "127.0.0.1", *key = "petal.key", *cert = "petal.pem";
580#ifdef USE_WINSOCK
581 WSADATA wsa_data;
582 if((c=WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0)
583 { printf("WSAStartup failed\n"); exit(1); }
584 atexit((void (*)(void))WSACleanup);
585#endif
586
587 /* parse the options */
588 while( (c=getopt(argc, argv, "a:c:k:hp:v")) != -1) {
589 switch(c) {
590 case 'a':
591 addr = optarg;
592 break;
593 case 'c':
594 cert = optarg;
595 break;
596 case 'k':
597 key = optarg;
598 break;
599 case 'p':
600 port = atoi(optarg);
601 break;
602 case 'v':
603 verb++;
604 break;
605 case '?':
606 case 'h':
607 default:
608 usage();
609 }
610 }
611 argc -= optind;
612 argv += optind;
613 if(argc != 0)
614 usage();
615
616#ifdef SIGPIPE
617 (void)signal(SIGPIPE, SIG_IGN);
618#endif
619 ERR_load_crypto_strings();
620 ERR_load_SSL_strings();
621 OpenSSL_add_all_algorithms();
622 (void)SSL_library_init();
623
624 do_service(addr, port, key, cert);
625
626 CRYPTO_cleanup_all_ex_data();
627 ERR_remove_state(0);
628 ERR_free_strings();
629 RAND_cleanup();
630 return 0;
631}