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