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