bootpd.c revision 18471
13229Spst/************************************************************************ 23229Spst Copyright 1988, 1991 by Carnegie Mellon University 33229Spst 43229Spst All Rights Reserved 53229Spst 63229SpstPermission to use, copy, modify, and distribute this software and its 73229Spstdocumentation for any purpose and without fee is hereby granted, provided 83229Spstthat the above copyright notice appear in all copies and that both that 93229Spstcopyright notice and this permission notice appear in supporting 103229Spstdocumentation, and that the name of Carnegie Mellon University not be used 113229Spstin advertising or publicity pertaining to distribution of the software 123229Spstwithout specific, written prior permission. 133229Spst 143229SpstCARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 153229SpstSOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. 163229SpstIN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 173229SpstDAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 183229SpstPROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 193229SpstACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 203229SpstSOFTWARE. 2118471Swosch 2218471Swosch $Id$ 2318471Swosch 243229Spst************************************************************************/ 253229Spst 263229Spst/* 273229Spst * BOOTP (bootstrap protocol) server daemon. 283229Spst * 293229Spst * Answers BOOTP request packets from booting client machines. 303229Spst * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol. 313229Spst * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions. 323229Spst * See RFC 1395 for option tags 14-17. 333229Spst * See accompanying man page -- bootpd.8 343229Spst * 353229Spst * HISTORY 363229Spst * See ./Changes 373229Spst * 383229Spst * BUGS 393229Spst * See ./ToDo 403229Spst */ 413229Spst 423229Spst 433229Spst 443229Spst#include <sys/types.h> 453229Spst#include <sys/param.h> 463229Spst#include <sys/socket.h> 473229Spst#include <sys/ioctl.h> 483229Spst#include <sys/file.h> 493229Spst#include <sys/time.h> 503229Spst#include <sys/stat.h> 5113575Spst#include <sys/utsname.h> 523229Spst 533229Spst#include <net/if.h> 543229Spst#include <netinet/in.h> 553229Spst#include <arpa/inet.h> /* inet_ntoa */ 563229Spst 573229Spst#ifndef NO_UNISTD 583229Spst#include <unistd.h> 593229Spst#endif 6013575Spst 613229Spst#include <stdlib.h> 623229Spst#include <signal.h> 633229Spst#include <stdio.h> 643229Spst#include <string.h> 653229Spst#include <errno.h> 663229Spst#include <ctype.h> 673229Spst#include <netdb.h> 683229Spst#include <syslog.h> 693229Spst#include <assert.h> 703229Spst 713229Spst#ifdef NO_SETSID 723229Spst# include <fcntl.h> /* for O_RDONLY, etc */ 733229Spst#endif 743229Spst 753229Spst#ifndef USE_BFUNCS 763229Spst# include <memory.h> 773229Spst/* Yes, memcpy is OK here (no overlapped copies). */ 783229Spst# define bcopy(a,b,c) memcpy(b,a,c) 793229Spst# define bzero(p,l) memset(p,0,l) 803229Spst# define bcmp(a,b,c) memcmp(a,b,c) 813229Spst#endif 823229Spst 833229Spst#include "bootp.h" 843229Spst#include "hash.h" 853229Spst#include "hwaddr.h" 863229Spst#include "bootpd.h" 873229Spst#include "dovend.h" 883229Spst#include "getif.h" 893229Spst#include "readfile.h" 903229Spst#include "report.h" 913229Spst#include "tzone.h" 923229Spst#include "patchlevel.h" 933229Spst 943229Spst#ifndef CONFIG_FILE 953229Spst#define CONFIG_FILE "/etc/bootptab" 963229Spst#endif 973229Spst#ifndef DUMPTAB_FILE 983229Spst#define DUMPTAB_FILE "/tmp/bootpd.dump" 993229Spst#endif 1003229Spst 1013229Spst 1023229Spst 1033229Spst/* 1043229Spst * Externals, forward declarations, and global variables 1053229Spst */ 1063229Spst 1073229Spst#ifdef __STDC__ 1083229Spst#define P(args) args 1093229Spst#else 1103229Spst#define P(args) () 1113229Spst#endif 1123229Spst 1133229Spstextern void dumptab P((char *)); 1143229Spst 1153229SpstPRIVATE void catcher P((int)); 1163229SpstPRIVATE int chk_access P((char *, int32 *)); 1173229Spst#ifdef VEND_CMU 1183229SpstPRIVATE void dovend_cmu P((struct bootp *, struct host *)); 1193229Spst#endif 1203229SpstPRIVATE void dovend_rfc1048 P((struct bootp *, struct host *, int32)); 1213229SpstPRIVATE void handle_reply P((void)); 1223229SpstPRIVATE void handle_request P((void)); 1233229SpstPRIVATE void sendreply P((int forward, int32 dest_override)); 1243229SpstPRIVATE void usage P((void)); 1253229Spst 1263229Spst#undef P 1273229Spst 1283229Spst/* 1293229Spst * IP port numbers for client and server obtained from /etc/services 1303229Spst */ 1313229Spst 1323229Spstu_short bootps_port, bootpc_port; 1333229Spst 1343229Spst 1353229Spst/* 1363229Spst * Internet socket and interface config structures 1373229Spst */ 1383229Spst 1393229Spststruct sockaddr_in bind_addr; /* Listening */ 1403229Spststruct sockaddr_in recv_addr; /* Packet source */ 1413229Spststruct sockaddr_in send_addr; /* destination */ 1423229Spst 1433229Spst 1443229Spst/* 1453229Spst * option defaults 1463229Spst */ 1473229Spstint debug = 0; /* Debugging flag (level) */ 1483229Spststruct timeval actualtimeout = 1493229Spst{ /* fifteen minutes */ 1503229Spst 15 * 60L, /* tv_sec */ 1513229Spst 0 /* tv_usec */ 1523229Spst}; 1533229Spst 1543229Spst/* 1553229Spst * General 1563229Spst */ 1573229Spst 1583229Spstint s; /* Socket file descriptor */ 1593229Spstchar *pktbuf; /* Receive packet buffer */ 1603229Spstint pktlen; 1613229Spstchar *progname; 1623229Spstchar *chdir_path; 1633229Spststruct in_addr my_ip_addr; 1643229Spst 16513575Spststruct utsname my_uname; 16613575Spstchar *hostname; 16713575Spst 1683229Spst/* Flags set by signal catcher. */ 1693229SpstPRIVATE int do_readtab = 0; 1703229SpstPRIVATE int do_dumptab = 0; 1713229Spst 1723229Spst/* 1733229Spst * Globals below are associated with the bootp database file (bootptab). 1743229Spst */ 1753229Spst 1763229Spstchar *bootptab = CONFIG_FILE; 1773229Spstchar *bootpd_dump = DUMPTAB_FILE; 1783229Spst 1793229Spst 1803229Spst 1813229Spst/* 1823229Spst * Initialization such as command-line processing is done and then the 1833229Spst * main server loop is started. 1843229Spst */ 1853229Spst 1863229Spstvoid 1873229Spstmain(argc, argv) 1883229Spst int argc; 1893229Spst char **argv; 1903229Spst{ 1913229Spst struct timeval *timeout; 1923229Spst struct bootp *bp; 1933229Spst struct servent *servp; 1943229Spst struct hostent *hep; 1953229Spst char *stmp; 1963229Spst int n, ba_len, ra_len; 1973229Spst int nfound, readfds; 1983229Spst int standalone; 19913575Spst#ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */ 20013575Spst struct sigaction sa; 20113575Spst#endif 2023229Spst 2033229Spst progname = strrchr(argv[0], '/'); 2043229Spst if (progname) progname++; 2053229Spst else progname = argv[0]; 2063229Spst 2073229Spst /* 2083229Spst * Initialize logging. 2093229Spst */ 2103229Spst report_init(0); /* uses progname */ 2113229Spst 2123229Spst /* 2133229Spst * Log startup 2143229Spst */ 2153229Spst report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL); 2163229Spst 2173229Spst /* Debugging for compilers with struct padding. */ 2183229Spst assert(sizeof(struct bootp) == BP_MINPKTSZ); 2193229Spst 2203229Spst /* Get space for receiving packets and composing replies. */ 2213229Spst pktbuf = malloc(MAX_MSG_SIZE); 2223229Spst if (!pktbuf) { 2233229Spst report(LOG_ERR, "malloc failed"); 2243229Spst exit(1); 2253229Spst } 2263229Spst bp = (struct bootp *) pktbuf; 2273229Spst 2283229Spst /* 2293229Spst * Check to see if a socket was passed to us from inetd. 2303229Spst * 2313229Spst * Use getsockname() to determine if descriptor 0 is indeed a socket 2323229Spst * (and thus we are probably a child of inetd) or if it is instead 2333229Spst * something else and we are running standalone. 2343229Spst */ 2353229Spst s = 0; 2363229Spst ba_len = sizeof(bind_addr); 2373229Spst bzero((char *) &bind_addr, ba_len); 2383229Spst errno = 0; 2393229Spst standalone = TRUE; 2403229Spst if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) { 2413229Spst /* 2423229Spst * Descriptor 0 is a socket. Assume we are a child of inetd. 2433229Spst */ 2443229Spst if (bind_addr.sin_family == AF_INET) { 2453229Spst standalone = FALSE; 2463229Spst bootps_port = ntohs(bind_addr.sin_port); 2473229Spst } else { 2483229Spst /* Some other type of socket? */ 2493229Spst report(LOG_ERR, "getsockname: not an INET socket"); 2503229Spst } 2513229Spst } 2523229Spst 2533229Spst /* 2543229Spst * Set defaults that might be changed by option switches. 2553229Spst */ 2563229Spst stmp = NULL; 2573229Spst timeout = &actualtimeout; 2583229Spst 25913575Spst if (uname(&my_uname) < 0) { 26013575Spst fprintf(stderr, "bootpd: can't get hostname\n"); 26113575Spst exit(1); 26213575Spst } 26313575Spst hostname = my_uname.nodename; 26413575Spst 2653229Spst /* 2663229Spst * Read switches. 2673229Spst */ 2683229Spst for (argc--, argv++; argc > 0; argc--, argv++) { 2693229Spst if (argv[0][0] != '-') 2703229Spst break; 2713229Spst switch (argv[0][1]) { 2723229Spst 2733229Spst case 'c': /* chdir_path */ 2743229Spst if (argv[0][2]) { 2753229Spst stmp = &(argv[0][2]); 2763229Spst } else { 2773229Spst argc--; 2783229Spst argv++; 2793229Spst stmp = argv[0]; 2803229Spst } 2813229Spst if (!stmp || (stmp[0] != '/')) { 2823229Spst fprintf(stderr, 2833229Spst "bootpd: invalid chdir specification\n"); 2843229Spst break; 2853229Spst } 2863229Spst chdir_path = stmp; 2873229Spst break; 2883229Spst 2893229Spst case 'd': /* debug level */ 2903229Spst if (argv[0][2]) { 2913229Spst stmp = &(argv[0][2]); 2923229Spst } else if (argv[1] && argv[1][0] == '-') { 2933229Spst /* 2943229Spst * Backwards-compatible behavior: 2953229Spst * no parameter, so just increment the debug flag. 2963229Spst */ 2973229Spst debug++; 2983229Spst break; 2993229Spst } else { 3003229Spst argc--; 3013229Spst argv++; 3023229Spst stmp = argv[0]; 3033229Spst } 3043229Spst if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 3053229Spst fprintf(stderr, 3063229Spst "%s: invalid debug level\n", progname); 3073229Spst break; 3083229Spst } 3093229Spst debug = n; 3103229Spst break; 3113229Spst 3123229Spst case 'h': /* override hostname */ 3133229Spst if (argv[0][2]) { 3143229Spst stmp = &(argv[0][2]); 3153229Spst } else { 3163229Spst argc--; 3173229Spst argv++; 3183229Spst stmp = argv[0]; 3193229Spst } 3203229Spst if (!stmp) { 3213229Spst fprintf(stderr, 3223229Spst "bootpd: missing hostname\n"); 3233229Spst break; 3243229Spst } 32513575Spst hostname = stmp; 3263229Spst break; 3273229Spst 3283229Spst case 'i': /* inetd mode */ 3293229Spst standalone = FALSE; 3303229Spst break; 3313229Spst 3323229Spst case 's': /* standalone mode */ 3333229Spst standalone = TRUE; 3343229Spst break; 3353229Spst 3363229Spst case 't': /* timeout */ 3373229Spst if (argv[0][2]) { 3383229Spst stmp = &(argv[0][2]); 3393229Spst } else { 3403229Spst argc--; 3413229Spst argv++; 3423229Spst stmp = argv[0]; 3433229Spst } 3443229Spst if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 3453229Spst fprintf(stderr, 3463229Spst "%s: invalid timeout specification\n", progname); 3473229Spst break; 3483229Spst } 3493229Spst actualtimeout.tv_sec = (int32) (60 * n); 3503229Spst /* 3513229Spst * If the actual timeout is zero, pass a NULL pointer 3523229Spst * to select so it blocks indefinitely, otherwise, 3533229Spst * point to the actual timeout value. 3543229Spst */ 3553229Spst timeout = (n > 0) ? &actualtimeout : NULL; 3563229Spst break; 3573229Spst 3583229Spst default: 3593229Spst fprintf(stderr, "%s: unknown switch: -%c\n", 3603229Spst progname, argv[0][1]); 3613229Spst usage(); 3623229Spst break; 3633229Spst 3643229Spst } /* switch */ 3653229Spst } /* for args */ 3663229Spst 3673229Spst /* 3683229Spst * Override default file names if specified on the command line. 3693229Spst */ 3703229Spst if (argc > 0) 3713229Spst bootptab = argv[0]; 3723229Spst 3733229Spst if (argc > 1) 3743229Spst bootpd_dump = argv[1]; 3753229Spst 3763229Spst /* 3773229Spst * Get my hostname and IP address. 3783229Spst */ 37913575Spst 3803229Spst hep = gethostbyname(hostname); 3813229Spst if (!hep) { 3823229Spst fprintf(stderr, "Can not get my IP address\n"); 3833229Spst exit(1); 3843229Spst } 3853229Spst bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr)); 3863229Spst 3873229Spst if (standalone) { 3883229Spst /* 3893229Spst * Go into background and disassociate from controlling terminal. 3903229Spst */ 3913229Spst if (debug < 3) { 3923229Spst if (fork()) 3933229Spst exit(0); 3943229Spst#ifdef NO_SETSID 3953229Spst setpgrp(0,0); 3963229Spst#ifdef TIOCNOTTY 3973229Spst n = open("/dev/tty", O_RDWR); 3983229Spst if (n >= 0) { 3993229Spst ioctl(n, TIOCNOTTY, (char *) 0); 4003229Spst (void) close(n); 4013229Spst } 4023229Spst#endif /* TIOCNOTTY */ 4033229Spst#else /* SETSID */ 4043229Spst if (setsid() < 0) 4053229Spst perror("setsid"); 4063229Spst#endif /* SETSID */ 4073229Spst } /* if debug < 3 */ 4083229Spst 4093229Spst /* 4103229Spst * Nuke any timeout value 4113229Spst */ 4123229Spst timeout = NULL; 4133229Spst 4143229Spst } /* if standalone (1st) */ 4153229Spst 4163229Spst /* Set the cwd (i.e. to /tftpboot) */ 4173229Spst if (chdir_path) { 4183229Spst if (chdir(chdir_path) < 0) 4193229Spst report(LOG_ERR, "%s: chdir failed", chdir_path); 4203229Spst } 4213229Spst 4223229Spst /* Get the timezone. */ 4233229Spst tzone_init(); 4243229Spst 4253229Spst /* Allocate hash tables. */ 4263229Spst rdtab_init(); 4273229Spst 4283229Spst /* 4293229Spst * Read the bootptab file. 4303229Spst */ 4313229Spst readtab(1); /* force read */ 4323229Spst 4333229Spst if (standalone) { 4343229Spst 4353229Spst /* 4363229Spst * Create a socket. 4373229Spst */ 4383229Spst if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 4393229Spst report(LOG_ERR, "socket: %s", get_network_errmsg()); 4403229Spst exit(1); 4413229Spst } 4423229Spst 4433229Spst /* 4443229Spst * Get server's listening port number 4453229Spst */ 4463229Spst servp = getservbyname("bootps", "udp"); 4473229Spst if (servp) { 4483229Spst bootps_port = ntohs((u_short) servp->s_port); 4493229Spst } else { 4503229Spst bootps_port = (u_short) IPPORT_BOOTPS; 4513229Spst report(LOG_ERR, 4523229Spst "udp/bootps: unknown service -- assuming port %d", 4533229Spst bootps_port); 4543229Spst } 4553229Spst 4563229Spst /* 4573229Spst * Bind socket to BOOTPS port. 4583229Spst */ 4593229Spst bind_addr.sin_family = AF_INET; 4603229Spst bind_addr.sin_addr.s_addr = INADDR_ANY; 4613229Spst bind_addr.sin_port = htons(bootps_port); 4623229Spst if (bind(s, (struct sockaddr *) &bind_addr, 4633229Spst sizeof(bind_addr)) < 0) 4643229Spst { 4653229Spst report(LOG_ERR, "bind: %s", get_network_errmsg()); 4663229Spst exit(1); 4673229Spst } 4683229Spst } /* if standalone (2nd)*/ 4693229Spst 4703229Spst /* 4713229Spst * Get destination port number so we can reply to client 4723229Spst */ 4733229Spst servp = getservbyname("bootpc", "udp"); 4743229Spst if (servp) { 4753229Spst bootpc_port = ntohs(servp->s_port); 4763229Spst } else { 4773229Spst report(LOG_ERR, 4783229Spst "udp/bootpc: unknown service -- assuming port %d", 4793229Spst IPPORT_BOOTPC); 4803229Spst bootpc_port = (u_short) IPPORT_BOOTPC; 4813229Spst } 4823229Spst 4833229Spst /* 4843229Spst * Set up signals to read or dump the table. 4853229Spst */ 48613575Spst#ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */ 48713575Spst sa.sa_handler = catcher; 48813575Spst sigemptyset(&sa.sa_mask); 48913575Spst sa.sa_flags = 0; 49013575Spst if (sigaction(SIGHUP, &sa, NULL) < 0) { 49113575Spst report(LOG_ERR, "sigaction: %s", get_errmsg()); 49213575Spst exit(1); 49313575Spst } 49413575Spst if (sigaction(SIGUSR1, &sa, NULL) < 0) { 49513575Spst report(LOG_ERR, "sigaction: %s", get_errmsg()); 49613575Spst exit(1); 49713575Spst } 49813575Spst#else /* SA_NOCLDSTOP */ 49913575Spst /* Old-fashioned UNIX signals */ 5003229Spst if ((int) signal(SIGHUP, catcher) < 0) { 5013229Spst report(LOG_ERR, "signal: %s", get_errmsg()); 5023229Spst exit(1); 5033229Spst } 5043229Spst if ((int) signal(SIGUSR1, catcher) < 0) { 5053229Spst report(LOG_ERR, "signal: %s", get_errmsg()); 5063229Spst exit(1); 5073229Spst } 50813575Spst#endif /* SA_NOCLDSTOP */ 5093229Spst 5103229Spst /* 5113229Spst * Process incoming requests. 5123229Spst */ 5133229Spst for (;;) { 51413575Spst struct timeval tv; 51513575Spst 5163229Spst readfds = 1 << s; 51713575Spst if (timeout) 51813575Spst tv = *timeout; 51913575Spst 52013575Spst nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL, 52113575Spst (timeout) ? &tv : NULL); 5223229Spst if (nfound < 0) { 5233229Spst if (errno != EINTR) { 5243229Spst report(LOG_ERR, "select: %s", get_errmsg()); 5253229Spst } 5263229Spst /* 5273229Spst * Call readtab() or dumptab() here to avoid the 5283229Spst * dangers of doing I/O from a signal handler. 5293229Spst */ 5303229Spst if (do_readtab) { 5313229Spst do_readtab = 0; 5323229Spst readtab(1); /* force read */ 5333229Spst } 5343229Spst if (do_dumptab) { 5353229Spst do_dumptab = 0; 5363229Spst dumptab(bootpd_dump); 5373229Spst } 5383229Spst continue; 5393229Spst } 5403229Spst if (!(readfds & (1 << s))) { 5413229Spst if (debug > 1) 5423229Spst report(LOG_INFO, "exiting after %ld minutes of inactivity", 5433229Spst actualtimeout.tv_sec / 60); 5443229Spst exit(0); 5453229Spst } 5463229Spst ra_len = sizeof(recv_addr); 5473229Spst n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0, 5483229Spst (struct sockaddr *) &recv_addr, &ra_len); 5493229Spst if (n <= 0) { 5503229Spst continue; 5513229Spst } 5523229Spst if (debug > 1) { 5533229Spst report(LOG_INFO, "recvd pkt from IP addr %s", 5543229Spst inet_ntoa(recv_addr.sin_addr)); 5553229Spst } 5563229Spst if (n < sizeof(struct bootp)) { 5573229Spst if (debug) { 55813575Spst report(LOG_NOTICE, "received short packet"); 5593229Spst } 5603229Spst continue; 5613229Spst } 5623229Spst pktlen = n; 5633229Spst 5643229Spst readtab(0); /* maybe re-read bootptab */ 5653229Spst 5663229Spst switch (bp->bp_op) { 5673229Spst case BOOTREQUEST: 5683229Spst handle_request(); 5693229Spst break; 5703229Spst case BOOTREPLY: 5713229Spst handle_reply(); 5723229Spst break; 5733229Spst } 5743229Spst } 5753229Spst} 5763229Spst 5773229Spst 5783229Spst 5793229Spst 5803229Spst/* 5813229Spst * Print "usage" message and exit 5823229Spst */ 5833229Spst 5843229SpstPRIVATE void 5853229Spstusage() 5863229Spst{ 5873229Spst fprintf(stderr, 5883229Spst "usage: bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n"); 5893229Spst fprintf(stderr, "\t -c n\tset current directory\n"); 5903229Spst fprintf(stderr, "\t -d n\tset debug level\n"); 5913229Spst fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n"); 5923229Spst fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n"); 5933229Spst fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n"); 5943229Spst exit(1); 5953229Spst} 5963229Spst 5973229Spst/* Signal catchers */ 5983229SpstPRIVATE void 5993229Spstcatcher(sig) 6003229Spst int sig; 6013229Spst{ 6023229Spst if (sig == SIGHUP) 6033229Spst do_readtab = 1; 6043229Spst if (sig == SIGUSR1) 6053229Spst do_dumptab = 1; 60613575Spst#if !defined(SA_NOCLDSTOP) && defined(SYSV) 60713575Spst /* For older "System V" derivatives with no sigaction(). */ 6083229Spst signal(sig, catcher); 6093229Spst#endif 6103229Spst} 6113229Spst 6123229Spst 6133229Spst 6143229Spst/* 6153229Spst * Process BOOTREQUEST packet. 6163229Spst * 6173229Spst * Note: This version of the bootpd.c server never forwards 6183229Spst * a request to another server. That is the job of a gateway 6193229Spst * program such as the "bootpgw" program included here. 6203229Spst * 6213229Spst * (Also this version does not interpret the hostname field of 6223229Spst * the request packet; it COULD do a name->address lookup and 6233229Spst * forward the request there.) 6243229Spst */ 6253229SpstPRIVATE void 6263229Spsthandle_request() 6273229Spst{ 6283229Spst struct bootp *bp = (struct bootp *) pktbuf; 6293229Spst struct host *hp = NULL; 6303229Spst struct host dummyhost; 6313229Spst int32 bootsize = 0; 6323229Spst unsigned hlen, hashcode; 6333229Spst int32 dest; 6343229Spst char realpath[1024]; 6353229Spst char *clntpath; 6363229Spst char *homedir, *bootfile; 6373229Spst int n; 6383229Spst 6393229Spst /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */ 6403229Spst 6413229Spst /* 6423229Spst * If the servername field is set, compare it against us. 6433229Spst * If we're not being addressed, ignore this request. 6443229Spst * If the server name field is null, throw in our name. 6453229Spst */ 6463229Spst if (strlen(bp->bp_sname)) { 6473229Spst if (strcmp(bp->bp_sname, hostname)) { 6483229Spst if (debug) 6493229Spst report(LOG_INFO, "\ 6503229Spstignoring request for server %s from client at %s address %s", 6513229Spst bp->bp_sname, netname(bp->bp_htype), 6523229Spst haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 6533229Spst /* XXX - Is it correct to ignore such a request? -gwr */ 6543229Spst return; 6553229Spst } 6563229Spst } else { 6573229Spst strcpy(bp->bp_sname, hostname); 6583229Spst } 6593229Spst 6603229Spst /* Convert the request into a reply. */ 6613229Spst bp->bp_op = BOOTREPLY; 6623229Spst if (bp->bp_ciaddr.s_addr == 0) { 6633229Spst /* 6643229Spst * client doesnt know his IP address, 6653229Spst * search by hardware address. 6663229Spst */ 6673229Spst if (debug > 1) { 6683229Spst report(LOG_INFO, "request from %s address %s", 6693229Spst netname(bp->bp_htype), 6703229Spst haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 6713229Spst } 6723229Spst hlen = haddrlength(bp->bp_htype); 6733229Spst if (hlen != bp->bp_hlen) { 6743229Spst report(LOG_NOTICE, "bad addr len from from %s address %s", 6753229Spst netname(bp->bp_htype), 6763229Spst haddrtoa(bp->bp_chaddr, hlen)); 6773229Spst } 6783229Spst dummyhost.htype = bp->bp_htype; 6793229Spst bcopy(bp->bp_chaddr, dummyhost.haddr, hlen); 6803229Spst hashcode = hash_HashFunction(bp->bp_chaddr, hlen); 6813229Spst hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp, 6823229Spst &dummyhost); 6833229Spst if (hp == NULL && 6843229Spst bp->bp_htype == HTYPE_IEEE802) 6853229Spst { 6863229Spst /* Try again with address in "canonical" form. */ 6873229Spst haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen); 6883229Spst if (debug > 1) { 6893229Spst report(LOG_INFO, "\ 6903229SpstHW addr type is IEEE 802. convert to %s and check again\n", 6913229Spst haddrtoa(dummyhost.haddr, bp->bp_hlen)); 6923229Spst } 6933229Spst hashcode = hash_HashFunction(dummyhost.haddr, hlen); 6943229Spst hp = (struct host *) hash_Lookup(hwhashtable, hashcode, 6953229Spst hwlookcmp, &dummyhost); 6963229Spst } 6973229Spst if (hp == NULL) { 6983229Spst /* 6993229Spst * XXX - Add dynamic IP address assignment? 7003229Spst */ 70113575Spst if (debug) 70213575Spst report(LOG_NOTICE, "unknown client %s address %s", 7033229Spst netname(bp->bp_htype), 7043229Spst haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 7053229Spst return; /* not found */ 7063229Spst } 7073229Spst (bp->bp_yiaddr).s_addr = hp->iaddr.s_addr; 7083229Spst 7093229Spst } else { 7103229Spst 7113229Spst /* 7123229Spst * search by IP address. 7133229Spst */ 7143229Spst if (debug > 1) { 7153229Spst report(LOG_INFO, "request from IP addr %s", 7163229Spst inet_ntoa(bp->bp_ciaddr)); 7173229Spst } 7183229Spst dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr; 7193229Spst hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4); 7203229Spst hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp, 7213229Spst &dummyhost); 7223229Spst if (hp == NULL) { 72313575Spst if (debug) { 7243229Spst report(LOG_NOTICE, "IP address not found: %s", 7253229Spst inet_ntoa(bp->bp_ciaddr)); 7263229Spst } 7273229Spst return; 7283229Spst } 7293229Spst } 7303229Spst 7313229Spst if (debug) { 7323229Spst report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr), 7333229Spst hp->hostname->string); 7343229Spst } 7353229Spst 7363229Spst /* 7373229Spst * If there is a response delay threshold, ignore requests 7383229Spst * with a timestamp lower than the threshold. 7393229Spst */ 7403229Spst if (hp->flags.min_wait) { 7413229Spst u_int32 t = (u_int32) ntohs(bp->bp_secs); 7423229Spst if (t < hp->min_wait) { 7433229Spst if (debug > 1) 7443229Spst report(LOG_INFO, 7453229Spst "ignoring request due to timestamp (%d < %d)", 7463229Spst t, hp->min_wait); 7473229Spst return; 7483229Spst } 7493229Spst } 7503229Spst 7513229Spst#ifdef YORK_EX_OPTION 7523229Spst /* 7533229Spst * The need for the "ex" tag arose out of the need to empty 7543229Spst * shared networked drives on diskless PCs. This solution is 7553229Spst * not very clean but it does work fairly well. 7563229Spst * Written by Edmund J. Sutcliffe <edmund@york.ac.uk> 7573229Spst * 7583229Spst * XXX - This could compromise security if a non-trusted user 7593229Spst * managed to write an entry in the bootptab with :ex=trojan: 7603229Spst * so I would leave this turned off unless you need it. -gwr 7613229Spst */ 7623229Spst /* Run a program, passing the client name as a parameter. */ 7633229Spst if (hp->flags.exec_file) { 7643229Spst char tst[100]; 7653229Spst /* XXX - Check string lengths? -gwr */ 7663229Spst strcpy (tst, hp->exec_file->string); 7673229Spst strcat (tst, " "); 7683229Spst strcat (tst, hp->hostname->string); 7693229Spst strcat (tst, " &"); 7703229Spst if (debug) 7713229Spst report(LOG_INFO, "executing %s", tst); 7723229Spst system(tst); /* Hope this finishes soon... */ 7733229Spst } 7743229Spst#endif /* YORK_EX_OPTION */ 7753229Spst 7763229Spst /* 7773229Spst * If a specific TFTP server address was specified in the bootptab file, 7783229Spst * fill it in, otherwise zero it. 7793229Spst * XXX - Rather than zero it, should it be the bootpd address? -gwr 7803229Spst */ 7813229Spst (bp->bp_siaddr).s_addr = (hp->flags.bootserver) ? 7823229Spst hp->bootserver.s_addr : 0L; 7833229Spst 7843229Spst#ifdef STANFORD_PROM_COMPAT 7853229Spst /* 7863229Spst * Stanford bootp PROMs (for a Sun?) have no way to leave 7873229Spst * the boot file name field blank (because the boot file 7883229Spst * name is automatically generated from some index). 7893229Spst * As a work-around, this little hack allows those PROMs to 7903229Spst * specify "sunboot14" with the same effect as a NULL name. 7913229Spst * (The user specifies boot device 14 or some such magic.) 7923229Spst */ 7933229Spst if (strcmp(bp->bp_file, "sunboot14") == 0) 7943229Spst bp->bp_file[0] = '\0'; /* treat it as unspecified */ 7953229Spst#endif 7963229Spst 7973229Spst /* 7983229Spst * Fill in the client's proper bootfile. 7993229Spst * 8003229Spst * If the client specifies an absolute path, try that file with a 8013229Spst * ".host" suffix and then without. If the file cannot be found, no 8023229Spst * reply is made at all. 8033229Spst * 8043229Spst * If the client specifies a null or relative file, use the following 8053229Spst * table to determine the appropriate action: 8063229Spst * 8073229Spst * Homedir Bootfile Client's file 8083229Spst * specified? specified? specification Action 8093229Spst * ------------------------------------------------------------------- 8103229Spst * No No Null Send null filename 8113229Spst * No No Relative Discard request 8123229Spst * No Yes Null Send if absolute else null 8133229Spst * No Yes Relative Discard request *XXX 8143229Spst * Yes No Null Send null filename 8153229Spst * Yes No Relative Lookup with ".host" 8163229Spst * Yes Yes Null Send home/boot or bootfile 8173229Spst * Yes Yes Relative Lookup with ".host" *XXX 8183229Spst * 8193229Spst */ 8203229Spst 8213229Spst /* 8223229Spst * XXX - I don't like the policy of ignoring a client when the 8233229Spst * boot file is not accessible. The TFTP server might not be 8243229Spst * running on the same machine as the BOOTP server, in which 8253229Spst * case checking accessibility of the boot file is pointless. 8263229Spst * 8273229Spst * Therefore, file accessibility is now demanded ONLY if you 8283229Spst * define CHECK_FILE_ACCESS in the Makefile options. -gwr 8293229Spst */ 8303229Spst 8313229Spst /* 8323229Spst * The "real" path is as seen by the BOOTP daemon on this 8333229Spst * machine, while the client path is relative to the TFTP 8343229Spst * daemon chroot directory (i.e. /tftpboot). 8353229Spst */ 8363229Spst if (hp->flags.tftpdir) { 8373229Spst strcpy(realpath, hp->tftpdir->string); 8383229Spst clntpath = &realpath[strlen(realpath)]; 8393229Spst } else { 8403229Spst realpath[0] = '\0'; 8413229Spst clntpath = realpath; 8423229Spst } 8433229Spst 8443229Spst /* 8453229Spst * Determine client's requested homedir and bootfile. 8463229Spst */ 8473229Spst homedir = NULL; 8483229Spst bootfile = NULL; 8493229Spst if (bp->bp_file[0]) { 8503229Spst homedir = bp->bp_file; 8513229Spst bootfile = strrchr(homedir, '/'); 8523229Spst if (bootfile) { 8533229Spst if (homedir == bootfile) 8543229Spst homedir = NULL; 8553229Spst *bootfile++ = '\0'; 8563229Spst } else { 8573229Spst /* no "/" in the string */ 8583229Spst bootfile = homedir; 8593229Spst homedir = NULL; 8603229Spst } 8613229Spst if (debug > 2) { 8623229Spst report(LOG_INFO, "requested path=\"%s\" file=\"%s\"", 8633229Spst (homedir) ? homedir : "", 8643229Spst (bootfile) ? bootfile : ""); 8653229Spst } 8663229Spst } 8673229Spst 8683229Spst /* 8693229Spst * Specifications in bootptab override client requested values. 8703229Spst */ 8713229Spst if (hp->flags.homedir) 8723229Spst homedir = hp->homedir->string; 8733229Spst if (hp->flags.bootfile) 8743229Spst bootfile = hp->bootfile->string; 8753229Spst 8763229Spst /* 8773229Spst * Construct bootfile path. 8783229Spst */ 8793229Spst if (homedir) { 8803229Spst if (homedir[0] != '/') 8813229Spst strcat(clntpath, "/"); 8823229Spst strcat(clntpath, homedir); 8833229Spst homedir = NULL; 8843229Spst } 8853229Spst if (bootfile) { 8863229Spst if (bootfile[0] != '/') 8873229Spst strcat(clntpath, "/"); 8883229Spst strcat(clntpath, bootfile); 8893229Spst bootfile = NULL; 8903229Spst } 8913229Spst 8923229Spst /* 8933229Spst * First try to find the file with a ".host" suffix 8943229Spst */ 8953229Spst n = strlen(clntpath); 8963229Spst strcat(clntpath, "."); 8973229Spst strcat(clntpath, hp->hostname->string); 8983229Spst if (chk_access(realpath, &bootsize) < 0) { 8993229Spst clntpath[n] = 0; /* Try it without the suffix */ 9003229Spst if (chk_access(realpath, &bootsize) < 0) { 9013229Spst /* neither "file.host" nor "file" was found */ 9023229Spst#ifdef CHECK_FILE_ACCESS 9033229Spst 9043229Spst if (bp->bp_file[0]) { 9053229Spst /* 9063229Spst * Client wanted specific file 9073229Spst * and we didn't have it. 9083229Spst */ 9093229Spst report(LOG_NOTICE, 9103229Spst "requested file not found: \"%s\"", clntpath); 9113229Spst return; 9123229Spst } 9133229Spst /* 9143229Spst * Client didn't ask for a specific file and we couldn't 9153229Spst * access the default file, so just zero-out the bootfile 9163229Spst * field in the packet and continue processing the reply. 9173229Spst */ 9183229Spst bzero(bp->bp_file, sizeof(bp->bp_file)); 9193229Spst goto null_file_name; 9203229Spst 9213229Spst#else /* CHECK_FILE_ACCESS */ 9223229Spst 9233229Spst /* Complain only if boot file size was needed. */ 9243229Spst if (hp->flags.bootsize_auto) { 9253229Spst report(LOG_ERR, "can not determine size of file \"%s\"", 9263229Spst clntpath); 9273229Spst } 9283229Spst 9293229Spst#endif /* CHECK_FILE_ACCESS */ 9303229Spst } 9313229Spst } 9323229Spst strncpy(bp->bp_file, clntpath, BP_FILE_LEN); 9333229Spst if (debug > 2) 9343229Spst report(LOG_INFO, "bootfile=\"%s\"", clntpath); 9353229Spst 93613575Spst#ifdef CHECK_FILE_ACCESS 9373229Spstnull_file_name: 93813575Spst#endif /* CHECK_FILE_ACCESS */ 9393229Spst 9403229Spst 9413229Spst /* 9423229Spst * Handle vendor options based on magic number. 9433229Spst */ 9443229Spst 9453229Spst if (debug > 1) { 9463229Spst report(LOG_INFO, "vendor magic field is %d.%d.%d.%d", 9473229Spst (int) ((bp->bp_vend)[0]), 9483229Spst (int) ((bp->bp_vend)[1]), 9493229Spst (int) ((bp->bp_vend)[2]), 9503229Spst (int) ((bp->bp_vend)[3])); 9513229Spst } 9523229Spst /* 9533229Spst * If this host isn't set for automatic vendor info then copy the 9543229Spst * specific cookie into the bootp packet, thus forcing a certain 9553229Spst * reply format. Only force reply format if user specified it. 9563229Spst */ 9573229Spst if (hp->flags.vm_cookie) { 9583229Spst /* Slam in the user specified magic number. */ 9593229Spst bcopy(hp->vm_cookie, bp->bp_vend, 4); 9603229Spst } 9613229Spst /* 9623229Spst * Figure out the format for the vendor-specific info. 9633229Spst * Note that bp->bp_vend may have been set above. 9643229Spst */ 9653229Spst if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) { 9663229Spst /* RFC1048 conformant bootp client */ 9673229Spst dovend_rfc1048(bp, hp, bootsize); 9683229Spst if (debug > 1) { 9693229Spst report(LOG_INFO, "sending reply (with RFC1048 options)"); 9703229Spst } 9713229Spst } 9723229Spst#ifdef VEND_CMU 9733229Spst else if (!bcmp(bp->bp_vend, vm_cmu, 4)) { 9743229Spst dovend_cmu(bp, hp); 9753229Spst if (debug > 1) { 9763229Spst report(LOG_INFO, "sending reply (with CMU options)"); 9773229Spst } 9783229Spst } 9793229Spst#endif 9803229Spst else { 9813229Spst if (debug > 1) { 9823229Spst report(LOG_INFO, "sending reply (with no options)"); 9833229Spst } 9843229Spst } 9853229Spst 9863229Spst dest = (hp->flags.reply_addr) ? 9873229Spst hp->reply_addr.s_addr : 0L; 9883229Spst 9893229Spst /* not forwarded */ 9903229Spst sendreply(0, dest); 9913229Spst} 9923229Spst 9933229Spst 9943229Spst/* 9953229Spst * Process BOOTREPLY packet. 9963229Spst */ 9973229SpstPRIVATE void 9983229Spsthandle_reply() 9993229Spst{ 10003229Spst if (debug) { 10013229Spst report(LOG_INFO, "processing boot reply"); 10023229Spst } 10033229Spst /* forwarded, no destination override */ 10043229Spst sendreply(1, 0); 10053229Spst} 10063229Spst 10073229Spst 10083229Spst/* 10093229Spst * Send a reply packet to the client. 'forward' flag is set if we are 10103229Spst * not the originator of this reply packet. 10113229Spst */ 10123229SpstPRIVATE void 10133229Spstsendreply(forward, dst_override) 10143229Spst int forward; 10153229Spst int32 dst_override; 10163229Spst{ 10173229Spst struct bootp *bp = (struct bootp *) pktbuf; 10183229Spst struct in_addr dst; 10193229Spst u_short port = bootpc_port; 10203229Spst unsigned char *ha; 102113575Spst int len, haf; 10223229Spst 10233229Spst /* 10243229Spst * XXX - Should honor bp_flags "broadcast" bit here. 10253229Spst * Temporary workaround: use the :ra=ADDR: option to 10263229Spst * set the reply address to the broadcast address. 10273229Spst */ 10283229Spst 10293229Spst /* 10303229Spst * If the destination address was specified explicitly 10313229Spst * (i.e. the broadcast address for HP compatiblity) 10323229Spst * then send the response to that address. Otherwise, 10333229Spst * act in accordance with RFC951: 10343229Spst * If the client IP address is specified, use that 10353229Spst * else if gateway IP address is specified, use that 10363229Spst * else make a temporary arp cache entry for the client's 10373229Spst * NEW IP/hardware address and use that. 10383229Spst */ 10393229Spst if (dst_override) { 10403229Spst dst.s_addr = dst_override; 10413229Spst if (debug > 1) { 10423229Spst report(LOG_INFO, "reply address override: %s", 10433229Spst inet_ntoa(dst)); 10443229Spst } 10453229Spst } else if (bp->bp_ciaddr.s_addr) { 10463229Spst dst = bp->bp_ciaddr; 10473229Spst } else if (bp->bp_giaddr.s_addr && forward == 0) { 10483229Spst dst = bp->bp_giaddr; 10493229Spst port = bootps_port; 10503229Spst if (debug > 1) { 10513229Spst report(LOG_INFO, "sending reply to gateway %s", 10523229Spst inet_ntoa(dst)); 10533229Spst } 10543229Spst } else { 10553229Spst dst = bp->bp_yiaddr; 10563229Spst ha = bp->bp_chaddr; 10573229Spst len = bp->bp_hlen; 10583229Spst if (len > MAXHADDRLEN) 10593229Spst len = MAXHADDRLEN; 106013575Spst haf = (int) bp->bp_htype; 106113575Spst if (haf == 0) 106213575Spst haf = HTYPE_ETHERNET; 10633229Spst 10643229Spst if (debug > 1) 10653229Spst report(LOG_INFO, "setarp %s - %s", 10663229Spst inet_ntoa(dst), haddrtoa(ha, len)); 106713575Spst setarp(s, &dst, haf, ha, len); 10683229Spst } 10693229Spst 10703229Spst if ((forward == 0) && 10713229Spst (bp->bp_siaddr.s_addr == 0)) 10723229Spst { 10733229Spst struct ifreq *ifr; 10743229Spst struct in_addr siaddr; 10753229Spst /* 10763229Spst * If we are originating this reply, we 10773229Spst * need to find our own interface address to 10783229Spst * put in the bp_siaddr field of the reply. 10793229Spst * If this server is multi-homed, pick the 10803229Spst * 'best' interface (the one on the same net 10813229Spst * as the client). Of course, the client may 10823229Spst * be on the other side of a BOOTP gateway... 10833229Spst */ 10843229Spst ifr = getif(s, &dst); 10853229Spst if (ifr) { 10863229Spst struct sockaddr_in *sip; 10873229Spst sip = (struct sockaddr_in *) &(ifr->ifr_addr); 10883229Spst siaddr = sip->sin_addr; 10893229Spst } else { 10903229Spst /* Just use my "official" IP address. */ 10913229Spst siaddr = my_ip_addr; 10923229Spst } 10933229Spst 10943229Spst /* XXX - No need to set bp_giaddr here. */ 10953229Spst 10963229Spst /* Finally, set the server address field. */ 10973229Spst bp->bp_siaddr = siaddr; 10983229Spst } 10993229Spst /* Set up socket address for send. */ 11003229Spst send_addr.sin_family = AF_INET; 11013229Spst send_addr.sin_port = htons(port); 11023229Spst send_addr.sin_addr = dst; 11033229Spst 11043229Spst /* Send reply with same size packet as request used. */ 11053229Spst if (sendto(s, pktbuf, pktlen, 0, 11063229Spst (struct sockaddr *) &send_addr, 11073229Spst sizeof(send_addr)) < 0) 11083229Spst { 11093229Spst report(LOG_ERR, "sendto: %s", get_network_errmsg()); 11103229Spst } 11113229Spst} /* sendreply */ 11123229Spst 11133229Spst 11143229Spst/* nmatch() - now in getif.c */ 11153229Spst/* setarp() - now in hwaddr.c */ 11163229Spst 11173229Spst 11183229Spst/* 11193229Spst * This call checks read access to a file. It returns 0 if the file given 11203229Spst * by "path" exists and is publically readable. A value of -1 is returned if 11213229Spst * access is not permitted or an error occurs. Successful calls also 11223229Spst * return the file size in bytes using the long pointer "filesize". 11233229Spst * 11243229Spst * The read permission bit for "other" users is checked. This bit must be 11253229Spst * set for tftpd(8) to allow clients to read the file. 11263229Spst */ 11273229Spst 11283229SpstPRIVATE int 11293229Spstchk_access(path, filesize) 11303229Spst char *path; 11313229Spst int32 *filesize; 11323229Spst{ 11333229Spst struct stat st; 11343229Spst 11353229Spst if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) { 11363229Spst *filesize = (int32) st.st_size; 11373229Spst return 0; 11383229Spst } else { 11393229Spst return -1; 11403229Spst } 11413229Spst} 11423229Spst 11433229Spst 11443229Spst/* 11453229Spst * Now in dumptab.c : 11463229Spst * dumptab() 11473229Spst * dump_host() 11483229Spst * list_ipaddresses() 11493229Spst */ 11503229Spst 11513229Spst#ifdef VEND_CMU 11523229Spst 11533229Spst/* 11543229Spst * Insert the CMU "vendor" data for the host pointed to by "hp" into the 11553229Spst * bootp packet pointed to by "bp". 11563229Spst */ 11573229Spst 11583229SpstPRIVATE void 11593229Spstdovend_cmu(bp, hp) 11603229Spst struct bootp *bp; 11613229Spst struct host *hp; 11623229Spst{ 11633229Spst struct cmu_vend *vendp; 11643229Spst struct in_addr_list *taddr; 11653229Spst 11663229Spst /* 11673229Spst * Initialize the entire vendor field to zeroes. 11683229Spst */ 11693229Spst bzero(bp->bp_vend, sizeof(bp->bp_vend)); 11703229Spst 11713229Spst /* 11723229Spst * Fill in vendor information. Subnet mask, default gateway, 11733229Spst * domain name server, ien name server, time server 11743229Spst */ 11753229Spst vendp = (struct cmu_vend *) bp->bp_vend; 11763229Spst strcpy(vendp->v_magic, (char *)vm_cmu); 11773229Spst if (hp->flags.subnet_mask) { 11783229Spst (vendp->v_smask).s_addr = hp->subnet_mask.s_addr; 11793229Spst (vendp->v_flags) |= VF_SMASK; 11803229Spst if (hp->flags.gateway) { 11813229Spst (vendp->v_dgate).s_addr = hp->gateway->addr->s_addr; 11823229Spst } 11833229Spst } 11843229Spst if (hp->flags.domain_server) { 11853229Spst taddr = hp->domain_server; 11863229Spst if (taddr->addrcount > 0) { 11873229Spst (vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr; 11883229Spst if (taddr->addrcount > 1) { 11893229Spst (vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr; 11903229Spst } 11913229Spst } 11923229Spst } 11933229Spst if (hp->flags.name_server) { 11943229Spst taddr = hp->name_server; 11953229Spst if (taddr->addrcount > 0) { 11963229Spst (vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr; 11973229Spst if (taddr->addrcount > 1) { 11983229Spst (vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr; 11993229Spst } 12003229Spst } 12013229Spst } 12023229Spst if (hp->flags.time_server) { 12033229Spst taddr = hp->time_server; 12043229Spst if (taddr->addrcount > 0) { 12053229Spst (vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr; 12063229Spst if (taddr->addrcount > 1) { 12073229Spst (vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr; 12083229Spst } 12093229Spst } 12103229Spst } 12113229Spst /* Log message now done by caller. */ 12123229Spst} /* dovend_cmu */ 12133229Spst 12143229Spst#endif /* VEND_CMU */ 12153229Spst 12163229Spst 12173229Spst 12183229Spst/* 12193229Spst * Insert the RFC1048 vendor data for the host pointed to by "hp" into the 12203229Spst * bootp packet pointed to by "bp". 12213229Spst */ 12223229Spst#define NEED(LEN, MSG) do \ 12233229Spst if (bytesleft < (LEN)) { \ 12243229Spst report(LOG_NOTICE, noroom, \ 12253229Spst hp->hostname->string, MSG); \ 12263229Spst return; \ 12273229Spst } while (0) 12283229SpstPRIVATE void 12293229Spstdovend_rfc1048(bp, hp, bootsize) 12303229Spst struct bootp *bp; 12313229Spst struct host *hp; 12323229Spst int32 bootsize; 12333229Spst{ 12343229Spst int bytesleft, len; 12353229Spst byte *vp; 12363229Spst 12373229Spst static char noroom[] = "%s: No room for \"%s\" option"; 12383229Spst 12393229Spst vp = bp->bp_vend; 12403229Spst 12413229Spst if (hp->flags.msg_size) { 12423229Spst pktlen = hp->msg_size; 12433229Spst } else { 12443229Spst /* 12453229Spst * If the request was longer than the official length, build 12463229Spst * a response of that same length where the additional length 12473229Spst * is assumed to be part of the bp_vend (options) area. 12483229Spst */ 12493229Spst if (pktlen > sizeof(*bp)) { 12503229Spst if (debug > 1) 12513229Spst report(LOG_INFO, "request message length=%d", pktlen); 12523229Spst } 12533229Spst /* 12543229Spst * Check whether the request contains the option: 12553229Spst * Maximum DHCP Message Size (RFC1533 sec. 9.8) 12563229Spst * and if so, override the response length with its value. 12573229Spst * This request must lie within the first BP_VEND_LEN 12583229Spst * bytes of the option space. 12593229Spst */ 12603229Spst { 12613229Spst byte *p, *ep; 12623229Spst byte tag, len; 12633229Spst short msgsz = 0; 12648870Srgrimes 12653229Spst p = vp + 4; 12663229Spst ep = p + BP_VEND_LEN - 4; 12673229Spst while (p < ep) { 12683229Spst tag = *p++; 12693229Spst /* Check for tags with no data first. */ 12703229Spst if (tag == TAG_PAD) 12713229Spst continue; 12723229Spst if (tag == TAG_END) 12733229Spst break; 12743229Spst /* Now scan the length byte. */ 12753229Spst len = *p++; 12763229Spst switch (tag) { 12773229Spst case TAG_MAX_MSGSZ: 12783229Spst if (len == 2) { 12793229Spst bcopy(p, (char*)&msgsz, 2); 12803229Spst msgsz = ntohs(msgsz); 12813229Spst } 12823229Spst break; 12833229Spst case TAG_SUBNET_MASK: 12843229Spst /* XXX - Should preserve this if given... */ 12853229Spst break; 12863229Spst } /* swtich */ 12873229Spst p += len; 12883229Spst } 12893229Spst 12903229Spst if (msgsz > sizeof(*bp)) { 12913229Spst if (debug > 1) 12923229Spst report(LOG_INFO, "request has DHCP msglen=%d", msgsz); 12933229Spst pktlen = msgsz; 12943229Spst } 12953229Spst } 12963229Spst } 12973229Spst 12983229Spst if (pktlen < sizeof(*bp)) { 12993229Spst report(LOG_ERR, "invalid response length=%d", pktlen); 13003229Spst pktlen = sizeof(*bp); 13013229Spst } 13023229Spst bytesleft = ((byte*)bp + pktlen) - vp; 13033229Spst if (pktlen > sizeof(*bp)) { 13043229Spst if (debug > 1) 13053229Spst report(LOG_INFO, "extended reply, length=%d, options=%d", 13063229Spst pktlen, bytesleft); 13073229Spst } 13083229Spst 13093229Spst /* Copy in the magic cookie */ 13103229Spst bcopy(vm_rfc1048, vp, 4); 13113229Spst vp += 4; 13123229Spst bytesleft -= 4; 13133229Spst 13143229Spst if (hp->flags.subnet_mask) { 13153229Spst /* always enough room here. */ 13163229Spst *vp++ = TAG_SUBNET_MASK;/* -1 byte */ 13173229Spst *vp++ = 4; /* -1 byte */ 13183229Spst insert_u_long(hp->subnet_mask.s_addr, &vp); /* -4 bytes */ 13193229Spst bytesleft -= 6; /* Fix real count */ 13203229Spst if (hp->flags.gateway) { 13213229Spst (void) insert_ip(TAG_GATEWAY, 13223229Spst hp->gateway, 13233229Spst &vp, &bytesleft); 13243229Spst } 13253229Spst } 13263229Spst if (hp->flags.bootsize) { 13273229Spst /* always enough room here */ 13283229Spst bootsize = (hp->flags.bootsize_auto) ? 13293229Spst ((bootsize + 511) / 512) : (hp->bootsize); /* Round up */ 13303229Spst *vp++ = TAG_BOOT_SIZE; 13313229Spst *vp++ = 2; 13323229Spst *vp++ = (byte) ((bootsize >> 8) & 0xFF); 13333229Spst *vp++ = (byte) (bootsize & 0xFF); 13343229Spst bytesleft -= 4; /* Tag, length, and 16 bit blocksize */ 13353229Spst } 13363229Spst /* 13373229Spst * This one is special: Remaining options go in the ext file. 13383229Spst * Only the subnet_mask, bootsize, and gateway should precede. 13393229Spst */ 13403229Spst if (hp->flags.exten_file) { 13413229Spst /* 13423229Spst * Check for room for exten_file. Add 3 to account for 13433229Spst * TAG_EXTEN_FILE, length, and TAG_END. 13443229Spst */ 13453229Spst len = strlen(hp->exten_file->string); 13463229Spst NEED((len + 3), "ef"); 13473229Spst *vp++ = TAG_EXTEN_FILE; 13483229Spst *vp++ = (byte) (len & 0xFF); 13493229Spst bcopy(hp->exten_file->string, vp, len); 13503229Spst vp += len; 13513229Spst *vp++ = TAG_END; 13523229Spst bytesleft -= len + 3; 13533229Spst return; /* no more options here. */ 13543229Spst } 13553229Spst /* 13563229Spst * The remaining options are inserted by the following 13573229Spst * function (which is shared with bootpef.c). 13583229Spst * Keep back one byte for the TAG_END. 13593229Spst */ 13603229Spst len = dovend_rfc1497(hp, vp, bytesleft - 1); 13613229Spst vp += len; 13623229Spst bytesleft -= len; 13633229Spst 13643229Spst /* There should be at least one byte left. */ 13653229Spst NEED(1, "(end)"); 13663229Spst *vp++ = TAG_END; 13673229Spst bytesleft--; 13683229Spst 13693229Spst /* Log message done by caller. */ 13703229Spst if (bytesleft > 0) { 13713229Spst /* 13723229Spst * Zero out any remaining part of the vendor area. 13733229Spst */ 13743229Spst bzero(vp, bytesleft); 13753229Spst } 13763229Spst} /* dovend_rfc1048 */ 13773229Spst#undef NEED 13783229Spst 13793229Spst 13803229Spst/* 13813229Spst * Now in readfile.c: 13823229Spst * hwlookcmp() 13833229Spst * iplookcmp() 13843229Spst */ 13853229Spst 13863229Spst/* haddrtoa() - now in hwaddr.c */ 13873229Spst/* 13883229Spst * Now in dovend.c: 13893229Spst * insert_ip() 13903229Spst * insert_generic() 13913229Spst * insert_u_long() 13923229Spst */ 13933229Spst 13943229Spst/* get_errmsg() - now in report.c */ 13953229Spst 13963229Spst/* 13973229Spst * Local Variables: 13983229Spst * tab-width: 4 13993229Spst * c-indent-level: 4 14003229Spst * c-argdecl-indent: 4 14013229Spst * c-continued-statement-offset: 4 14023229Spst * c-continued-brace-offset: -4 14033229Spst * c-label-offset: -4 14043229Spst * c-brace-offset: 0 14053229Spst * End: 14063229Spst */ 1407