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: stable/11/libexec/bootpd/bootpd.c 342229 2018-12-19 18:19:15Z emaste $"); 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 639342229Semaste if (bp->bp_htype >= hwinfocnt) { 640342229Semaste report(LOG_NOTICE, "bad hw addr type %u", bp->bp_htype); 641342229Semaste return; 642342229Semaste } 64341734Seivind bp->bp_file[sizeof(bp->bp_file)-1] = '\0'; 64441734Seivind 6453229Spst /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */ 6463229Spst 6473229Spst /* 6483229Spst * If the servername field is set, compare it against us. 6493229Spst * If we're not being addressed, ignore this request. 6503229Spst * If the server name field is null, throw in our name. 6513229Spst */ 6523229Spst if (strlen(bp->bp_sname)) { 6533229Spst if (strcmp(bp->bp_sname, hostname)) { 6543229Spst if (debug) 6553229Spst report(LOG_INFO, "\ 6563229Spstignoring request for server %s from client at %s address %s", 6573229Spst bp->bp_sname, netname(bp->bp_htype), 6583229Spst haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 6593229Spst /* XXX - Is it correct to ignore such a request? -gwr */ 6603229Spst return; 6613229Spst } 6623229Spst } else { 6633229Spst strcpy(bp->bp_sname, hostname); 6643229Spst } 6653229Spst 6663229Spst /* Convert the request into a reply. */ 6673229Spst bp->bp_op = BOOTREPLY; 6683229Spst if (bp->bp_ciaddr.s_addr == 0) { 6693229Spst /* 670229780Suqs * client doesn't know his IP address, 6713229Spst * search by hardware address. 6723229Spst */ 6733229Spst if (debug > 1) { 6743229Spst report(LOG_INFO, "request from %s address %s", 6753229Spst netname(bp->bp_htype), 6763229Spst haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 6773229Spst } 6783229Spst hlen = haddrlength(bp->bp_htype); 6793229Spst if (hlen != bp->bp_hlen) { 680116371Sjmg report(LOG_NOTICE, "bad addr len from %s address %s", 6813229Spst netname(bp->bp_htype), 6823229Spst haddrtoa(bp->bp_chaddr, hlen)); 6833229Spst } 6843229Spst dummyhost.htype = bp->bp_htype; 6853229Spst bcopy(bp->bp_chaddr, dummyhost.haddr, hlen); 6863229Spst hashcode = hash_HashFunction(bp->bp_chaddr, hlen); 6873229Spst hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp, 6883229Spst &dummyhost); 6893229Spst if (hp == NULL && 6903229Spst bp->bp_htype == HTYPE_IEEE802) 6913229Spst { 6923229Spst /* Try again with address in "canonical" form. */ 6933229Spst haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen); 6943229Spst if (debug > 1) { 6953229Spst report(LOG_INFO, "\ 6963229SpstHW addr type is IEEE 802. convert to %s and check again\n", 6973229Spst haddrtoa(dummyhost.haddr, bp->bp_hlen)); 6983229Spst } 6993229Spst hashcode = hash_HashFunction(dummyhost.haddr, hlen); 7003229Spst hp = (struct host *) hash_Lookup(hwhashtable, hashcode, 7013229Spst hwlookcmp, &dummyhost); 7023229Spst } 7033229Spst if (hp == NULL) { 7043229Spst /* 7053229Spst * XXX - Add dynamic IP address assignment? 7063229Spst */ 70713575Spst if (debug) 70813575Spst report(LOG_NOTICE, "unknown client %s address %s", 7093229Spst netname(bp->bp_htype), 7103229Spst haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 7113229Spst return; /* not found */ 7123229Spst } 7133229Spst (bp->bp_yiaddr).s_addr = hp->iaddr.s_addr; 7143229Spst 7153229Spst } else { 7163229Spst 7173229Spst /* 7183229Spst * search by IP address. 7193229Spst */ 7203229Spst if (debug > 1) { 7213229Spst report(LOG_INFO, "request from IP addr %s", 7223229Spst inet_ntoa(bp->bp_ciaddr)); 7233229Spst } 7243229Spst dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr; 7253229Spst hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4); 7263229Spst hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp, 7273229Spst &dummyhost); 7283229Spst if (hp == NULL) { 72913575Spst if (debug) { 7303229Spst report(LOG_NOTICE, "IP address not found: %s", 7313229Spst inet_ntoa(bp->bp_ciaddr)); 7323229Spst } 7333229Spst return; 7343229Spst } 7353229Spst } 7363229Spst 7373229Spst if (debug) { 7383229Spst report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr), 7393229Spst hp->hostname->string); 7403229Spst } 7413229Spst 7423229Spst /* 7433229Spst * If there is a response delay threshold, ignore requests 7443229Spst * with a timestamp lower than the threshold. 7453229Spst */ 7463229Spst if (hp->flags.min_wait) { 7473229Spst u_int32 t = (u_int32) ntohs(bp->bp_secs); 7483229Spst if (t < hp->min_wait) { 7493229Spst if (debug > 1) 7503229Spst report(LOG_INFO, 7513229Spst "ignoring request due to timestamp (%d < %d)", 7523229Spst t, hp->min_wait); 7533229Spst return; 7543229Spst } 7553229Spst } 7563229Spst 7573229Spst#ifdef YORK_EX_OPTION 7583229Spst /* 7593229Spst * The need for the "ex" tag arose out of the need to empty 7603229Spst * shared networked drives on diskless PCs. This solution is 7613229Spst * not very clean but it does work fairly well. 7623229Spst * Written by Edmund J. Sutcliffe <edmund@york.ac.uk> 7633229Spst * 7643229Spst * XXX - This could compromise security if a non-trusted user 7653229Spst * managed to write an entry in the bootptab with :ex=trojan: 7663229Spst * so I would leave this turned off unless you need it. -gwr 7673229Spst */ 7683229Spst /* Run a program, passing the client name as a parameter. */ 7693229Spst if (hp->flags.exec_file) { 7703229Spst char tst[100]; 7713229Spst /* XXX - Check string lengths? -gwr */ 7723229Spst strcpy (tst, hp->exec_file->string); 7733229Spst strcat (tst, " "); 7743229Spst strcat (tst, hp->hostname->string); 7753229Spst strcat (tst, " &"); 7763229Spst if (debug) 7773229Spst report(LOG_INFO, "executing %s", tst); 7783229Spst system(tst); /* Hope this finishes soon... */ 7793229Spst } 7803229Spst#endif /* YORK_EX_OPTION */ 7813229Spst 7823229Spst /* 7833229Spst * If a specific TFTP server address was specified in the bootptab file, 7843229Spst * fill it in, otherwise zero it. 7853229Spst * XXX - Rather than zero it, should it be the bootpd address? -gwr 7863229Spst */ 7873229Spst (bp->bp_siaddr).s_addr = (hp->flags.bootserver) ? 7883229Spst hp->bootserver.s_addr : 0L; 7893229Spst 7903229Spst#ifdef STANFORD_PROM_COMPAT 7913229Spst /* 7923229Spst * Stanford bootp PROMs (for a Sun?) have no way to leave 7933229Spst * the boot file name field blank (because the boot file 7943229Spst * name is automatically generated from some index). 7953229Spst * As a work-around, this little hack allows those PROMs to 7963229Spst * specify "sunboot14" with the same effect as a NULL name. 7973229Spst * (The user specifies boot device 14 or some such magic.) 7983229Spst */ 7993229Spst if (strcmp(bp->bp_file, "sunboot14") == 0) 8003229Spst bp->bp_file[0] = '\0'; /* treat it as unspecified */ 8013229Spst#endif 8023229Spst 8033229Spst /* 8043229Spst * Fill in the client's proper bootfile. 8053229Spst * 8063229Spst * If the client specifies an absolute path, try that file with a 8073229Spst * ".host" suffix and then without. If the file cannot be found, no 8083229Spst * reply is made at all. 8093229Spst * 8103229Spst * If the client specifies a null or relative file, use the following 8113229Spst * table to determine the appropriate action: 8123229Spst * 8133229Spst * Homedir Bootfile Client's file 8143229Spst * specified? specified? specification Action 8153229Spst * ------------------------------------------------------------------- 8163229Spst * No No Null Send null filename 8173229Spst * No No Relative Discard request 8183229Spst * No Yes Null Send if absolute else null 8193229Spst * No Yes Relative Discard request *XXX 8203229Spst * Yes No Null Send null filename 8213229Spst * Yes No Relative Lookup with ".host" 8223229Spst * Yes Yes Null Send home/boot or bootfile 8233229Spst * Yes Yes Relative Lookup with ".host" *XXX 8243229Spst * 8253229Spst */ 8263229Spst 8273229Spst /* 8283229Spst * XXX - I don't like the policy of ignoring a client when the 8293229Spst * boot file is not accessible. The TFTP server might not be 8303229Spst * running on the same machine as the BOOTP server, in which 8313229Spst * case checking accessibility of the boot file is pointless. 8323229Spst * 8333229Spst * Therefore, file accessibility is now demanded ONLY if you 8343229Spst * define CHECK_FILE_ACCESS in the Makefile options. -gwr 8353229Spst */ 8363229Spst 8373229Spst /* 8383229Spst * The "real" path is as seen by the BOOTP daemon on this 8393229Spst * machine, while the client path is relative to the TFTP 8403229Spst * daemon chroot directory (i.e. /tftpboot). 8413229Spst */ 8423229Spst if (hp->flags.tftpdir) { 84341699Sdillon snprintf(realpath, sizeof(realpath), "%s", hp->tftpdir->string); 8443229Spst clntpath = &realpath[strlen(realpath)]; 8453229Spst } else { 8463229Spst realpath[0] = '\0'; 8473229Spst clntpath = realpath; 8483229Spst } 8493229Spst 8503229Spst /* 8513229Spst * Determine client's requested homedir and bootfile. 8523229Spst */ 8533229Spst homedir = NULL; 8543229Spst bootfile = NULL; 8553229Spst if (bp->bp_file[0]) { 8563229Spst homedir = bp->bp_file; 8573229Spst bootfile = strrchr(homedir, '/'); 8583229Spst if (bootfile) { 8593229Spst if (homedir == bootfile) 8603229Spst homedir = NULL; 8613229Spst *bootfile++ = '\0'; 8623229Spst } else { 8633229Spst /* no "/" in the string */ 8643229Spst bootfile = homedir; 8653229Spst homedir = NULL; 8663229Spst } 8673229Spst if (debug > 2) { 8683229Spst report(LOG_INFO, "requested path=\"%s\" file=\"%s\"", 8693229Spst (homedir) ? homedir : "", 8703229Spst (bootfile) ? bootfile : ""); 8713229Spst } 8723229Spst } 8733229Spst 8743229Spst /* 8753229Spst * Specifications in bootptab override client requested values. 8763229Spst */ 8773229Spst if (hp->flags.homedir) 8783229Spst homedir = hp->homedir->string; 8793229Spst if (hp->flags.bootfile) 8803229Spst bootfile = hp->bootfile->string; 8813229Spst 8823229Spst /* 8833229Spst * Construct bootfile path. 8843229Spst */ 8853229Spst if (homedir) { 8863229Spst if (homedir[0] != '/') 8873229Spst strcat(clntpath, "/"); 8883229Spst strcat(clntpath, homedir); 8893229Spst homedir = NULL; 8903229Spst } 8913229Spst if (bootfile) { 8923229Spst if (bootfile[0] != '/') 8933229Spst strcat(clntpath, "/"); 8943229Spst strcat(clntpath, bootfile); 8953229Spst bootfile = NULL; 8963229Spst } 8973229Spst 8983229Spst /* 8993229Spst * First try to find the file with a ".host" suffix 9003229Spst */ 9013229Spst n = strlen(clntpath); 9023229Spst strcat(clntpath, "."); 9033229Spst strcat(clntpath, hp->hostname->string); 9043229Spst if (chk_access(realpath, &bootsize) < 0) { 9053229Spst clntpath[n] = 0; /* Try it without the suffix */ 9063229Spst if (chk_access(realpath, &bootsize) < 0) { 9073229Spst /* neither "file.host" nor "file" was found */ 9083229Spst#ifdef CHECK_FILE_ACCESS 9093229Spst 9103229Spst if (bp->bp_file[0]) { 9113229Spst /* 9123229Spst * Client wanted specific file 9133229Spst * and we didn't have it. 9143229Spst */ 9153229Spst report(LOG_NOTICE, 9163229Spst "requested file not found: \"%s\"", clntpath); 9173229Spst return; 9183229Spst } 9193229Spst /* 9203229Spst * Client didn't ask for a specific file and we couldn't 9213229Spst * access the default file, so just zero-out the bootfile 9223229Spst * field in the packet and continue processing the reply. 9233229Spst */ 9243229Spst bzero(bp->bp_file, sizeof(bp->bp_file)); 9253229Spst goto null_file_name; 9263229Spst 9273229Spst#else /* CHECK_FILE_ACCESS */ 9283229Spst 9293229Spst /* Complain only if boot file size was needed. */ 9303229Spst if (hp->flags.bootsize_auto) { 9313229Spst report(LOG_ERR, "can not determine size of file \"%s\"", 9323229Spst clntpath); 9333229Spst } 9343229Spst 9353229Spst#endif /* CHECK_FILE_ACCESS */ 9363229Spst } 9373229Spst } 9383229Spst strncpy(bp->bp_file, clntpath, BP_FILE_LEN); 9393229Spst if (debug > 2) 9403229Spst report(LOG_INFO, "bootfile=\"%s\"", clntpath); 9413229Spst 94213575Spst#ifdef CHECK_FILE_ACCESS 9433229Spstnull_file_name: 94413575Spst#endif /* CHECK_FILE_ACCESS */ 9453229Spst 9463229Spst 9473229Spst /* 9483229Spst * Handle vendor options based on magic number. 9493229Spst */ 9503229Spst 9513229Spst if (debug > 1) { 9523229Spst report(LOG_INFO, "vendor magic field is %d.%d.%d.%d", 9533229Spst (int) ((bp->bp_vend)[0]), 9543229Spst (int) ((bp->bp_vend)[1]), 9553229Spst (int) ((bp->bp_vend)[2]), 9563229Spst (int) ((bp->bp_vend)[3])); 9573229Spst } 9583229Spst /* 9593229Spst * If this host isn't set for automatic vendor info then copy the 9603229Spst * specific cookie into the bootp packet, thus forcing a certain 9613229Spst * reply format. Only force reply format if user specified it. 9623229Spst */ 9633229Spst if (hp->flags.vm_cookie) { 9643229Spst /* Slam in the user specified magic number. */ 9653229Spst bcopy(hp->vm_cookie, bp->bp_vend, 4); 9663229Spst } 9673229Spst /* 9683229Spst * Figure out the format for the vendor-specific info. 9693229Spst * Note that bp->bp_vend may have been set above. 9703229Spst */ 9713229Spst if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) { 9723229Spst /* RFC1048 conformant bootp client */ 9733229Spst dovend_rfc1048(bp, hp, bootsize); 9743229Spst if (debug > 1) { 9753229Spst report(LOG_INFO, "sending reply (with RFC1048 options)"); 9763229Spst } 9773229Spst } 9783229Spst#ifdef VEND_CMU 9793229Spst else if (!bcmp(bp->bp_vend, vm_cmu, 4)) { 9803229Spst dovend_cmu(bp, hp); 9813229Spst if (debug > 1) { 9823229Spst report(LOG_INFO, "sending reply (with CMU options)"); 9833229Spst } 9843229Spst } 9853229Spst#endif 9863229Spst else { 9873229Spst if (debug > 1) { 9883229Spst report(LOG_INFO, "sending reply (with no options)"); 9893229Spst } 9903229Spst } 9913229Spst 9923229Spst dest = (hp->flags.reply_addr) ? 9933229Spst hp->reply_addr.s_addr : 0L; 9943229Spst 9953229Spst /* not forwarded */ 9963229Spst sendreply(0, dest); 9973229Spst} 9983229Spst 9993229Spst 10003229Spst/* 10013229Spst * Process BOOTREPLY packet. 10023229Spst */ 10033229SpstPRIVATE void 10043229Spsthandle_reply() 10053229Spst{ 10063229Spst if (debug) { 10073229Spst report(LOG_INFO, "processing boot reply"); 10083229Spst } 10093229Spst /* forwarded, no destination override */ 10103229Spst sendreply(1, 0); 10113229Spst} 10123229Spst 10133229Spst 10143229Spst/* 10153229Spst * Send a reply packet to the client. 'forward' flag is set if we are 10163229Spst * not the originator of this reply packet. 10173229Spst */ 10183229SpstPRIVATE void 10193229Spstsendreply(forward, dst_override) 10203229Spst int forward; 10213229Spst int32 dst_override; 10223229Spst{ 10233229Spst struct bootp *bp = (struct bootp *) pktbuf; 10243229Spst struct in_addr dst; 10253229Spst u_short port = bootpc_port; 10263229Spst unsigned char *ha; 102713575Spst int len, haf; 10283229Spst 10293229Spst /* 10303229Spst * XXX - Should honor bp_flags "broadcast" bit here. 10313229Spst * Temporary workaround: use the :ra=ADDR: option to 10323229Spst * set the reply address to the broadcast address. 10333229Spst */ 10343229Spst 10353229Spst /* 10363229Spst * If the destination address was specified explicitly 1037116371Sjmg * (i.e. the broadcast address for HP compatibility) 10383229Spst * then send the response to that address. Otherwise, 10393229Spst * act in accordance with RFC951: 10403229Spst * If the client IP address is specified, use that 10413229Spst * else if gateway IP address is specified, use that 10423229Spst * else make a temporary arp cache entry for the client's 10433229Spst * NEW IP/hardware address and use that. 10443229Spst */ 10453229Spst if (dst_override) { 10463229Spst dst.s_addr = dst_override; 10473229Spst if (debug > 1) { 10483229Spst report(LOG_INFO, "reply address override: %s", 10493229Spst inet_ntoa(dst)); 10503229Spst } 10513229Spst } else if (bp->bp_ciaddr.s_addr) { 10523229Spst dst = bp->bp_ciaddr; 10533229Spst } else if (bp->bp_giaddr.s_addr && forward == 0) { 10543229Spst dst = bp->bp_giaddr; 10553229Spst port = bootps_port; 10563229Spst if (debug > 1) { 10573229Spst report(LOG_INFO, "sending reply to gateway %s", 10583229Spst inet_ntoa(dst)); 10593229Spst } 10603229Spst } else { 10613229Spst dst = bp->bp_yiaddr; 10623229Spst ha = bp->bp_chaddr; 10633229Spst len = bp->bp_hlen; 10643229Spst if (len > MAXHADDRLEN) 10653229Spst len = MAXHADDRLEN; 106613575Spst haf = (int) bp->bp_htype; 106713575Spst if (haf == 0) 106813575Spst haf = HTYPE_ETHERNET; 10693229Spst 10703229Spst if (debug > 1) 10713229Spst report(LOG_INFO, "setarp %s - %s", 10723229Spst inet_ntoa(dst), haddrtoa(ha, len)); 107313575Spst setarp(s, &dst, haf, ha, len); 10743229Spst } 10753229Spst 10763229Spst if ((forward == 0) && 10773229Spst (bp->bp_siaddr.s_addr == 0)) 10783229Spst { 10793229Spst struct ifreq *ifr; 10803229Spst struct in_addr siaddr; 10813229Spst /* 10823229Spst * If we are originating this reply, we 10833229Spst * need to find our own interface address to 10843229Spst * put in the bp_siaddr field of the reply. 10853229Spst * If this server is multi-homed, pick the 10863229Spst * 'best' interface (the one on the same net 10873229Spst * as the client). Of course, the client may 10883229Spst * be on the other side of a BOOTP gateway... 10893229Spst */ 10903229Spst ifr = getif(s, &dst); 10913229Spst if (ifr) { 10923229Spst struct sockaddr_in *sip; 10933229Spst sip = (struct sockaddr_in *) &(ifr->ifr_addr); 10943229Spst siaddr = sip->sin_addr; 10953229Spst } else { 10963229Spst /* Just use my "official" IP address. */ 10973229Spst siaddr = my_ip_addr; 10983229Spst } 10993229Spst 11003229Spst /* XXX - No need to set bp_giaddr here. */ 11013229Spst 11023229Spst /* Finally, set the server address field. */ 11033229Spst bp->bp_siaddr = siaddr; 11043229Spst } 11053229Spst /* Set up socket address for send. */ 11063229Spst send_addr.sin_family = AF_INET; 11073229Spst send_addr.sin_port = htons(port); 11083229Spst send_addr.sin_addr = dst; 11093229Spst 11103229Spst /* Send reply with same size packet as request used. */ 11113229Spst if (sendto(s, pktbuf, pktlen, 0, 11123229Spst (struct sockaddr *) &send_addr, 11133229Spst sizeof(send_addr)) < 0) 11143229Spst { 11153229Spst report(LOG_ERR, "sendto: %s", get_network_errmsg()); 11163229Spst } 11173229Spst} /* sendreply */ 11183229Spst 11193229Spst 11203229Spst/* nmatch() - now in getif.c */ 11213229Spst/* setarp() - now in hwaddr.c */ 11223229Spst 11233229Spst 11243229Spst/* 11253229Spst * This call checks read access to a file. It returns 0 if the file given 1126229780Suqs * by "path" exists and is publicly readable. A value of -1 is returned if 11273229Spst * access is not permitted or an error occurs. Successful calls also 11283229Spst * return the file size in bytes using the long pointer "filesize". 11293229Spst * 11303229Spst * The read permission bit for "other" users is checked. This bit must be 11313229Spst * set for tftpd(8) to allow clients to read the file. 11323229Spst */ 11333229Spst 11343229SpstPRIVATE int 11353229Spstchk_access(path, filesize) 11363229Spst char *path; 11373229Spst int32 *filesize; 11383229Spst{ 11393229Spst struct stat st; 11403229Spst 11413229Spst if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) { 11423229Spst *filesize = (int32) st.st_size; 11433229Spst return 0; 11443229Spst } else { 11453229Spst return -1; 11463229Spst } 11473229Spst} 11483229Spst 11493229Spst 11503229Spst/* 11513229Spst * Now in dumptab.c : 11523229Spst * dumptab() 11533229Spst * dump_host() 11543229Spst * list_ipaddresses() 11553229Spst */ 11563229Spst 11573229Spst#ifdef VEND_CMU 11583229Spst 11593229Spst/* 11603229Spst * Insert the CMU "vendor" data for the host pointed to by "hp" into the 11613229Spst * bootp packet pointed to by "bp". 11623229Spst */ 11633229Spst 11643229SpstPRIVATE void 11653229Spstdovend_cmu(bp, hp) 11663229Spst struct bootp *bp; 11673229Spst struct host *hp; 11683229Spst{ 11693229Spst struct cmu_vend *vendp; 11703229Spst struct in_addr_list *taddr; 11713229Spst 11723229Spst /* 11733229Spst * Initialize the entire vendor field to zeroes. 11743229Spst */ 11753229Spst bzero(bp->bp_vend, sizeof(bp->bp_vend)); 11763229Spst 11773229Spst /* 11783229Spst * Fill in vendor information. Subnet mask, default gateway, 11793229Spst * domain name server, ien name server, time server 11803229Spst */ 11813229Spst vendp = (struct cmu_vend *) bp->bp_vend; 11823229Spst strcpy(vendp->v_magic, (char *)vm_cmu); 11833229Spst if (hp->flags.subnet_mask) { 11843229Spst (vendp->v_smask).s_addr = hp->subnet_mask.s_addr; 11853229Spst (vendp->v_flags) |= VF_SMASK; 11863229Spst if (hp->flags.gateway) { 11873229Spst (vendp->v_dgate).s_addr = hp->gateway->addr->s_addr; 11883229Spst } 11893229Spst } 11903229Spst if (hp->flags.domain_server) { 11913229Spst taddr = hp->domain_server; 11923229Spst if (taddr->addrcount > 0) { 11933229Spst (vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr; 11943229Spst if (taddr->addrcount > 1) { 11953229Spst (vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr; 11963229Spst } 11973229Spst } 11983229Spst } 11993229Spst if (hp->flags.name_server) { 12003229Spst taddr = hp->name_server; 12013229Spst if (taddr->addrcount > 0) { 12023229Spst (vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr; 12033229Spst if (taddr->addrcount > 1) { 12043229Spst (vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr; 12053229Spst } 12063229Spst } 12073229Spst } 12083229Spst if (hp->flags.time_server) { 12093229Spst taddr = hp->time_server; 12103229Spst if (taddr->addrcount > 0) { 12113229Spst (vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr; 12123229Spst if (taddr->addrcount > 1) { 12133229Spst (vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr; 12143229Spst } 12153229Spst } 12163229Spst } 12173229Spst /* Log message now done by caller. */ 12183229Spst} /* dovend_cmu */ 12193229Spst 12203229Spst#endif /* VEND_CMU */ 12213229Spst 12223229Spst 12233229Spst 12243229Spst/* 12253229Spst * Insert the RFC1048 vendor data for the host pointed to by "hp" into the 12263229Spst * bootp packet pointed to by "bp". 12273229Spst */ 12283229Spst#define NEED(LEN, MSG) do \ 12293229Spst if (bytesleft < (LEN)) { \ 12303229Spst report(LOG_NOTICE, noroom, \ 12313229Spst hp->hostname->string, MSG); \ 12323229Spst return; \ 12333229Spst } while (0) 12343229SpstPRIVATE void 12353229Spstdovend_rfc1048(bp, hp, bootsize) 12363229Spst struct bootp *bp; 12373229Spst struct host *hp; 12383229Spst int32 bootsize; 12393229Spst{ 12403229Spst int bytesleft, len; 12413229Spst byte *vp; 12423229Spst 124390159Skris static const char noroom[] = "%s: No room for \"%s\" option"; 12443229Spst 12453229Spst vp = bp->bp_vend; 12463229Spst 12473229Spst if (hp->flags.msg_size) { 12483229Spst pktlen = hp->msg_size; 12493229Spst } else { 12503229Spst /* 12513229Spst * If the request was longer than the official length, build 12523229Spst * a response of that same length where the additional length 12533229Spst * is assumed to be part of the bp_vend (options) area. 12543229Spst */ 12553229Spst if (pktlen > sizeof(*bp)) { 12563229Spst if (debug > 1) 12573229Spst report(LOG_INFO, "request message length=%d", pktlen); 12583229Spst } 12593229Spst /* 12603229Spst * Check whether the request contains the option: 12613229Spst * Maximum DHCP Message Size (RFC1533 sec. 9.8) 12623229Spst * and if so, override the response length with its value. 12633229Spst * This request must lie within the first BP_VEND_LEN 12643229Spst * bytes of the option space. 12653229Spst */ 12663229Spst { 12673229Spst byte *p, *ep; 12683229Spst byte tag, len; 12693229Spst short msgsz = 0; 12708870Srgrimes 12713229Spst p = vp + 4; 12723229Spst ep = p + BP_VEND_LEN - 4; 12733229Spst while (p < ep) { 12743229Spst tag = *p++; 12753229Spst /* Check for tags with no data first. */ 12763229Spst if (tag == TAG_PAD) 12773229Spst continue; 12783229Spst if (tag == TAG_END) 12793229Spst break; 12803229Spst /* Now scan the length byte. */ 12813229Spst len = *p++; 12823229Spst switch (tag) { 12833229Spst case TAG_MAX_MSGSZ: 12843229Spst if (len == 2) { 12853229Spst bcopy(p, (char*)&msgsz, 2); 12863229Spst msgsz = ntohs(msgsz); 12873229Spst } 12883229Spst break; 12893229Spst case TAG_SUBNET_MASK: 12903229Spst /* XXX - Should preserve this if given... */ 12913229Spst break; 12923229Spst } /* swtich */ 12933229Spst p += len; 12943229Spst } 12953229Spst 129683941Siedowse if (msgsz > sizeof(*bp) + BP_MSG_OVERHEAD) { 12973229Spst if (debug > 1) 12983229Spst report(LOG_INFO, "request has DHCP msglen=%d", msgsz); 129983941Siedowse pktlen = msgsz - BP_MSG_OVERHEAD; 13003229Spst } 13013229Spst } 13023229Spst } 13033229Spst 13043229Spst if (pktlen < sizeof(*bp)) { 13053229Spst report(LOG_ERR, "invalid response length=%d", pktlen); 13063229Spst pktlen = sizeof(*bp); 13073229Spst } 13083229Spst bytesleft = ((byte*)bp + pktlen) - vp; 13093229Spst if (pktlen > sizeof(*bp)) { 13103229Spst if (debug > 1) 13113229Spst report(LOG_INFO, "extended reply, length=%d, options=%d", 13123229Spst pktlen, bytesleft); 13133229Spst } 13143229Spst 13153229Spst /* Copy in the magic cookie */ 13163229Spst bcopy(vm_rfc1048, vp, 4); 13173229Spst vp += 4; 13183229Spst bytesleft -= 4; 13193229Spst 13203229Spst if (hp->flags.subnet_mask) { 13213229Spst /* always enough room here. */ 13223229Spst *vp++ = TAG_SUBNET_MASK;/* -1 byte */ 13233229Spst *vp++ = 4; /* -1 byte */ 13243229Spst insert_u_long(hp->subnet_mask.s_addr, &vp); /* -4 bytes */ 13253229Spst bytesleft -= 6; /* Fix real count */ 13263229Spst if (hp->flags.gateway) { 13273229Spst (void) insert_ip(TAG_GATEWAY, 13283229Spst hp->gateway, 13293229Spst &vp, &bytesleft); 13303229Spst } 13313229Spst } 13323229Spst if (hp->flags.bootsize) { 13333229Spst /* always enough room here */ 13343229Spst bootsize = (hp->flags.bootsize_auto) ? 13353229Spst ((bootsize + 511) / 512) : (hp->bootsize); /* Round up */ 13363229Spst *vp++ = TAG_BOOT_SIZE; 13373229Spst *vp++ = 2; 13383229Spst *vp++ = (byte) ((bootsize >> 8) & 0xFF); 13393229Spst *vp++ = (byte) (bootsize & 0xFF); 13403229Spst bytesleft -= 4; /* Tag, length, and 16 bit blocksize */ 13413229Spst } 13423229Spst /* 13433229Spst * This one is special: Remaining options go in the ext file. 13443229Spst * Only the subnet_mask, bootsize, and gateway should precede. 13453229Spst */ 13463229Spst if (hp->flags.exten_file) { 13473229Spst /* 13483229Spst * Check for room for exten_file. Add 3 to account for 13493229Spst * TAG_EXTEN_FILE, length, and TAG_END. 13503229Spst */ 13513229Spst len = strlen(hp->exten_file->string); 13523229Spst NEED((len + 3), "ef"); 13533229Spst *vp++ = TAG_EXTEN_FILE; 13543229Spst *vp++ = (byte) (len & 0xFF); 13553229Spst bcopy(hp->exten_file->string, vp, len); 13563229Spst vp += len; 13573229Spst *vp++ = TAG_END; 13583229Spst bytesleft -= len + 3; 13593229Spst return; /* no more options here. */ 13603229Spst } 13613229Spst /* 13623229Spst * The remaining options are inserted by the following 13633229Spst * function (which is shared with bootpef.c). 13643229Spst * Keep back one byte for the TAG_END. 13653229Spst */ 13663229Spst len = dovend_rfc1497(hp, vp, bytesleft - 1); 13673229Spst vp += len; 13683229Spst bytesleft -= len; 13693229Spst 13703229Spst /* There should be at least one byte left. */ 13713229Spst NEED(1, "(end)"); 13723229Spst *vp++ = TAG_END; 13733229Spst bytesleft--; 13743229Spst 13753229Spst /* Log message done by caller. */ 13763229Spst if (bytesleft > 0) { 13773229Spst /* 13783229Spst * Zero out any remaining part of the vendor area. 13793229Spst */ 13803229Spst bzero(vp, bytesleft); 13813229Spst } 13823229Spst} /* dovend_rfc1048 */ 13833229Spst#undef NEED 13843229Spst 13853229Spst 13863229Spst/* 13873229Spst * Now in readfile.c: 13883229Spst * hwlookcmp() 13893229Spst * iplookcmp() 13903229Spst */ 13913229Spst 13923229Spst/* haddrtoa() - now in hwaddr.c */ 13933229Spst/* 13943229Spst * Now in dovend.c: 13953229Spst * insert_ip() 13963229Spst * insert_generic() 13973229Spst * insert_u_long() 13983229Spst */ 13993229Spst 14003229Spst/* get_errmsg() - now in report.c */ 14013229Spst 14023229Spst/* 14033229Spst * Local Variables: 14043229Spst * tab-width: 4 14053229Spst * c-indent-level: 4 14063229Spst * c-argdecl-indent: 4 14073229Spst * c-continued-statement-offset: 4 14083229Spst * c-continued-brace-offset: -4 14093229Spst * c-label-offset: -4 14103229Spst * c-brace-offset: 0 14113229Spst * End: 14123229Spst */ 1413