xref: /openbsd-src/usr.sbin/unbound/testcode/petal.c (revision a43524d9cc222a049058246319ec6a29f2d9ca78)
1712b2f30Ssthen /*
2712b2f30Ssthen  * petal.c - https daemon that is small and beautiful.
3712b2f30Ssthen  *
4712b2f30Ssthen  * Copyright (c) 2010, NLnet Labs. All rights reserved.
5712b2f30Ssthen  *
6712b2f30Ssthen  * This software is open source.
7712b2f30Ssthen  *
8712b2f30Ssthen  * Redistribution and use in source and binary forms, with or without
9712b2f30Ssthen  * modification, are permitted provided that the following conditions
10712b2f30Ssthen  * are met:
11712b2f30Ssthen  *
12712b2f30Ssthen  * Redistributions of source code must retain the above copyright notice,
13712b2f30Ssthen  * this list of conditions and the following disclaimer.
14712b2f30Ssthen  *
15712b2f30Ssthen  * Redistributions in binary form must reproduce the above copyright notice,
16712b2f30Ssthen  * this list of conditions and the following disclaimer in the documentation
17712b2f30Ssthen  * and/or other materials provided with the distribution.
18712b2f30Ssthen  *
19712b2f30Ssthen  * Neither the name of the NLNET LABS nor the names of its contributors may
20712b2f30Ssthen  * be used to endorse or promote products derived from this software without
21712b2f30Ssthen  * specific prior written permission.
22712b2f30Ssthen  *
23712b2f30Ssthen  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24712b2f30Ssthen  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25712b2f30Ssthen  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26712b2f30Ssthen  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27712b2f30Ssthen  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28712b2f30Ssthen  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29712b2f30Ssthen  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30712b2f30Ssthen  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31712b2f30Ssthen  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32712b2f30Ssthen  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33712b2f30Ssthen  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34712b2f30Ssthen  */
35712b2f30Ssthen 
36712b2f30Ssthen /**
37712b2f30Ssthen  * \file
38712b2f30Ssthen  *
39712b2f30Ssthen  * HTTP1.1/SSL server.
40712b2f30Ssthen  */
41712b2f30Ssthen 
42712b2f30Ssthen #include "config.h"
43712b2f30Ssthen #ifdef HAVE_GETOPT_H
44712b2f30Ssthen #include <getopt.h>
45712b2f30Ssthen #endif
46712b2f30Ssthen #ifdef HAVE_OPENSSL_SSL_H
47712b2f30Ssthen #include <openssl/ssl.h>
48712b2f30Ssthen #endif
49712b2f30Ssthen #ifdef HAVE_OPENSSL_ERR_H
50712b2f30Ssthen #include <openssl/err.h>
51712b2f30Ssthen #endif
52712b2f30Ssthen #ifdef HAVE_OPENSSL_RAND_H
53712b2f30Ssthen #include <openssl/rand.h>
54712b2f30Ssthen #endif
55712b2f30Ssthen #include <openssl/x509.h>
56712b2f30Ssthen #include <openssl/pem.h>
57712b2f30Ssthen #include <ctype.h>
58712b2f30Ssthen #include <signal.h>
59712b2f30Ssthen #if defined(UNBOUND_ALLOC_LITE) || defined(UNBOUND_ALLOC_STATS)
60712b2f30Ssthen #ifdef malloc
61712b2f30Ssthen #undef malloc
62712b2f30Ssthen #endif
63712b2f30Ssthen #ifdef free
64712b2f30Ssthen #undef free
65712b2f30Ssthen #endif
66712b2f30Ssthen #endif /* alloc lite or alloc stats */
67712b2f30Ssthen 
68712b2f30Ssthen /** verbosity for this application */
69712b2f30Ssthen static int verb = 0;
70712b2f30Ssthen 
71712b2f30Ssthen /** Give petal usage, and exit (1). */
72712b2f30Ssthen static void
73712b2f30Ssthen usage(void)
74712b2f30Ssthen {
75712b2f30Ssthen 	printf("Usage:	petal [opts]\n");
76712b2f30Ssthen 	printf("	https daemon serves files from ./'host'/filename\n");
77712b2f30Ssthen 	printf("	(no hostname: from the 'default' directory)\n");
78712b2f30Ssthen 	printf("-a addr		bind to this address, 127.0.0.1\n");
79712b2f30Ssthen 	printf("-p port		port number, default 443\n");
80712b2f30Ssthen 	printf("-k keyfile	SSL private key file (PEM), petal.key\n");
81712b2f30Ssthen 	printf("-c certfile	SSL certificate file (PEM), petal.pem\n");
82712b2f30Ssthen 	printf("-v		more verbose\n");
83712b2f30Ssthen 	printf("-h		show this usage help\n");
84712b2f30Ssthen 	printf("Version %s\n", PACKAGE_VERSION);
85712b2f30Ssthen 	printf("BSD licensed, see LICENSE in source package for details.\n");
86712b2f30Ssthen 	printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
87712b2f30Ssthen 	exit(1);
88712b2f30Ssthen }
89712b2f30Ssthen 
90712b2f30Ssthen /** fatal exit */
91712b2f30Ssthen static void print_exit(const char* str) {printf("error %s\n", str); exit(1);}
92712b2f30Ssthen /** print errno */
93712b2f30Ssthen static void log_errno(const char* str)
94712b2f30Ssthen {printf("error %s: %s\n", str, strerror(errno));}
95712b2f30Ssthen 
96712b2f30Ssthen /** parse a text IP address into a sockaddr */
97712b2f30Ssthen static int
98712b2f30Ssthen parse_ip_addr(char* str, int port, struct sockaddr_storage* ret, socklen_t* l)
99712b2f30Ssthen {
100712b2f30Ssthen 	socklen_t len = 0;
101712b2f30Ssthen 	struct sockaddr_storage* addr = NULL;
102712b2f30Ssthen 	struct sockaddr_in6 a6;
103712b2f30Ssthen 	struct sockaddr_in a;
104712b2f30Ssthen 	uint16_t p = (uint16_t)port;
105712b2f30Ssthen 	int fam = 0;
106712b2f30Ssthen 	memset(&a6, 0, sizeof(a6));
107712b2f30Ssthen 	memset(&a, 0, sizeof(a));
108712b2f30Ssthen 
109712b2f30Ssthen 	if(inet_pton(AF_INET6, str, &a6.sin6_addr) > 0) {
110712b2f30Ssthen 		/* it is an IPv6 */
111712b2f30Ssthen 		fam = AF_INET6;
112712b2f30Ssthen 		a6.sin6_family = AF_INET6;
113712b2f30Ssthen 		a6.sin6_port = (in_port_t)htons(p);
114712b2f30Ssthen 		addr = (struct sockaddr_storage*)&a6;
115712b2f30Ssthen 		len = (socklen_t)sizeof(struct sockaddr_in6);
116712b2f30Ssthen 	}
117712b2f30Ssthen 	if(inet_pton(AF_INET, str, &a.sin_addr) > 0) {
118712b2f30Ssthen 		/* it is an IPv4 */
119712b2f30Ssthen 		fam = AF_INET;
120712b2f30Ssthen 		a.sin_family = AF_INET;
121712b2f30Ssthen 		a.sin_port = (in_port_t)htons(p);
122712b2f30Ssthen 		addr = (struct sockaddr_storage*)&a;
123712b2f30Ssthen 		len = (socklen_t)sizeof(struct sockaddr_in);
124712b2f30Ssthen 	}
125712b2f30Ssthen 	if(!len) print_exit("cannot parse addr");
126712b2f30Ssthen 	*l = len;
127712b2f30Ssthen 	memmove(ret, addr, len);
128712b2f30Ssthen 	return fam;
129712b2f30Ssthen }
130712b2f30Ssthen 
131712b2f30Ssthen /** close the fd */
132712b2f30Ssthen static void
133712b2f30Ssthen fd_close(int fd)
134712b2f30Ssthen {
135712b2f30Ssthen #ifndef USE_WINSOCK
136712b2f30Ssthen 	close(fd);
137712b2f30Ssthen #else
138712b2f30Ssthen 	closesocket(fd);
139712b2f30Ssthen #endif
140712b2f30Ssthen }
141712b2f30Ssthen 
142712b2f30Ssthen /**
143712b2f30Ssthen  * Read one line from SSL
144712b2f30Ssthen  * zero terminates.
145712b2f30Ssthen  * skips "\r\n" (but not copied to buf).
146712b2f30Ssthen  * @param ssl: the SSL connection to read from (blocking).
147712b2f30Ssthen  * @param buf: buffer to return line in.
148712b2f30Ssthen  * @param len: size of the buffer.
149712b2f30Ssthen  * @return 0 on error, 1 on success.
150712b2f30Ssthen  */
151712b2f30Ssthen static int
152712b2f30Ssthen read_ssl_line(SSL* ssl, char* buf, size_t len)
153712b2f30Ssthen {
154712b2f30Ssthen 	size_t n = 0;
155712b2f30Ssthen 	int r;
156712b2f30Ssthen 	int endnl = 0;
157712b2f30Ssthen 	while(1) {
158712b2f30Ssthen 		if(n >= len) {
159712b2f30Ssthen 			if(verb) printf("line too long\n");
160712b2f30Ssthen 			return 0;
161712b2f30Ssthen 		}
162712b2f30Ssthen 		if((r = SSL_read(ssl, buf+n, 1)) <= 0) {
163712b2f30Ssthen 			if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) {
164712b2f30Ssthen 				/* EOF */
165712b2f30Ssthen 				break;
166712b2f30Ssthen 			}
167712b2f30Ssthen 			if(verb) printf("could not SSL_read\n");
168712b2f30Ssthen 			return 0;
169712b2f30Ssthen 		}
170712b2f30Ssthen 		if(endnl && buf[n] == '\n') {
171712b2f30Ssthen 			break;
172712b2f30Ssthen 		} else if(endnl) {
173712b2f30Ssthen 			/* bad data */
174712b2f30Ssthen 			if(verb) printf("error: stray linefeeds\n");
175712b2f30Ssthen 			return 0;
176712b2f30Ssthen 		} else if(buf[n] == '\r') {
177712b2f30Ssthen 			/* skip \r, and also \n on the wire */
178712b2f30Ssthen 			endnl = 1;
179712b2f30Ssthen 			continue;
180712b2f30Ssthen 		} else if(buf[n] == '\n') {
181712b2f30Ssthen 			/* skip the \n, we are done */
182712b2f30Ssthen 			break;
183712b2f30Ssthen 		} else n++;
184712b2f30Ssthen 	}
185712b2f30Ssthen 	buf[n] = 0;
186712b2f30Ssthen 	return 1;
187712b2f30Ssthen }
188712b2f30Ssthen 
189712b2f30Ssthen /** process one http header */
190712b2f30Ssthen static int
191712b2f30Ssthen process_one_header(char* buf, char* file, size_t flen, char* host, size_t hlen,
192712b2f30Ssthen 	int* vs)
193712b2f30Ssthen {
194712b2f30Ssthen 	if(strncasecmp(buf, "GET ", 4) == 0) {
195712b2f30Ssthen 		char* e = strstr(buf, " HTTP/1.1");
196712b2f30Ssthen 		if(!e) e = strstr(buf, " http/1.1");
197712b2f30Ssthen 		if(!e) {
198712b2f30Ssthen 			e = strstr(buf, " HTTP/1.0");
199712b2f30Ssthen 			if(!e) e = strstr(buf, " http/1.0");
200712b2f30Ssthen 			if(!e) e = strrchr(buf, ' ');
201712b2f30Ssthen 			if(!e) e = strrchr(buf, '\t');
202712b2f30Ssthen 			if(e) *vs = 10;
203712b2f30Ssthen 		}
204712b2f30Ssthen 		if(e) *e = 0;
205712b2f30Ssthen 		if(strlen(buf) < 4) return 0;
206712b2f30Ssthen 		(void)strlcpy(file, buf+4, flen);
207712b2f30Ssthen 	} else if(strncasecmp(buf, "Host: ", 6) == 0) {
208712b2f30Ssthen 		(void)strlcpy(host, buf+6, hlen);
209712b2f30Ssthen 	}
210712b2f30Ssthen 	return 1;
211712b2f30Ssthen }
212712b2f30Ssthen 
213712b2f30Ssthen /** read http headers and process them */
214712b2f30Ssthen static int
215712b2f30Ssthen read_http_headers(SSL* ssl, char* file, size_t flen, char* host, size_t hlen,
216712b2f30Ssthen 	int* vs)
217712b2f30Ssthen {
218712b2f30Ssthen 	char buf[1024];
219712b2f30Ssthen 	file[0] = 0;
220712b2f30Ssthen 	host[0] = 0;
221712b2f30Ssthen 	while(read_ssl_line(ssl, buf, sizeof(buf))) {
222712b2f30Ssthen 		if(verb>=2) printf("read: %s\n", buf);
22383152a15Ssthen 		if(buf[0] == 0) {
22483152a15Ssthen 			int e = ERR_peek_error();
22583152a15Ssthen 			printf("error string: %s\n", ERR_reason_error_string(e));
226712b2f30Ssthen 			return 1;
22783152a15Ssthen 		}
228712b2f30Ssthen 		if(!process_one_header(buf, file, flen, host, hlen, vs))
229712b2f30Ssthen 			return 0;
230712b2f30Ssthen 	}
231712b2f30Ssthen 	return 0;
232712b2f30Ssthen }
233712b2f30Ssthen 
234712b2f30Ssthen /** setup SSL context */
235712b2f30Ssthen static SSL_CTX*
236712b2f30Ssthen setup_ctx(char* key, char* cert)
237712b2f30Ssthen {
238712b2f30Ssthen 	SSL_CTX* ctx = SSL_CTX_new(SSLv23_server_method());
239712b2f30Ssthen 	if(!ctx) print_exit("out of memory");
240d5d2b63dSsthen #if SSL_OP_NO_SSLv2 != 0
241712b2f30Ssthen 	(void)SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
242d5d2b63dSsthen #endif
243712b2f30Ssthen 	(void)SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
244a6cc1574Ssthen #ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL
245a6cc1574Ssthen 	SSL_CTX_set_security_level(ctx, 0); /* for keys in tests */
246a6cc1574Ssthen #endif
24783152a15Ssthen 	if(!SSL_CTX_use_certificate_chain_file(ctx, cert)) {
24883152a15Ssthen 		int e = ERR_peek_error();
24983152a15Ssthen 		printf("error string: %s\n", ERR_reason_error_string(e));
250712b2f30Ssthen 		print_exit("cannot read cert");
25183152a15Ssthen 	}
252712b2f30Ssthen 	if(!SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM))
253712b2f30Ssthen 		print_exit("cannot read key");
254712b2f30Ssthen 	if(!SSL_CTX_check_private_key(ctx))
255712b2f30Ssthen 		print_exit("private key is not correct");
256712b2f30Ssthen #if HAVE_DECL_SSL_CTX_SET_ECDH_AUTO
257712b2f30Ssthen 	if (!SSL_CTX_set_ecdh_auto(ctx,1))
258712b2f30Ssthen 		if(verb>=1) printf("failed to set_ecdh_auto, not enabling ECDHE\n");
259*a43524d9Ssthen #elif defined(USE_ECDSA) && defined(HAVE_SSL_CTX_SET_TMP_ECDH)
260712b2f30Ssthen 	if(1) {
261712b2f30Ssthen 		EC_KEY *ecdh = EC_KEY_new_by_curve_name (NID_X9_62_prime256v1);
262712b2f30Ssthen 		if (!ecdh) {
263712b2f30Ssthen 			if(verb>=1) printf("could not find p256, not enabling ECDHE\n");
264712b2f30Ssthen 		} else {
265712b2f30Ssthen 			if (1 != SSL_CTX_set_tmp_ecdh (ctx, ecdh)) {
266712b2f30Ssthen 				if(verb>=1) printf("Error in SSL_CTX_set_tmp_ecdh, not enabling ECDHE\n");
267712b2f30Ssthen 			}
268712b2f30Ssthen 			EC_KEY_free(ecdh);
269712b2f30Ssthen 		}
270712b2f30Ssthen 	}
271712b2f30Ssthen #endif
272712b2f30Ssthen 	if(!SSL_CTX_load_verify_locations(ctx, cert, NULL))
273712b2f30Ssthen 		print_exit("cannot load cert verify locations");
274712b2f30Ssthen 	return ctx;
275712b2f30Ssthen }
276712b2f30Ssthen 
277712b2f30Ssthen /** setup listening TCP */
278712b2f30Ssthen static int
279712b2f30Ssthen setup_fd(char* addr, int port)
280712b2f30Ssthen {
281712b2f30Ssthen 	struct sockaddr_storage ad;
282712b2f30Ssthen 	socklen_t len;
283712b2f30Ssthen 	int fd;
284712b2f30Ssthen 	int c = 1;
285712b2f30Ssthen 	int fam = parse_ip_addr(addr, port, &ad, &len);
286712b2f30Ssthen 	fd = socket(fam, SOCK_STREAM, 0);
287712b2f30Ssthen 	if(fd == -1) {
288712b2f30Ssthen 		log_errno("socket");
289712b2f30Ssthen 		return -1;
290712b2f30Ssthen 	}
291712b2f30Ssthen 	if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
292712b2f30Ssthen 		(void*)&c, (socklen_t) sizeof(int)) < 0) {
293712b2f30Ssthen 		log_errno("setsockopt(SOL_SOCKET, SO_REUSEADDR)");
294712b2f30Ssthen 	}
295712b2f30Ssthen 	if(bind(fd, (struct sockaddr*)&ad, len) == -1) {
296712b2f30Ssthen 		log_errno("bind");
297712b2f30Ssthen 		fd_close(fd);
298712b2f30Ssthen 		return -1;
299712b2f30Ssthen 	}
300712b2f30Ssthen 	if(listen(fd, 5) == -1) {
301712b2f30Ssthen 		log_errno("listen");
302712b2f30Ssthen 		fd_close(fd);
303712b2f30Ssthen 		return -1;
304712b2f30Ssthen 	}
305712b2f30Ssthen 	return fd;
306712b2f30Ssthen }
307712b2f30Ssthen 
308712b2f30Ssthen /** setup SSL connection to the client */
309712b2f30Ssthen static SSL*
310712b2f30Ssthen setup_ssl(int s, SSL_CTX* ctx)
311712b2f30Ssthen {
312712b2f30Ssthen 	SSL* ssl = SSL_new(ctx);
313712b2f30Ssthen 	if(!ssl) return NULL;
314712b2f30Ssthen 	SSL_set_accept_state(ssl);
315b0dfc31bSsthen 	(void)SSL_set_mode(ssl, (long)SSL_MODE_AUTO_RETRY);
316712b2f30Ssthen 	if(!SSL_set_fd(ssl, s)) {
317712b2f30Ssthen 		SSL_free(ssl);
318712b2f30Ssthen 		return NULL;
319712b2f30Ssthen 	}
320712b2f30Ssthen 	return ssl;
321712b2f30Ssthen }
322712b2f30Ssthen 
323712b2f30Ssthen /** check a file name for safety */
324712b2f30Ssthen static int
325712b2f30Ssthen file_name_is_safe(char* s)
326712b2f30Ssthen {
327712b2f30Ssthen 	size_t l = strlen(s);
328712b2f30Ssthen 	if(s[0] != '/')
329712b2f30Ssthen 		return 0; /* must start with / */
330712b2f30Ssthen 	if(strstr(s, "/../"))
331712b2f30Ssthen 		return 0; /* no updirs in URL */
332712b2f30Ssthen 	if(l>=3 && s[l-1]=='.' && s[l-2]=='.' && s[l-3]=='/')
333712b2f30Ssthen 		return 0; /* ends with /.. */
334712b2f30Ssthen 	return 1;
335712b2f30Ssthen }
336712b2f30Ssthen 
33766a34dc2Ssthen /** adjust host */
338712b2f30Ssthen static void
33966a34dc2Ssthen adjust_host(char* host)
340712b2f30Ssthen {
341712b2f30Ssthen 	size_t i, len;
342712b2f30Ssthen 	/* remove a port number if present */
343712b2f30Ssthen 	if(strrchr(host, ':'))
344712b2f30Ssthen 		*strrchr(host, ':') = 0;
345712b2f30Ssthen 	/* lowercase */
346712b2f30Ssthen 	len = strlen(host);
347712b2f30Ssthen 	for(i=0; i<len; i++)
348712b2f30Ssthen 		host[i] = tolower((unsigned char)host[i]);
34966a34dc2Ssthen }
35066a34dc2Ssthen 
35166a34dc2Ssthen /** adjust filename */
35266a34dc2Ssthen static void
35366a34dc2Ssthen adjust_file(char* file)
35466a34dc2Ssthen {
35566a34dc2Ssthen 	size_t i, len;
356712b2f30Ssthen 	len = strlen(file);
357712b2f30Ssthen 	for(i=0; i<len; i++)
358712b2f30Ssthen 		file[i] = tolower((unsigned char)file[i]);
359712b2f30Ssthen }
360712b2f30Ssthen 
361712b2f30Ssthen /** check a host name for safety */
362712b2f30Ssthen static int
363712b2f30Ssthen host_name_is_safe(char* s)
364712b2f30Ssthen {
365712b2f30Ssthen 	if(strchr(s, '/'))
366712b2f30Ssthen 		return 0;
367712b2f30Ssthen 	if(strcmp(s, "..") == 0)
368712b2f30Ssthen 		return 0;
369712b2f30Ssthen 	if(strcmp(s, ".") == 0)
370712b2f30Ssthen 		return 0;
371712b2f30Ssthen 	return 1;
372712b2f30Ssthen }
373712b2f30Ssthen 
374712b2f30Ssthen /** provide file in whole transfer */
375712b2f30Ssthen static void
376712b2f30Ssthen provide_file_10(SSL* ssl, char* fname)
377712b2f30Ssthen {
378712b2f30Ssthen 	char* buf, *at;
379712b2f30Ssthen 	size_t len, avail, header_reserve=1024;
380712b2f30Ssthen 	FILE* in = fopen(fname,
381712b2f30Ssthen #ifndef USE_WINSOCK
382712b2f30Ssthen 		"r"
383712b2f30Ssthen #else
384712b2f30Ssthen 		"rb"
385712b2f30Ssthen #endif
386712b2f30Ssthen 		);
387712b2f30Ssthen 	size_t r;
388712b2f30Ssthen 	const char* rcode = "200 OK";
389712b2f30Ssthen 	if(!in) {
390712b2f30Ssthen 		char hdr[1024];
391712b2f30Ssthen 		rcode = "404 File not found";
392712b2f30Ssthen 		snprintf(hdr, sizeof(hdr), "HTTP/1.1 %s\r\n\r\n", rcode);
393712b2f30Ssthen 		r = strlen(hdr);
394712b2f30Ssthen 		if(SSL_write(ssl, hdr, (int)r) <= 0) {
395712b2f30Ssthen 			/* write failure */
396712b2f30Ssthen 		}
397712b2f30Ssthen 		return;
398712b2f30Ssthen 	}
399712b2f30Ssthen 	fseek(in, 0, SEEK_END);
400712b2f30Ssthen 	len = (size_t)ftell(in);
401712b2f30Ssthen 	fseek(in, 0, SEEK_SET);
402712b2f30Ssthen 	/* plus some space for the header */
403712b2f30Ssthen 	buf = (char*)malloc(len+header_reserve);
404712b2f30Ssthen 	if(!buf) {
405712b2f30Ssthen 		fclose(in);
406712b2f30Ssthen 		return;
407712b2f30Ssthen 	}
408712b2f30Ssthen 	avail = len+header_reserve;
409712b2f30Ssthen 	at = buf;
410712b2f30Ssthen 	snprintf(at, avail, "HTTP/1.1 %s\r\n", rcode);
411712b2f30Ssthen 	r = strlen(at);
412712b2f30Ssthen 	at += r;
413712b2f30Ssthen 	avail -= r;
414712b2f30Ssthen 	snprintf(at, avail, "Server: petal/%s\r\n", PACKAGE_VERSION);
415712b2f30Ssthen 	r = strlen(at);
416712b2f30Ssthen 	at += r;
417712b2f30Ssthen 	avail -= r;
418712b2f30Ssthen 	snprintf(at, avail, "Content-Length: %u\r\n", (unsigned)len);
419712b2f30Ssthen 	r = strlen(at);
420712b2f30Ssthen 	at += r;
421712b2f30Ssthen 	avail -= r;
422712b2f30Ssthen 	snprintf(at, avail, "\r\n");
423712b2f30Ssthen 	r = strlen(at);
424712b2f30Ssthen 	at += r;
425712b2f30Ssthen 	avail -= r;
426712b2f30Ssthen 	if(avail < len) { /* robust */
427712b2f30Ssthen 		free(buf);
428712b2f30Ssthen 		fclose(in);
429712b2f30Ssthen 		return;
430712b2f30Ssthen 	}
431712b2f30Ssthen 	if(fread(at, 1, len, in) != len) {
432712b2f30Ssthen 		free(buf);
433712b2f30Ssthen 		fclose(in);
434712b2f30Ssthen 		return;
435712b2f30Ssthen 	}
436712b2f30Ssthen 	fclose(in);
437712b2f30Ssthen 	at += len;
4388771e50fSsthen 	/* avail -= len; unused */
439712b2f30Ssthen 	if(SSL_write(ssl, buf, at-buf) <= 0) {
440712b2f30Ssthen 		/* write failure */
441712b2f30Ssthen 	}
442712b2f30Ssthen 	free(buf);
443712b2f30Ssthen }
444712b2f30Ssthen 
445712b2f30Ssthen /** provide file over SSL, chunked encoding */
446712b2f30Ssthen static void
447712b2f30Ssthen provide_file_chunked(SSL* ssl, char* fname)
448712b2f30Ssthen {
449712b2f30Ssthen 	char buf[16384];
450712b2f30Ssthen 	char* tmpbuf = NULL;
451712b2f30Ssthen 	char* at = buf;
452712b2f30Ssthen 	size_t avail = sizeof(buf);
453712b2f30Ssthen 	size_t r;
454712b2f30Ssthen 	FILE* in = fopen(fname,
455712b2f30Ssthen #ifndef USE_WINSOCK
456712b2f30Ssthen 		"r"
457712b2f30Ssthen #else
458712b2f30Ssthen 		"rb"
459712b2f30Ssthen #endif
460712b2f30Ssthen 		);
461712b2f30Ssthen 	const char* rcode = "200 OK";
462712b2f30Ssthen 	if(!in) {
463712b2f30Ssthen 		rcode = "404 File not found";
464712b2f30Ssthen 	}
465712b2f30Ssthen 
466712b2f30Ssthen 	/* print headers */
467712b2f30Ssthen 	snprintf(at, avail, "HTTP/1.1 %s\r\n", rcode);
468712b2f30Ssthen 	r = strlen(at);
469712b2f30Ssthen 	at += r;
470712b2f30Ssthen 	avail -= r;
471712b2f30Ssthen 	snprintf(at, avail, "Server: petal/%s\r\n", PACKAGE_VERSION);
472712b2f30Ssthen 	r = strlen(at);
473712b2f30Ssthen 	at += r;
474712b2f30Ssthen 	avail -= r;
475712b2f30Ssthen 	snprintf(at, avail, "Transfer-Encoding: chunked\r\n");
476712b2f30Ssthen 	r = strlen(at);
477712b2f30Ssthen 	at += r;
478712b2f30Ssthen 	avail -= r;
479712b2f30Ssthen 	snprintf(at, avail, "Connection: close\r\n");
480712b2f30Ssthen 	r = strlen(at);
481712b2f30Ssthen 	at += r;
482712b2f30Ssthen 	avail -= r;
483712b2f30Ssthen 	snprintf(at, avail, "\r\n");
484712b2f30Ssthen 	r = strlen(at);
485712b2f30Ssthen 	at += r;
486712b2f30Ssthen 	avail -= r;
487712b2f30Ssthen 	if(avail < 16) { /* robust */
488712b2f30Ssthen 		if(in) fclose(in);
489712b2f30Ssthen 		return;
490712b2f30Ssthen 	}
491712b2f30Ssthen 
492712b2f30Ssthen 	do {
493712b2f30Ssthen 		size_t red;
494712b2f30Ssthen 		free(tmpbuf);
495712b2f30Ssthen 		tmpbuf = malloc(avail-16);
496712b2f30Ssthen 		if(!tmpbuf)
497712b2f30Ssthen 			break;
498712b2f30Ssthen 		/* read chunk; space-16 for xxxxCRLF..CRLF0CRLFCRLF (3 spare)*/
499712b2f30Ssthen 		red = in?fread(tmpbuf, 1, avail-16, in):0;
500712b2f30Ssthen 		/* prepare chunk */
501712b2f30Ssthen 		snprintf(at, avail, "%x\r\n", (unsigned)red);
502712b2f30Ssthen 		r = strlen(at);
503712b2f30Ssthen 		if(verb >= 3)
504712b2f30Ssthen 		{printf("chunk len %x\n", (unsigned)red); fflush(stdout);}
505712b2f30Ssthen 		at += r;
506712b2f30Ssthen 		avail -= r;
507712b2f30Ssthen 		if(red != 0) {
508712b2f30Ssthen 			if(red > avail) break; /* robust */
509712b2f30Ssthen 			memmove(at, tmpbuf, red);
510712b2f30Ssthen 			at += red;
511712b2f30Ssthen 			avail -= red;
512712b2f30Ssthen 			snprintf(at, avail, "\r\n");
513712b2f30Ssthen 			r = strlen(at);
514712b2f30Ssthen 			at += r;
515712b2f30Ssthen 			avail -= r;
516712b2f30Ssthen 		}
517712b2f30Ssthen 		if(in && feof(in) && red != 0) {
518712b2f30Ssthen 			snprintf(at, avail, "0\r\n");
519712b2f30Ssthen 			r = strlen(at);
520712b2f30Ssthen 			at += r;
521712b2f30Ssthen 			avail -= r;
522712b2f30Ssthen 		}
523712b2f30Ssthen 		if(!in || feof(in)) {
524712b2f30Ssthen 			snprintf(at, avail, "\r\n");
525712b2f30Ssthen 			r = strlen(at);
526712b2f30Ssthen 			at += r;
5278771e50fSsthen 			/* avail -= r; unused */
528712b2f30Ssthen 		}
529712b2f30Ssthen 		/* send chunk */
530712b2f30Ssthen 		if(SSL_write(ssl, buf, at-buf) <= 0) {
531712b2f30Ssthen 			/* SSL error */
532712b2f30Ssthen 			break;
533712b2f30Ssthen 		}
534712b2f30Ssthen 
535712b2f30Ssthen 		/* setup for next chunk */
536712b2f30Ssthen 		at = buf;
537712b2f30Ssthen 		avail = sizeof(buf);
538712b2f30Ssthen 	} while(in && !feof(in) && !ferror(in));
539712b2f30Ssthen 
540712b2f30Ssthen 	free(tmpbuf);
541712b2f30Ssthen 	if(in) fclose(in);
542712b2f30Ssthen }
543712b2f30Ssthen 
544712b2f30Ssthen /** provide service to the ssl descriptor */
545712b2f30Ssthen static void
546712b2f30Ssthen service_ssl(SSL* ssl, struct sockaddr_storage* from, socklen_t falen)
547712b2f30Ssthen {
548712b2f30Ssthen 	char file[1024];
549712b2f30Ssthen 	char host[1024];
550712b2f30Ssthen 	char combined[2048];
551712b2f30Ssthen 	int vs = 11;
552712b2f30Ssthen 	if(!read_http_headers(ssl, file, sizeof(file), host, sizeof(host),
553712b2f30Ssthen 		&vs))
554712b2f30Ssthen 		return;
55566a34dc2Ssthen 	if(host[0] != 0) adjust_host(host);
55666a34dc2Ssthen 	if(file[0] != 0) adjust_file(file);
557712b2f30Ssthen 	if(host[0] == 0 || !host_name_is_safe(host))
558712b2f30Ssthen 		(void)strlcpy(host, "default", sizeof(host));
559712b2f30Ssthen 	if(!file_name_is_safe(file)) {
560712b2f30Ssthen 		return;
561712b2f30Ssthen 	}
562712b2f30Ssthen 	snprintf(combined, sizeof(combined), "%s%s", host, file);
563712b2f30Ssthen 	if(verb) {
564712b2f30Ssthen 		char out[100];
565712b2f30Ssthen 		void* a = &((struct sockaddr_in*)from)->sin_addr;
566712b2f30Ssthen 		if(falen != (socklen_t)sizeof(struct sockaddr_in))
567712b2f30Ssthen 			a = &((struct sockaddr_in6*)from)->sin6_addr;
568712b2f30Ssthen 		out[0]=0;
569712b2f30Ssthen 		(void)inet_ntop((int)((struct sockaddr_in*)from)->sin_family,
570712b2f30Ssthen 			a, out, (socklen_t)sizeof(out));
571712b2f30Ssthen 		printf("%s requests %s\n", out, combined);
572712b2f30Ssthen 		fflush(stdout);
573712b2f30Ssthen 	}
574712b2f30Ssthen 	if(vs == 10)
575712b2f30Ssthen 		provide_file_10(ssl, combined);
576712b2f30Ssthen 	else	provide_file_chunked(ssl, combined);
577712b2f30Ssthen }
578712b2f30Ssthen 
579712b2f30Ssthen /** provide ssl service */
580712b2f30Ssthen static void
581712b2f30Ssthen do_service(char* addr, int port, char* key, char* cert)
582712b2f30Ssthen {
583712b2f30Ssthen 	SSL_CTX* sslctx = setup_ctx(key, cert);
584712b2f30Ssthen 	int fd = setup_fd(addr, port);
585712b2f30Ssthen 	if(fd == -1) print_exit("could not setup sockets");
586712b2f30Ssthen 	if(verb) {printf("petal start\n"); fflush(stdout);}
58783152a15Ssthen 	while(1) {
588712b2f30Ssthen 		struct sockaddr_storage from;
589712b2f30Ssthen 		socklen_t flen = (socklen_t)sizeof(from);
5908771e50fSsthen 		int s;
5918771e50fSsthen 		memset(&from, 0, sizeof(from));
5928771e50fSsthen 		s = accept(fd, (struct sockaddr*)&from, &flen);
593712b2f30Ssthen 		if(verb) fflush(stdout);
594712b2f30Ssthen 		if(s != -1) {
595712b2f30Ssthen 			SSL* ssl = setup_ssl(s, sslctx);
596712b2f30Ssthen 			if(verb) fflush(stdout);
597712b2f30Ssthen 			if(ssl) {
598712b2f30Ssthen 				service_ssl(ssl, &from, flen);
599712b2f30Ssthen 				if(verb) fflush(stdout);
600712b2f30Ssthen 				SSL_shutdown(ssl);
601712b2f30Ssthen 				SSL_free(ssl);
602712b2f30Ssthen 			}
603712b2f30Ssthen 			fd_close(s);
604712b2f30Ssthen 		} else if (verb >=2) log_errno("accept");
605712b2f30Ssthen 		if(verb) fflush(stdout);
606712b2f30Ssthen 	}
607712b2f30Ssthen 	/* if we get a kill signal, the process dies and the OS reaps us */
608712b2f30Ssthen 	if(verb) printf("petal end\n");
609712b2f30Ssthen 	fd_close(fd);
610712b2f30Ssthen 	SSL_CTX_free(sslctx);
611712b2f30Ssthen }
612712b2f30Ssthen 
613712b2f30Ssthen /** getopt global, in case header files fail to declare it. */
614712b2f30Ssthen extern int optind;
615712b2f30Ssthen /** getopt global, in case header files fail to declare it. */
616712b2f30Ssthen extern char* optarg;
617712b2f30Ssthen 
618712b2f30Ssthen /** Main routine for petal */
619712b2f30Ssthen int main(int argc, char* argv[])
620712b2f30Ssthen {
621712b2f30Ssthen 	int c;
622712b2f30Ssthen 	int port = 443;
623712b2f30Ssthen 	char* addr = "127.0.0.1", *key = "petal.key", *cert = "petal.pem";
624712b2f30Ssthen #ifdef USE_WINSOCK
625712b2f30Ssthen 	WSADATA wsa_data;
626712b2f30Ssthen 	if((c=WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0)
627712b2f30Ssthen 	{	printf("WSAStartup failed\n"); exit(1); }
628712b2f30Ssthen 	atexit((void (*)(void))WSACleanup);
629712b2f30Ssthen #endif
630712b2f30Ssthen 
631712b2f30Ssthen 	/* parse the options */
632712b2f30Ssthen 	while( (c=getopt(argc, argv, "a:c:k:hp:v")) != -1) {
633712b2f30Ssthen 		switch(c) {
634712b2f30Ssthen 		case 'a':
635712b2f30Ssthen 			addr = optarg;
636712b2f30Ssthen 			break;
637712b2f30Ssthen 		case 'c':
638712b2f30Ssthen 			cert = optarg;
639712b2f30Ssthen 			break;
640712b2f30Ssthen 		case 'k':
641712b2f30Ssthen 			key = optarg;
642712b2f30Ssthen 			break;
643712b2f30Ssthen 		case 'p':
644712b2f30Ssthen 			port = atoi(optarg);
645712b2f30Ssthen 			break;
646712b2f30Ssthen 		case 'v':
647712b2f30Ssthen 			verb++;
648712b2f30Ssthen 			break;
649712b2f30Ssthen 		case '?':
650712b2f30Ssthen 		case 'h':
651712b2f30Ssthen 		default:
652712b2f30Ssthen 			usage();
653712b2f30Ssthen 		}
654712b2f30Ssthen 	}
655712b2f30Ssthen 	argc -= optind;
6568771e50fSsthen 	/* argv += optind; not using further arguments */
657712b2f30Ssthen 	if(argc != 0)
658712b2f30Ssthen 		usage();
659712b2f30Ssthen 
660712b2f30Ssthen #ifdef SIGPIPE
661712b2f30Ssthen 	(void)signal(SIGPIPE, SIG_IGN);
662712b2f30Ssthen #endif
663712b2f30Ssthen #ifdef HAVE_ERR_LOAD_CRYPTO_STRINGS
664712b2f30Ssthen 	ERR_load_crypto_strings();
665712b2f30Ssthen #endif
666712b2f30Ssthen #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL)
667712b2f30Ssthen 	ERR_load_SSL_strings();
668712b2f30Ssthen #endif
669712b2f30Ssthen #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO)
670b0dfc31bSsthen #  ifndef S_SPLINT_S
671712b2f30Ssthen 	OpenSSL_add_all_algorithms();
672b0dfc31bSsthen #  endif
673712b2f30Ssthen #else
674712b2f30Ssthen 	OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS
675712b2f30Ssthen 		| OPENSSL_INIT_ADD_ALL_DIGESTS
676712b2f30Ssthen 		| OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
677712b2f30Ssthen #endif
678712b2f30Ssthen #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL)
679712b2f30Ssthen 	(void)SSL_library_init();
680712b2f30Ssthen #else
681712b2f30Ssthen 	(void)OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL);
682712b2f30Ssthen #endif
683712b2f30Ssthen 
684712b2f30Ssthen 	do_service(addr, port, key, cert);
685712b2f30Ssthen 
686712b2f30Ssthen #ifdef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA
687712b2f30Ssthen 	CRYPTO_cleanup_all_ex_data();
688712b2f30Ssthen #endif
689712b2f30Ssthen #ifdef HAVE_ERR_FREE_STRINGS
690712b2f30Ssthen 	ERR_free_strings();
691712b2f30Ssthen #endif
692712b2f30Ssthen 	return 0;
693712b2f30Ssthen }
694