]>
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 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");
248 /** setup listening TCP */
250 setup_fd(char* addr
, int port
)
252 struct sockaddr_storage ad
;
256 int fam
= parse_ip_addr(addr
, port
, &ad
, &len
);
257 fd
= socket(fam
, SOCK_STREAM
, 0);
262 if(setsockopt(fd
, SOL_SOCKET
, SO_REUSEADDR
,
263 (void*)&c
, (socklen_t
) sizeof(int)) < 0) {
264 log_errno("setsockopt(SOL_SOCKET, SO_REUSEADDR)");
266 if(bind(fd
, (struct sockaddr
*)&ad
, len
) == -1) {
271 if(listen(fd
, 5) == -1) {
279 /** setup SSL connection to the client */
281 setup_ssl(int s
, SSL_CTX
* ctx
)
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
)) {
294 /** check a file name for safety */
296 file_name_is_safe(char* s
)
298 size_t l
= strlen(s
);
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 /.. */
308 /** adjust host and filename */
310 adjust_host_file(char* host
, char* file
)
313 /* remove a port number if present */
314 if(strrchr(host
, ':'))
315 *strrchr(host
, ':') = 0;
319 host
[i
] = tolower((unsigned char)host
[i
]);
322 file
[i
] = tolower((unsigned char)file
[i
]);
325 /** check a host name for safety */
327 host_name_is_safe(char* s
)
331 if(strcmp(s
, "..") == 0)
333 if(strcmp(s
, ".") == 0)
338 /** provide file in whole transfer */
340 provide_file_10(SSL
* ssl
, char* fname
)
343 size_t len
, avail
, header_reserve
=1024;
344 FILE* in
= fopen(fname
,
352 const char* rcode
= "200 OK";
355 rcode
= "404 File not found";
356 snprintf(hdr
, sizeof(hdr
), "HTTP/1.1 %s\r\n\r\n", rcode
);
358 if(SSL_write(ssl
, hdr
, (int)r
) <= 0) {
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
);
372 avail
= len
+header_reserve
;
374 snprintf(at
, avail
, "HTTP/1.1 %s\r\n", rcode
);
378 snprintf(at
, avail
, "Server: petal/%s\r\n", PACKAGE_VERSION
);
382 snprintf(at
, avail
, "Content-Length: %u\r\n", (unsigned)len
);
386 snprintf(at
, avail
, "\r\n");
390 if(avail
< len
) { /* robust */
395 if(fread(at
, 1, len
, in
) != len
) {
403 if(SSL_write(ssl
, buf
, at
-buf
) <= 0) {
409 /** provide file over SSL, chunked encoding */
411 provide_file_chunked(SSL
* ssl
, char* fname
)
415 size_t avail
= sizeof(buf
);
417 FILE* in
= fopen(fname
,
424 const char* rcode
= "200 OK";
426 rcode
= "404 File not found";
430 snprintf(at
, avail
, "HTTP/1.1 %s\r\n", rcode
);
434 snprintf(at
, avail
, "Server: petal/%s\r\n", PACKAGE_VERSION
);
438 snprintf(at
, avail
, "Transfer-Encoding: chunked\r\n");
442 snprintf(at
, avail
, "Connection: close\r\n");
446 snprintf(at
, avail
, "\r\n");
450 if(avail
< 16) { /* robust */
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;
460 snprintf(at
, avail
, "%x\r\n", (unsigned)red
);
463 {printf("chunk len %x\n", (unsigned)red
); fflush(stdout
);}
467 if(red
> avail
) break; /* robust */
468 memmove(at
, tmpbuf
, red
);
471 snprintf(at
, avail
, "\r\n");
476 if(in
&& feof(in
) && red
!= 0) {
477 snprintf(at
, avail
, "0\r\n");
482 if(!in
|| feof(in
)) {
483 snprintf(at
, avail
, "\r\n");
489 if(SSL_write(ssl
, buf
, at
-buf
) <= 0) {
494 /* setup for next chunk */
497 } while(in
&& !feof(in
) && !ferror(in
));
502 /** provide service to the ssl descriptor */
504 service_ssl(SSL
* ssl
, struct sockaddr_storage
* from
, socklen_t falen
)
510 if(!read_http_headers(ssl
, file
, sizeof(file
), host
, sizeof(host
),
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
)) {
519 snprintf(combined
, sizeof(combined
), "%s%s", host
, file
);
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
;
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
);
532 provide_file_10(ssl
, combined
);
533 else provide_file_chunked(ssl
, combined
);
536 /** provide ssl service */
538 do_service(char* addr
, int port
, char* key
, char* cert
)
540 SSL_CTX
* sslctx
= setup_ctx(key
, cert
);
541 int fd
= setup_fd(addr
, port
);
543 if(fd
== -1) print_exit("could not setup sockets");
544 if(verb
) {printf("petal start\n"); fflush(stdout
);}
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
);
551 SSL
* ssl
= setup_ssl(s
, sslctx
);
552 if(verb
) fflush(stdout
);
554 service_ssl(ssl
, &from
, flen
);
555 if(verb
) fflush(stdout
);
560 } else if (verb
>=2) log_errno("accept");
561 if(verb
) fflush(stdout
);
563 /* if we get a kill signal, the process dies and the OS reaps us */
564 if(verb
) printf("petal end\n");
566 SSL_CTX_free(sslctx
);
569 /** getopt global, in case header files fail to declare it. */
571 /** getopt global, in case header files fail to declare it. */
574 /** Main routine for petal */
575 int main(int argc
, char* argv
[])
579 char* addr
= "127.0.0.1", *key
= "petal.key", *cert
= "petal.pem";
582 if((c
=WSAStartup(MAKEWORD(2,2), &wsa_data
)) != 0)
583 { printf("WSAStartup failed\n"); exit(1); }
584 atexit((void (*)(void))WSACleanup
);
587 /* parse the options */
588 while( (c
=getopt(argc
, argv
, "a:c:k:hp:v")) != -1) {
617 (void)signal(SIGPIPE
, SIG_IGN
);
619 ERR_load_crypto_strings();
620 ERR_load_SSL_strings();
621 OpenSSL_add_all_algorithms();
622 (void)SSL_library_init();
624 do_service(addr
, port
, key
, cert
);
626 CRYPTO_cleanup_all_ex_data();