]>
git.saurik.com Git - apple/network_cmds.git/blob - unbound/testcode/petal.c
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();