1 /*	$OpenBSD: util.c,v 1.151 2020/02/24 23:54:28 millert Exp $	*/
2 
3 /*
4  * Copyright (c) 2000,2001 Markus Friedl.  All rights reserved.
5  * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
6  * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
7  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21 
22 #include "includes.h"
23 
24 #include <sys/types.h>
25 #include <sys/queue.h>
26 #include <sys/tree.h>
27 #include <sys/socket.h>
28 #include <sys/stat.h>
29 #include <sys/resource.h>
30 
31 #include <netinet/in.h>
32 #include <arpa/inet.h>
33 
34 #include <ctype.h>
35 #include <errno.h>
36 #include <event.h>
37 #include <fcntl.h>
38 #include <fts.h>
39 #include <imsg.h>
40 #include <inttypes.h>
41 #include <libgen.h>
42 #include <netdb.h>
43 #include <pwd.h>
44 #include <limits.h>
45 #include <resolv.h>
46 #include <stdarg.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <syslog.h>
51 #include <time.h>
52 #include <unistd.h>
53 
54 #include "smtpd.h"
55 #include "log.h"
56 
57 const char *log_in6addr(const struct in6_addr *);
58 const char *log_sockaddr(struct sockaddr *);
59 static int  parse_mailname_file(char *, size_t);
60 
61 int	tracing = 0;
62 int	foreground_log = 0;
63 
64 void *
xmalloc(size_t size)65 xmalloc(size_t size)
66 {
67 	void	*r;
68 
69 	if ((r = malloc(size)) == NULL)
70 		fatal("malloc");
71 
72 	return (r);
73 }
74 
75 void *
xcalloc(size_t nmemb,size_t size)76 xcalloc(size_t nmemb, size_t size)
77 {
78 	void	*r;
79 
80 	if ((r = calloc(nmemb, size)) == NULL)
81 		fatal("calloc");
82 
83 	return (r);
84 }
85 
86 char *
xstrdup(const char * str)87 xstrdup(const char *str)
88 {
89 	char	*r;
90 
91 	if ((r = strdup(str)) == NULL)
92 		fatal("strdup");
93 
94 	return (r);
95 }
96 
97 void *
xmemdup(const void * ptr,size_t size)98 xmemdup(const void *ptr, size_t size)
99 {
100 	void	*r;
101 
102 	if ((r = malloc(size)) == NULL)
103 		fatal("malloc");
104 
105 	memmove(r, ptr, size);
106 
107 	return (r);
108 }
109 
110 int
xasprintf(char ** ret,const char * format,...)111 xasprintf(char **ret, const char *format, ...)
112 {
113 	int r;
114 	va_list ap;
115 
116 	va_start(ap, format);
117 	r = vasprintf(ret, format, ap);
118 	va_end(ap);
119 	if (r == -1)
120 		fatal("vasprintf");
121 
122 	return (r);
123 }
124 
125 
126 #if !defined(NO_IO)
127 int
io_xprintf(struct io * io,const char * fmt,...)128 io_xprintf(struct io *io, const char *fmt, ...)
129 {
130 	va_list	ap;
131 	int len;
132 
133 	va_start(ap, fmt);
134 	len = io_vprintf(io, fmt, ap);
135 	va_end(ap);
136 	if (len == -1)
137 		fatal("io_xprintf(%p, %s, ...)", io, fmt);
138 
139 	return len;
140 }
141 
142 int
io_xprint(struct io * io,const char * str)143 io_xprint(struct io *io, const char *str)
144 {
145 	int len;
146 
147 	len = io_print(io, str);
148 	if (len == -1)
149 		fatal("io_xprint(%p, %s, ...)", io, str);
150 
151 	return len;
152 }
153 #endif
154 
155 char *
strip(char * s)156 strip(char *s)
157 {
158 	size_t	 l;
159 
160 	while (isspace((unsigned char)*s))
161 		s++;
162 
163 	for (l = strlen(s); l; l--) {
164 		if (!isspace((unsigned char)s[l-1]))
165 			break;
166 		s[l-1] = '\0';
167 	}
168 
169 	return (s);
170 }
171 
172 int
bsnprintf(char * str,size_t size,const char * format,...)173 bsnprintf(char *str, size_t size, const char *format, ...)
174 {
175 	int ret;
176 	va_list ap;
177 
178 	va_start(ap, format);
179 	ret = vsnprintf(str, size, format, ap);
180 	va_end(ap);
181 	if (ret < 0 || ret >= (int)size)
182 		return 0;
183 
184 	return 1;
185 }
186 
187 
188 int
ckdir(const char * path,mode_t mode,uid_t owner,gid_t group,int create)189 ckdir(const char *path, mode_t mode, uid_t owner, gid_t group, int create)
190 {
191 	char		mode_str[12];
192 	int		ret;
193 	struct stat	sb;
194 
195 	if (stat(path, &sb) == -1) {
196 		if (errno != ENOENT || create == 0) {
197 			log_warn("stat: %s", path);
198 			return (0);
199 		}
200 
201 		/* chmod is deferred to avoid umask effect */
202 		if (mkdir(path, 0) == -1) {
203 			log_warn("mkdir: %s", path);
204 			return (0);
205 		}
206 
207 		if (chown(path, owner, group) == -1) {
208 			log_warn("chown: %s", path);
209 			return (0);
210 		}
211 
212 		if (chmod(path, mode) == -1) {
213 			log_warn("chmod: %s", path);
214 			return (0);
215 		}
216 
217 		if (stat(path, &sb) == -1) {
218 			log_warn("stat: %s", path);
219 			return (0);
220 		}
221 	}
222 
223 	ret = 1;
224 
225 	/* check if it's a directory */
226 	if (!S_ISDIR(sb.st_mode)) {
227 		ret = 0;
228 		log_warnx("%s is not a directory", path);
229 	}
230 
231 	/* check that it is owned by owner/group */
232 	if (sb.st_uid != owner) {
233 		ret = 0;
234 		log_warnx("%s is not owned by uid %d", path, owner);
235 	}
236 	if (sb.st_gid != group) {
237 		ret = 0;
238 		log_warnx("%s is not owned by gid %d", path, group);
239 	}
240 
241 	/* check permission */
242 	if ((sb.st_mode & 07777) != mode) {
243 		ret = 0;
244 		strmode(mode, mode_str);
245 		mode_str[10] = '\0';
246 		log_warnx("%s must be %s (%o)", path, mode_str + 1, mode);
247 	}
248 
249 	return ret;
250 }
251 
252 int
rmtree(char * path,int keepdir)253 rmtree(char *path, int keepdir)
254 {
255 	char		*path_argv[2];
256 	FTS		*fts;
257 	FTSENT		*e;
258 	int		 ret, depth;
259 
260 	path_argv[0] = path;
261 	path_argv[1] = NULL;
262 	ret = 0;
263 	depth = 0;
264 
265 	fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
266 	if (fts == NULL) {
267 		log_warn("fts_open: %s", path);
268 		return (-1);
269 	}
270 
271 	while ((e = fts_read(fts)) != NULL) {
272 		switch (e->fts_info) {
273 		case FTS_D:
274 			depth++;
275 			break;
276 		case FTS_DP:
277 		case FTS_DNR:
278 			depth--;
279 			if (keepdir && depth == 0)
280 				continue;
281 			if (rmdir(e->fts_path) == -1) {
282 				log_warn("rmdir: %s", e->fts_path);
283 				ret = -1;
284 			}
285 			break;
286 
287 		case FTS_F:
288 			if (unlink(e->fts_path) == -1) {
289 				log_warn("unlink: %s", e->fts_path);
290 				ret = -1;
291 			}
292 		}
293 	}
294 
295 	fts_close(fts);
296 
297 	return (ret);
298 }
299 
300 int
mvpurge(char * from,char * to)301 mvpurge(char *from, char *to)
302 {
303 	size_t		 n;
304 	int		 retry;
305 	const char	*sep;
306 	char		 buf[PATH_MAX];
307 
308 	if ((n = strlen(to)) == 0)
309 		fatalx("to is empty");
310 
311 	sep = (to[n - 1] == '/') ? "" : "/";
312 	retry = 0;
313 
314 again:
315 	(void)snprintf(buf, sizeof buf, "%s%s%u", to, sep, arc4random());
316 	if (rename(from, buf) == -1) {
317 		/* ENOTDIR has actually 2 meanings, and incorrect input
318 		 * could lead to an infinite loop. Consider that after
319 		 * 20 tries something is hopelessly wrong.
320 		 */
321 		if (errno == ENOTEMPTY || errno == EISDIR || errno == ENOTDIR) {
322 			if ((retry++) >= 20)
323 				return (-1);
324 			goto again;
325 		}
326 		return -1;
327 	}
328 
329 	return 0;
330 }
331 
332 
333 int
mktmpfile(void)334 mktmpfile(void)
335 {
336 	char		path[PATH_MAX];
337 	int		fd;
338 
339 	if (!bsnprintf(path, sizeof(path), "%s/smtpd.XXXXXXXXXX",
340 		PATH_TEMPORARY)) {
341 		log_warn("snprintf");
342 		fatal("exiting");
343 	}
344 
345 	if ((fd = mkstemp(path)) == -1) {
346 		log_warn("cannot create temporary file %s", path);
347 		fatal("exiting");
348 	}
349 	unlink(path);
350 	return (fd);
351 }
352 
353 
354 /* Close file, signifying temporary error condition (if any) to the caller. */
355 int
safe_fclose(FILE * fp)356 safe_fclose(FILE *fp)
357 {
358 	if (ferror(fp)) {
359 		fclose(fp);
360 		return 0;
361 	}
362 	if (fflush(fp)) {
363 		fclose(fp);
364 		if (errno == ENOSPC)
365 			return 0;
366 		fatal("safe_fclose: fflush");
367 	}
368 	if (fsync(fileno(fp)))
369 		fatal("safe_fclose: fsync");
370 	if (fclose(fp))
371 		fatal("safe_fclose: fclose");
372 
373 	return 1;
374 }
375 
376 int
hostname_match(const char * hostname,const char * pattern)377 hostname_match(const char *hostname, const char *pattern)
378 {
379 	while (*pattern != '\0' && *hostname != '\0') {
380 		if (*pattern == '*') {
381 			while (*pattern == '*')
382 				pattern++;
383 			while (*hostname != '\0' &&
384 			    tolower((unsigned char)*hostname) !=
385 			    tolower((unsigned char)*pattern))
386 				hostname++;
387 			continue;
388 		}
389 
390 		if (tolower((unsigned char)*pattern) !=
391 		    tolower((unsigned char)*hostname))
392 			return 0;
393 		pattern++;
394 		hostname++;
395 	}
396 
397 	return (*hostname == '\0' && *pattern == '\0');
398 }
399 
400 int
mailaddr_match(const struct mailaddr * maddr1,const struct mailaddr * maddr2)401 mailaddr_match(const struct mailaddr *maddr1, const struct mailaddr *maddr2)
402 {
403 	struct mailaddr m1 = *maddr1;
404 	struct mailaddr m2 = *maddr2;
405 	char	       *p;
406 
407 	/* catchall */
408 	if (m2.user[0] == '\0' && m2.domain[0] == '\0')
409 		return 1;
410 
411 	if (m2.domain[0] && !hostname_match(m1.domain, m2.domain))
412 		return 0;
413 
414 	if (m2.user[0]) {
415 		/* if address from table has a tag, we must respect it */
416 		if (strchr(m2.user, *env->sc_subaddressing_delim) == NULL) {
417 			/* otherwise, strip tag from session address if any */
418 			p = strchr(m1.user, *env->sc_subaddressing_delim);
419 			if (p)
420 				*p = '\0';
421 		}
422 		if (strcasecmp(m1.user, m2.user))
423 			return 0;
424 	}
425 	return 1;
426 }
427 
428 int
valid_localpart(const char * s)429 valid_localpart(const char *s)
430 {
431 #define IS_ATEXT(c) (isalnum((unsigned char)(c)) || strchr(MAILADDR_ALLOWED, (c)))
432 nextatom:
433 	if (!IS_ATEXT(*s) || *s == '\0')
434 		return 0;
435 	while (*(++s) != '\0') {
436 		if (*s == '.')
437 			break;
438 		if (IS_ATEXT(*s))
439 			continue;
440 		return 0;
441 	}
442 	if (*s == '.') {
443 		s++;
444 		goto nextatom;
445 	}
446 	return 1;
447 }
448 
449 int
valid_domainpart(const char * s)450 valid_domainpart(const char *s)
451 {
452 	struct in_addr	 ina;
453 	struct in6_addr	 ina6;
454 	char		*c, domain[SMTPD_MAXDOMAINPARTSIZE];
455 	const char	*p;
456 	size_t		 dlen;
457 
458 	if (*s == '[') {
459 		if (strncasecmp("[IPv6:", s, 6) == 0)
460 			p = s + 6;
461 		else
462 			p = s + 1;
463 
464 		if (strlcpy(domain, p, sizeof domain) >= sizeof domain)
465 			return 0;
466 
467 		c = strchr(domain, ']');
468 		if (!c || c[1] != '\0')
469 			return 0;
470 
471 		*c = '\0';
472 
473 		if (inet_pton(AF_INET6, domain, &ina6) == 1)
474 			return 1;
475 		if (inet_pton(AF_INET, domain, &ina) == 1)
476 			return 1;
477 
478 		return 0;
479 	}
480 
481 	if (*s == '\0')
482 		return 0;
483 
484 	dlen = strlen(s);
485 	if (dlen >= sizeof domain)
486 		return 0;
487 
488 	if (s[dlen - 1] == '.')
489 		return 0;
490 
491 	return res_hnok(s);
492 }
493 
494 #define LABELCHR(c) ((c) == '-' || (c) == '_' || isalpha((unsigned char)(c)) || isdigit((unsigned char)(c)))
495 #define LABELMAX 63
496 #define DNAMEMAX 253
497 
498 int
valid_domainname(const char * str)499 valid_domainname(const char *str)
500 {
501 	const char *label, *s;
502 
503 	/*
504 	 * Expect a sequence of dot-separated labels, possibly with a trailing
505 	 * dot. The empty string is rejected, as well a single dot.
506 	 */
507 	for (s = str; *s; s++) {
508 
509 		/* Start of a new label. */
510 		label = s;
511 		while (LABELCHR(*s))
512 			s++;
513 
514 		/* Must have at least one char and at most LABELMAX. */
515 		if (s == label || s - label > LABELMAX)
516 			return 0;
517 
518 		/* If last label, stop here. */
519 		if (*s == '\0')
520 			break;
521 
522 		/* Expect a dot as label separator or last char. */
523 		if (*s != '.')
524 			return 0;
525 	}
526 
527 	/* Must have at leat one label and no more than DNAMEMAX chars. */
528 	if (s == str || s - str > DNAMEMAX)
529 		return 0;
530 
531 	return 1;
532 }
533 
534 int
valid_smtp_response(const char * s)535 valid_smtp_response(const char *s)
536 {
537 	if (strlen(s) < 5)
538 		return 0;
539 
540 	if ((s[0] < '2' || s[0] > '5') ||
541 	    (s[1] < '0' || s[1] > '9') ||
542 	    (s[2] < '0' || s[2] > '9') ||
543 	    (s[3] != ' '))
544 		return 0;
545 
546 	return 1;
547 }
548 
549 int
secure_file(int fd,char * path,char * userdir,uid_t uid,int mayread)550 secure_file(int fd, char *path, char *userdir, uid_t uid, int mayread)
551 {
552 	char		 buf[PATH_MAX];
553 	char		 homedir[PATH_MAX];
554 	struct stat	 st;
555 	char		*cp;
556 
557 	if (realpath(path, buf) == NULL)
558 		return 0;
559 
560 	if (realpath(userdir, homedir) == NULL)
561 		homedir[0] = '\0';
562 
563 	/* Check the open file to avoid races. */
564 	if (fstat(fd, &st) == -1 ||
565 	    !S_ISREG(st.st_mode) ||
566 	    st.st_uid != uid ||
567 	    (st.st_mode & (mayread ? 022 : 066)) != 0)
568 		return 0;
569 
570 	/* For each component of the canonical path, walking upwards. */
571 	for (;;) {
572 		if ((cp = dirname(buf)) == NULL)
573 			return 0;
574 		(void)strlcpy(buf, cp, sizeof(buf));
575 
576 		if (stat(buf, &st) == -1 ||
577 		    (st.st_uid != 0 && st.st_uid != uid) ||
578 		    (st.st_mode & 022) != 0)
579 			return 0;
580 
581 		/* We can stop checking after reaching homedir level. */
582 		if (strcmp(homedir, buf) == 0)
583 			break;
584 
585 		/*
586 		 * dirname should always complete with a "/" path,
587 		 * but we can be paranoid and check for "." too
588 		 */
589 		if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0))
590 			break;
591 	}
592 
593 	return 1;
594 }
595 
596 void
addargs(arglist * args,char * fmt,...)597 addargs(arglist *args, char *fmt, ...)
598 {
599 	va_list ap;
600 	char *cp;
601 	uint nalloc;
602 	int r;
603 	char	**tmp;
604 
605 	va_start(ap, fmt);
606 	r = vasprintf(&cp, fmt, ap);
607 	va_end(ap);
608 	if (r == -1)
609 		fatal("addargs: argument too long");
610 
611 	nalloc = args->nalloc;
612 	if (args->list == NULL) {
613 		nalloc = 32;
614 		args->num = 0;
615 	} else if (args->num+2 >= nalloc)
616 		nalloc *= 2;
617 
618 	tmp = reallocarray(args->list, nalloc, sizeof(char *));
619 	if (tmp == NULL)
620 		fatal("addargs: reallocarray");
621 	args->list = tmp;
622 	args->nalloc = nalloc;
623 	args->list[args->num++] = cp;
624 	args->list[args->num] = NULL;
625 }
626 
627 int
lowercase(char * buf,const char * s,size_t len)628 lowercase(char *buf, const char *s, size_t len)
629 {
630 	if (len == 0)
631 		return 0;
632 
633 	if (strlcpy(buf, s, len) >= len)
634 		return 0;
635 
636 	while (*buf != '\0') {
637 		*buf = tolower((unsigned char)*buf);
638 		buf++;
639 	}
640 
641 	return 1;
642 }
643 
644 int
uppercase(char * buf,const char * s,size_t len)645 uppercase(char *buf, const char *s, size_t len)
646 {
647 	if (len == 0)
648 		return 0;
649 
650 	if (strlcpy(buf, s, len) >= len)
651 		return 0;
652 
653 	while (*buf != '\0') {
654 		*buf = toupper((unsigned char)*buf);
655 		buf++;
656 	}
657 
658 	return 1;
659 }
660 
661 void
xlowercase(char * buf,const char * s,size_t len)662 xlowercase(char *buf, const char *s, size_t len)
663 {
664 	if (len == 0)
665 		fatalx("lowercase: len == 0");
666 
667 	if (!lowercase(buf, s, len))
668 		fatalx("lowercase: truncation");
669 }
670 
671 uint64_t
generate_uid(void)672 generate_uid(void)
673 {
674 	static uint32_t id;
675 	static uint8_t	inited;
676 	uint64_t	uid;
677 
678 	if (!inited) {
679 		id = arc4random();
680 		inited = 1;
681 	}
682 	while ((uid = ((uint64_t)(id++) << 32 | arc4random())) == 0)
683 		;
684 
685 	return (uid);
686 }
687 
688 int
session_socket_error(int fd)689 session_socket_error(int fd)
690 {
691 	int		error;
692 	socklen_t	len;
693 
694 	len = sizeof(error);
695 	if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) == -1)
696 		fatal("session_socket_error: getsockopt");
697 
698 	return (error);
699 }
700 
701 const char *
parse_smtp_response(char * line,size_t len,char ** msg,int * cont)702 parse_smtp_response(char *line, size_t len, char **msg, int *cont)
703 {
704 	if (len >= LINE_MAX)
705 		return "line too long";
706 
707 	if (len > 3) {
708 		if (msg)
709 			*msg = line + 4;
710 		if (cont)
711 			*cont = (line[3] == '-');
712 	} else if (len == 3) {
713 		if (msg)
714 			*msg = line + 3;
715 		if (cont)
716 			*cont = 0;
717 	} else
718 		return "line too short";
719 
720 	/* validate reply code */
721 	if (line[0] < '2' || line[0] > '5' || !isdigit((unsigned char)line[1]) ||
722 	    !isdigit((unsigned char)line[2]))
723 		return "reply code out of range";
724 
725 	return NULL;
726 }
727 
728 static int
parse_mailname_file(char * hostname,size_t len)729 parse_mailname_file(char *hostname, size_t len)
730 {
731 	FILE	*fp;
732 	char	*buf = NULL;
733 	size_t	 bufsz = 0;
734 	ssize_t	 buflen;
735 
736 	if ((fp = fopen(MAILNAME_FILE, "r")) == NULL)
737 		return 1;
738 
739 	if ((buflen = getline(&buf, &bufsz, fp)) == -1)
740 		goto error;
741 
742 	if (buf[buflen - 1] == '\n')
743 		buf[buflen - 1] = '\0';
744 
745 	if (strlcpy(hostname, buf, len) >= len) {
746 		fprintf(stderr, MAILNAME_FILE " entry too long");
747 		goto error;
748 	}
749 
750 	return 0;
751 error:
752 	fclose(fp);
753 	free(buf);
754 	return 1;
755 }
756 
757 int
getmailname(char * hostname,size_t len)758 getmailname(char *hostname, size_t len)
759 {
760 	struct addrinfo	 hints, *res = NULL;
761 	int		 error;
762 
763 	/* Try MAILNAME_FILE first */
764 	if (parse_mailname_file(hostname, len) == 0)
765 		return 0;
766 
767 	/* Next, gethostname(3) */
768 	if (gethostname(hostname, len) == -1) {
769 		fprintf(stderr, "getmailname: gethostname() failed\n");
770 		return -1;
771 	}
772 
773 	if (strchr(hostname, '.') != NULL)
774 		return 0;
775 
776 	/* Canonicalize if domain part is missing */
777 	memset(&hints, 0, sizeof hints);
778 	hints.ai_family = PF_UNSPEC;
779 	hints.ai_flags = AI_CANONNAME;
780 	error = getaddrinfo(hostname, NULL, &hints, &res);
781 	if (error)
782 		return 0; /* Continue with non-canon hostname */
783 
784 	if (strlcpy(hostname, res->ai_canonname, len) >= len) {
785 		fprintf(stderr, "hostname too long");
786 		freeaddrinfo(res);
787 		return -1;
788 	}
789 
790 	freeaddrinfo(res);
791 	return 0;
792 }
793 
794 int
base64_encode(unsigned char const * src,size_t srclen,char * dest,size_t destsize)795 base64_encode(unsigned char const *src, size_t srclen,
796 	      char *dest, size_t destsize)
797 {
798 	return __b64_ntop(src, srclen, dest, destsize);
799 }
800 
801 int
base64_decode(char const * src,unsigned char * dest,size_t destsize)802 base64_decode(char const *src, unsigned char *dest, size_t destsize)
803 {
804 	return __b64_pton(src, dest, destsize);
805 }
806 
807 int
base64_encode_rfc3548(unsigned char const * src,size_t srclen,char * dest,size_t destsize)808 base64_encode_rfc3548(unsigned char const *src, size_t srclen,
809 	      char *dest, size_t destsize)
810 {
811 	size_t i;
812 	int ret;
813 
814 	if ((ret = base64_encode(src, srclen, dest, destsize)) == -1)
815 		return -1;
816 
817 	for (i = 0; i < destsize; ++i) {
818 		if (dest[i] == '/')
819 			dest[i] = '_';
820 		else if (dest[i] == '+')
821 			dest[i] = '-';
822 	}
823 
824 	return ret;
825 }
826 
827 void
log_trace(int mask,const char * emsg,...)828 log_trace(int mask, const char *emsg, ...)
829 {
830 	va_list	 ap;
831 
832 	if (tracing & mask) {
833 		va_start(ap, emsg);
834 		vlog(LOG_DEBUG, emsg, ap);
835 		va_end(ap);
836 	}
837 }
838 
839 void
log_trace_verbose(int v)840 log_trace_verbose(int v)
841 {
842 	tracing = v;
843 
844 	/* Set debug logging in log.c */
845 	log_setverbose(v & TRACE_DEBUG ? 2 : foreground_log);
846 }
847 
848 void
xclosefrom(int lowfd)849 xclosefrom(int lowfd)
850 {
851 #if defined HAVE_CLOSEFROM_INT
852     if (closefrom(lowfd) == -1)
853         err(1, "closefrom");
854 #else
855     closefrom(lowfd);
856 #endif
857 }
858 
859 void
portable_freeaddrinfo(struct addrinfo * ai)860 portable_freeaddrinfo(struct addrinfo *ai)
861 {
862 	struct addrinfo *p;
863 
864 	do {
865 		p = ai;
866 		ai = ai->ai_next;
867 		free(p->ai_canonname);
868 		free(p);
869 	} while (ai);
870 }
871