Home | History | Annotate | Line # | Download | only in testcode
petal.c revision 1.1.1.6
      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 */
     69 static int verb = 0;
     70 
     71 /** Give petal usage, and exit (1). */
     72 static void
     73 usage(void)
     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 			int e = ERR_peek_error();
    225 			printf("error string: %s\n", ERR_reason_error_string(e));
    226 			return 1;
    227 		}
    228 		if(!process_one_header(buf, file, flen, host, hlen, vs))
    229 			return 0;
    230 	}
    231 	return 0;
    232 }
    233 
    234 /** setup SSL context */
    235 static SSL_CTX*
    236 setup_ctx(char* key, char* cert)
    237 {
    238 	SSL_CTX* ctx = SSL_CTX_new(SSLv23_server_method());
    239 	if(!ctx) print_exit("out of memory");
    240 #if SSL_OP_NO_SSLv2 != 0
    241 	(void)SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
    242 #endif
    243 	(void)SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
    244 #ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL
    245 	SSL_CTX_set_security_level(ctx, 0); /* for keys in tests */
    246 #endif
    247 	if(!SSL_CTX_use_certificate_chain_file(ctx, cert)) {
    248 		int e = ERR_peek_error();
    249 		printf("error string: %s\n", ERR_reason_error_string(e));
    250 		print_exit("cannot read cert");
    251 	}
    252 	if(!SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM))
    253 		print_exit("cannot read key");
    254 	if(!SSL_CTX_check_private_key(ctx))
    255 		print_exit("private key is not correct");
    256 #if HAVE_DECL_SSL_CTX_SET_ECDH_AUTO
    257 	if (!SSL_CTX_set_ecdh_auto(ctx,1))
    258 		if(verb>=1) printf("failed to set_ecdh_auto, not enabling ECDHE\n");
    259 #elif defined(USE_ECDSA)
    260 	if(1) {
    261 		EC_KEY *ecdh = EC_KEY_new_by_curve_name (NID_X9_62_prime256v1);
    262 		if (!ecdh) {
    263 			if(verb>=1) printf("could not find p256, not enabling ECDHE\n");
    264 		} else {
    265 			if (1 != SSL_CTX_set_tmp_ecdh (ctx, ecdh)) {
    266 				if(verb>=1) printf("Error in SSL_CTX_set_tmp_ecdh, not enabling ECDHE\n");
    267 			}
    268 			EC_KEY_free(ecdh);
    269 		}
    270 	}
    271 #endif
    272 	if(!SSL_CTX_load_verify_locations(ctx, cert, NULL))
    273 		print_exit("cannot load cert verify locations");
    274 	return ctx;
    275 }
    276 
    277 /** setup listening TCP */
    278 static int
    279 setup_fd(char* addr, int port)
    280 {
    281 	struct sockaddr_storage ad;
    282 	socklen_t len;
    283 	int fd;
    284 	int c = 1;
    285 	int fam = parse_ip_addr(addr, port, &ad, &len);
    286 	fd = socket(fam, SOCK_STREAM, 0);
    287 	if(fd == -1) {
    288 		log_errno("socket");
    289 		return -1;
    290 	}
    291 	if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
    292 		(void*)&c, (socklen_t) sizeof(int)) < 0) {
    293 		log_errno("setsockopt(SOL_SOCKET, SO_REUSEADDR)");
    294 	}
    295 	if(bind(fd, (struct sockaddr*)&ad, len) == -1) {
    296 		log_errno("bind");
    297 		fd_close(fd);
    298 		return -1;
    299 	}
    300 	if(listen(fd, 5) == -1) {
    301 		log_errno("listen");
    302 		fd_close(fd);
    303 		return -1;
    304 	}
    305 	return fd;
    306 }
    307 
    308 /** setup SSL connection to the client */
    309 static SSL*
    310 setup_ssl(int s, SSL_CTX* ctx)
    311 {
    312 	SSL* ssl = SSL_new(ctx);
    313 	if(!ssl) return NULL;
    314 	SSL_set_accept_state(ssl);
    315 	(void)SSL_set_mode(ssl, (long)SSL_MODE_AUTO_RETRY);
    316 	if(!SSL_set_fd(ssl, s)) {
    317 		SSL_free(ssl);
    318 		return NULL;
    319 	}
    320 	return ssl;
    321 }
    322 
    323 /** check a file name for safety */
    324 static int
    325 file_name_is_safe(char* s)
    326 {
    327 	size_t l = strlen(s);
    328 	if(s[0] != '/')
    329 		return 0; /* must start with / */
    330 	if(strstr(s, "/../"))
    331 		return 0; /* no updirs in URL */
    332 	if(l>=3 && s[l-1]=='.' && s[l-2]=='.' && s[l-3]=='/')
    333 		return 0; /* ends with /.. */
    334 	return 1;
    335 }
    336 
    337 /** adjust host */
    338 static void
    339 adjust_host(char* host)
    340 {
    341 	size_t i, len;
    342 	/* remove a port number if present */
    343 	if(strrchr(host, ':'))
    344 		*strrchr(host, ':') = 0;
    345 	/* lowercase */
    346 	len = strlen(host);
    347 	for(i=0; i<len; i++)
    348 		host[i] = tolower((unsigned char)host[i]);
    349 }
    350 
    351 /** adjust filename */
    352 static void
    353 adjust_file(char* file)
    354 {
    355 	size_t i, len;
    356 	len = strlen(file);
    357 	for(i=0; i<len; i++)
    358 		file[i] = tolower((unsigned char)file[i]);
    359 }
    360 
    361 /** check a host name for safety */
    362 static int
    363 host_name_is_safe(char* s)
    364 {
    365 	if(strchr(s, '/'))
    366 		return 0;
    367 	if(strcmp(s, "..") == 0)
    368 		return 0;
    369 	if(strcmp(s, ".") == 0)
    370 		return 0;
    371 	return 1;
    372 }
    373 
    374 /** provide file in whole transfer */
    375 static void
    376 provide_file_10(SSL* ssl, char* fname)
    377 {
    378 	char* buf, *at;
    379 	size_t len, avail, header_reserve=1024;
    380 	FILE* in = fopen(fname,
    381 #ifndef USE_WINSOCK
    382 		"r"
    383 #else
    384 		"rb"
    385 #endif
    386 		);
    387 	size_t r;
    388 	const char* rcode = "200 OK";
    389 	if(!in) {
    390 		char hdr[1024];
    391 		rcode = "404 File not found";
    392 		snprintf(hdr, sizeof(hdr), "HTTP/1.1 %s\r\n\r\n", rcode);
    393 		r = strlen(hdr);
    394 		if(SSL_write(ssl, hdr, (int)r) <= 0) {
    395 			/* write failure */
    396 		}
    397 		return;
    398 	}
    399 	fseek(in, 0, SEEK_END);
    400 	len = (size_t)ftell(in);
    401 	fseek(in, 0, SEEK_SET);
    402 	/* plus some space for the header */
    403 	buf = (char*)malloc(len+header_reserve);
    404 	if(!buf) {
    405 		fclose(in);
    406 		return;
    407 	}
    408 	avail = len+header_reserve;
    409 	at = buf;
    410 	snprintf(at, avail, "HTTP/1.1 %s\r\n", rcode);
    411 	r = strlen(at);
    412 	at += r;
    413 	avail -= r;
    414 	snprintf(at, avail, "Server: petal/%s\r\n", PACKAGE_VERSION);
    415 	r = strlen(at);
    416 	at += r;
    417 	avail -= r;
    418 	snprintf(at, avail, "Content-Length: %u\r\n", (unsigned)len);
    419 	r = strlen(at);
    420 	at += r;
    421 	avail -= r;
    422 	snprintf(at, avail, "\r\n");
    423 	r = strlen(at);
    424 	at += r;
    425 	avail -= r;
    426 	if(avail < len) { /* robust */
    427 		free(buf);
    428 		fclose(in);
    429 		return;
    430 	}
    431 	if(fread(at, 1, len, in) != len) {
    432 		free(buf);
    433 		fclose(in);
    434 		return;
    435 	}
    436 	fclose(in);
    437 	at += len;
    438 	/* avail -= len; unused */
    439 	if(SSL_write(ssl, buf, at-buf) <= 0) {
    440 		/* write failure */
    441 	}
    442 	free(buf);
    443 }
    444 
    445 /** provide file over SSL, chunked encoding */
    446 static void
    447 provide_file_chunked(SSL* ssl, char* fname)
    448 {
    449 	char buf[16384];
    450 	char* tmpbuf = NULL;
    451 	char* at = buf;
    452 	size_t avail = sizeof(buf);
    453 	size_t r;
    454 	FILE* in = fopen(fname,
    455 #ifndef USE_WINSOCK
    456 		"r"
    457 #else
    458 		"rb"
    459 #endif
    460 		);
    461 	const char* rcode = "200 OK";
    462 	if(!in) {
    463 		rcode = "404 File not found";
    464 	}
    465 
    466 	/* print headers */
    467 	snprintf(at, avail, "HTTP/1.1 %s\r\n", rcode);
    468 	r = strlen(at);
    469 	at += r;
    470 	avail -= r;
    471 	snprintf(at, avail, "Server: petal/%s\r\n", PACKAGE_VERSION);
    472 	r = strlen(at);
    473 	at += r;
    474 	avail -= r;
    475 	snprintf(at, avail, "Transfer-Encoding: chunked\r\n");
    476 	r = strlen(at);
    477 	at += r;
    478 	avail -= r;
    479 	snprintf(at, avail, "Connection: close\r\n");
    480 	r = strlen(at);
    481 	at += r;
    482 	avail -= r;
    483 	snprintf(at, avail, "\r\n");
    484 	r = strlen(at);
    485 	at += r;
    486 	avail -= r;
    487 	if(avail < 16) { /* robust */
    488 		if(in) fclose(in);
    489 		return;
    490 	}
    491 
    492 	do {
    493 		size_t red;
    494 		free(tmpbuf);
    495 		tmpbuf = malloc(avail-16);
    496 		if(!tmpbuf)
    497 			break;
    498 		/* read chunk; space-16 for xxxxCRLF..CRLF0CRLFCRLF (3 spare)*/
    499 		red = in?fread(tmpbuf, 1, avail-16, in):0;
    500 		/* prepare chunk */
    501 		snprintf(at, avail, "%x\r\n", (unsigned)red);
    502 		r = strlen(at);
    503 		if(verb >= 3)
    504 		{printf("chunk len %x\n", (unsigned)red); fflush(stdout);}
    505 		at += r;
    506 		avail -= r;
    507 		if(red != 0) {
    508 			if(red > avail) break; /* robust */
    509 			memmove(at, tmpbuf, red);
    510 			at += red;
    511 			avail -= red;
    512 			snprintf(at, avail, "\r\n");
    513 			r = strlen(at);
    514 			at += r;
    515 			avail -= r;
    516 		}
    517 		if(in && feof(in) && red != 0) {
    518 			snprintf(at, avail, "0\r\n");
    519 			r = strlen(at);
    520 			at += r;
    521 			avail -= r;
    522 		}
    523 		if(!in || feof(in)) {
    524 			snprintf(at, avail, "\r\n");
    525 			r = strlen(at);
    526 			at += r;
    527 			/* avail -= r; unused */
    528 		}
    529 		/* send chunk */
    530 		if(SSL_write(ssl, buf, at-buf) <= 0) {
    531 			/* SSL error */
    532 			break;
    533 		}
    534 
    535 		/* setup for next chunk */
    536 		at = buf;
    537 		avail = sizeof(buf);
    538 	} while(in && !feof(in) && !ferror(in));
    539 
    540 	free(tmpbuf);
    541 	if(in) fclose(in);
    542 }
    543 
    544 /** provide service to the ssl descriptor */
    545 static void
    546 service_ssl(SSL* ssl, struct sockaddr_storage* from, socklen_t falen)
    547 {
    548 	char file[1024];
    549 	char host[1024];
    550 	char combined[2048];
    551 	int vs = 11;
    552 	if(!read_http_headers(ssl, file, sizeof(file), host, sizeof(host),
    553 		&vs))
    554 		return;
    555 	if(host[0] != 0) adjust_host(host);
    556 	if(file[0] != 0) adjust_file(file);
    557 	if(host[0] == 0 || !host_name_is_safe(host))
    558 		(void)strlcpy(host, "default", sizeof(host));
    559 	if(!file_name_is_safe(file)) {
    560 		return;
    561 	}
    562 	snprintf(combined, sizeof(combined), "%s%s", host, file);
    563 	if(verb) {
    564 		char out[100];
    565 		void* a = &((struct sockaddr_in*)from)->sin_addr;
    566 		if(falen != (socklen_t)sizeof(struct sockaddr_in))
    567 			a = &((struct sockaddr_in6*)from)->sin6_addr;
    568 		out[0]=0;
    569 		(void)inet_ntop((int)((struct sockaddr_in*)from)->sin_family,
    570 			a, out, (socklen_t)sizeof(out));
    571 		printf("%s requests %s\n", out, combined);
    572 		fflush(stdout);
    573 	}
    574 	if(vs == 10)
    575 		provide_file_10(ssl, combined);
    576 	else	provide_file_chunked(ssl, combined);
    577 }
    578 
    579 /** provide ssl service */
    580 static void
    581 do_service(char* addr, int port, char* key, char* cert)
    582 {
    583 	SSL_CTX* sslctx = setup_ctx(key, cert);
    584 	int fd = setup_fd(addr, port);
    585 	if(fd == -1) print_exit("could not setup sockets");
    586 	if(verb) {printf("petal start\n"); fflush(stdout);}
    587 	while(1) {
    588 		struct sockaddr_storage from;
    589 		socklen_t flen = (socklen_t)sizeof(from);
    590 		int s;
    591 		memset(&from, 0, sizeof(from));
    592 		s = accept(fd, (struct sockaddr*)&from, &flen);
    593 		if(verb) fflush(stdout);
    594 		if(s != -1) {
    595 			SSL* ssl = setup_ssl(s, sslctx);
    596 			if(verb) fflush(stdout);
    597 			if(ssl) {
    598 				service_ssl(ssl, &from, flen);
    599 				if(verb) fflush(stdout);
    600 				SSL_shutdown(ssl);
    601 				SSL_free(ssl);
    602 			}
    603 			fd_close(s);
    604 		} else if (verb >=2) log_errno("accept");
    605 		if(verb) fflush(stdout);
    606 	}
    607 	/* if we get a kill signal, the process dies and the OS reaps us */
    608 	if(verb) printf("petal end\n");
    609 	fd_close(fd);
    610 	SSL_CTX_free(sslctx);
    611 }
    612 
    613 /** getopt global, in case header files fail to declare it. */
    614 extern int optind;
    615 /** getopt global, in case header files fail to declare it. */
    616 extern char* optarg;
    617 
    618 /** Main routine for petal */
    619 int main(int argc, char* argv[])
    620 {
    621 	int c;
    622 	int port = 443;
    623 	char* addr = "127.0.0.1", *key = "petal.key", *cert = "petal.pem";
    624 #ifdef USE_WINSOCK
    625 	WSADATA wsa_data;
    626 	if((c=WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0)
    627 	{	printf("WSAStartup failed\n"); exit(1); }
    628 	atexit((void (*)(void))WSACleanup);
    629 #endif
    630 
    631 	/* parse the options */
    632 	while( (c=getopt(argc, argv, "a:c:k:hp:v")) != -1) {
    633 		switch(c) {
    634 		case 'a':
    635 			addr = optarg;
    636 			break;
    637 		case 'c':
    638 			cert = optarg;
    639 			break;
    640 		case 'k':
    641 			key = optarg;
    642 			break;
    643 		case 'p':
    644 			port = atoi(optarg);
    645 			break;
    646 		case 'v':
    647 			verb++;
    648 			break;
    649 		case '?':
    650 		case 'h':
    651 		default:
    652 			usage();
    653 		}
    654 	}
    655 	argc -= optind;
    656 	/* argv += optind; not using further arguments */
    657 	if(argc != 0)
    658 		usage();
    659 
    660 #ifdef SIGPIPE
    661 	(void)signal(SIGPIPE, SIG_IGN);
    662 #endif
    663 #ifdef HAVE_ERR_LOAD_CRYPTO_STRINGS
    664 	ERR_load_crypto_strings();
    665 #endif
    666 #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL)
    667 	ERR_load_SSL_strings();
    668 #endif
    669 #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO)
    670 #  ifndef S_SPLINT_S
    671 	OpenSSL_add_all_algorithms();
    672 #  endif
    673 #else
    674 	OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS
    675 		| OPENSSL_INIT_ADD_ALL_DIGESTS
    676 		| OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
    677 #endif
    678 #if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL)
    679 	(void)SSL_library_init();
    680 #else
    681 	(void)OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL);
    682 #endif
    683 
    684 	do_service(addr, port, key, cert);
    685 
    686 #ifdef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA
    687 	CRYPTO_cleanup_all_ex_data();
    688 #endif
    689 #ifdef HAVE_ERR_FREE_STRINGS
    690 	ERR_free_strings();
    691 #endif
    692 	return 0;
    693 }
    694