264 265 /* 266 * Read switches. 267 */ 268 for (argc--, argv++; argc > 0; argc--, argv++) { 269 if (argv[0][0] != '-') 270 break; 271 switch (argv[0][1]) { 272 273 case 'c': /* chdir_path */ 274 if (argv[0][2]) { 275 stmp = &(argv[0][2]); 276 } else { 277 argc--; 278 argv++; 279 stmp = argv[0]; 280 } 281 if (!stmp || (stmp[0] != '/')) { 282 report(LOG_ERR, 283 "bootpd: invalid chdir specification\n"); 284 break; 285 } 286 chdir_path = stmp; 287 break; 288 289 case 'd': /* debug level */ 290 if (argv[0][2]) { 291 stmp = &(argv[0][2]); 292 } else if (argv[1] && argv[1][0] == '-') { 293 /* 294 * Backwards-compatible behavior: 295 * no parameter, so just increment the debug flag. 296 */ 297 debug++; 298 break; 299 } else { 300 argc--; 301 argv++; 302 stmp = argv[0]; 303 } 304 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 305 report(LOG_ERR, 306 "%s: invalid debug level\n", progname); 307 break; 308 } 309 debug = n; 310 break; 311 312 case 'h': /* override hostname */ 313 if (argv[0][2]) { 314 stmp = &(argv[0][2]); 315 } else { 316 argc--; 317 argv++; 318 stmp = argv[0]; 319 } 320 if (!stmp) { 321 report(LOG_ERR, 322 "bootpd: missing hostname\n"); 323 break; 324 } 325 hostname = stmp; 326 break; 327 328 case 'i': /* inetd mode */ 329 standalone = FALSE; 330 break; 331 332 case 's': /* standalone mode */ 333 standalone = TRUE; 334 break; 335 336 case 't': /* timeout */ 337 if (argv[0][2]) { 338 stmp = &(argv[0][2]); 339 } else { 340 argc--; 341 argv++; 342 stmp = argv[0]; 343 } 344 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 345 report(LOG_ERR, 346 "%s: invalid timeout specification\n", progname); 347 break; 348 } 349 actualtimeout.tv_sec = (int32) (60 * n); 350 /* 351 * If the actual timeout is zero, pass a NULL pointer 352 * to select so it blocks indefinitely, otherwise, 353 * point to the actual timeout value. 354 */ 355 timeout = (n > 0) ? &actualtimeout : NULL; 356 break; 357 358 default: 359 report(LOG_ERR, "%s: unknown switch: -%c\n", 360 progname, argv[0][1]); 361 usage(); 362 break; 363 364 } /* switch */ 365 } /* for args */ 366 367 /* 368 * Override default file names if specified on the command line. 369 */ 370 if (argc > 0) 371 bootptab = argv[0]; 372 373 if (argc > 1) 374 bootpd_dump = argv[1]; 375 376 /* 377 * Get my hostname and IP address. 378 */ 379 380 hep = gethostbyname(hostname); 381 if (!hep) { 382 report(LOG_ERR, "Can not get my IP address\n"); 383 exit(1); 384 } 385 bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr)); 386 387 if (standalone) { 388 /* 389 * Go into background and disassociate from controlling terminal. 390 */ 391 if (debug < 3) { 392 if (fork()) 393 exit(0); 394#ifdef NO_SETSID 395 setpgrp(0,0); 396#ifdef TIOCNOTTY 397 n = open("/dev/tty", O_RDWR); 398 if (n >= 0) { 399 ioctl(n, TIOCNOTTY, (char *) 0); 400 (void) close(n); 401 } 402#endif /* TIOCNOTTY */ 403#else /* SETSID */ 404 if (setsid() < 0) 405 perror("setsid"); 406#endif /* SETSID */ 407 } /* if debug < 3 */ 408 409 /* 410 * Nuke any timeout value 411 */ 412 timeout = NULL; 413 414 } /* if standalone (1st) */ 415 416 /* Set the cwd (i.e. to /tftpboot) */ 417 if (chdir_path) { 418 if (chdir(chdir_path) < 0) 419 report(LOG_ERR, "%s: chdir failed", chdir_path); 420 } 421 422 /* Get the timezone. */ 423 tzone_init(); 424 425 /* Allocate hash tables. */ 426 rdtab_init(); 427 428 /* 429 * Read the bootptab file. 430 */ 431 readtab(1); /* force read */ 432 433 if (standalone) { 434 435 /* 436 * Create a socket. 437 */ 438 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 439 report(LOG_ERR, "socket: %s", get_network_errmsg()); 440 exit(1); 441 } 442 443 /* 444 * Get server's listening port number 445 */ 446 servp = getservbyname("bootps", "udp"); 447 if (servp) { 448 bootps_port = ntohs((u_short) servp->s_port); 449 } else { 450 bootps_port = (u_short) IPPORT_BOOTPS; 451 report(LOG_ERR, 452 "udp/bootps: unknown service -- assuming port %d", 453 bootps_port); 454 } 455 456 /* 457 * Bind socket to BOOTPS port. 458 */ 459 bind_addr.sin_family = AF_INET; 460 bind_addr.sin_addr.s_addr = INADDR_ANY; 461 bind_addr.sin_port = htons(bootps_port); 462 if (bind(s, (struct sockaddr *) &bind_addr, 463 sizeof(bind_addr)) < 0) 464 { 465 report(LOG_ERR, "bind: %s", get_network_errmsg()); 466 exit(1); 467 } 468 } /* if standalone (2nd)*/ 469 470 /* 471 * Get destination port number so we can reply to client 472 */ 473 servp = getservbyname("bootpc", "udp"); 474 if (servp) { 475 bootpc_port = ntohs(servp->s_port); 476 } else { 477 report(LOG_ERR, 478 "udp/bootpc: unknown service -- assuming port %d", 479 IPPORT_BOOTPC); 480 bootpc_port = (u_short) IPPORT_BOOTPC; 481 } 482 483 /* 484 * Set up signals to read or dump the table. 485 */ 486#ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */ 487 sa.sa_handler = catcher; 488 sigemptyset(&sa.sa_mask); 489 sa.sa_flags = 0; 490 if (sigaction(SIGHUP, &sa, NULL) < 0) { 491 report(LOG_ERR, "sigaction: %s", get_errmsg()); 492 exit(1); 493 } 494 if (sigaction(SIGUSR1, &sa, NULL) < 0) { 495 report(LOG_ERR, "sigaction: %s", get_errmsg()); 496 exit(1); 497 } 498#else /* SA_NOCLDSTOP */ 499 /* Old-fashioned UNIX signals */ 500 if ((int) signal(SIGHUP, catcher) < 0) { 501 report(LOG_ERR, "signal: %s", get_errmsg()); 502 exit(1); 503 } 504 if ((int) signal(SIGUSR1, catcher) < 0) { 505 report(LOG_ERR, "signal: %s", get_errmsg()); 506 exit(1); 507 } 508#endif /* SA_NOCLDSTOP */ 509 510 /* 511 * Process incoming requests. 512 */ 513 for (;;) { 514 struct timeval tv; 515 516 readfds = 1 << s; 517 if (timeout) 518 tv = *timeout; 519 520 nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL, 521 (timeout) ? &tv : NULL); 522 if (nfound < 0) { 523 if (errno != EINTR) { 524 report(LOG_ERR, "select: %s", get_errmsg()); 525 } 526 /* 527 * Call readtab() or dumptab() here to avoid the 528 * dangers of doing I/O from a signal handler. 529 */ 530 if (do_readtab) { 531 do_readtab = 0; 532 readtab(1); /* force read */ 533 } 534 if (do_dumptab) { 535 do_dumptab = 0; 536 dumptab(bootpd_dump); 537 } 538 continue; 539 } 540 if (!(readfds & (1 << s))) { 541 if (debug > 1) 542 report(LOG_INFO, "exiting after %ld minutes of inactivity", 543 actualtimeout.tv_sec / 60); 544 exit(0); 545 } 546 ra_len = sizeof(recv_addr); 547 n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0, 548 (struct sockaddr *) &recv_addr, &ra_len); 549 if (n <= 0) { 550 continue; 551 } 552 if (debug > 1) { 553 report(LOG_INFO, "recvd pkt from IP addr %s", 554 inet_ntoa(recv_addr.sin_addr)); 555 } 556 if (n < sizeof(struct bootp)) { 557 if (debug) { 558 report(LOG_NOTICE, "received short packet"); 559 } 560 continue; 561 } 562 pktlen = n; 563 564 readtab(0); /* maybe re-read bootptab */ 565 566 switch (bp->bp_op) { 567 case BOOTREQUEST: 568 handle_request(); 569 break; 570 case BOOTREPLY: 571 handle_reply(); 572 break; 573 } 574 } 575} 576 577 578 579 580/* 581 * Print "usage" message and exit 582 */ 583 584PRIVATE void 585usage() 586{ 587 fprintf(stderr, 588 "usage: bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n"); 589 fprintf(stderr, "\t -c n\tset current directory\n"); 590 fprintf(stderr, "\t -d n\tset debug level\n"); 591 fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n"); 592 fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n"); 593 fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n"); 594 exit(1); 595} 596 597/* Signal catchers */ 598PRIVATE void 599catcher(sig) 600 int sig; 601{ 602 if (sig == SIGHUP) 603 do_readtab = 1; 604 if (sig == SIGUSR1) 605 do_dumptab = 1; 606#if !defined(SA_NOCLDSTOP) && defined(SYSV) 607 /* For older "System V" derivatives with no sigaction(). */ 608 signal(sig, catcher); 609#endif 610} 611 612 613 614/* 615 * Process BOOTREQUEST packet. 616 * 617 * Note: This version of the bootpd.c server never forwards 618 * a request to another server. That is the job of a gateway 619 * program such as the "bootpgw" program included here. 620 * 621 * (Also this version does not interpret the hostname field of 622 * the request packet; it COULD do a name->address lookup and 623 * forward the request there.) 624 */ 625PRIVATE void 626handle_request() 627{ 628 struct bootp *bp = (struct bootp *) pktbuf; 629 struct host *hp = NULL; 630 struct host dummyhost; 631 int32 bootsize = 0; 632 unsigned hlen, hashcode; 633 int32 dest; 634 char realpath[1024]; 635 char *clntpath; 636 char *homedir, *bootfile; 637 int n; 638 639 /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */ 640 641 /* 642 * If the servername field is set, compare it against us. 643 * If we're not being addressed, ignore this request. 644 * If the server name field is null, throw in our name. 645 */ 646 if (strlen(bp->bp_sname)) { 647 if (strcmp(bp->bp_sname, hostname)) { 648 if (debug) 649 report(LOG_INFO, "\ 650ignoring request for server %s from client at %s address %s", 651 bp->bp_sname, netname(bp->bp_htype), 652 haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 653 /* XXX - Is it correct to ignore such a request? -gwr */ 654 return; 655 } 656 } else { 657 strcpy(bp->bp_sname, hostname); 658 } 659 660 /* Convert the request into a reply. */ 661 bp->bp_op = BOOTREPLY; 662 if (bp->bp_ciaddr.s_addr == 0) { 663 /* 664 * client doesnt know his IP address, 665 * search by hardware address. 666 */ 667 if (debug > 1) { 668 report(LOG_INFO, "request from %s address %s", 669 netname(bp->bp_htype), 670 haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 671 } 672 hlen = haddrlength(bp->bp_htype); 673 if (hlen != bp->bp_hlen) { 674 report(LOG_NOTICE, "bad addr len from from %s address %s", 675 netname(bp->bp_htype), 676 haddrtoa(bp->bp_chaddr, hlen)); 677 } 678 dummyhost.htype = bp->bp_htype; 679 bcopy(bp->bp_chaddr, dummyhost.haddr, hlen); 680 hashcode = hash_HashFunction(bp->bp_chaddr, hlen); 681 hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp, 682 &dummyhost); 683 if (hp == NULL && 684 bp->bp_htype == HTYPE_IEEE802) 685 { 686 /* Try again with address in "canonical" form. */ 687 haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen); 688 if (debug > 1) { 689 report(LOG_INFO, "\ 690HW addr type is IEEE 802. convert to %s and check again\n", 691 haddrtoa(dummyhost.haddr, bp->bp_hlen)); 692 } 693 hashcode = hash_HashFunction(dummyhost.haddr, hlen); 694 hp = (struct host *) hash_Lookup(hwhashtable, hashcode, 695 hwlookcmp, &dummyhost); 696 } 697 if (hp == NULL) { 698 /* 699 * XXX - Add dynamic IP address assignment? 700 */ 701 if (debug) 702 report(LOG_NOTICE, "unknown client %s address %s", 703 netname(bp->bp_htype), 704 haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 705 return; /* not found */ 706 } 707 (bp->bp_yiaddr).s_addr = hp->iaddr.s_addr; 708 709 } else { 710 711 /* 712 * search by IP address. 713 */ 714 if (debug > 1) { 715 report(LOG_INFO, "request from IP addr %s", 716 inet_ntoa(bp->bp_ciaddr)); 717 } 718 dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr; 719 hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4); 720 hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp, 721 &dummyhost); 722 if (hp == NULL) { 723 if (debug) { 724 report(LOG_NOTICE, "IP address not found: %s", 725 inet_ntoa(bp->bp_ciaddr)); 726 } 727 return; 728 } 729 } 730 731 if (debug) { 732 report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr), 733 hp->hostname->string); 734 } 735 736 /* 737 * If there is a response delay threshold, ignore requests 738 * with a timestamp lower than the threshold. 739 */ 740 if (hp->flags.min_wait) { 741 u_int32 t = (u_int32) ntohs(bp->bp_secs); 742 if (t < hp->min_wait) { 743 if (debug > 1) 744 report(LOG_INFO, 745 "ignoring request due to timestamp (%d < %d)", 746 t, hp->min_wait); 747 return; 748 } 749 } 750 751#ifdef YORK_EX_OPTION 752 /* 753 * The need for the "ex" tag arose out of the need to empty 754 * shared networked drives on diskless PCs. This solution is 755 * not very clean but it does work fairly well. 756 * Written by Edmund J. Sutcliffe <edmund@york.ac.uk> 757 * 758 * XXX - This could compromise security if a non-trusted user 759 * managed to write an entry in the bootptab with :ex=trojan: 760 * so I would leave this turned off unless you need it. -gwr 761 */ 762 /* Run a program, passing the client name as a parameter. */ 763 if (hp->flags.exec_file) { 764 char tst[100]; 765 /* XXX - Check string lengths? -gwr */ 766 strcpy (tst, hp->exec_file->string); 767 strcat (tst, " "); 768 strcat (tst, hp->hostname->string); 769 strcat (tst, " &"); 770 if (debug) 771 report(LOG_INFO, "executing %s", tst); 772 system(tst); /* Hope this finishes soon... */ 773 } 774#endif /* YORK_EX_OPTION */ 775 776 /* 777 * If a specific TFTP server address was specified in the bootptab file, 778 * fill it in, otherwise zero it. 779 * XXX - Rather than zero it, should it be the bootpd address? -gwr 780 */ 781 (bp->bp_siaddr).s_addr = (hp->flags.bootserver) ? 782 hp->bootserver.s_addr : 0L; 783 784#ifdef STANFORD_PROM_COMPAT 785 /* 786 * Stanford bootp PROMs (for a Sun?) have no way to leave 787 * the boot file name field blank (because the boot file 788 * name is automatically generated from some index). 789 * As a work-around, this little hack allows those PROMs to 790 * specify "sunboot14" with the same effect as a NULL name. 791 * (The user specifies boot device 14 or some such magic.) 792 */ 793 if (strcmp(bp->bp_file, "sunboot14") == 0) 794 bp->bp_file[0] = '\0'; /* treat it as unspecified */ 795#endif 796 797 /* 798 * Fill in the client's proper bootfile. 799 * 800 * If the client specifies an absolute path, try that file with a 801 * ".host" suffix and then without. If the file cannot be found, no 802 * reply is made at all. 803 * 804 * If the client specifies a null or relative file, use the following 805 * table to determine the appropriate action: 806 * 807 * Homedir Bootfile Client's file 808 * specified? specified? specification Action 809 * ------------------------------------------------------------------- 810 * No No Null Send null filename 811 * No No Relative Discard request 812 * No Yes Null Send if absolute else null 813 * No Yes Relative Discard request *XXX 814 * Yes No Null Send null filename 815 * Yes No Relative Lookup with ".host" 816 * Yes Yes Null Send home/boot or bootfile 817 * Yes Yes Relative Lookup with ".host" *XXX 818 * 819 */ 820 821 /* 822 * XXX - I don't like the policy of ignoring a client when the 823 * boot file is not accessible. The TFTP server might not be 824 * running on the same machine as the BOOTP server, in which 825 * case checking accessibility of the boot file is pointless. 826 * 827 * Therefore, file accessibility is now demanded ONLY if you 828 * define CHECK_FILE_ACCESS in the Makefile options. -gwr 829 */ 830 831 /* 832 * The "real" path is as seen by the BOOTP daemon on this 833 * machine, while the client path is relative to the TFTP 834 * daemon chroot directory (i.e. /tftpboot). 835 */ 836 if (hp->flags.tftpdir) { 837 strcpy(realpath, hp->tftpdir->string); 838 clntpath = &realpath[strlen(realpath)]; 839 } else { 840 realpath[0] = '\0'; 841 clntpath = realpath; 842 } 843 844 /* 845 * Determine client's requested homedir and bootfile. 846 */ 847 homedir = NULL; 848 bootfile = NULL; 849 if (bp->bp_file[0]) { 850 homedir = bp->bp_file; 851 bootfile = strrchr(homedir, '/'); 852 if (bootfile) { 853 if (homedir == bootfile) 854 homedir = NULL; 855 *bootfile++ = '\0'; 856 } else { 857 /* no "/" in the string */ 858 bootfile = homedir; 859 homedir = NULL; 860 } 861 if (debug > 2) { 862 report(LOG_INFO, "requested path=\"%s\" file=\"%s\"", 863 (homedir) ? homedir : "", 864 (bootfile) ? bootfile : ""); 865 } 866 } 867 868 /* 869 * Specifications in bootptab override client requested values. 870 */ 871 if (hp->flags.homedir) 872 homedir = hp->homedir->string; 873 if (hp->flags.bootfile) 874 bootfile = hp->bootfile->string; 875 876 /* 877 * Construct bootfile path. 878 */ 879 if (homedir) { 880 if (homedir[0] != '/') 881 strcat(clntpath, "/"); 882 strcat(clntpath, homedir); 883 homedir = NULL; 884 } 885 if (bootfile) { 886 if (bootfile[0] != '/') 887 strcat(clntpath, "/"); 888 strcat(clntpath, bootfile); 889 bootfile = NULL; 890 } 891 892 /* 893 * First try to find the file with a ".host" suffix 894 */ 895 n = strlen(clntpath); 896 strcat(clntpath, "."); 897 strcat(clntpath, hp->hostname->string); 898 if (chk_access(realpath, &bootsize) < 0) { 899 clntpath[n] = 0; /* Try it without the suffix */ 900 if (chk_access(realpath, &bootsize) < 0) { 901 /* neither "file.host" nor "file" was found */ 902#ifdef CHECK_FILE_ACCESS 903 904 if (bp->bp_file[0]) { 905 /* 906 * Client wanted specific file 907 * and we didn't have it. 908 */ 909 report(LOG_NOTICE, 910 "requested file not found: \"%s\"", clntpath); 911 return; 912 } 913 /* 914 * Client didn't ask for a specific file and we couldn't 915 * access the default file, so just zero-out the bootfile 916 * field in the packet and continue processing the reply. 917 */ 918 bzero(bp->bp_file, sizeof(bp->bp_file)); 919 goto null_file_name; 920 921#else /* CHECK_FILE_ACCESS */ 922 923 /* Complain only if boot file size was needed. */ 924 if (hp->flags.bootsize_auto) { 925 report(LOG_ERR, "can not determine size of file \"%s\"", 926 clntpath); 927 } 928 929#endif /* CHECK_FILE_ACCESS */ 930 } 931 } 932 strncpy(bp->bp_file, clntpath, BP_FILE_LEN); 933 if (debug > 2) 934 report(LOG_INFO, "bootfile=\"%s\"", clntpath); 935 936#ifdef CHECK_FILE_ACCESS 937null_file_name: 938#endif /* CHECK_FILE_ACCESS */ 939 940 941 /* 942 * Handle vendor options based on magic number. 943 */ 944 945 if (debug > 1) { 946 report(LOG_INFO, "vendor magic field is %d.%d.%d.%d", 947 (int) ((bp->bp_vend)[0]), 948 (int) ((bp->bp_vend)[1]), 949 (int) ((bp->bp_vend)[2]), 950 (int) ((bp->bp_vend)[3])); 951 } 952 /* 953 * If this host isn't set for automatic vendor info then copy the 954 * specific cookie into the bootp packet, thus forcing a certain 955 * reply format. Only force reply format if user specified it. 956 */ 957 if (hp->flags.vm_cookie) { 958 /* Slam in the user specified magic number. */ 959 bcopy(hp->vm_cookie, bp->bp_vend, 4); 960 } 961 /* 962 * Figure out the format for the vendor-specific info. 963 * Note that bp->bp_vend may have been set above. 964 */ 965 if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) { 966 /* RFC1048 conformant bootp client */ 967 dovend_rfc1048(bp, hp, bootsize); 968 if (debug > 1) { 969 report(LOG_INFO, "sending reply (with RFC1048 options)"); 970 } 971 } 972#ifdef VEND_CMU 973 else if (!bcmp(bp->bp_vend, vm_cmu, 4)) { 974 dovend_cmu(bp, hp); 975 if (debug > 1) { 976 report(LOG_INFO, "sending reply (with CMU options)"); 977 } 978 } 979#endif 980 else { 981 if (debug > 1) { 982 report(LOG_INFO, "sending reply (with no options)"); 983 } 984 } 985 986 dest = (hp->flags.reply_addr) ? 987 hp->reply_addr.s_addr : 0L; 988 989 /* not forwarded */ 990 sendreply(0, dest); 991} 992 993 994/* 995 * Process BOOTREPLY packet. 996 */ 997PRIVATE void 998handle_reply() 999{ 1000 if (debug) { 1001 report(LOG_INFO, "processing boot reply"); 1002 } 1003 /* forwarded, no destination override */ 1004 sendreply(1, 0); 1005} 1006 1007 1008/* 1009 * Send a reply packet to the client. 'forward' flag is set if we are 1010 * not the originator of this reply packet. 1011 */ 1012PRIVATE void 1013sendreply(forward, dst_override) 1014 int forward; 1015 int32 dst_override; 1016{ 1017 struct bootp *bp = (struct bootp *) pktbuf; 1018 struct in_addr dst; 1019 u_short port = bootpc_port; 1020 unsigned char *ha; 1021 int len, haf; 1022 1023 /* 1024 * XXX - Should honor bp_flags "broadcast" bit here. 1025 * Temporary workaround: use the :ra=ADDR: option to 1026 * set the reply address to the broadcast address. 1027 */ 1028 1029 /* 1030 * If the destination address was specified explicitly 1031 * (i.e. the broadcast address for HP compatiblity) 1032 * then send the response to that address. Otherwise, 1033 * act in accordance with RFC951: 1034 * If the client IP address is specified, use that 1035 * else if gateway IP address is specified, use that 1036 * else make a temporary arp cache entry for the client's 1037 * NEW IP/hardware address and use that. 1038 */ 1039 if (dst_override) { 1040 dst.s_addr = dst_override; 1041 if (debug > 1) { 1042 report(LOG_INFO, "reply address override: %s", 1043 inet_ntoa(dst)); 1044 } 1045 } else if (bp->bp_ciaddr.s_addr) { 1046 dst = bp->bp_ciaddr; 1047 } else if (bp->bp_giaddr.s_addr && forward == 0) { 1048 dst = bp->bp_giaddr; 1049 port = bootps_port; 1050 if (debug > 1) { 1051 report(LOG_INFO, "sending reply to gateway %s", 1052 inet_ntoa(dst)); 1053 } 1054 } else { 1055 dst = bp->bp_yiaddr; 1056 ha = bp->bp_chaddr; 1057 len = bp->bp_hlen; 1058 if (len > MAXHADDRLEN) 1059 len = MAXHADDRLEN; 1060 haf = (int) bp->bp_htype; 1061 if (haf == 0) 1062 haf = HTYPE_ETHERNET; 1063 1064 if (debug > 1) 1065 report(LOG_INFO, "setarp %s - %s", 1066 inet_ntoa(dst), haddrtoa(ha, len)); 1067 setarp(s, &dst, haf, ha, len); 1068 } 1069 1070 if ((forward == 0) && 1071 (bp->bp_siaddr.s_addr == 0)) 1072 { 1073 struct ifreq *ifr; 1074 struct in_addr siaddr; 1075 /* 1076 * If we are originating this reply, we 1077 * need to find our own interface address to 1078 * put in the bp_siaddr field of the reply. 1079 * If this server is multi-homed, pick the 1080 * 'best' interface (the one on the same net 1081 * as the client). Of course, the client may 1082 * be on the other side of a BOOTP gateway... 1083 */ 1084 ifr = getif(s, &dst); 1085 if (ifr) { 1086 struct sockaddr_in *sip; 1087 sip = (struct sockaddr_in *) &(ifr->ifr_addr); 1088 siaddr = sip->sin_addr; 1089 } else { 1090 /* Just use my "official" IP address. */ 1091 siaddr = my_ip_addr; 1092 } 1093 1094 /* XXX - No need to set bp_giaddr here. */ 1095 1096 /* Finally, set the server address field. */ 1097 bp->bp_siaddr = siaddr; 1098 } 1099 /* Set up socket address for send. */ 1100 send_addr.sin_family = AF_INET; 1101 send_addr.sin_port = htons(port); 1102 send_addr.sin_addr = dst; 1103 1104 /* Send reply with same size packet as request used. */ 1105 if (sendto(s, pktbuf, pktlen, 0, 1106 (struct sockaddr *) &send_addr, 1107 sizeof(send_addr)) < 0) 1108 { 1109 report(LOG_ERR, "sendto: %s", get_network_errmsg()); 1110 } 1111} /* sendreply */ 1112 1113 1114/* nmatch() - now in getif.c */ 1115/* setarp() - now in hwaddr.c */ 1116 1117 1118/* 1119 * This call checks read access to a file. It returns 0 if the file given 1120 * by "path" exists and is publically readable. A value of -1 is returned if 1121 * access is not permitted or an error occurs. Successful calls also 1122 * return the file size in bytes using the long pointer "filesize". 1123 * 1124 * The read permission bit for "other" users is checked. This bit must be 1125 * set for tftpd(8) to allow clients to read the file. 1126 */ 1127 1128PRIVATE int 1129chk_access(path, filesize) 1130 char *path; 1131 int32 *filesize; 1132{ 1133 struct stat st; 1134 1135 if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) { 1136 *filesize = (int32) st.st_size; 1137 return 0; 1138 } else { 1139 return -1; 1140 } 1141} 1142 1143 1144/* 1145 * Now in dumptab.c : 1146 * dumptab() 1147 * dump_host() 1148 * list_ipaddresses() 1149 */ 1150 1151#ifdef VEND_CMU 1152 1153/* 1154 * Insert the CMU "vendor" data for the host pointed to by "hp" into the 1155 * bootp packet pointed to by "bp". 1156 */ 1157 1158PRIVATE void 1159dovend_cmu(bp, hp) 1160 struct bootp *bp; 1161 struct host *hp; 1162{ 1163 struct cmu_vend *vendp; 1164 struct in_addr_list *taddr; 1165 1166 /* 1167 * Initialize the entire vendor field to zeroes. 1168 */ 1169 bzero(bp->bp_vend, sizeof(bp->bp_vend)); 1170 1171 /* 1172 * Fill in vendor information. Subnet mask, default gateway, 1173 * domain name server, ien name server, time server 1174 */ 1175 vendp = (struct cmu_vend *) bp->bp_vend; 1176 strcpy(vendp->v_magic, (char *)vm_cmu); 1177 if (hp->flags.subnet_mask) { 1178 (vendp->v_smask).s_addr = hp->subnet_mask.s_addr; 1179 (vendp->v_flags) |= VF_SMASK; 1180 if (hp->flags.gateway) { 1181 (vendp->v_dgate).s_addr = hp->gateway->addr->s_addr; 1182 } 1183 } 1184 if (hp->flags.domain_server) { 1185 taddr = hp->domain_server; 1186 if (taddr->addrcount > 0) { 1187 (vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr; 1188 if (taddr->addrcount > 1) { 1189 (vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr; 1190 } 1191 } 1192 } 1193 if (hp->flags.name_server) { 1194 taddr = hp->name_server; 1195 if (taddr->addrcount > 0) { 1196 (vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr; 1197 if (taddr->addrcount > 1) { 1198 (vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr; 1199 } 1200 } 1201 } 1202 if (hp->flags.time_server) { 1203 taddr = hp->time_server; 1204 if (taddr->addrcount > 0) { 1205 (vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr; 1206 if (taddr->addrcount > 1) { 1207 (vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr; 1208 } 1209 } 1210 } 1211 /* Log message now done by caller. */ 1212} /* dovend_cmu */ 1213 1214#endif /* VEND_CMU */ 1215 1216 1217 1218/* 1219 * Insert the RFC1048 vendor data for the host pointed to by "hp" into the 1220 * bootp packet pointed to by "bp". 1221 */ 1222#define NEED(LEN, MSG) do \ 1223 if (bytesleft < (LEN)) { \ 1224 report(LOG_NOTICE, noroom, \ 1225 hp->hostname->string, MSG); \ 1226 return; \ 1227 } while (0) 1228PRIVATE void 1229dovend_rfc1048(bp, hp, bootsize) 1230 struct bootp *bp; 1231 struct host *hp; 1232 int32 bootsize; 1233{ 1234 int bytesleft, len; 1235 byte *vp; 1236 1237 static char noroom[] = "%s: No room for \"%s\" option"; 1238 1239 vp = bp->bp_vend; 1240 1241 if (hp->flags.msg_size) { 1242 pktlen = hp->msg_size; 1243 } else { 1244 /* 1245 * If the request was longer than the official length, build 1246 * a response of that same length where the additional length 1247 * is assumed to be part of the bp_vend (options) area. 1248 */ 1249 if (pktlen > sizeof(*bp)) { 1250 if (debug > 1) 1251 report(LOG_INFO, "request message length=%d", pktlen); 1252 } 1253 /* 1254 * Check whether the request contains the option: 1255 * Maximum DHCP Message Size (RFC1533 sec. 9.8) 1256 * and if so, override the response length with its value. 1257 * This request must lie within the first BP_VEND_LEN 1258 * bytes of the option space. 1259 */ 1260 { 1261 byte *p, *ep; 1262 byte tag, len; 1263 short msgsz = 0; 1264 1265 p = vp + 4; 1266 ep = p + BP_VEND_LEN - 4; 1267 while (p < ep) { 1268 tag = *p++; 1269 /* Check for tags with no data first. */ 1270 if (tag == TAG_PAD) 1271 continue; 1272 if (tag == TAG_END) 1273 break; 1274 /* Now scan the length byte. */ 1275 len = *p++; 1276 switch (tag) { 1277 case TAG_MAX_MSGSZ: 1278 if (len == 2) { 1279 bcopy(p, (char*)&msgsz, 2); 1280 msgsz = ntohs(msgsz); 1281 } 1282 break; 1283 case TAG_SUBNET_MASK: 1284 /* XXX - Should preserve this if given... */ 1285 break; 1286 } /* swtich */ 1287 p += len; 1288 } 1289 1290 if (msgsz > sizeof(*bp)) { 1291 if (debug > 1) 1292 report(LOG_INFO, "request has DHCP msglen=%d", msgsz); 1293 pktlen = msgsz; 1294 } 1295 } 1296 } 1297 1298 if (pktlen < sizeof(*bp)) { 1299 report(LOG_ERR, "invalid response length=%d", pktlen); 1300 pktlen = sizeof(*bp); 1301 } 1302 bytesleft = ((byte*)bp + pktlen) - vp; 1303 if (pktlen > sizeof(*bp)) { 1304 if (debug > 1) 1305 report(LOG_INFO, "extended reply, length=%d, options=%d", 1306 pktlen, bytesleft); 1307 } 1308 1309 /* Copy in the magic cookie */ 1310 bcopy(vm_rfc1048, vp, 4); 1311 vp += 4; 1312 bytesleft -= 4; 1313 1314 if (hp->flags.subnet_mask) { 1315 /* always enough room here. */ 1316 *vp++ = TAG_SUBNET_MASK;/* -1 byte */ 1317 *vp++ = 4; /* -1 byte */ 1318 insert_u_long(hp->subnet_mask.s_addr, &vp); /* -4 bytes */ 1319 bytesleft -= 6; /* Fix real count */ 1320 if (hp->flags.gateway) { 1321 (void) insert_ip(TAG_GATEWAY, 1322 hp->gateway, 1323 &vp, &bytesleft); 1324 } 1325 } 1326 if (hp->flags.bootsize) { 1327 /* always enough room here */ 1328 bootsize = (hp->flags.bootsize_auto) ? 1329 ((bootsize + 511) / 512) : (hp->bootsize); /* Round up */ 1330 *vp++ = TAG_BOOT_SIZE; 1331 *vp++ = 2; 1332 *vp++ = (byte) ((bootsize >> 8) & 0xFF); 1333 *vp++ = (byte) (bootsize & 0xFF); 1334 bytesleft -= 4; /* Tag, length, and 16 bit blocksize */ 1335 } 1336 /* 1337 * This one is special: Remaining options go in the ext file. 1338 * Only the subnet_mask, bootsize, and gateway should precede. 1339 */ 1340 if (hp->flags.exten_file) { 1341 /* 1342 * Check for room for exten_file. Add 3 to account for 1343 * TAG_EXTEN_FILE, length, and TAG_END. 1344 */ 1345 len = strlen(hp->exten_file->string); 1346 NEED((len + 3), "ef"); 1347 *vp++ = TAG_EXTEN_FILE; 1348 *vp++ = (byte) (len & 0xFF); 1349 bcopy(hp->exten_file->string, vp, len); 1350 vp += len; 1351 *vp++ = TAG_END; 1352 bytesleft -= len + 3; 1353 return; /* no more options here. */ 1354 } 1355 /* 1356 * The remaining options are inserted by the following 1357 * function (which is shared with bootpef.c). 1358 * Keep back one byte for the TAG_END. 1359 */ 1360 len = dovend_rfc1497(hp, vp, bytesleft - 1); 1361 vp += len; 1362 bytesleft -= len; 1363 1364 /* There should be at least one byte left. */ 1365 NEED(1, "(end)"); 1366 *vp++ = TAG_END; 1367 bytesleft--; 1368 1369 /* Log message done by caller. */ 1370 if (bytesleft > 0) { 1371 /* 1372 * Zero out any remaining part of the vendor area. 1373 */ 1374 bzero(vp, bytesleft); 1375 } 1376} /* dovend_rfc1048 */ 1377#undef NEED 1378 1379 1380/* 1381 * Now in readfile.c: 1382 * hwlookcmp() 1383 * iplookcmp() 1384 */ 1385 1386/* haddrtoa() - now in hwaddr.c */ 1387/* 1388 * Now in dovend.c: 1389 * insert_ip() 1390 * insert_generic() 1391 * insert_u_long() 1392 */ 1393 1394/* get_errmsg() - now in report.c */ 1395 1396/* 1397 * Local Variables: 1398 * tab-width: 4 1399 * c-indent-level: 4 1400 * c-argdecl-indent: 4 1401 * c-continued-statement-offset: 4 1402 * c-continued-brace-offset: -4 1403 * c-label-offset: -4 1404 * c-brace-offset: 0 1405 * End: 1406 */
| 263 264 /* 265 * Read switches. 266 */ 267 for (argc--, argv++; argc > 0; argc--, argv++) { 268 if (argv[0][0] != '-') 269 break; 270 switch (argv[0][1]) { 271 272 case 'c': /* chdir_path */ 273 if (argv[0][2]) { 274 stmp = &(argv[0][2]); 275 } else { 276 argc--; 277 argv++; 278 stmp = argv[0]; 279 } 280 if (!stmp || (stmp[0] != '/')) { 281 report(LOG_ERR, 282 "bootpd: invalid chdir specification\n"); 283 break; 284 } 285 chdir_path = stmp; 286 break; 287 288 case 'd': /* debug level */ 289 if (argv[0][2]) { 290 stmp = &(argv[0][2]); 291 } else if (argv[1] && argv[1][0] == '-') { 292 /* 293 * Backwards-compatible behavior: 294 * no parameter, so just increment the debug flag. 295 */ 296 debug++; 297 break; 298 } else { 299 argc--; 300 argv++; 301 stmp = argv[0]; 302 } 303 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 304 report(LOG_ERR, 305 "%s: invalid debug level\n", progname); 306 break; 307 } 308 debug = n; 309 break; 310 311 case 'h': /* override hostname */ 312 if (argv[0][2]) { 313 stmp = &(argv[0][2]); 314 } else { 315 argc--; 316 argv++; 317 stmp = argv[0]; 318 } 319 if (!stmp) { 320 report(LOG_ERR, 321 "bootpd: missing hostname\n"); 322 break; 323 } 324 hostname = stmp; 325 break; 326 327 case 'i': /* inetd mode */ 328 standalone = FALSE; 329 break; 330 331 case 's': /* standalone mode */ 332 standalone = TRUE; 333 break; 334 335 case 't': /* timeout */ 336 if (argv[0][2]) { 337 stmp = &(argv[0][2]); 338 } else { 339 argc--; 340 argv++; 341 stmp = argv[0]; 342 } 343 if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 344 report(LOG_ERR, 345 "%s: invalid timeout specification\n", progname); 346 break; 347 } 348 actualtimeout.tv_sec = (int32) (60 * n); 349 /* 350 * If the actual timeout is zero, pass a NULL pointer 351 * to select so it blocks indefinitely, otherwise, 352 * point to the actual timeout value. 353 */ 354 timeout = (n > 0) ? &actualtimeout : NULL; 355 break; 356 357 default: 358 report(LOG_ERR, "%s: unknown switch: -%c\n", 359 progname, argv[0][1]); 360 usage(); 361 break; 362 363 } /* switch */ 364 } /* for args */ 365 366 /* 367 * Override default file names if specified on the command line. 368 */ 369 if (argc > 0) 370 bootptab = argv[0]; 371 372 if (argc > 1) 373 bootpd_dump = argv[1]; 374 375 /* 376 * Get my hostname and IP address. 377 */ 378 379 hep = gethostbyname(hostname); 380 if (!hep) { 381 report(LOG_ERR, "Can not get my IP address\n"); 382 exit(1); 383 } 384 bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr)); 385 386 if (standalone) { 387 /* 388 * Go into background and disassociate from controlling terminal. 389 */ 390 if (debug < 3) { 391 if (fork()) 392 exit(0); 393#ifdef NO_SETSID 394 setpgrp(0,0); 395#ifdef TIOCNOTTY 396 n = open("/dev/tty", O_RDWR); 397 if (n >= 0) { 398 ioctl(n, TIOCNOTTY, (char *) 0); 399 (void) close(n); 400 } 401#endif /* TIOCNOTTY */ 402#else /* SETSID */ 403 if (setsid() < 0) 404 perror("setsid"); 405#endif /* SETSID */ 406 } /* if debug < 3 */ 407 408 /* 409 * Nuke any timeout value 410 */ 411 timeout = NULL; 412 413 } /* if standalone (1st) */ 414 415 /* Set the cwd (i.e. to /tftpboot) */ 416 if (chdir_path) { 417 if (chdir(chdir_path) < 0) 418 report(LOG_ERR, "%s: chdir failed", chdir_path); 419 } 420 421 /* Get the timezone. */ 422 tzone_init(); 423 424 /* Allocate hash tables. */ 425 rdtab_init(); 426 427 /* 428 * Read the bootptab file. 429 */ 430 readtab(1); /* force read */ 431 432 if (standalone) { 433 434 /* 435 * Create a socket. 436 */ 437 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 438 report(LOG_ERR, "socket: %s", get_network_errmsg()); 439 exit(1); 440 } 441 442 /* 443 * Get server's listening port number 444 */ 445 servp = getservbyname("bootps", "udp"); 446 if (servp) { 447 bootps_port = ntohs((u_short) servp->s_port); 448 } else { 449 bootps_port = (u_short) IPPORT_BOOTPS; 450 report(LOG_ERR, 451 "udp/bootps: unknown service -- assuming port %d", 452 bootps_port); 453 } 454 455 /* 456 * Bind socket to BOOTPS port. 457 */ 458 bind_addr.sin_family = AF_INET; 459 bind_addr.sin_addr.s_addr = INADDR_ANY; 460 bind_addr.sin_port = htons(bootps_port); 461 if (bind(s, (struct sockaddr *) &bind_addr, 462 sizeof(bind_addr)) < 0) 463 { 464 report(LOG_ERR, "bind: %s", get_network_errmsg()); 465 exit(1); 466 } 467 } /* if standalone (2nd)*/ 468 469 /* 470 * Get destination port number so we can reply to client 471 */ 472 servp = getservbyname("bootpc", "udp"); 473 if (servp) { 474 bootpc_port = ntohs(servp->s_port); 475 } else { 476 report(LOG_ERR, 477 "udp/bootpc: unknown service -- assuming port %d", 478 IPPORT_BOOTPC); 479 bootpc_port = (u_short) IPPORT_BOOTPC; 480 } 481 482 /* 483 * Set up signals to read or dump the table. 484 */ 485#ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */ 486 sa.sa_handler = catcher; 487 sigemptyset(&sa.sa_mask); 488 sa.sa_flags = 0; 489 if (sigaction(SIGHUP, &sa, NULL) < 0) { 490 report(LOG_ERR, "sigaction: %s", get_errmsg()); 491 exit(1); 492 } 493 if (sigaction(SIGUSR1, &sa, NULL) < 0) { 494 report(LOG_ERR, "sigaction: %s", get_errmsg()); 495 exit(1); 496 } 497#else /* SA_NOCLDSTOP */ 498 /* Old-fashioned UNIX signals */ 499 if ((int) signal(SIGHUP, catcher) < 0) { 500 report(LOG_ERR, "signal: %s", get_errmsg()); 501 exit(1); 502 } 503 if ((int) signal(SIGUSR1, catcher) < 0) { 504 report(LOG_ERR, "signal: %s", get_errmsg()); 505 exit(1); 506 } 507#endif /* SA_NOCLDSTOP */ 508 509 /* 510 * Process incoming requests. 511 */ 512 for (;;) { 513 struct timeval tv; 514 515 readfds = 1 << s; 516 if (timeout) 517 tv = *timeout; 518 519 nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL, 520 (timeout) ? &tv : NULL); 521 if (nfound < 0) { 522 if (errno != EINTR) { 523 report(LOG_ERR, "select: %s", get_errmsg()); 524 } 525 /* 526 * Call readtab() or dumptab() here to avoid the 527 * dangers of doing I/O from a signal handler. 528 */ 529 if (do_readtab) { 530 do_readtab = 0; 531 readtab(1); /* force read */ 532 } 533 if (do_dumptab) { 534 do_dumptab = 0; 535 dumptab(bootpd_dump); 536 } 537 continue; 538 } 539 if (!(readfds & (1 << s))) { 540 if (debug > 1) 541 report(LOG_INFO, "exiting after %ld minutes of inactivity", 542 actualtimeout.tv_sec / 60); 543 exit(0); 544 } 545 ra_len = sizeof(recv_addr); 546 n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0, 547 (struct sockaddr *) &recv_addr, &ra_len); 548 if (n <= 0) { 549 continue; 550 } 551 if (debug > 1) { 552 report(LOG_INFO, "recvd pkt from IP addr %s", 553 inet_ntoa(recv_addr.sin_addr)); 554 } 555 if (n < sizeof(struct bootp)) { 556 if (debug) { 557 report(LOG_NOTICE, "received short packet"); 558 } 559 continue; 560 } 561 pktlen = n; 562 563 readtab(0); /* maybe re-read bootptab */ 564 565 switch (bp->bp_op) { 566 case BOOTREQUEST: 567 handle_request(); 568 break; 569 case BOOTREPLY: 570 handle_reply(); 571 break; 572 } 573 } 574} 575 576 577 578 579/* 580 * Print "usage" message and exit 581 */ 582 583PRIVATE void 584usage() 585{ 586 fprintf(stderr, 587 "usage: bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n"); 588 fprintf(stderr, "\t -c n\tset current directory\n"); 589 fprintf(stderr, "\t -d n\tset debug level\n"); 590 fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n"); 591 fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n"); 592 fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n"); 593 exit(1); 594} 595 596/* Signal catchers */ 597PRIVATE void 598catcher(sig) 599 int sig; 600{ 601 if (sig == SIGHUP) 602 do_readtab = 1; 603 if (sig == SIGUSR1) 604 do_dumptab = 1; 605#if !defined(SA_NOCLDSTOP) && defined(SYSV) 606 /* For older "System V" derivatives with no sigaction(). */ 607 signal(sig, catcher); 608#endif 609} 610 611 612 613/* 614 * Process BOOTREQUEST packet. 615 * 616 * Note: This version of the bootpd.c server never forwards 617 * a request to another server. That is the job of a gateway 618 * program such as the "bootpgw" program included here. 619 * 620 * (Also this version does not interpret the hostname field of 621 * the request packet; it COULD do a name->address lookup and 622 * forward the request there.) 623 */ 624PRIVATE void 625handle_request() 626{ 627 struct bootp *bp = (struct bootp *) pktbuf; 628 struct host *hp = NULL; 629 struct host dummyhost; 630 int32 bootsize = 0; 631 unsigned hlen, hashcode; 632 int32 dest; 633 char realpath[1024]; 634 char *clntpath; 635 char *homedir, *bootfile; 636 int n; 637 638 /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */ 639 640 /* 641 * If the servername field is set, compare it against us. 642 * If we're not being addressed, ignore this request. 643 * If the server name field is null, throw in our name. 644 */ 645 if (strlen(bp->bp_sname)) { 646 if (strcmp(bp->bp_sname, hostname)) { 647 if (debug) 648 report(LOG_INFO, "\ 649ignoring request for server %s from client at %s address %s", 650 bp->bp_sname, netname(bp->bp_htype), 651 haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 652 /* XXX - Is it correct to ignore such a request? -gwr */ 653 return; 654 } 655 } else { 656 strcpy(bp->bp_sname, hostname); 657 } 658 659 /* Convert the request into a reply. */ 660 bp->bp_op = BOOTREPLY; 661 if (bp->bp_ciaddr.s_addr == 0) { 662 /* 663 * client doesnt know his IP address, 664 * search by hardware address. 665 */ 666 if (debug > 1) { 667 report(LOG_INFO, "request from %s address %s", 668 netname(bp->bp_htype), 669 haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 670 } 671 hlen = haddrlength(bp->bp_htype); 672 if (hlen != bp->bp_hlen) { 673 report(LOG_NOTICE, "bad addr len from from %s address %s", 674 netname(bp->bp_htype), 675 haddrtoa(bp->bp_chaddr, hlen)); 676 } 677 dummyhost.htype = bp->bp_htype; 678 bcopy(bp->bp_chaddr, dummyhost.haddr, hlen); 679 hashcode = hash_HashFunction(bp->bp_chaddr, hlen); 680 hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp, 681 &dummyhost); 682 if (hp == NULL && 683 bp->bp_htype == HTYPE_IEEE802) 684 { 685 /* Try again with address in "canonical" form. */ 686 haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen); 687 if (debug > 1) { 688 report(LOG_INFO, "\ 689HW addr type is IEEE 802. convert to %s and check again\n", 690 haddrtoa(dummyhost.haddr, bp->bp_hlen)); 691 } 692 hashcode = hash_HashFunction(dummyhost.haddr, hlen); 693 hp = (struct host *) hash_Lookup(hwhashtable, hashcode, 694 hwlookcmp, &dummyhost); 695 } 696 if (hp == NULL) { 697 /* 698 * XXX - Add dynamic IP address assignment? 699 */ 700 if (debug) 701 report(LOG_NOTICE, "unknown client %s address %s", 702 netname(bp->bp_htype), 703 haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 704 return; /* not found */ 705 } 706 (bp->bp_yiaddr).s_addr = hp->iaddr.s_addr; 707 708 } else { 709 710 /* 711 * search by IP address. 712 */ 713 if (debug > 1) { 714 report(LOG_INFO, "request from IP addr %s", 715 inet_ntoa(bp->bp_ciaddr)); 716 } 717 dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr; 718 hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4); 719 hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp, 720 &dummyhost); 721 if (hp == NULL) { 722 if (debug) { 723 report(LOG_NOTICE, "IP address not found: %s", 724 inet_ntoa(bp->bp_ciaddr)); 725 } 726 return; 727 } 728 } 729 730 if (debug) { 731 report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr), 732 hp->hostname->string); 733 } 734 735 /* 736 * If there is a response delay threshold, ignore requests 737 * with a timestamp lower than the threshold. 738 */ 739 if (hp->flags.min_wait) { 740 u_int32 t = (u_int32) ntohs(bp->bp_secs); 741 if (t < hp->min_wait) { 742 if (debug > 1) 743 report(LOG_INFO, 744 "ignoring request due to timestamp (%d < %d)", 745 t, hp->min_wait); 746 return; 747 } 748 } 749 750#ifdef YORK_EX_OPTION 751 /* 752 * The need for the "ex" tag arose out of the need to empty 753 * shared networked drives on diskless PCs. This solution is 754 * not very clean but it does work fairly well. 755 * Written by Edmund J. Sutcliffe <edmund@york.ac.uk> 756 * 757 * XXX - This could compromise security if a non-trusted user 758 * managed to write an entry in the bootptab with :ex=trojan: 759 * so I would leave this turned off unless you need it. -gwr 760 */ 761 /* Run a program, passing the client name as a parameter. */ 762 if (hp->flags.exec_file) { 763 char tst[100]; 764 /* XXX - Check string lengths? -gwr */ 765 strcpy (tst, hp->exec_file->string); 766 strcat (tst, " "); 767 strcat (tst, hp->hostname->string); 768 strcat (tst, " &"); 769 if (debug) 770 report(LOG_INFO, "executing %s", tst); 771 system(tst); /* Hope this finishes soon... */ 772 } 773#endif /* YORK_EX_OPTION */ 774 775 /* 776 * If a specific TFTP server address was specified in the bootptab file, 777 * fill it in, otherwise zero it. 778 * XXX - Rather than zero it, should it be the bootpd address? -gwr 779 */ 780 (bp->bp_siaddr).s_addr = (hp->flags.bootserver) ? 781 hp->bootserver.s_addr : 0L; 782 783#ifdef STANFORD_PROM_COMPAT 784 /* 785 * Stanford bootp PROMs (for a Sun?) have no way to leave 786 * the boot file name field blank (because the boot file 787 * name is automatically generated from some index). 788 * As a work-around, this little hack allows those PROMs to 789 * specify "sunboot14" with the same effect as a NULL name. 790 * (The user specifies boot device 14 or some such magic.) 791 */ 792 if (strcmp(bp->bp_file, "sunboot14") == 0) 793 bp->bp_file[0] = '\0'; /* treat it as unspecified */ 794#endif 795 796 /* 797 * Fill in the client's proper bootfile. 798 * 799 * If the client specifies an absolute path, try that file with a 800 * ".host" suffix and then without. If the file cannot be found, no 801 * reply is made at all. 802 * 803 * If the client specifies a null or relative file, use the following 804 * table to determine the appropriate action: 805 * 806 * Homedir Bootfile Client's file 807 * specified? specified? specification Action 808 * ------------------------------------------------------------------- 809 * No No Null Send null filename 810 * No No Relative Discard request 811 * No Yes Null Send if absolute else null 812 * No Yes Relative Discard request *XXX 813 * Yes No Null Send null filename 814 * Yes No Relative Lookup with ".host" 815 * Yes Yes Null Send home/boot or bootfile 816 * Yes Yes Relative Lookup with ".host" *XXX 817 * 818 */ 819 820 /* 821 * XXX - I don't like the policy of ignoring a client when the 822 * boot file is not accessible. The TFTP server might not be 823 * running on the same machine as the BOOTP server, in which 824 * case checking accessibility of the boot file is pointless. 825 * 826 * Therefore, file accessibility is now demanded ONLY if you 827 * define CHECK_FILE_ACCESS in the Makefile options. -gwr 828 */ 829 830 /* 831 * The "real" path is as seen by the BOOTP daemon on this 832 * machine, while the client path is relative to the TFTP 833 * daemon chroot directory (i.e. /tftpboot). 834 */ 835 if (hp->flags.tftpdir) { 836 strcpy(realpath, hp->tftpdir->string); 837 clntpath = &realpath[strlen(realpath)]; 838 } else { 839 realpath[0] = '\0'; 840 clntpath = realpath; 841 } 842 843 /* 844 * Determine client's requested homedir and bootfile. 845 */ 846 homedir = NULL; 847 bootfile = NULL; 848 if (bp->bp_file[0]) { 849 homedir = bp->bp_file; 850 bootfile = strrchr(homedir, '/'); 851 if (bootfile) { 852 if (homedir == bootfile) 853 homedir = NULL; 854 *bootfile++ = '\0'; 855 } else { 856 /* no "/" in the string */ 857 bootfile = homedir; 858 homedir = NULL; 859 } 860 if (debug > 2) { 861 report(LOG_INFO, "requested path=\"%s\" file=\"%s\"", 862 (homedir) ? homedir : "", 863 (bootfile) ? bootfile : ""); 864 } 865 } 866 867 /* 868 * Specifications in bootptab override client requested values. 869 */ 870 if (hp->flags.homedir) 871 homedir = hp->homedir->string; 872 if (hp->flags.bootfile) 873 bootfile = hp->bootfile->string; 874 875 /* 876 * Construct bootfile path. 877 */ 878 if (homedir) { 879 if (homedir[0] != '/') 880 strcat(clntpath, "/"); 881 strcat(clntpath, homedir); 882 homedir = NULL; 883 } 884 if (bootfile) { 885 if (bootfile[0] != '/') 886 strcat(clntpath, "/"); 887 strcat(clntpath, bootfile); 888 bootfile = NULL; 889 } 890 891 /* 892 * First try to find the file with a ".host" suffix 893 */ 894 n = strlen(clntpath); 895 strcat(clntpath, "."); 896 strcat(clntpath, hp->hostname->string); 897 if (chk_access(realpath, &bootsize) < 0) { 898 clntpath[n] = 0; /* Try it without the suffix */ 899 if (chk_access(realpath, &bootsize) < 0) { 900 /* neither "file.host" nor "file" was found */ 901#ifdef CHECK_FILE_ACCESS 902 903 if (bp->bp_file[0]) { 904 /* 905 * Client wanted specific file 906 * and we didn't have it. 907 */ 908 report(LOG_NOTICE, 909 "requested file not found: \"%s\"", clntpath); 910 return; 911 } 912 /* 913 * Client didn't ask for a specific file and we couldn't 914 * access the default file, so just zero-out the bootfile 915 * field in the packet and continue processing the reply. 916 */ 917 bzero(bp->bp_file, sizeof(bp->bp_file)); 918 goto null_file_name; 919 920#else /* CHECK_FILE_ACCESS */ 921 922 /* Complain only if boot file size was needed. */ 923 if (hp->flags.bootsize_auto) { 924 report(LOG_ERR, "can not determine size of file \"%s\"", 925 clntpath); 926 } 927 928#endif /* CHECK_FILE_ACCESS */ 929 } 930 } 931 strncpy(bp->bp_file, clntpath, BP_FILE_LEN); 932 if (debug > 2) 933 report(LOG_INFO, "bootfile=\"%s\"", clntpath); 934 935#ifdef CHECK_FILE_ACCESS 936null_file_name: 937#endif /* CHECK_FILE_ACCESS */ 938 939 940 /* 941 * Handle vendor options based on magic number. 942 */ 943 944 if (debug > 1) { 945 report(LOG_INFO, "vendor magic field is %d.%d.%d.%d", 946 (int) ((bp->bp_vend)[0]), 947 (int) ((bp->bp_vend)[1]), 948 (int) ((bp->bp_vend)[2]), 949 (int) ((bp->bp_vend)[3])); 950 } 951 /* 952 * If this host isn't set for automatic vendor info then copy the 953 * specific cookie into the bootp packet, thus forcing a certain 954 * reply format. Only force reply format if user specified it. 955 */ 956 if (hp->flags.vm_cookie) { 957 /* Slam in the user specified magic number. */ 958 bcopy(hp->vm_cookie, bp->bp_vend, 4); 959 } 960 /* 961 * Figure out the format for the vendor-specific info. 962 * Note that bp->bp_vend may have been set above. 963 */ 964 if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) { 965 /* RFC1048 conformant bootp client */ 966 dovend_rfc1048(bp, hp, bootsize); 967 if (debug > 1) { 968 report(LOG_INFO, "sending reply (with RFC1048 options)"); 969 } 970 } 971#ifdef VEND_CMU 972 else if (!bcmp(bp->bp_vend, vm_cmu, 4)) { 973 dovend_cmu(bp, hp); 974 if (debug > 1) { 975 report(LOG_INFO, "sending reply (with CMU options)"); 976 } 977 } 978#endif 979 else { 980 if (debug > 1) { 981 report(LOG_INFO, "sending reply (with no options)"); 982 } 983 } 984 985 dest = (hp->flags.reply_addr) ? 986 hp->reply_addr.s_addr : 0L; 987 988 /* not forwarded */ 989 sendreply(0, dest); 990} 991 992 993/* 994 * Process BOOTREPLY packet. 995 */ 996PRIVATE void 997handle_reply() 998{ 999 if (debug) { 1000 report(LOG_INFO, "processing boot reply"); 1001 } 1002 /* forwarded, no destination override */ 1003 sendreply(1, 0); 1004} 1005 1006 1007/* 1008 * Send a reply packet to the client. 'forward' flag is set if we are 1009 * not the originator of this reply packet. 1010 */ 1011PRIVATE void 1012sendreply(forward, dst_override) 1013 int forward; 1014 int32 dst_override; 1015{ 1016 struct bootp *bp = (struct bootp *) pktbuf; 1017 struct in_addr dst; 1018 u_short port = bootpc_port; 1019 unsigned char *ha; 1020 int len, haf; 1021 1022 /* 1023 * XXX - Should honor bp_flags "broadcast" bit here. 1024 * Temporary workaround: use the :ra=ADDR: option to 1025 * set the reply address to the broadcast address. 1026 */ 1027 1028 /* 1029 * If the destination address was specified explicitly 1030 * (i.e. the broadcast address for HP compatiblity) 1031 * then send the response to that address. Otherwise, 1032 * act in accordance with RFC951: 1033 * If the client IP address is specified, use that 1034 * else if gateway IP address is specified, use that 1035 * else make a temporary arp cache entry for the client's 1036 * NEW IP/hardware address and use that. 1037 */ 1038 if (dst_override) { 1039 dst.s_addr = dst_override; 1040 if (debug > 1) { 1041 report(LOG_INFO, "reply address override: %s", 1042 inet_ntoa(dst)); 1043 } 1044 } else if (bp->bp_ciaddr.s_addr) { 1045 dst = bp->bp_ciaddr; 1046 } else if (bp->bp_giaddr.s_addr && forward == 0) { 1047 dst = bp->bp_giaddr; 1048 port = bootps_port; 1049 if (debug > 1) { 1050 report(LOG_INFO, "sending reply to gateway %s", 1051 inet_ntoa(dst)); 1052 } 1053 } else { 1054 dst = bp->bp_yiaddr; 1055 ha = bp->bp_chaddr; 1056 len = bp->bp_hlen; 1057 if (len > MAXHADDRLEN) 1058 len = MAXHADDRLEN; 1059 haf = (int) bp->bp_htype; 1060 if (haf == 0) 1061 haf = HTYPE_ETHERNET; 1062 1063 if (debug > 1) 1064 report(LOG_INFO, "setarp %s - %s", 1065 inet_ntoa(dst), haddrtoa(ha, len)); 1066 setarp(s, &dst, haf, ha, len); 1067 } 1068 1069 if ((forward == 0) && 1070 (bp->bp_siaddr.s_addr == 0)) 1071 { 1072 struct ifreq *ifr; 1073 struct in_addr siaddr; 1074 /* 1075 * If we are originating this reply, we 1076 * need to find our own interface address to 1077 * put in the bp_siaddr field of the reply. 1078 * If this server is multi-homed, pick the 1079 * 'best' interface (the one on the same net 1080 * as the client). Of course, the client may 1081 * be on the other side of a BOOTP gateway... 1082 */ 1083 ifr = getif(s, &dst); 1084 if (ifr) { 1085 struct sockaddr_in *sip; 1086 sip = (struct sockaddr_in *) &(ifr->ifr_addr); 1087 siaddr = sip->sin_addr; 1088 } else { 1089 /* Just use my "official" IP address. */ 1090 siaddr = my_ip_addr; 1091 } 1092 1093 /* XXX - No need to set bp_giaddr here. */ 1094 1095 /* Finally, set the server address field. */ 1096 bp->bp_siaddr = siaddr; 1097 } 1098 /* Set up socket address for send. */ 1099 send_addr.sin_family = AF_INET; 1100 send_addr.sin_port = htons(port); 1101 send_addr.sin_addr = dst; 1102 1103 /* Send reply with same size packet as request used. */ 1104 if (sendto(s, pktbuf, pktlen, 0, 1105 (struct sockaddr *) &send_addr, 1106 sizeof(send_addr)) < 0) 1107 { 1108 report(LOG_ERR, "sendto: %s", get_network_errmsg()); 1109 } 1110} /* sendreply */ 1111 1112 1113/* nmatch() - now in getif.c */ 1114/* setarp() - now in hwaddr.c */ 1115 1116 1117/* 1118 * This call checks read access to a file. It returns 0 if the file given 1119 * by "path" exists and is publically readable. A value of -1 is returned if 1120 * access is not permitted or an error occurs. Successful calls also 1121 * return the file size in bytes using the long pointer "filesize". 1122 * 1123 * The read permission bit for "other" users is checked. This bit must be 1124 * set for tftpd(8) to allow clients to read the file. 1125 */ 1126 1127PRIVATE int 1128chk_access(path, filesize) 1129 char *path; 1130 int32 *filesize; 1131{ 1132 struct stat st; 1133 1134 if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) { 1135 *filesize = (int32) st.st_size; 1136 return 0; 1137 } else { 1138 return -1; 1139 } 1140} 1141 1142 1143/* 1144 * Now in dumptab.c : 1145 * dumptab() 1146 * dump_host() 1147 * list_ipaddresses() 1148 */ 1149 1150#ifdef VEND_CMU 1151 1152/* 1153 * Insert the CMU "vendor" data for the host pointed to by "hp" into the 1154 * bootp packet pointed to by "bp". 1155 */ 1156 1157PRIVATE void 1158dovend_cmu(bp, hp) 1159 struct bootp *bp; 1160 struct host *hp; 1161{ 1162 struct cmu_vend *vendp; 1163 struct in_addr_list *taddr; 1164 1165 /* 1166 * Initialize the entire vendor field to zeroes. 1167 */ 1168 bzero(bp->bp_vend, sizeof(bp->bp_vend)); 1169 1170 /* 1171 * Fill in vendor information. Subnet mask, default gateway, 1172 * domain name server, ien name server, time server 1173 */ 1174 vendp = (struct cmu_vend *) bp->bp_vend; 1175 strcpy(vendp->v_magic, (char *)vm_cmu); 1176 if (hp->flags.subnet_mask) { 1177 (vendp->v_smask).s_addr = hp->subnet_mask.s_addr; 1178 (vendp->v_flags) |= VF_SMASK; 1179 if (hp->flags.gateway) { 1180 (vendp->v_dgate).s_addr = hp->gateway->addr->s_addr; 1181 } 1182 } 1183 if (hp->flags.domain_server) { 1184 taddr = hp->domain_server; 1185 if (taddr->addrcount > 0) { 1186 (vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr; 1187 if (taddr->addrcount > 1) { 1188 (vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr; 1189 } 1190 } 1191 } 1192 if (hp->flags.name_server) { 1193 taddr = hp->name_server; 1194 if (taddr->addrcount > 0) { 1195 (vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr; 1196 if (taddr->addrcount > 1) { 1197 (vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr; 1198 } 1199 } 1200 } 1201 if (hp->flags.time_server) { 1202 taddr = hp->time_server; 1203 if (taddr->addrcount > 0) { 1204 (vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr; 1205 if (taddr->addrcount > 1) { 1206 (vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr; 1207 } 1208 } 1209 } 1210 /* Log message now done by caller. */ 1211} /* dovend_cmu */ 1212 1213#endif /* VEND_CMU */ 1214 1215 1216 1217/* 1218 * Insert the RFC1048 vendor data for the host pointed to by "hp" into the 1219 * bootp packet pointed to by "bp". 1220 */ 1221#define NEED(LEN, MSG) do \ 1222 if (bytesleft < (LEN)) { \ 1223 report(LOG_NOTICE, noroom, \ 1224 hp->hostname->string, MSG); \ 1225 return; \ 1226 } while (0) 1227PRIVATE void 1228dovend_rfc1048(bp, hp, bootsize) 1229 struct bootp *bp; 1230 struct host *hp; 1231 int32 bootsize; 1232{ 1233 int bytesleft, len; 1234 byte *vp; 1235 1236 static char noroom[] = "%s: No room for \"%s\" option"; 1237 1238 vp = bp->bp_vend; 1239 1240 if (hp->flags.msg_size) { 1241 pktlen = hp->msg_size; 1242 } else { 1243 /* 1244 * If the request was longer than the official length, build 1245 * a response of that same length where the additional length 1246 * is assumed to be part of the bp_vend (options) area. 1247 */ 1248 if (pktlen > sizeof(*bp)) { 1249 if (debug > 1) 1250 report(LOG_INFO, "request message length=%d", pktlen); 1251 } 1252 /* 1253 * Check whether the request contains the option: 1254 * Maximum DHCP Message Size (RFC1533 sec. 9.8) 1255 * and if so, override the response length with its value. 1256 * This request must lie within the first BP_VEND_LEN 1257 * bytes of the option space. 1258 */ 1259 { 1260 byte *p, *ep; 1261 byte tag, len; 1262 short msgsz = 0; 1263 1264 p = vp + 4; 1265 ep = p + BP_VEND_LEN - 4; 1266 while (p < ep) { 1267 tag = *p++; 1268 /* Check for tags with no data first. */ 1269 if (tag == TAG_PAD) 1270 continue; 1271 if (tag == TAG_END) 1272 break; 1273 /* Now scan the length byte. */ 1274 len = *p++; 1275 switch (tag) { 1276 case TAG_MAX_MSGSZ: 1277 if (len == 2) { 1278 bcopy(p, (char*)&msgsz, 2); 1279 msgsz = ntohs(msgsz); 1280 } 1281 break; 1282 case TAG_SUBNET_MASK: 1283 /* XXX - Should preserve this if given... */ 1284 break; 1285 } /* swtich */ 1286 p += len; 1287 } 1288 1289 if (msgsz > sizeof(*bp)) { 1290 if (debug > 1) 1291 report(LOG_INFO, "request has DHCP msglen=%d", msgsz); 1292 pktlen = msgsz; 1293 } 1294 } 1295 } 1296 1297 if (pktlen < sizeof(*bp)) { 1298 report(LOG_ERR, "invalid response length=%d", pktlen); 1299 pktlen = sizeof(*bp); 1300 } 1301 bytesleft = ((byte*)bp + pktlen) - vp; 1302 if (pktlen > sizeof(*bp)) { 1303 if (debug > 1) 1304 report(LOG_INFO, "extended reply, length=%d, options=%d", 1305 pktlen, bytesleft); 1306 } 1307 1308 /* Copy in the magic cookie */ 1309 bcopy(vm_rfc1048, vp, 4); 1310 vp += 4; 1311 bytesleft -= 4; 1312 1313 if (hp->flags.subnet_mask) { 1314 /* always enough room here. */ 1315 *vp++ = TAG_SUBNET_MASK;/* -1 byte */ 1316 *vp++ = 4; /* -1 byte */ 1317 insert_u_long(hp->subnet_mask.s_addr, &vp); /* -4 bytes */ 1318 bytesleft -= 6; /* Fix real count */ 1319 if (hp->flags.gateway) { 1320 (void) insert_ip(TAG_GATEWAY, 1321 hp->gateway, 1322 &vp, &bytesleft); 1323 } 1324 } 1325 if (hp->flags.bootsize) { 1326 /* always enough room here */ 1327 bootsize = (hp->flags.bootsize_auto) ? 1328 ((bootsize + 511) / 512) : (hp->bootsize); /* Round up */ 1329 *vp++ = TAG_BOOT_SIZE; 1330 *vp++ = 2; 1331 *vp++ = (byte) ((bootsize >> 8) & 0xFF); 1332 *vp++ = (byte) (bootsize & 0xFF); 1333 bytesleft -= 4; /* Tag, length, and 16 bit blocksize */ 1334 } 1335 /* 1336 * This one is special: Remaining options go in the ext file. 1337 * Only the subnet_mask, bootsize, and gateway should precede. 1338 */ 1339 if (hp->flags.exten_file) { 1340 /* 1341 * Check for room for exten_file. Add 3 to account for 1342 * TAG_EXTEN_FILE, length, and TAG_END. 1343 */ 1344 len = strlen(hp->exten_file->string); 1345 NEED((len + 3), "ef"); 1346 *vp++ = TAG_EXTEN_FILE; 1347 *vp++ = (byte) (len & 0xFF); 1348 bcopy(hp->exten_file->string, vp, len); 1349 vp += len; 1350 *vp++ = TAG_END; 1351 bytesleft -= len + 3; 1352 return; /* no more options here. */ 1353 } 1354 /* 1355 * The remaining options are inserted by the following 1356 * function (which is shared with bootpef.c). 1357 * Keep back one byte for the TAG_END. 1358 */ 1359 len = dovend_rfc1497(hp, vp, bytesleft - 1); 1360 vp += len; 1361 bytesleft -= len; 1362 1363 /* There should be at least one byte left. */ 1364 NEED(1, "(end)"); 1365 *vp++ = TAG_END; 1366 bytesleft--; 1367 1368 /* Log message done by caller. */ 1369 if (bytesleft > 0) { 1370 /* 1371 * Zero out any remaining part of the vendor area. 1372 */ 1373 bzero(vp, bytesleft); 1374 } 1375} /* dovend_rfc1048 */ 1376#undef NEED 1377 1378 1379/* 1380 * Now in readfile.c: 1381 * hwlookcmp() 1382 * iplookcmp() 1383 */ 1384 1385/* haddrtoa() - now in hwaddr.c */ 1386/* 1387 * Now in dovend.c: 1388 * insert_ip() 1389 * insert_generic() 1390 * insert_u_long() 1391 */ 1392 1393/* get_errmsg() - now in report.c */ 1394 1395/* 1396 * Local Variables: 1397 * tab-width: 4 1398 * c-indent-level: 4 1399 * c-argdecl-indent: 4 1400 * c-continued-statement-offset: 4 1401 * c-continued-brace-offset: -4 1402 * c-label-offset: -4 1403 * c-brace-offset: 0 1404 * End: 1405 */
|