1/*	$OpenBSD: util.c,v 1.44 2024/02/03 00:38:08 jsg Exp $	*/
2
3/*
4 * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20#include <sys/queue.h>
21#include <sys/socket.h>
22#include <sys/uio.h>
23
24#include <netdb.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <unistd.h>
28#include <string.h>
29#include <errno.h>
30#include <limits.h>
31#include <fcntl.h>
32#include <ctype.h>
33#include <event.h>
34
35#include "iked.h"
36#include "ikev2.h"
37
38int
39socket_af(struct sockaddr *sa, in_port_t port)
40{
41	errno = 0;
42	switch (sa->sa_family) {
43	case AF_INET:
44		((struct sockaddr_in *)sa)->sin_port = port;
45		((struct sockaddr_in *)sa)->sin_len =
46		    sizeof(struct sockaddr_in);
47		break;
48	case AF_INET6:
49		((struct sockaddr_in6 *)sa)->sin6_port = port;
50		((struct sockaddr_in6 *)sa)->sin6_len =
51		    sizeof(struct sockaddr_in6);
52		break;
53	default:
54		errno = EPFNOSUPPORT;
55		return (-1);
56	}
57
58	return (0);
59}
60
61in_port_t
62socket_getport(struct sockaddr *sa)
63{
64	switch (sa->sa_family) {
65	case AF_INET:
66		return (ntohs(((struct sockaddr_in *)sa)->sin_port));
67	case AF_INET6:
68		return (ntohs(((struct sockaddr_in6 *)sa)->sin6_port));
69	default:
70		return (0);
71	}
72
73	/* NOTREACHED */
74	return (0);
75}
76
77int
78socket_setport(struct sockaddr *sa, in_port_t port)
79{
80	switch (sa->sa_family) {
81	case AF_INET:
82		((struct sockaddr_in *)sa)->sin_port = htons(port);
83		break;
84	case AF_INET6:
85		((struct sockaddr_in6 *)sa)->sin6_port = htons(port);
86		break;
87	default:
88		return (-1);
89	}
90	return (0);
91}
92
93int
94socket_getaddr(int s, struct sockaddr_storage *ss)
95{
96	socklen_t sslen = sizeof(*ss);
97
98	return (getsockname(s, (struct sockaddr *)ss, &sslen));
99}
100
101int
102socket_bypass(int s, struct sockaddr *sa)
103{
104	int	 v, *a;
105	int	 a4[] = {
106		    IPPROTO_IP,
107		    IP_AUTH_LEVEL,
108		    IP_ESP_TRANS_LEVEL,
109		    IP_ESP_NETWORK_LEVEL,
110#ifdef IPV6_IPCOMP_LEVEL
111		    IP_IPCOMP_LEVEL
112#endif
113	};
114	int	 a6[] = {
115		    IPPROTO_IPV6,
116		    IPV6_AUTH_LEVEL,
117		    IPV6_ESP_TRANS_LEVEL,
118		    IPV6_ESP_NETWORK_LEVEL,
119#ifdef IPV6_IPCOMP_LEVEL
120		    IPV6_IPCOMP_LEVEL
121#endif
122	};
123
124	switch (sa->sa_family) {
125	case AF_INET:
126		a = a4;
127		break;
128	case AF_INET6:
129		a = a6;
130		break;
131	default:
132		log_warn("%s: invalid address family", __func__);
133		return (-1);
134	}
135
136	v = IPSEC_LEVEL_BYPASS;
137	if (setsockopt(s, a[0], a[1], &v, sizeof(v)) == -1) {
138		log_warn("%s: AUTH_LEVEL", __func__);
139		return (-1);
140	}
141	if (setsockopt(s, a[0], a[2], &v, sizeof(v)) == -1) {
142		log_warn("%s: ESP_TRANS_LEVEL", __func__);
143		return (-1);
144	}
145	if (setsockopt(s, a[0], a[3], &v, sizeof(v)) == -1) {
146		log_warn("%s: ESP_NETWORK_LEVEL", __func__);
147		return (-1);
148	}
149#ifdef IP_IPCOMP_LEVEL
150	if (setsockopt(s, a[0], a[4], &v, sizeof(v)) == -1) {
151		log_warn("%s: IPCOMP_LEVEL", __func__);
152		return (-1);
153	}
154#endif
155
156	return (0);
157}
158
159int
160udp_bind(struct sockaddr *sa, in_port_t port)
161{
162	int	 s, val;
163
164	if (socket_af(sa, port) == -1) {
165		log_warn("%s: failed to set UDP port", __func__);
166		return (-1);
167	}
168
169	if ((s = socket(sa->sa_family,
170	    SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP)) == -1) {
171		log_warn("%s: failed to get UDP socket", __func__);
172		return (-1);
173	}
174
175	/* Skip IPsec processing (don't encrypt) for IKE messages */
176	if (socket_bypass(s, sa) == -1) {
177		log_warn("%s: failed to bypass IPsec on IKE socket",
178		    __func__);
179		goto bad;
180	}
181
182	val = 1;
183	if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(int)) == -1) {
184		log_warn("%s: failed to set reuseport", __func__);
185		goto bad;
186	}
187	val = 1;
188	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(int)) == -1) {
189		log_warn("%s: failed to set reuseaddr", __func__);
190		goto bad;
191	}
192
193	if (sa->sa_family == AF_INET) {
194		val = 1;
195		if (setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR,
196		    &val, sizeof(int)) == -1) {
197			log_warn("%s: failed to set IPv4 packet info",
198			    __func__);
199			goto bad;
200		}
201	} else {
202		val = 1;
203		if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
204		    &val, sizeof(int)) == -1) {
205			log_warn("%s: failed to set IPv6 packet info",
206			    __func__);
207			goto bad;
208		}
209	}
210
211	if (bind(s, sa, sa->sa_len) == -1) {
212		log_warn("%s: failed to bind UDP socket", __func__);
213		goto bad;
214	}
215
216	return (s);
217 bad:
218	close(s);
219	return (-1);
220}
221
222int
223sockaddr_cmp(struct sockaddr *a, struct sockaddr *b, int prefixlen)
224{
225	struct sockaddr_in	*a4, *b4;
226	struct sockaddr_in6	*a6, *b6;
227	uint32_t		 av[4], bv[4], mv[4];
228
229	if (a->sa_family == AF_UNSPEC || b->sa_family == AF_UNSPEC)
230		return (0);
231	else if (a->sa_family > b->sa_family)
232		return (1);
233	else if (a->sa_family < b->sa_family)
234		return (-1);
235
236	if (prefixlen == -1)
237		memset(&mv, 0xff, sizeof(mv));
238
239	switch (a->sa_family) {
240	case AF_INET:
241		a4 = (struct sockaddr_in *)a;
242		b4 = (struct sockaddr_in *)b;
243
244		av[0] = a4->sin_addr.s_addr;
245		bv[0] = b4->sin_addr.s_addr;
246		if (prefixlen != -1)
247			mv[0] = prefixlen2mask(prefixlen);
248
249		if ((av[0] & mv[0]) > (bv[0] & mv[0]))
250			return (1);
251		if ((av[0] & mv[0]) < (bv[0] & mv[0]))
252			return (-1);
253		break;
254	case AF_INET6:
255		a6 = (struct sockaddr_in6 *)a;
256		b6 = (struct sockaddr_in6 *)b;
257
258		memcpy(&av, &a6->sin6_addr.s6_addr, 16);
259		memcpy(&bv, &b6->sin6_addr.s6_addr, 16);
260		if (prefixlen != -1)
261			prefixlen2mask6(prefixlen, mv);
262
263		if ((av[3] & mv[3]) > (bv[3] & mv[3]))
264			return (1);
265		if ((av[3] & mv[3]) < (bv[3] & mv[3]))
266			return (-1);
267		if ((av[2] & mv[2]) > (bv[2] & mv[2]))
268			return (1);
269		if ((av[2] & mv[2]) < (bv[2] & mv[2]))
270			return (-1);
271		if ((av[1] & mv[1]) > (bv[1] & mv[1]))
272			return (1);
273		if ((av[1] & mv[1]) < (bv[1] & mv[1]))
274			return (-1);
275		if ((av[0] & mv[0]) > (bv[0] & mv[0]))
276			return (1);
277		if ((av[0] & mv[0]) < (bv[0] & mv[0]))
278			return (-1);
279		break;
280	}
281
282	return (0);
283}
284
285ssize_t
286sendtofrom(int s, void *buf, size_t len, int flags, struct sockaddr *to,
287    socklen_t tolen, struct sockaddr *from, socklen_t fromlen)
288{
289	struct iovec		 iov;
290	struct msghdr		 msg;
291	struct cmsghdr		*cmsg;
292	struct in6_pktinfo	*pkt6;
293	struct sockaddr_in	*in;
294	struct sockaddr_in6	*in6;
295	union {
296		struct cmsghdr	hdr;
297		char		inbuf[CMSG_SPACE(sizeof(struct in_addr))];
298		char		in6buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
299	} cmsgbuf;
300
301	bzero(&msg, sizeof(msg));
302	bzero(&cmsgbuf, sizeof(cmsgbuf));
303
304	iov.iov_base = buf;
305	iov.iov_len = len;
306	msg.msg_iov = &iov;
307	msg.msg_iovlen = 1;
308	msg.msg_name = to;
309	msg.msg_namelen = tolen;
310	msg.msg_control = &cmsgbuf;
311	msg.msg_controllen = sizeof(cmsgbuf);
312
313	cmsg = CMSG_FIRSTHDR(&msg);
314	switch (to->sa_family) {
315	case AF_INET:
316		msg.msg_controllen = sizeof(cmsgbuf.inbuf);
317		cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
318		cmsg->cmsg_level = IPPROTO_IP;
319		cmsg->cmsg_type = IP_SENDSRCADDR;
320		in = (struct sockaddr_in *)from;
321		memcpy(CMSG_DATA(cmsg), &in->sin_addr, sizeof(struct in_addr));
322		break;
323	case AF_INET6:
324		msg.msg_controllen = sizeof(cmsgbuf.in6buf);
325		cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
326		cmsg->cmsg_level = IPPROTO_IPV6;
327		cmsg->cmsg_type = IPV6_PKTINFO;
328		in6 = (struct sockaddr_in6 *)from;
329		pkt6 = (struct in6_pktinfo *)CMSG_DATA(cmsg);
330		pkt6->ipi6_addr = in6->sin6_addr;
331		break;
332	}
333
334	return sendmsg(s, &msg, flags);
335}
336
337ssize_t
338recvfromto(int s, void *buf, size_t len, int flags, struct sockaddr *from,
339    socklen_t *fromlen, struct sockaddr *to, socklen_t *tolen)
340{
341	struct iovec		 iov;
342	struct msghdr		 msg;
343	struct cmsghdr		*cmsg;
344	struct in6_pktinfo	*pkt6;
345	struct sockaddr_in	*in;
346	struct sockaddr_in6	*in6;
347	ssize_t			 ret;
348	union {
349		struct cmsghdr hdr;
350		char	buf[CMSG_SPACE(sizeof(struct sockaddr_storage))];
351	} cmsgbuf;
352
353	bzero(&msg, sizeof(msg));
354	bzero(&cmsgbuf.buf, sizeof(cmsgbuf.buf));
355
356	iov.iov_base = buf;
357	iov.iov_len = len;
358	msg.msg_iov = &iov;
359	msg.msg_iovlen = 1;
360	msg.msg_name = from;
361	msg.msg_namelen = *fromlen;
362	msg.msg_control = &cmsgbuf.buf;
363	msg.msg_controllen = sizeof(cmsgbuf.buf);
364
365	if ((ret = recvmsg(s, &msg, flags)) == -1)
366		return (-1);
367
368	*fromlen = from->sa_len;
369
370	if (getsockname(s, to, tolen) != 0)
371		*tolen = 0;
372
373	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
374	    cmsg = CMSG_NXTHDR(&msg, cmsg)) {
375		switch (from->sa_family) {
376		case AF_INET:
377			if (cmsg->cmsg_level == IPPROTO_IP &&
378			    cmsg->cmsg_type == IP_RECVDSTADDR) {
379				in = (struct sockaddr_in *)to;
380				in->sin_family = AF_INET;
381				in->sin_len = *tolen = sizeof(*in);
382				memcpy(&in->sin_addr, CMSG_DATA(cmsg),
383				    sizeof(struct in_addr));
384			}
385			break;
386		case AF_INET6:
387			if (cmsg->cmsg_level == IPPROTO_IPV6 &&
388			    cmsg->cmsg_type == IPV6_PKTINFO) {
389				in6 = (struct sockaddr_in6 *)to;
390				in6->sin6_family = AF_INET6;
391				in6->sin6_len = *tolen = sizeof(*in6);
392				pkt6 = (struct in6_pktinfo *)CMSG_DATA(cmsg);
393				memcpy(&in6->sin6_addr, &pkt6->ipi6_addr,
394				    sizeof(struct in6_addr));
395				if (IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr))
396					in6->sin6_scope_id =
397					    pkt6->ipi6_ifindex;
398			}
399			break;
400		}
401	}
402
403	return (ret);
404}
405
406const char *
407print_spi(uint64_t spi, int size)
408{
409	static char		 buf[IKED_CYCLE_BUFFERS][32];
410	static int		 i = 0;
411	char			*ptr;
412
413	ptr = buf[i];
414
415	switch (size) {
416	case 2:
417		snprintf(ptr, 32, "0x%04x", (uint16_t)spi);
418		break;
419	case 4:
420		snprintf(ptr, 32, "0x%08x", (uint32_t)spi);
421		break;
422	case 8:
423		snprintf(ptr, 32, "0x%016llx", spi);
424		break;
425	default:
426		snprintf(ptr, 32, "%llu", spi);
427		break;
428	}
429
430	if (++i >= IKED_CYCLE_BUFFERS)
431		i = 0;
432
433	return (ptr);
434}
435
436const char *
437print_map(unsigned int type, struct iked_constmap *map)
438{
439	unsigned int		 i;
440	static char		 buf[IKED_CYCLE_BUFFERS][32];
441	static int		 idx = 0;
442	const char		*name = NULL;
443
444	if (idx >= IKED_CYCLE_BUFFERS)
445		idx = 0;
446	bzero(buf[idx], sizeof(buf[idx]));
447
448	for (i = 0; map[i].cm_name != NULL; i++) {
449		if (map[i].cm_type == type)
450			name = map[i].cm_name;
451	}
452
453	if (name == NULL)
454		snprintf(buf[idx], sizeof(buf[idx]), "<UNKNOWN:%u>", type);
455	else
456		strlcpy(buf[idx], name, sizeof(buf[idx]));
457
458	return (buf[idx++]);
459}
460
461void
462lc_idtype(char *str)
463{
464	for (; *str != '\0' && *str != '/'; str++)
465		*str = tolower((unsigned char)*str);
466}
467
468void
469print_hex(const uint8_t *buf, off_t offset, size_t length)
470{
471	unsigned int	 i;
472
473	if (log_getverbose() < 3 || !length)
474		return;
475
476	for (i = 0; i < length; i++) {
477		if (i && (i % 4) == 0) {
478			if ((i % 32) == 0)
479				print_debug("\n");
480			else
481				print_debug(" ");
482		}
483		print_debug("%02x", buf[offset + i]);
484	}
485	print_debug("\n");
486}
487
488void
489print_hexval(const uint8_t *buf, off_t offset, size_t length)
490{
491	unsigned int	 i;
492
493	if (log_getverbose() < 2 || !length)
494		return;
495
496	print_debug("0x");
497	for (i = 0; i < length; i++)
498		print_debug("%02x", buf[offset + i]);
499	print_debug("\n");
500}
501
502void
503print_hexbuf(struct ibuf *ibuf)
504{
505	print_hex(ibuf_data(ibuf), 0, ibuf_size(ibuf));
506}
507
508const char *
509print_bits(unsigned short v, unsigned char *bits)
510{
511	static char	 buf[IKED_CYCLE_BUFFERS][BUFSIZ];
512	static int	 idx = 0;
513	unsigned int	 i, any = 0, j = 0;
514	unsigned char	 c;
515
516	if (!bits)
517		return ("");
518
519	if (++idx >= IKED_CYCLE_BUFFERS)
520		idx = 0;
521
522	bzero(buf[idx], sizeof(buf[idx]));
523
524	bits++;
525	while ((i = *bits++)) {
526		if (v & (1 << (i-1))) {
527			if (any) {
528				buf[idx][j++] = ',';
529				if (j >= sizeof(buf[idx]))
530					return (buf[idx]);
531			}
532			any = 1;
533			for (; (c = *bits) > 32; bits++) {
534				buf[idx][j++] = tolower((unsigned char)c);
535				if (j >= sizeof(buf[idx]))
536					return (buf[idx]);
537			}
538		} else
539			for (; *bits > 32; bits++)
540				;
541	}
542
543	return (buf[idx]);
544}
545
546uint8_t
547mask2prefixlen(struct sockaddr *sa)
548{
549	struct sockaddr_in	*sa_in = (struct sockaddr_in *)sa;
550	in_addr_t		 ina = sa_in->sin_addr.s_addr;
551
552	if (ina == 0)
553		return (0);
554	else
555		return (33 - ffs(ntohl(ina)));
556}
557
558uint8_t
559mask2prefixlen6(struct sockaddr *sa)
560{
561	struct sockaddr_in6	*sa_in6 = (struct sockaddr_in6 *)sa;
562	uint8_t			*ap, *ep;
563	unsigned int		 l = 0;
564
565	/*
566	 * sin6_len is the size of the sockaddr so substract the offset of
567	 * the possibly truncated sin6_addr struct.
568	 */
569	ap = (uint8_t *)&sa_in6->sin6_addr;
570	ep = (uint8_t *)sa_in6 + sa_in6->sin6_len;
571	for (; ap < ep; ap++) {
572		/* this "beauty" is adopted from sbin/route/show.c ... */
573		switch (*ap) {
574		case 0xff:
575			l += 8;
576			break;
577		case 0xfe:
578			l += 7;
579			goto done;
580		case 0xfc:
581			l += 6;
582			goto done;
583		case 0xf8:
584			l += 5;
585			goto done;
586		case 0xf0:
587			l += 4;
588			goto done;
589		case 0xe0:
590			l += 3;
591			goto done;
592		case 0xc0:
593			l += 2;
594			goto done;
595		case 0x80:
596			l += 1;
597			goto done;
598		case 0x00:
599			goto done;
600		default:
601			fatalx("non contiguous inet6 netmask");
602		}
603	}
604
605done:
606	if (l > sizeof(struct in6_addr) * 8)
607		fatalx("%s: prefixlen %d out of bound", __func__, l);
608	return (l);
609}
610
611uint32_t
612prefixlen2mask(uint8_t prefixlen)
613{
614	if (prefixlen == 0)
615		return (0);
616
617	if (prefixlen > 32)
618		prefixlen = 32;
619
620	return (htonl(0xffffffff << (32 - prefixlen)));
621}
622
623struct in6_addr *
624prefixlen2mask6(uint8_t prefixlen, uint32_t *mask)
625{
626	static struct in6_addr  s6;
627	int			i;
628
629	if (prefixlen > 128)
630		prefixlen = 128;
631
632	bzero(&s6, sizeof(s6));
633	for (i = 0; i < prefixlen / 8; i++)
634		s6.s6_addr[i] = 0xff;
635	i = prefixlen % 8;
636	if (i)
637		s6.s6_addr[prefixlen / 8] = 0xff00 >> i;
638
639	memcpy(mask, &s6, sizeof(s6));
640
641	return (&s6);
642}
643
644const char *
645print_addr(void *addr)
646{
647	static char	 sbuf[IKED_CYCLE_BUFFERS][NI_MAXHOST + 7];
648	static int	 idx;
649	struct sockaddr	*sa = addr;
650	char		*buf;
651	size_t		 len;
652	char		 pbuf[7];
653	in_port_t	 port;
654
655	buf = sbuf[idx];
656	len = sizeof(sbuf[idx]);
657	if (++idx >= IKED_CYCLE_BUFFERS)
658		idx = 0;
659
660	if (sa->sa_family == AF_UNSPEC) {
661		strlcpy(buf, "any", len);
662		return (buf);
663	}
664
665	if (getnameinfo(sa, sa->sa_len,
666	    buf, len, NULL, 0, NI_NUMERICHOST) != 0) {
667		strlcpy(buf, "unknown", len);
668		return (buf);
669	}
670
671	if ((port = socket_getport(sa)) != 0) {
672		snprintf(pbuf, sizeof(pbuf), ":%d", port);
673		(void)strlcat(buf, pbuf, len);
674	}
675
676	return (buf);
677}
678
679char *
680get_string(uint8_t *ptr, size_t len)
681{
682	size_t	 i;
683
684	for (i = 0; i < len; i++)
685		if (!isprint(ptr[i]))
686			break;
687
688	return strndup(ptr, i);
689}
690
691const char *
692print_proto(uint8_t proto)
693{
694	struct protoent *p;
695	static char	 buf[IKED_CYCLE_BUFFERS][BUFSIZ];
696	static int	 idx = 0;
697
698	if (idx >= IKED_CYCLE_BUFFERS)
699		idx = 0;
700
701	if ((p = getprotobynumber(proto)) != NULL)
702		strlcpy(buf[idx], p->p_name, sizeof(buf[idx]));
703	else
704		snprintf(buf[idx], sizeof(buf[idx]), "%u", proto);
705
706	return (buf[idx++]);
707}
708
709int
710expand_string(char *label, size_t len, const char *srch, const char *repl)
711{
712	char *tmp;
713	char *p, *q;
714
715	if ((tmp = calloc(1, len)) == NULL) {
716		log_debug("%s: calloc", __func__);
717		return (-1);
718	}
719	p = label;
720	while ((q = strstr(p, srch)) != NULL) {
721		*q = '\0';
722		if ((strlcat(tmp, p, len) >= len) ||
723		    (strlcat(tmp, repl, len) >= len)) {
724			log_debug("%s: string too long", __func__);
725			free(tmp);
726			return (-1);
727		}
728		q += strlen(srch);
729		p = q;
730	}
731	if (strlcat(tmp, p, len) >= len) {
732		log_debug("%s: string too long", __func__);
733		free(tmp);
734		return (-1);
735	}
736	strlcpy(label, tmp, len);	/* always fits */
737	free(tmp);
738
739	return (0);
740}
741
742uint8_t *
743string2unicode(const char *ascii, size_t *outlen)
744{
745	uint8_t		*uc = NULL;
746	size_t		 i, len = strlen(ascii);
747
748	if ((uc = calloc(1, (len * 2) + 2)) == NULL)
749		return (NULL);
750
751	for (i = 0; i < len; i++) {
752		/* XXX what about the byte order? */
753		uc[i * 2] = ascii[i];
754	}
755	*outlen = len * 2;
756
757	return (uc);
758}
759
760void
761print_debug(const char *emsg, ...)
762{
763	va_list	 ap;
764
765	if (log_getverbose() > 2) {
766		va_start(ap, emsg);
767		vfprintf(stderr, emsg, ap);
768		va_end(ap);
769	}
770}
771
772void
773print_verbose(const char *emsg, ...)
774{
775	va_list	 ap;
776
777	if (log_getverbose()) {
778		va_start(ap, emsg);
779		vfprintf(stderr, emsg, ap);
780		va_end(ap);
781	}
782}
783