]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - testcode/petal.c
import unbound 1.4.17
[FreeBSD/FreeBSD.git] / testcode / petal.c
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 LIMITED
25  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
27  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * 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 */
69 static int verb = 0;
70
71 /** Give petal usage, and exit (1). */
72 static void
73 usage()
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 */
91 static void print_exit(const char* str) {printf("error %s\n", str); exit(1);}
92 /** print errno */
93 static 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 */
97 static int
98 parse_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 */
132 static void
133 fd_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  */
151 static int
152 read_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 */
190 static int
191 process_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 */
214 static int
215 read_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 */
232 static SSL_CTX*
233 setup_ctx(char* key, char* cert)
234 {
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         if(!SSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_PEM))
239                 print_exit("cannot read cert");
240         if(!SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM))
241                 print_exit("cannot read key");
242         if(!SSL_CTX_check_private_key(ctx))
243                 print_exit("private key is not correct");
244         if(!SSL_CTX_load_verify_locations(ctx, cert, NULL))
245                 print_exit("cannot load cert verify locations");
246         return ctx;
247 }
248
249 /** setup listening TCP */
250 static int
251 setup_fd(char* addr, int port)
252 {
253         struct sockaddr_storage ad;
254         socklen_t len;
255         int fd;
256         int c = 1;
257         int fam = parse_ip_addr(addr, port, &ad, &len);
258         fd = socket(fam, SOCK_STREAM, 0);
259         if(fd == -1) {
260                 log_errno("socket");
261                 return -1;
262         }
263         if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
264                 (void*)&c, (socklen_t) sizeof(int)) < 0) {
265                 log_errno("setsockopt(SOL_SOCKET, SO_REUSEADDR)");
266         }
267         if(bind(fd, (struct sockaddr*)&ad, len) == -1) {
268                 log_errno("bind");
269                 fd_close(fd);
270                 return -1;
271         }
272         if(listen(fd, 5) == -1) {
273                 log_errno("listen");
274                 fd_close(fd);
275                 return -1;
276         }
277         return fd;
278 }
279
280 /** setup SSL connection to the client */
281 static SSL*
282 setup_ssl(int s, SSL_CTX* ctx)
283 {
284         SSL* ssl = SSL_new(ctx);
285         if(!ssl) return NULL;
286         SSL_set_accept_state(ssl);
287         (void)SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
288         if(!SSL_set_fd(ssl, s)) {
289                 SSL_free(ssl);
290                 return NULL;
291         }
292         return ssl;
293 }
294
295 /** check a file name for safety */
296 static int
297 file_name_is_safe(char* s)
298 {
299         size_t l = strlen(s);
300         if(s[0] != '/')
301                 return 0; /* must start with / */
302         if(strstr(s, "/../"))
303                 return 0; /* no updirs in URL */
304         if(l>=3 && s[l-1]=='.' && s[l-2]=='.' && s[l-3]=='/')
305                 return 0; /* ends with /.. */
306         return 1;
307 }
308
309 /** adjust host and filename */
310 static void
311 adjust_host_file(char* host, char* file)
312 {
313         size_t i, len;
314         /* remove a port number if present */
315         if(strrchr(host, ':'))
316                 *strrchr(host, ':') = 0;
317         /* lowercase */
318         len = strlen(host);
319         for(i=0; i<len; i++)
320                 host[i] = tolower((unsigned char)host[i]);
321         len = strlen(file);
322         for(i=0; i<len; i++)
323                 file[i] = tolower((unsigned char)file[i]);
324 }
325
326 /** check a host name for safety */
327 static int
328 host_name_is_safe(char* s)
329 {
330         if(strchr(s, '/'))
331                 return 0;
332         if(strcmp(s, "..") == 0)
333                 return 0;
334         if(strcmp(s, ".") == 0)
335                 return 0;
336         return 1;
337 }
338
339 /** provide file in whole transfer */
340 static void
341 provide_file_10(SSL* ssl, char* fname)
342 {
343         char* buf, *at;
344         size_t len, avail, header_reserve=1024;
345         FILE* in = fopen(fname, 
346 #ifndef USE_WINSOCK
347                 "r"
348 #else
349                 "rb"
350 #endif
351                 );
352         int r;
353         const char* rcode = "200 OK";
354         if(!in) {
355                 char hdr[1024];
356                 rcode = "404 File not found";
357                 r = snprintf(hdr, sizeof(hdr), "HTTP/1.1 %s\r\n\r\n", rcode);
358                 if(SSL_write(ssl, hdr, 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         r = snprintf(at, avail, "HTTP/1.1 %s\r\n", rcode);
375         at += r;
376         avail -= r;
377         r = snprintf(at, avail, "Server: petal/%s\r\n", PACKAGE_VERSION);
378         at += r;
379         avail -= r;
380         r = snprintf(at, avail, "Content-Length: %u\r\n", (unsigned)len);
381         at += r;
382         avail -= r;
383         r = snprintf(at, avail, "\r\n");
384         at += r;
385         avail -= r;
386         if(avail < len) { /* robust */
387                 free(buf);
388                 fclose(in);
389                 return;
390         }
391         if(fread(at, 1, len, in) != len) {
392                 free(buf);
393                 fclose(in);
394                 return;
395         }
396         fclose(in);
397         at += len;
398         avail -= len;
399         if(SSL_write(ssl, buf, at-buf) <= 0) {
400                 /* write failure */
401         }
402         free(buf);
403 }
404
405 /** provide file over SSL, chunked encoding */
406 static void
407 provide_file_chunked(SSL* ssl, char* fname)
408 {
409         char buf[16384];
410         char* at = buf;
411         size_t avail = sizeof(buf);
412         int r;
413         FILE* in = fopen(fname, 
414 #ifndef USE_WINSOCK
415                 "r"
416 #else
417                 "rb"
418 #endif
419                 );
420         const char* rcode = "200 OK";
421         if(!in) {
422                 rcode = "404 File not found";
423         }
424
425         /* print headers */
426         r = snprintf(at, avail, "HTTP/1.1 %s\r\n", rcode);
427         at += r;
428         avail -= r;
429         r = snprintf(at, avail, "Server: petal/%s\r\n", PACKAGE_VERSION);
430         at += r;
431         avail -= r;
432         r = snprintf(at, avail, "Transfer-Encoding: chunked\r\n");
433         at += r;
434         avail -= r;
435         r = snprintf(at, avail, "Connection: close\r\n");
436         at += r;
437         avail -= r;
438         r = snprintf(at, avail, "\r\n");
439         at += r;
440         avail -= r;
441         if(avail < 16) { /* robust */
442                 if(in) fclose(in);
443                 return;
444         }
445
446         do {
447                 char tmpbuf[sizeof(buf)];
448                 /* read chunk; space-16 for xxxxCRLF..CRLF0CRLFCRLF (3 spare)*/
449                 size_t red = in?fread(tmpbuf, 1, avail-16, in):0;
450                 /* prepare chunk */
451                 r = snprintf(at, avail, "%x\r\n", (unsigned)red);
452                 if(verb >= 3)
453                 {printf("chunk len %x\n", (unsigned)red); fflush(stdout);}
454                 at += r;
455                 avail -= r;
456                 if(red != 0) {
457                         if(red > avail) break; /* robust */
458                         memmove(at, tmpbuf, red);
459                         at += red;
460                         avail -= red;
461                         r = snprintf(at, avail, "\r\n");
462                         at += r;
463                         avail -= r;
464                 }
465                 if(in && feof(in) && red != 0) {
466                         r = snprintf(at, avail, "0\r\n");
467                         at += r;
468                         avail -= r;
469                 }
470                 if(!in || feof(in)) {
471                         r = snprintf(at, avail, "\r\n");
472                         at += r;
473                         avail -= r;
474                 }
475                 /* send chunk */
476                 if(SSL_write(ssl, buf, at-buf) <= 0) {
477                         /* SSL error */
478                         break;
479                 }
480
481                 /* setup for next chunk */
482                 at = buf;
483                 avail = sizeof(buf);
484         } while(in && !feof(in) && !ferror(in));
485
486         if(in) fclose(in);
487 }
488
489 /** provide service to the ssl descriptor */
490 static void
491 service_ssl(SSL* ssl, struct sockaddr_storage* from, socklen_t falen)
492 {
493         char file[1024];
494         char host[1024];
495         char combined[2048];
496         int vs = 11;
497         if(!read_http_headers(ssl, file, sizeof(file), host, sizeof(host),
498                 &vs))
499                 return;
500         adjust_host_file(host, file);
501         if(host[0] == 0 || !host_name_is_safe(host))
502                 (void)strlcpy(host, "default", sizeof(host));
503         if(!file_name_is_safe(file)) {
504                 return;
505         }
506         snprintf(combined, sizeof(combined), "%s%s", host, file);
507         if(verb) {
508                 char out[100];
509                 void* a = &((struct sockaddr_in*)from)->sin_addr;
510                 if(falen != (socklen_t)sizeof(struct sockaddr_in))
511                         a = &((struct sockaddr_in6*)from)->sin6_addr;
512                 out[0]=0;
513                 (void)inet_ntop((int)((struct sockaddr_in*)from)->sin_family,
514                         a, out, (socklen_t)sizeof(out));
515                 printf("%s requests %s\n", out, combined);
516                 fflush(stdout);
517         }
518         if(vs == 10)
519                 provide_file_10(ssl, combined);
520         else    provide_file_chunked(ssl, combined);
521 }
522
523 /** provide ssl service */
524 static void
525 do_service(char* addr, int port, char* key, char* cert)
526 {
527         SSL_CTX* sslctx = setup_ctx(key, cert);
528         int fd = setup_fd(addr, port);
529         int go = 1;
530         if(fd == -1) print_exit("could not setup sockets");
531         if(verb) {printf("petal start\n"); fflush(stdout);}
532         while(go) {
533                 struct sockaddr_storage from;
534                 socklen_t flen = (socklen_t)sizeof(from);
535                 int s = accept(fd, (struct sockaddr*)&from, &flen);
536                 if(verb) fflush(stdout);
537                 if(s != -1) {
538                         SSL* ssl = setup_ssl(s, sslctx);
539                         if(verb) fflush(stdout);
540                         if(ssl) {
541                                 service_ssl(ssl, &from, flen);
542                                 if(verb) fflush(stdout);
543                                 SSL_shutdown(ssl);
544                                 SSL_free(ssl);
545                         }
546                         fd_close(s);
547                 } else if (verb >=2) log_errno("accept");
548                 if(verb) fflush(stdout);
549         }
550         /* if we get a kill signal, the process dies and the OS reaps us */
551         if(verb) printf("petal end\n");
552         fd_close(fd);
553         SSL_CTX_free(sslctx);
554 }
555
556 /** getopt global, in case header files fail to declare it. */
557 extern int optind;
558 /** getopt global, in case header files fail to declare it. */
559 extern char* optarg;
560
561 /** Main routine for petal */
562 int main(int argc, char* argv[])
563 {
564         int c;
565         int port = 443;
566         char* addr = "127.0.0.1", *key = "petal.key", *cert = "petal.pem";
567 #ifdef USE_WINSOCK
568         WSADATA wsa_data;
569         if((c=WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0)
570         {       printf("WSAStartup failed\n"); exit(1); }
571         atexit((void (*)(void))WSACleanup);
572 #endif
573
574         /* parse the options */
575         while( (c=getopt(argc, argv, "a:c:k:hp:v")) != -1) {
576                 switch(c) {
577                 case 'a':
578                         addr = optarg;
579                         break;
580                 case 'c':
581                         cert = optarg;
582                         break;
583                 case 'k':
584                         key = optarg;
585                         break;
586                 case 'p':
587                         port = atoi(optarg);
588                         break;
589                 case 'v':
590                         verb++;
591                         break;
592                 case '?':
593                 case 'h':
594                 default:
595                         usage();
596                 }
597         }
598         argc -= optind;
599         argv += optind;
600         if(argc != 0)
601                 usage();
602
603 #ifdef SIGPIPE
604         (void)signal(SIGPIPE, SIG_IGN);
605 #endif
606         ERR_load_crypto_strings();
607         ERR_load_SSL_strings();
608         OpenSSL_add_all_algorithms();
609         (void)SSL_library_init();
610
611         do_service(addr, port, key, cert);
612
613         CRYPTO_cleanup_all_ex_data();
614         ERR_remove_state(0);
615         ERR_free_strings();
616         RAND_cleanup();
617         return 0;
618 }