1 /* $OpenBSD: rarpd.c,v 1.76 2019/06/28 13:32:50 deraadt Exp $ */ 2 /* $NetBSD: rarpd.c,v 1.25 1998/04/23 02:48:33 mrg Exp $ */ 3 4 /* 5 * Copyright (c) 1990 The Regents of the University of California. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that: (1) source code distributions 10 * retain the above copyright notice and this paragraph in its entirety, (2) 11 * distributions including binary code include the above copyright notice and 12 * this paragraph in its entirety in the documentation or other materials 13 * provided with the distribution, and (3) all advertising materials mentioning 14 * features or use of this software display the following acknowledgement: 15 * ``This product includes software developed by the University of California, 16 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of 17 * the University nor the names of its contributors may be used to endorse 18 * or promote products derived from this software without specific prior 19 * written permission. 20 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 21 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 22 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 23 */ 24 25 /* 26 * rarpd - Reverse ARP Daemon 27 */ 28 29 #include <sys/socket.h> 30 #include <sys/ioctl.h> 31 #include <net/bpf.h> 32 #include <net/if.h> 33 #include <net/if_dl.h> 34 #include <net/if_types.h> 35 #include <netinet/in.h> 36 #include <netinet/if_ether.h> 37 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <syslog.h> 41 #include <string.h> 42 #include <stdarg.h> 43 #include <unistd.h> 44 #include <limits.h> 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <netdb.h> 48 #include <arpa/inet.h> 49 #include <dirent.h> 50 #include <poll.h> 51 #include <ifaddrs.h> 52 53 /* 54 * The structures for each interface. 55 */ 56 struct if_addr { 57 in_addr_t ia_ipaddr; /* IP address of this interface */ 58 in_addr_t ia_netmask; /* subnet or net mask */ 59 struct if_addr *ia_next; 60 }; 61 62 struct if_info { 63 int ii_fd; /* BPF file descriptor */ 64 char ii_name[IFNAMSIZ]; /* if name, e.g. "en0" */ 65 u_char ii_eaddr[ETHER_ADDR_LEN]; /* Ethernet address of this iface */ 66 struct if_addr *ii_addrs; /* Networks this interface is on */ 67 struct if_info *ii_next; 68 }; 69 /* 70 * The list of all interfaces that are being listened to. rarp_loop() 71 * "selects" on the descriptors in this list. 72 */ 73 struct if_info *iflist; 74 75 int rarp_open(char *); 76 void init_one(char *); 77 void init_all(void); 78 void rarp_loop(void); 79 void lookup_addrs(char *, struct if_info *); 80 __dead void usage(void); 81 void rarp_process(struct if_info *, u_char *); 82 void rarp_reply(struct if_info *, struct if_addr *, 83 struct ether_header *, u_int32_t, struct hostent *); 84 void arptab_init(void); 85 int arptab_set(u_char *, u_int32_t); 86 __dead void error(const char *,...); 87 void warning(const char *,...); 88 void debug(const char *,...); 89 u_int32_t ipaddrtonetmask(u_int32_t); 90 int rarp_bootable(u_int32_t); 91 92 int aflag = 0; /* listen on "all" interfaces */ 93 int dflag = 0; /* print debugging messages */ 94 int fflag = 0; /* don't fork */ 95 int lflag = 0; /* log all replies */ 96 int tflag = 0; /* tftpboot check */ 97 98 #ifndef TFTP_DIR 99 #define TFTP_DIR "/tftpboot" 100 #endif 101 102 int 103 main(int argc, char *argv[]) 104 { 105 extern char *__progname; 106 extern int optind, opterr; 107 int op; 108 109 /* All error reporting is done through syslogs. */ 110 openlog(__progname, LOG_PID | LOG_CONS, LOG_DAEMON); 111 112 opterr = 0; 113 while ((op = getopt(argc, argv, "adflt")) != -1) { 114 switch (op) { 115 case 'a': 116 ++aflag; 117 break; 118 case 'd': 119 ++dflag; 120 break; 121 case 'f': 122 ++fflag; 123 break; 124 case 'l': 125 ++lflag; 126 break; 127 case 't': 128 ++tflag; 129 break; 130 default: 131 usage(); 132 } 133 } 134 argc -= optind; 135 argv += optind; 136 137 if ((aflag && argc > 0) || (!aflag && argc == 0)) 138 usage(); 139 140 if (aflag) 141 init_all(); 142 else 143 while (argc > 0) { 144 init_one(argv[0]); 145 argc--; 146 argv++; 147 } 148 149 if ((!fflag) && (!dflag)) { 150 if (daemon(0, 0) == -1) 151 error("failed to daemonize: %s", strerror(errno)); 152 } 153 rarp_loop(); 154 exit(0); 155 } 156 157 /* 158 * Add 'ifname' to the interface list. Lookup its IP address and network 159 * mask and Ethernet address, and open a BPF file for it. 160 */ 161 void 162 init_one(char *ifname) 163 { 164 struct if_info *p; 165 int fd; 166 167 /* first check to see if this "if" was already opened? */ 168 for (p = iflist; p; p = p->ii_next) 169 if (!strncmp(p->ii_name, ifname, IFNAMSIZ)) 170 return; 171 172 fd = rarp_open(ifname); 173 if (fd < 0) 174 return; 175 176 p = malloc(sizeof(*p)); 177 if (p == 0) 178 error("malloc: %s", strerror(errno)); 179 180 p->ii_next = iflist; 181 iflist = p; 182 183 p->ii_fd = fd; 184 strncpy(p->ii_name, ifname, IFNAMSIZ); 185 p->ii_addrs = NULL; 186 lookup_addrs(ifname, p); 187 } 188 /* 189 * Initialize all "candidate" interfaces that are in the system 190 * configuration list. A "candidate" is up, not loopback and not 191 * point to point. 192 */ 193 void 194 init_all(void) 195 { 196 struct ifaddrs *ifap, *ifa; 197 struct sockaddr_dl *sdl; 198 199 if (getifaddrs(&ifap) != 0) 200 error("getifaddrs: %s", strerror(errno)); 201 202 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 203 sdl = (struct sockaddr_dl *)ifa->ifa_addr; 204 if (sdl->sdl_family != AF_LINK || sdl->sdl_type != IFT_ETHER || 205 sdl->sdl_alen != 6) 206 continue; 207 208 if ((ifa->ifa_flags & 209 (IFF_UP | IFF_LOOPBACK | IFF_POINTOPOINT)) != IFF_UP) 210 continue; 211 init_one(ifa->ifa_name); 212 } 213 freeifaddrs(ifap); 214 } 215 216 __dead void 217 usage(void) 218 { 219 (void) fprintf(stderr, "usage: rarpd [-adflt] if0 [... ifN]\n"); 220 exit(1); 221 } 222 223 static struct bpf_insn insns[] = { 224 BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 12), 225 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_REVARP, 0, 3), 226 BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 20), 227 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ARPOP_REVREQUEST, 0, 1), 228 BPF_STMT(BPF_RET | BPF_K, sizeof(struct ether_arp) + 229 sizeof(struct ether_header)), 230 BPF_STMT(BPF_RET | BPF_K, 0), 231 }; 232 233 static struct bpf_program filter = { 234 sizeof insns / sizeof(insns[0]), 235 insns 236 }; 237 238 /* 239 * Open a BPF file and attach it to the interface named 'device'. 240 * Set immediate mode, and set a filter that accepts only RARP requests. 241 */ 242 int 243 rarp_open(char *device) 244 { 245 int fd, immediate; 246 struct ifreq ifr; 247 u_int dlt; 248 249 if ((fd = open("/dev/bpf", O_RDWR)) == -1) 250 error("/dev/bpf: %s", strerror(errno)); 251 252 /* Set immediate mode so packets are processed as they arrive. */ 253 immediate = 1; 254 if (ioctl(fd, BIOCIMMEDIATE, &immediate) == -1) { 255 error("BIOCIMMEDIATE: %s", strerror(errno)); 256 } 257 258 (void) strncpy(ifr.ifr_name, device, sizeof ifr.ifr_name); 259 if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) == -1) { 260 if (aflag) { /* for -a skip not ethernet interfaces */ 261 close(fd); 262 return -1; 263 } 264 error("BIOCSETIF: %s", strerror(errno)); 265 } 266 267 /* 268 * Check that the data link layer is an Ethernet; this code 269 * won't work with anything else. 270 */ 271 if (ioctl(fd, BIOCGDLT, (caddr_t) &dlt) == -1) 272 error("BIOCGDLT: %s", strerror(errno)); 273 if (dlt != DLT_EN10MB) { 274 if (aflag) { /* for -a skip not ethernet interfaces */ 275 close(fd); 276 return -1; 277 } 278 error("%s is not an ethernet", device); 279 } 280 /* Set filter program. */ 281 if (ioctl(fd, BIOCSETF, (caddr_t)&filter) == -1) 282 error("BIOCSETF: %s", strerror(errno)); 283 return fd; 284 } 285 /* 286 * Perform various sanity checks on the RARP request packet. Return 287 * false on failure and log the reason. 288 */ 289 static int 290 rarp_check(u_char *p, int len) 291 { 292 struct ether_header *ep = (struct ether_header *) p; 293 struct ether_arp *ap = (struct ether_arp *) (p + sizeof(*ep)); 294 295 (void) debug("got a packet"); 296 297 if (len < sizeof(*ep) + sizeof(*ap)) { 298 warning("truncated request"); 299 return 0; 300 } 301 /* XXX This test might be better off broken out... */ 302 if (ntohs (ep->ether_type) != ETHERTYPE_REVARP || 303 ntohs (ap->arp_hrd) != ARPHRD_ETHER || 304 ntohs (ap->arp_op) != ARPOP_REVREQUEST || 305 ntohs (ap->arp_pro) != ETHERTYPE_IP || 306 ap->arp_hln != 6 || ap->arp_pln != 4) { 307 warning("request fails sanity check"); 308 return 0; 309 } 310 if (memcmp((char *) &ep->ether_shost, (char *) &ap->arp_sha, 6) != 0) { 311 warning("ether/arp sender address mismatch"); 312 return 0; 313 } 314 if (memcmp((char *) &ap->arp_sha, (char *) &ap->arp_tha, 6) != 0) { 315 warning("ether/arp target address mismatch"); 316 return 0; 317 } 318 return 1; 319 } 320 321 /* 322 * Loop indefinitely listening for RARP requests on the 323 * interfaces in 'iflist'. 324 */ 325 void 326 rarp_loop(void) 327 { 328 int cc, fd, numfd = 0, i; 329 u_int bufsize; 330 struct pollfd *pfd; 331 u_char *buf, *bp, *ep; 332 struct if_info *ii; 333 334 if (iflist == 0) 335 error("no interfaces"); 336 if (ioctl(iflist->ii_fd, BIOCGBLEN, (caddr_t)&bufsize) == -1) 337 error("BIOCGBLEN: %s", strerror(errno)); 338 339 arptab_init(); 340 341 if (unveil(TFTP_DIR, "r") == -1) 342 error("unveil"); 343 if (unveil("/etc/ethers", "r") == -1) 344 error("unveil"); 345 if (pledge("stdio rpath dns", NULL) == -1) 346 error("pledge"); 347 348 buf = malloc((size_t) bufsize); 349 if (buf == 0) 350 error("malloc: %s", strerror(errno)); 351 /* 352 * Initialize the set of descriptors to listen to. 353 */ 354 for (ii = iflist; ii; ii = ii->ii_next) 355 numfd++; 356 pfd = reallocarray(NULL, numfd, sizeof(*pfd)); 357 if (pfd == NULL) 358 error("reallocarray: %s", strerror(errno)); 359 for (i = 0, ii = iflist; ii; ii = ii->ii_next, i++) { 360 pfd[i].fd = ii->ii_fd; 361 pfd[i].events = POLLIN; 362 } 363 364 while (1) { 365 if (poll(pfd, numfd, -1) == -1) { 366 if (errno == EINTR) 367 continue; 368 error("poll: %s", strerror(errno)); 369 } 370 for (i = 0, ii = iflist; ii; ii = ii->ii_next, i++) { 371 if (pfd[i].revents == 0) 372 continue; 373 fd = ii->ii_fd; 374 again: 375 cc = read(fd, (char *)buf, bufsize); 376 /* Don't choke when we get ptraced */ 377 if (cc == -1 && errno == EINTR) 378 goto again; 379 if (cc == -1) 380 error("read: %s", strerror(errno)); 381 /* Loop through the packet(s) */ 382 #define bhp ((struct bpf_hdr *)bp) 383 bp = buf; 384 ep = bp + cc; 385 while (bp < ep) { 386 int caplen, hdrlen; 387 388 caplen = bhp->bh_caplen; 389 hdrlen = bhp->bh_hdrlen; 390 if (rarp_check(bp + hdrlen, caplen)) 391 rarp_process(ii, bp + hdrlen); 392 bp += BPF_WORDALIGN(hdrlen + caplen); 393 } 394 } 395 } 396 free(pfd); 397 } 398 399 /* 400 * True if this server can boot the host whose IP address is 'addr'. 401 * This check is made by looking in the tftp directory for the 402 * configuration file. 403 */ 404 int 405 rarp_bootable(u_int32_t addr) 406 { 407 struct dirent *dent; 408 char ipname[40]; 409 static DIR *dd = 0; 410 DIR *d; 411 412 (void) snprintf(ipname, sizeof ipname, "%08X", addr); 413 /* If directory is already open, rewind it. Otherwise, open it. */ 414 if ((d = dd)) 415 rewinddir(d); 416 else { 417 if (chdir(TFTP_DIR) == -1) 418 error("chdir: %s", strerror(errno)); 419 d = opendir("."); 420 if (d == 0) 421 error("opendir: %s", strerror(errno)); 422 dd = d; 423 } 424 while ((dent = readdir(d))) 425 if (strncmp(dent->d_name, ipname, 8) == 0) 426 return 1; 427 return 0; 428 } 429 430 431 /* 432 * Given a list of IP addresses, 'alist', return the first address that 433 * is on network 'net'; 'netmask' is a mask indicating the network portion 434 * of the address. 435 */ 436 static u_int32_t 437 choose_ipaddr(u_int32_t **alist, u_int32_t net, u_int32_t netmask) 438 { 439 for (; *alist; ++alist) { 440 if ((**alist & netmask) == net) 441 return **alist; 442 } 443 return 0; 444 } 445 /* 446 * Answer the RARP request in 'pkt', on the interface 'ii'. 'pkt' has 447 * already been checked for validity. The reply is overlaid on the request. 448 */ 449 void 450 rarp_process(struct if_info *ii, u_char *pkt) 451 { 452 char ename[HOST_NAME_MAX+1]; 453 u_int32_t target_ipaddr; 454 struct ether_header *ep; 455 struct ether_addr *ea; 456 struct hostent *hp; 457 struct in_addr in; 458 struct if_addr *ia; 459 460 ep = (struct ether_header *) pkt; 461 ea = (struct ether_addr *) &ep->ether_shost; 462 463 debug("%s", ether_ntoa(ea)); 464 if (ether_ntohost(ename, ea) != 0) { 465 debug("ether_ntohost failed"); 466 return; 467 } 468 if ((hp = gethostbyname(ename)) == 0) { 469 debug("gethostbyname (%s) failed", ename); 470 return; 471 } 472 473 /* Choose correct address from list. */ 474 if (hp->h_addrtype != AF_INET) 475 error("cannot handle non IP addresses"); 476 for (target_ipaddr = 0, ia = ii->ii_addrs; ia; ia = ia->ia_next) { 477 target_ipaddr = choose_ipaddr((u_int32_t **) hp->h_addr_list, 478 ia->ia_ipaddr & ia->ia_netmask, ia->ia_netmask); 479 if (target_ipaddr) 480 break; 481 } 482 483 if (target_ipaddr == 0) { 484 for (ia = ii->ii_addrs; ia; ia = ia->ia_next) { 485 in.s_addr = ia->ia_ipaddr & ia->ia_netmask; 486 warning("cannot find %s on net %s", 487 ename, inet_ntoa(in)); 488 } 489 return; 490 } 491 if (tflag == 0 || rarp_bootable(htonl(target_ipaddr))) 492 rarp_reply(ii, ia, ep, target_ipaddr, hp); 493 debug("reply sent"); 494 } 495 496 /* 497 * Lookup the ethernet address of the interface attached to the BPF 498 * file descriptor 'fd'; return it in 'eaddr'. 499 */ 500 void 501 lookup_addrs(char *ifname, struct if_info *p) 502 { 503 struct ifaddrs *ifap, *ifa; 504 struct sockaddr_dl *sdl; 505 u_char *eaddr = p->ii_eaddr; 506 struct if_addr *ia, **iap = &p->ii_addrs; 507 struct in_addr in; 508 int found = 0; 509 510 if (getifaddrs(&ifap) != 0) 511 error("getifaddrs: %s", strerror(errno)); 512 513 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 514 if (strcmp(ifa->ifa_name, ifname)) 515 continue; 516 sdl = (struct sockaddr_dl *) ifa->ifa_addr; 517 if (sdl->sdl_family == AF_LINK && 518 sdl->sdl_type == IFT_ETHER && sdl->sdl_alen == 6) { 519 memcpy((caddr_t)eaddr, (caddr_t)LLADDR(sdl), 6); 520 if (dflag) 521 fprintf(stderr, "%s: %x:%x:%x:%x:%x:%x\n", 522 ifa->ifa_name, 523 eaddr[0], eaddr[1], eaddr[2], 524 eaddr[3], eaddr[4], eaddr[5]); 525 found = 1; 526 } else if (sdl->sdl_family == AF_INET) { 527 ia = malloc(sizeof (struct if_addr)); 528 if (ia == NULL) 529 error("lookup_addrs: malloc: %s", 530 strerror(errno)); 531 ia->ia_next = NULL; 532 ia->ia_ipaddr = 533 ((struct sockaddr_in *) ifa->ifa_addr)-> 534 sin_addr.s_addr; 535 ia->ia_netmask = 536 ((struct sockaddr_in *) ifa->ifa_netmask)-> 537 sin_addr.s_addr; 538 /* Figure out a mask from the IP address class. */ 539 if (ia->ia_netmask == 0) 540 ia->ia_netmask = 541 ipaddrtonetmask(ia->ia_ipaddr); 542 if (dflag) { 543 in.s_addr = ia->ia_ipaddr; 544 fprintf(stderr, "\t%s\n", 545 inet_ntoa(in)); 546 } 547 *iap = ia; 548 iap = &ia->ia_next; 549 } 550 } 551 freeifaddrs(ifap); 552 if (!found) 553 error("lookup_addrs: Never saw interface `%s'!", ifname); 554 } 555 556 /* 557 * Build a reverse ARP packet and sent it out on the interface. 558 * 'ep' points to a valid ARPOP_REVREQUEST. The ARPOP_REVREPLY is built 559 * on top of the request, then written to the network. 560 * 561 * RFC 903 defines the ether_arp fields as follows. The following comments 562 * are taken (more or less) straight from this document. 563 * 564 * ARPOP_REVREQUEST 565 * 566 * arp_sha is the hardware address of the sender of the packet. 567 * arp_spa is undefined. 568 * arp_tha is the 'target' hardware address. 569 * In the case where the sender wishes to determine his own 570 * protocol address, this, like arp_sha, will be the hardware 571 * address of the sender. 572 * arp_tpa is undefined. 573 * 574 * ARPOP_REVREPLY 575 * 576 * arp_sha is the hardware address of the responder (the sender of the 577 * reply packet). 578 * arp_spa is the protocol address of the responder (see the note below). 579 * arp_tha is the hardware address of the target, and should be the same as 580 * that which was given in the request. 581 * arp_tpa is the protocol address of the target, that is, the desired address. 582 * 583 * Note that the requirement that arp_spa be filled in with the responder's 584 * protocol is purely for convenience. For instance, if a system were to use 585 * both ARP and RARP, then the inclusion of the valid protocol-hardware 586 * address pair (arp_spa, arp_sha) may eliminate the need for a subsequent 587 * ARP request. 588 */ 589 void 590 rarp_reply(struct if_info *ii, struct if_addr *ia, struct ether_header *ep, 591 u_int32_t ipaddr, struct hostent *hp) 592 { 593 struct ether_arp *ap = (struct ether_arp *) (ep + 1); 594 int len, n; 595 596 /* 597 * Poke the kernel arp tables with the ethernet/ip address 598 * combination given. When processing a reply, we must 599 * do this so that the booting host (i.e. the guy running 600 * rarpd), won't try to ARP for the hardware address of the 601 * guy being booted (he cannot answer the ARP). 602 */ 603 if (arptab_set((u_char *)&ap->arp_sha, ipaddr) > 0) 604 syslog(LOG_ERR, "couldn't update arp table"); 605 606 /* Build the rarp reply by modifying the rarp request in place. */ 607 ep->ether_type = htons(ETHERTYPE_REVARP); 608 ap->ea_hdr.ar_hrd = htons(ARPHRD_ETHER); 609 ap->ea_hdr.ar_pro = htons(ETHERTYPE_IP); 610 ap->arp_op = htons(ARPOP_REVREPLY); 611 612 memcpy((char *) &ep->ether_dhost, (char *) &ap->arp_sha, 6); 613 memcpy((char *) &ep->ether_shost, (char *) ii->ii_eaddr, 6); 614 memcpy((char *) &ap->arp_sha, (char *) ii->ii_eaddr, 6); 615 616 memcpy((char *) ap->arp_tpa, (char *) &ipaddr, 4); 617 /* Target hardware is unchanged. */ 618 memcpy((char *) ap->arp_spa, (char *) &ia->ia_ipaddr, 4); 619 620 if (lflag) { 621 struct ether_addr ea; 622 623 memcpy(&ea.ether_addr_octet, &ap->arp_sha, 6); 624 syslog(LOG_INFO, "%s asked; %s replied", hp->h_name, 625 ether_ntoa(&ea)); 626 } 627 628 len = sizeof(*ep) + sizeof(*ap); 629 n = write(ii->ii_fd, (char *) ep, len); 630 if (n != len) 631 warning("write: only %d of %d bytes written", n, len); 632 } 633 /* 634 * Get the netmask of an IP address. 635 */ 636 u_int32_t 637 ipaddrtonetmask(u_int32_t addr) 638 { 639 if (IN_CLASSA(addr)) 640 return IN_CLASSA_NET; 641 if (IN_CLASSB(addr)) 642 return IN_CLASSB_NET; 643 if (IN_CLASSC(addr)) 644 return IN_CLASSC_NET; 645 error("unknown IP address class: %08X", addr); 646 } 647 648 void 649 warning(const char *fmt,...) 650 { 651 va_list ap; 652 653 if (dflag) { 654 (void) fprintf(stderr, "rarpd: warning: "); 655 va_start(ap, fmt); 656 (void) vfprintf(stderr, fmt, ap); 657 va_end(ap); 658 (void) fprintf(stderr, "\n"); 659 } 660 va_start(ap, fmt); 661 vsyslog(LOG_ERR, fmt, ap); 662 va_end(ap); 663 } 664 665 __dead void 666 error(const char *fmt,...) 667 { 668 va_list ap; 669 670 if (dflag) { 671 (void) fprintf(stderr, "rarpd: error: "); 672 va_start(ap, fmt); 673 (void) vfprintf(stderr, fmt, ap); 674 va_end(ap); 675 (void) fprintf(stderr, "\n"); 676 } 677 va_start(ap, fmt); 678 vsyslog(LOG_ERR, fmt, ap); 679 va_end(ap); 680 exit(1); 681 } 682 683 void 684 debug(const char *fmt,...) 685 { 686 va_list ap; 687 688 if (dflag) { 689 va_start(ap, fmt); 690 (void) fprintf(stderr, "rarpd: "); 691 (void) vfprintf(stderr, fmt, ap); 692 va_end(ap); 693 (void) fprintf(stderr, "\n"); 694 } 695 } 696