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