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