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