1 /* $OpenBSD: util.c,v 1.39 2020/04/10 20:58:33 tobhe 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 38 int 39 socket_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 61 in_port_t 62 socket_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 77 int 78 socket_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 93 int 94 socket_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 101 int 102 socket_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 159 int 160 udp_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 222 int 223 sockaddr_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 285 ssize_t 286 sendtofrom(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 337 ssize_t 338 recvfromto(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 406 const char * 407 print_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 436 const char * 437 print_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 461 void 462 lc_idtype(char *str) 463 { 464 for (; *str != '\0' && *str != '/'; str++) 465 *str = tolower((unsigned char)*str); 466 } 467 468 void 469 print_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 488 void 489 print_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 502 const char * 503 print_bits(unsigned short v, unsigned char *bits) 504 { 505 static char buf[IKED_CYCLE_BUFFERS][BUFSIZ]; 506 static int idx = 0; 507 unsigned int i, any = 0, j = 0; 508 unsigned char c; 509 510 if (!bits) 511 return (""); 512 513 if (++idx >= IKED_CYCLE_BUFFERS) 514 idx = 0; 515 516 bzero(buf[idx], sizeof(buf[idx])); 517 518 bits++; 519 while ((i = *bits++)) { 520 if (v & (1 << (i-1))) { 521 if (any) { 522 buf[idx][j++] = ','; 523 if (j >= sizeof(buf[idx])) 524 return (buf[idx]); 525 } 526 any = 1; 527 for (; (c = *bits) > 32; bits++) { 528 buf[idx][j++] = tolower((unsigned char)c); 529 if (j >= sizeof(buf[idx])) 530 return (buf[idx]); 531 } 532 } else 533 for (; *bits > 32; bits++) 534 ; 535 } 536 537 return (buf[idx]); 538 } 539 540 uint8_t 541 mask2prefixlen(struct sockaddr *sa) 542 { 543 struct sockaddr_in *sa_in = (struct sockaddr_in *)sa; 544 in_addr_t ina = sa_in->sin_addr.s_addr; 545 546 if (ina == 0) 547 return (0); 548 else 549 return (33 - ffs(ntohl(ina))); 550 } 551 552 uint8_t 553 mask2prefixlen6(struct sockaddr *sa) 554 { 555 struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)sa; 556 uint8_t *ap, *ep; 557 unsigned int l = 0; 558 559 /* 560 * sin6_len is the size of the sockaddr so substract the offset of 561 * the possibly truncated sin6_addr struct. 562 */ 563 ap = (uint8_t *)&sa_in6->sin6_addr; 564 ep = (uint8_t *)sa_in6 + sa_in6->sin6_len; 565 for (; ap < ep; ap++) { 566 /* this "beauty" is adopted from sbin/route/show.c ... */ 567 switch (*ap) { 568 case 0xff: 569 l += 8; 570 break; 571 case 0xfe: 572 l += 7; 573 goto done; 574 case 0xfc: 575 l += 6; 576 goto done; 577 case 0xf8: 578 l += 5; 579 goto done; 580 case 0xf0: 581 l += 4; 582 goto done; 583 case 0xe0: 584 l += 3; 585 goto done; 586 case 0xc0: 587 l += 2; 588 goto done; 589 case 0x80: 590 l += 1; 591 goto done; 592 case 0x00: 593 goto done; 594 default: 595 fatalx("non contiguous inet6 netmask"); 596 } 597 } 598 599 done: 600 if (l > sizeof(struct in6_addr) * 8) 601 fatalx("%s: prefixlen %d out of bound", __func__, l); 602 return (l); 603 } 604 605 uint32_t 606 prefixlen2mask(uint8_t prefixlen) 607 { 608 if (prefixlen == 0) 609 return (0); 610 611 if (prefixlen > 32) 612 prefixlen = 32; 613 614 return (htonl(0xffffffff << (32 - prefixlen))); 615 } 616 617 struct in6_addr * 618 prefixlen2mask6(uint8_t prefixlen, uint32_t *mask) 619 { 620 static struct in6_addr s6; 621 int i; 622 623 if (prefixlen > 128) 624 prefixlen = 128; 625 626 bzero(&s6, sizeof(s6)); 627 for (i = 0; i < prefixlen / 8; i++) 628 s6.s6_addr[i] = 0xff; 629 i = prefixlen % 8; 630 if (i) 631 s6.s6_addr[prefixlen / 8] = 0xff00 >> i; 632 633 memcpy(mask, &s6, sizeof(s6)); 634 635 return (&s6); 636 } 637 638 const char * 639 print_host(struct sockaddr *sa, char *buf, size_t len) 640 { 641 static char sbuf[IKED_CYCLE_BUFFERS][NI_MAXHOST + 7]; 642 static int idx = 0; 643 char pbuf[7]; 644 in_port_t port; 645 646 if (buf == NULL) { 647 buf = sbuf[idx]; 648 len = sizeof(sbuf[idx]); 649 if (++idx >= IKED_CYCLE_BUFFERS) 650 idx = 0; 651 } 652 653 if (sa->sa_family == AF_UNSPEC) { 654 strlcpy(buf, "any", len); 655 return (buf); 656 } 657 658 if (getnameinfo(sa, sa->sa_len, 659 buf, len, NULL, 0, NI_NUMERICHOST) != 0) { 660 strlcpy(buf, "unknown", len); 661 return (buf); 662 } 663 664 if ((port = socket_getport(sa)) != 0) { 665 snprintf(pbuf, sizeof(pbuf), ":%d", port); 666 (void)strlcat(buf, pbuf, len); 667 } 668 669 return (buf); 670 } 671 672 char * 673 get_string(uint8_t *ptr, size_t len) 674 { 675 size_t i; 676 677 for (i = 0; i < len; i++) 678 if (!isprint(ptr[i])) 679 break; 680 681 return strndup(ptr, i); 682 } 683 684 const char * 685 print_proto(uint8_t proto) 686 { 687 struct protoent *p; 688 static char buf[IKED_CYCLE_BUFFERS][BUFSIZ]; 689 static int idx = 0; 690 691 if (idx >= IKED_CYCLE_BUFFERS) 692 idx = 0; 693 694 if ((p = getprotobynumber(proto)) != NULL) 695 strlcpy(buf[idx], p->p_name, sizeof(buf[idx])); 696 else 697 snprintf(buf[idx], sizeof(buf), "%u", proto); 698 699 700 return (buf[idx++]); 701 } 702 703 int 704 expand_string(char *label, size_t len, const char *srch, const char *repl) 705 { 706 char *tmp; 707 char *p, *q; 708 709 if ((tmp = calloc(1, len)) == NULL) { 710 log_debug("%s: calloc", __func__); 711 return (-1); 712 } 713 p = q = label; 714 while ((q = strstr(p, srch)) != NULL) { 715 *q = '\0'; 716 if ((strlcat(tmp, p, len) >= len) || 717 (strlcat(tmp, repl, len) >= len)) { 718 log_debug("%s: string too long", __func__); 719 free(tmp); 720 return (-1); 721 } 722 q += strlen(srch); 723 p = q; 724 } 725 if (strlcat(tmp, p, len) >= len) { 726 log_debug("%s: string too long", __func__); 727 free(tmp); 728 return (-1); 729 } 730 strlcpy(label, tmp, len); /* always fits */ 731 free(tmp); 732 733 return (0); 734 } 735 736 uint8_t * 737 string2unicode(const char *ascii, size_t *outlen) 738 { 739 uint8_t *uc = NULL; 740 size_t i, len = strlen(ascii); 741 742 if ((uc = calloc(1, (len * 2) + 2)) == NULL) 743 return (NULL); 744 745 for (i = 0; i < len; i++) { 746 /* XXX what about the byte order? */ 747 uc[i * 2] = ascii[i]; 748 } 749 *outlen = len * 2; 750 751 return (uc); 752 } 753 754 void 755 print_debug(const char *emsg, ...) 756 { 757 va_list ap; 758 759 if (log_getverbose() > 2) { 760 va_start(ap, emsg); 761 vfprintf(stderr, emsg, ap); 762 va_end(ap); 763 } 764 } 765 766 void 767 print_verbose(const char *emsg, ...) 768 { 769 va_list ap; 770 771 if (log_getverbose()) { 772 va_start(ap, emsg); 773 vfprintf(stderr, emsg, ap); 774 va_end(ap); 775 } 776 } 777