bootpd.c revision 83941
1293734Sarybchik/************************************************************************ 2300607Sarybchik Copyright 1988, 1991 by Carnegie Mellon University 3293734Sarybchik 4293734Sarybchik All Rights Reserved 5293734Sarybchik 6293734SarybchikPermission to use, copy, modify, and distribute this software and its 7293734Sarybchikdocumentation for any purpose and without fee is hereby granted, provided 8293734Sarybchikthat the above copyright notice appear in all copies and that both that 9293734Sarybchikcopyright notice and this permission notice appear in supporting 10293734Sarybchikdocumentation, and that the name of Carnegie Mellon University not be used 11293734Sarybchikin advertising or publicity pertaining to distribution of the software 12293734Sarybchikwithout specific, written prior permission. 13293734Sarybchik 14293734SarybchikCARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 15293734SarybchikSOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. 16293734SarybchikIN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 17293734SarybchikDAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 18293734SarybchikPROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 19293734SarybchikACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 20293734SarybchikSOFTWARE. 21293734Sarybchik 22293734Sarybchik $FreeBSD: head/libexec/bootpd/bootpd.c 83941 2001-09-25 21:02:10Z iedowse $ 23293734Sarybchik 24293734Sarybchik************************************************************************/ 25293734Sarybchik 26293734Sarybchik/* 27293734Sarybchik * BOOTP (bootstrap protocol) server daemon. 28293734Sarybchik * 29293734Sarybchik * Answers BOOTP request packets from booting client machines. 30293734Sarybchik * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol. 31293734Sarybchik * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions. 32293734Sarybchik * See RFC 1395 for option tags 14-17. 33293734Sarybchik * See accompanying man page -- bootpd.8 34293734Sarybchik * 35293734Sarybchik * HISTORY 36293734Sarybchik * See ./Changes 37293734Sarybchik * 38293734Sarybchik * BUGS 39293734Sarybchik * See ./ToDo 40293748Sarybchik */ 41293748Sarybchik 42293748Sarybchik 43293748Sarybchik 44293748Sarybchik#include <sys/types.h> 45293748Sarybchik#include <sys/param.h> 46293748Sarybchik#include <sys/socket.h> 47293734Sarybchik#include <sys/ioctl.h> 48299720Sarybchik#include <sys/file.h> 49299720Sarybchik#include <sys/time.h> 50299720Sarybchik#include <sys/stat.h> 51299720Sarybchik#include <sys/utsname.h> 52299720Sarybchik 53299720Sarybchik#include <net/if.h> 54299720Sarybchik#include <netinet/in.h> 55299720Sarybchik#include <arpa/inet.h> /* inet_ntoa */ 56299720Sarybchik 57299720Sarybchik#ifndef NO_UNISTD 58299720Sarybchik#include <unistd.h> 59299720Sarybchik#endif 60299720Sarybchik 61299720Sarybchik#include <stdlib.h> 62299720Sarybchik#include <signal.h> 63299720Sarybchik#include <stdio.h> 64299720Sarybchik#include <string.h> 65299720Sarybchik#include <errno.h> 66299720Sarybchik#include <ctype.h> 67299720Sarybchik#include <netdb.h> 68299720Sarybchik#include <paths.h> 69299720Sarybchik#include <syslog.h> 70299720Sarybchik#include <assert.h> 71299720Sarybchik 72299720Sarybchik#ifdef NO_SETSID 73299720Sarybchik# include <fcntl.h> /* for O_RDONLY, etc */ 74299720Sarybchik#endif 75299720Sarybchik 76299720Sarybchik#ifndef USE_BFUNCS 77299720Sarybchik# include <memory.h> 78299720Sarybchik/* Yes, memcpy is OK here (no overlapped copies). */ 79299720Sarybchik# define bcopy(a,b,c) memcpy(b,a,c) 80299720Sarybchik# define bzero(p,l) memset(p,0,l) 81299720Sarybchik# define bcmp(a,b,c) memcmp(a,b,c) 82299720Sarybchik#endif 83299720Sarybchik 84299720Sarybchik#include "bootp.h" 85299720Sarybchik#include "hash.h" 86299720Sarybchik#include "hwaddr.h" 87301122Sarybchik#include "bootpd.h" 88310939Sarybchik#include "dovend.h" 89299720Sarybchik#include "getif.h" 90299720Sarybchik#include "readfile.h" 91299720Sarybchik#include "report.h" 92299720Sarybchik#include "tzone.h" 93299720Sarybchik#include "patchlevel.h" 94299720Sarybchik 95299720Sarybchik#ifndef CONFIG_FILE 96299720Sarybchik#define CONFIG_FILE "/etc/bootptab" 97299720Sarybchik#endif 98299720Sarybchik#ifndef DUMPTAB_FILE 99299720Sarybchik#define DUMPTAB_FILE "/tmp/bootpd.dump" 100299720Sarybchik#endif 101299720Sarybchik 102299720Sarybchik 103299720Sarybchik 104299720Sarybchik/* 105299720Sarybchik * Externals, forward declarations, and global variables 106299720Sarybchik */ 107299720Sarybchik 108299720Sarybchik#ifdef __STDC__ 109299720Sarybchik#define P(args) args 110299720Sarybchik#else 111299720Sarybchik#define P(args) () 112299720Sarybchik#endif 113299720Sarybchik 114299720Sarybchikextern void dumptab P((char *)); 115299720Sarybchik 116299720SarybchikPRIVATE void catcher P((int)); 117299720SarybchikPRIVATE int chk_access P((char *, int32 *)); 118299720Sarybchik#ifdef VEND_CMU 119299720SarybchikPRIVATE void dovend_cmu P((struct bootp *, struct host *)); 120299720Sarybchik#endif 121299720SarybchikPRIVATE void dovend_rfc1048 P((struct bootp *, struct host *, int32)); 122299720SarybchikPRIVATE void handle_reply P((void)); 123299720SarybchikPRIVATE void handle_request P((void)); 124299720SarybchikPRIVATE void sendreply P((int forward, int32 dest_override)); 125299720SarybchikPRIVATE void usage P((void)); 126299720Sarybchik 127299720Sarybchik#undef P 128299720Sarybchik 129299720Sarybchik/* 130299720Sarybchik * IP port numbers for client and server obtained from /etc/services 131299720Sarybchik */ 132299720Sarybchik 133299720Sarybchiku_short bootps_port, bootpc_port; 134299720Sarybchik 135299720Sarybchik 136299720Sarybchik/* 137299720Sarybchik * Internet socket and interface config structures 138299720Sarybchik */ 139299720Sarybchik 140299720Sarybchikstruct sockaddr_in bind_addr; /* Listening */ 141299720Sarybchikstruct sockaddr_in recv_addr; /* Packet source */ 142299720Sarybchikstruct sockaddr_in send_addr; /* destination */ 143299720Sarybchik 144299720Sarybchik 145299720Sarybchik/* 146299720Sarybchik * option defaults 147299720Sarybchik */ 148299720Sarybchikint debug = 0; /* Debugging flag (level) */ 149299720Sarybchikstruct timeval actualtimeout = 150299720Sarybchik{ /* fifteen minutes */ 151299720Sarybchik 15 * 60L, /* tv_sec */ 152299720Sarybchik 0 /* tv_usec */ 153299720Sarybchik}; 154299720Sarybchik 155299720Sarybchik/* 156299720Sarybchik * General 157299720Sarybchik */ 158299720Sarybchik 159299720Sarybchikint s; /* Socket file descriptor */ 160299720Sarybchikchar *pktbuf; /* Receive packet buffer */ 161299720Sarybchikint pktlen; 162299720Sarybchikchar *progname; 163299720Sarybchikchar *chdir_path; 164299720Sarybchikstruct in_addr my_ip_addr; 165299720Sarybchik 166299720Sarybchikstatic const char *hostname; 167299720Sarybchikstatic char default_hostname[MAXHOSTNAMELEN]; 168299720Sarybchik 169299720Sarybchik/* Flags set by signal catcher. */ 170299720SarybchikPRIVATE int do_readtab = 0; 171299720SarybchikPRIVATE int do_dumptab = 0; 172299720Sarybchik 173299720Sarybchik/* 174293887Sarybchik * Globals below are associated with the bootp database file (bootptab). 175299720Sarybchik */ 176299720Sarybchik 177299720Sarybchikchar *bootptab = CONFIG_FILE; 178299720Sarybchikchar *bootpd_dump = DUMPTAB_FILE; 179299720Sarybchik 180299720Sarybchik 181299720Sarybchik 182299720Sarybchik/* 183299720Sarybchik * Initialization such as command-line processing is done and then the 184299720Sarybchik * main server loop is started. 185299720Sarybchik */ 186299720Sarybchik 187299720Sarybchikint 188299720Sarybchikmain(argc, argv) 189299720Sarybchik int argc; 190299720Sarybchik char **argv; 191299720Sarybchik{ 192299720Sarybchik struct timeval *timeout; 193299720Sarybchik struct bootp *bp; 194299720Sarybchik struct servent *servp; 195299720Sarybchik struct hostent *hep; 196299720Sarybchik char *stmp; 197299720Sarybchik int n, ba_len, ra_len; 198299720Sarybchik int nfound, readfds; 199299720Sarybchik int standalone; 200299720Sarybchik#ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */ 201299720Sarybchik struct sigaction sa; 202299720Sarybchik#endif 203299720Sarybchik 204299720Sarybchik progname = strrchr(argv[0], '/'); 205299720Sarybchik if (progname) progname++; 206299720Sarybchik else progname = argv[0]; 207299720Sarybchik 208299720Sarybchik /* 209299720Sarybchik * Initialize logging. 210299720Sarybchik */ 211299720Sarybchik report_init(0); /* uses progname */ 212299720Sarybchik 213299720Sarybchik /* 214299720Sarybchik * Log startup 215299720Sarybchik */ 216299720Sarybchik report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL); 217299720Sarybchik 218299720Sarybchik /* Debugging for compilers with struct padding. */ 219299720Sarybchik assert(sizeof(struct bootp) == BP_MINPKTSZ); 220299720Sarybchik 221299720Sarybchik /* Get space for receiving packets and composing replies. */ 222299720Sarybchik pktbuf = malloc(MAX_MSG_SIZE); 223299720Sarybchik if (!pktbuf) { 224299720Sarybchik report(LOG_ERR, "malloc failed"); 225299720Sarybchik exit(1); 226299720Sarybchik } 227299720Sarybchik bp = (struct bootp *) pktbuf; 228299720Sarybchik 229299720Sarybchik /* 230299720Sarybchik * Check to see if a socket was passed to us from inetd. 231299720Sarybchik * 232299720Sarybchik * Use getsockname() to determine if descriptor 0 is indeed a socket 233299720Sarybchik * (and thus we are probably a child of inetd) or if it is instead 234299720Sarybchik * something else and we are running standalone. 235299720Sarybchik */ 236299720Sarybchik s = 0; 237299720Sarybchik ba_len = sizeof(bind_addr); 238299720Sarybchik bzero((char *) &bind_addr, ba_len); 239299720Sarybchik errno = 0; 240299720Sarybchik standalone = TRUE; 241300008Sarybchik if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) { 242300008Sarybchik /* 243300008Sarybchik * Descriptor 0 is a socket. Assume we are a child of inetd. 244300008Sarybchik */ 245300008Sarybchik if (bind_addr.sin_family == AF_INET) { 246299720Sarybchik standalone = FALSE; 247299720Sarybchik bootps_port = ntohs(bind_addr.sin_port); 248299720Sarybchik } else { 249299720Sarybchik /* Some other type of socket? */ 250299720Sarybchik report(LOG_ERR, "getsockname: not an INET socket"); 251299720Sarybchik } 252299720Sarybchik } 253299720Sarybchik 254299720Sarybchik /* 255299720Sarybchik * Set defaults that might be changed by option switches. 256299720Sarybchik */ 257299720Sarybchik stmp = NULL; 258299720Sarybchik timeout = &actualtimeout; 259299720Sarybchik 260299720Sarybchik if (gethostname(default_hostname, sizeof(default_hostname) - 1) < 0) { 261299720Sarybchik report(LOG_ERR, "bootpd: can't get hostname\n"); 262299720Sarybchik exit(1); 263299720Sarybchik } 264299720Sarybchik default_hostname[sizeof(default_hostname) - 1] = '\0'; 265299720Sarybchik hostname = default_hostname; 266299720Sarybchik 267299720Sarybchik /* 268299720Sarybchik * Read switches. 269299720Sarybchik */ 270299720Sarybchik for (argc--, argv++; argc > 0; argc--, argv++) { 271299720Sarybchik if (argv[0][0] != '-') 272299720Sarybchik break; 273299720Sarybchik switch (argv[0][1]) { 274299720Sarybchik 275299720Sarybchik case 'c': /* chdir_path */ 276311017Sarybchik if (argv[0][2]) { 277311017Sarybchik stmp = &(argv[0][2]); 278311017Sarybchik } else { 279311017Sarybchik argc--; 280311017Sarybchik argv++; 281311017Sarybchik stmp = argv[0]; 282299720Sarybchik } 283299720Sarybchik if (!stmp || (stmp[0] != '/')) { 284299720Sarybchik report(LOG_ERR, 285299720Sarybchik "bootpd: invalid chdir specification\n"); 286299720Sarybchik break; 287299720Sarybchik } 288299720Sarybchik chdir_path = stmp; 289299720Sarybchik break; 290299720Sarybchik 291299720Sarybchik case 'd': /* debug level */ 292299720Sarybchik if (argv[0][2]) { 293299720Sarybchik stmp = &(argv[0][2]); 294299720Sarybchik } else if (argv[1] && argv[1][0] == '-') { 295299720Sarybchik /* 296299720Sarybchik * Backwards-compatible behavior: 297299720Sarybchik * no parameter, so just increment the debug flag. 298299720Sarybchik */ 299299720Sarybchik debug++; 300299720Sarybchik break; 301299720Sarybchik } else { 302299720Sarybchik argc--; 303299720Sarybchik argv++; 304299720Sarybchik stmp = argv[0]; 305299720Sarybchik } 306310934Sarybchik if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 307310934Sarybchik report(LOG_ERR, 308310934Sarybchik "%s: invalid debug level\n", progname); 309310934Sarybchik break; 310310934Sarybchik } 311299720Sarybchik debug = n; 312299720Sarybchik break; 313299720Sarybchik 314299720Sarybchik case 'h': /* override hostname */ 315299720Sarybchik if (argv[0][2]) { 316299720Sarybchik stmp = &(argv[0][2]); 317299720Sarybchik } else { 318299720Sarybchik argc--; 319299720Sarybchik argv++; 320299720Sarybchik stmp = argv[0]; 321299720Sarybchik } 322299720Sarybchik if (!stmp) { 323299720Sarybchik report(LOG_ERR, 324299720Sarybchik "bootpd: missing hostname\n"); 325299720Sarybchik break; 326299720Sarybchik } 327299720Sarybchik hostname = stmp; 328299720Sarybchik break; 329299720Sarybchik 330299720Sarybchik case 'i': /* inetd mode */ 331299720Sarybchik standalone = FALSE; 332299720Sarybchik break; 333299720Sarybchik 334299720Sarybchik case 's': /* standalone mode */ 335299720Sarybchik standalone = TRUE; 336299720Sarybchik break; 337299720Sarybchik 338299720Sarybchik case 't': /* timeout */ 339299720Sarybchik if (argv[0][2]) { 340299720Sarybchik stmp = &(argv[0][2]); 341299720Sarybchik } else { 342299720Sarybchik argc--; 343299720Sarybchik argv++; 344299720Sarybchik stmp = argv[0]; 345299720Sarybchik } 346299720Sarybchik if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) { 347299720Sarybchik report(LOG_ERR, 348299720Sarybchik "%s: invalid timeout specification\n", progname); 349299720Sarybchik break; 350299720Sarybchik } 351299720Sarybchik actualtimeout.tv_sec = (int32) (60 * n); 352299720Sarybchik /* 353299720Sarybchik * If the actual timeout is zero, pass a NULL pointer 354299720Sarybchik * to select so it blocks indefinitely, otherwise, 355299720Sarybchik * point to the actual timeout value. 356299720Sarybchik */ 357299720Sarybchik timeout = (n > 0) ? &actualtimeout : NULL; 358299720Sarybchik break; 359299720Sarybchik 360299720Sarybchik default: 361299720Sarybchik report(LOG_ERR, "%s: unknown switch: -%c\n", 362299720Sarybchik progname, argv[0][1]); 363299720Sarybchik usage(); 364299720Sarybchik break; 365299720Sarybchik 366299720Sarybchik } /* switch */ 367299720Sarybchik } /* for args */ 368299720Sarybchik 369299720Sarybchik /* 370299720Sarybchik * Override default file names if specified on the command line. 371299720Sarybchik */ 372299720Sarybchik if (argc > 0) 373299720Sarybchik bootptab = argv[0]; 374299720Sarybchik 375299720Sarybchik if (argc > 1) 376299720Sarybchik bootpd_dump = argv[1]; 377299720Sarybchik 378299720Sarybchik /* 379299720Sarybchik * Get my hostname and IP address. 380299720Sarybchik */ 381299720Sarybchik 382299720Sarybchik hep = gethostbyname(hostname); 383299720Sarybchik if (!hep) { 384299720Sarybchik report(LOG_ERR, "Can not get my IP address\n"); 385299720Sarybchik exit(1); 386299720Sarybchik } 387311481Sarybchik bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr)); 388299720Sarybchik 389299720Sarybchik if (standalone) { 390299720Sarybchik /* 391299720Sarybchik * Go into background and disassociate from controlling terminal. 392299720Sarybchik */ 393299720Sarybchik if (debug < 3) { 394299720Sarybchik if (fork()) 395299720Sarybchik exit(0); 396299720Sarybchik#ifdef NO_SETSID 397299720Sarybchik setpgrp(0,0); 398299720Sarybchik#ifdef TIOCNOTTY 399299720Sarybchik n = open(_PATH_TTY, O_RDWR); 400299720Sarybchik if (n >= 0) { 401299720Sarybchik ioctl(n, TIOCNOTTY, (char *) 0); 402299720Sarybchik (void) close(n); 403299720Sarybchik } 404299720Sarybchik#endif /* TIOCNOTTY */ 405299720Sarybchik#else /* SETSID */ 406299720Sarybchik if (setsid() < 0) 407299720Sarybchik perror("setsid"); 408299720Sarybchik#endif /* SETSID */ 409299720Sarybchik } /* if debug < 3 */ 410299720Sarybchik 411299720Sarybchik /* 412299720Sarybchik * Nuke any timeout value 413299720Sarybchik */ 414299720Sarybchik timeout = NULL; 415299720Sarybchik 416299720Sarybchik } /* if standalone (1st) */ 417299720Sarybchik 418299720Sarybchik /* Set the cwd (i.e. to /tftpboot) */ 419299720Sarybchik if (chdir_path) { 420299720Sarybchik if (chdir(chdir_path) < 0) 421299720Sarybchik report(LOG_ERR, "%s: chdir failed", chdir_path); 422299720Sarybchik } 423299720Sarybchik 424299720Sarybchik /* Get the timezone. */ 425299720Sarybchik tzone_init(); 426299720Sarybchik 427299720Sarybchik /* Allocate hash tables. */ 428299720Sarybchik rdtab_init(); 429299720Sarybchik 430299720Sarybchik /* 431299720Sarybchik * Read the bootptab file. 432299720Sarybchik */ 433299720Sarybchik readtab(1); /* force read */ 434299720Sarybchik 435299720Sarybchik if (standalone) { 436299720Sarybchik 437299720Sarybchik /* 438299720Sarybchik * Create a socket. 439299720Sarybchik */ 440299720Sarybchik if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 441299720Sarybchik report(LOG_ERR, "socket: %s", get_network_errmsg()); 442299720Sarybchik exit(1); 443299720Sarybchik } 444299720Sarybchik 445299720Sarybchik /* 446299720Sarybchik * Get server's listening port number 447299720Sarybchik */ 448299720Sarybchik servp = getservbyname("bootps", "udp"); 449299720Sarybchik if (servp) { 450299720Sarybchik bootps_port = ntohs((u_short) servp->s_port); 451299720Sarybchik } else { 452299720Sarybchik bootps_port = (u_short) IPPORT_BOOTPS; 453299720Sarybchik report(LOG_ERR, 454311481Sarybchik "udp/bootps: unknown service -- assuming port %d", 455299720Sarybchik bootps_port); 456299720Sarybchik } 457299720Sarybchik 458299720Sarybchik /* 459299720Sarybchik * Bind socket to BOOTPS port. 460299720Sarybchik */ 461299720Sarybchik bind_addr.sin_family = AF_INET; 462299720Sarybchik bind_addr.sin_addr.s_addr = INADDR_ANY; 463299720Sarybchik bind_addr.sin_port = htons(bootps_port); 464299720Sarybchik if (bind(s, (struct sockaddr *) &bind_addr, 465299720Sarybchik sizeof(bind_addr)) < 0) 466299720Sarybchik { 467299720Sarybchik report(LOG_ERR, "bind: %s", get_network_errmsg()); 468299720Sarybchik exit(1); 469299720Sarybchik } 470299720Sarybchik } /* if standalone (2nd)*/ 471299720Sarybchik 472299720Sarybchik /* 473299720Sarybchik * Get destination port number so we can reply to client 474299720Sarybchik */ 475299720Sarybchik servp = getservbyname("bootpc", "udp"); 476299720Sarybchik if (servp) { 477299720Sarybchik bootpc_port = ntohs(servp->s_port); 478299720Sarybchik } else { 479299720Sarybchik report(LOG_ERR, 480299720Sarybchik "udp/bootpc: unknown service -- assuming port %d", 481299720Sarybchik IPPORT_BOOTPC); 482299720Sarybchik bootpc_port = (u_short) IPPORT_BOOTPC; 483299720Sarybchik } 484299720Sarybchik 485299720Sarybchik /* 486299720Sarybchik * Set up signals to read or dump the table. 487299720Sarybchik */ 488299720Sarybchik#ifdef SA_NOCLDSTOP /* Have POSIX sigaction(2). */ 489299720Sarybchik sa.sa_handler = catcher; 490299720Sarybchik sigemptyset(&sa.sa_mask); 491299720Sarybchik sa.sa_flags = 0; 492299720Sarybchik if (sigaction(SIGHUP, &sa, NULL) < 0) { 493299720Sarybchik report(LOG_ERR, "sigaction: %s", get_errmsg()); 494299720Sarybchik exit(1); 495299720Sarybchik } 496299720Sarybchik if (sigaction(SIGUSR1, &sa, NULL) < 0) { 497299720Sarybchik report(LOG_ERR, "sigaction: %s", get_errmsg()); 498299720Sarybchik exit(1); 499299720Sarybchik } 500299720Sarybchik#else /* SA_NOCLDSTOP */ 501299720Sarybchik /* Old-fashioned UNIX signals */ 502299720Sarybchik if ((int) signal(SIGHUP, catcher) < 0) { 503299720Sarybchik report(LOG_ERR, "signal: %s", get_errmsg()); 504299720Sarybchik exit(1); 505299720Sarybchik } 506299720Sarybchik if ((int) signal(SIGUSR1, catcher) < 0) { 507299720Sarybchik report(LOG_ERR, "signal: %s", get_errmsg()); 508299720Sarybchik exit(1); 509299720Sarybchik } 510299720Sarybchik#endif /* SA_NOCLDSTOP */ 511299720Sarybchik 512299720Sarybchik /* 513299720Sarybchik * Process incoming requests. 514299720Sarybchik */ 515299720Sarybchik for (;;) { 516299720Sarybchik struct timeval tv; 517299720Sarybchik 518299720Sarybchik readfds = 1 << s; 519299720Sarybchik if (timeout) 520299720Sarybchik tv = *timeout; 521299720Sarybchik 522299720Sarybchik nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL, 523299720Sarybchik (timeout) ? &tv : NULL); 524299720Sarybchik if (nfound < 0) { 525299720Sarybchik if (errno != EINTR) { 526299720Sarybchik report(LOG_ERR, "select: %s", get_errmsg()); 527299720Sarybchik } 528299720Sarybchik /* 529299720Sarybchik * Call readtab() or dumptab() here to avoid the 530299720Sarybchik * dangers of doing I/O from a signal handler. 531299720Sarybchik */ 532299720Sarybchik if (do_readtab) { 533299720Sarybchik do_readtab = 0; 534299720Sarybchik readtab(1); /* force read */ 535299720Sarybchik } 536299720Sarybchik if (do_dumptab) { 537299720Sarybchik do_dumptab = 0; 538299720Sarybchik dumptab(bootpd_dump); 539299720Sarybchik } 540299720Sarybchik continue; 541299720Sarybchik } 542299720Sarybchik if (!(readfds & (1 << s))) { 543299720Sarybchik if (debug > 1) 544299720Sarybchik report(LOG_INFO, "exiting after %ld minutes of inactivity", 545299720Sarybchik actualtimeout.tv_sec / 60); 546299720Sarybchik exit(0); 547299720Sarybchik } 548299720Sarybchik ra_len = sizeof(recv_addr); 549299720Sarybchik n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0, 550299720Sarybchik (struct sockaddr *) &recv_addr, &ra_len); 551299720Sarybchik if (n <= 0) { 552299720Sarybchik continue; 553299720Sarybchik } 554299720Sarybchik if (debug > 1) { 555299720Sarybchik report(LOG_INFO, "recvd pkt from IP addr %s", 556299720Sarybchik inet_ntoa(recv_addr.sin_addr)); 557299720Sarybchik } 558299720Sarybchik if (n < sizeof(struct bootp)) { 559299720Sarybchik if (debug) { 560299720Sarybchik report(LOG_NOTICE, "received short packet"); 561299720Sarybchik } 562299720Sarybchik continue; 563299720Sarybchik } 564299720Sarybchik pktlen = n; 565299720Sarybchik 566299720Sarybchik readtab(0); /* maybe re-read bootptab */ 567299720Sarybchik 568299720Sarybchik switch (bp->bp_op) { 569299720Sarybchik case BOOTREQUEST: 570299720Sarybchik handle_request(); 571299720Sarybchik break; 572299720Sarybchik case BOOTREPLY: 573299720Sarybchik handle_reply(); 574299720Sarybchik break; 575299720Sarybchik } 576299720Sarybchik } 577299720Sarybchik return 0; 578299720Sarybchik} 579299720Sarybchik 580299720Sarybchik 581299720Sarybchik 582299720Sarybchik 583299720Sarybchik/* 584299720Sarybchik * Print "usage" message and exit 585299720Sarybchik */ 586299720Sarybchik 587299720SarybchikPRIVATE void 588299720Sarybchikusage() 589299720Sarybchik{ 590299720Sarybchik fprintf(stderr, 591299720Sarybchik "usage: bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n"); 592299720Sarybchik fprintf(stderr, "\t -c n\tset current directory\n"); 593299720Sarybchik fprintf(stderr, "\t -d n\tset debug level\n"); 594299720Sarybchik fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n"); 595299720Sarybchik fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n"); 596299720Sarybchik fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n"); 597299720Sarybchik exit(1); 598299720Sarybchik} 599299720Sarybchik 600299720Sarybchik/* Signal catchers */ 601299720SarybchikPRIVATE void 602299720Sarybchikcatcher(sig) 603299720Sarybchik int sig; 604299720Sarybchik{ 605299720Sarybchik if (sig == SIGHUP) 606299720Sarybchik do_readtab = 1; 607299720Sarybchik if (sig == SIGUSR1) 608299720Sarybchik do_dumptab = 1; 609299720Sarybchik#if !defined(SA_NOCLDSTOP) && defined(SYSV) 610299720Sarybchik /* For older "System V" derivatives with no sigaction(). */ 611310944Sarybchik signal(sig, catcher); 612299720Sarybchik#endif 613310944Sarybchik} 614310944Sarybchik 615310944Sarybchik 616310944Sarybchik 617310944Sarybchik/* 618310944Sarybchik * Process BOOTREQUEST packet. 619310944Sarybchik * 620310944Sarybchik * Note: This version of the bootpd.c server never forwards 621310944Sarybchik * a request to another server. That is the job of a gateway 622310944Sarybchik * program such as the "bootpgw" program included here. 623310944Sarybchik * 624310944Sarybchik * (Also this version does not interpret the hostname field of 625310944Sarybchik * the request packet; it COULD do a name->address lookup and 626310944Sarybchik * forward the request there.) 627310944Sarybchik */ 628310944SarybchikPRIVATE void 629310944Sarybchikhandle_request() 630310944Sarybchik{ 631310944Sarybchik struct bootp *bp = (struct bootp *) pktbuf; 632310944Sarybchik struct host *hp = NULL; 633310944Sarybchik struct host dummyhost; 634310944Sarybchik int32 bootsize = 0; 635310944Sarybchik unsigned hlen, hashcode; 636310944Sarybchik int32 dest; 637310944Sarybchik char realpath[1024]; 638310944Sarybchik char *clntpath; 639310944Sarybchik char *homedir, *bootfile; 640299720Sarybchik int n; 641299720Sarybchik 642299720Sarybchik bp->bp_file[sizeof(bp->bp_file)-1] = '\0'; 643299720Sarybchik 644299720Sarybchik /* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */ 645299720Sarybchik 646299720Sarybchik /* 647299720Sarybchik * If the servername field is set, compare it against us. 648299720Sarybchik * If we're not being addressed, ignore this request. 649299720Sarybchik * If the server name field is null, throw in our name. 650299720Sarybchik */ 651299720Sarybchik if (strlen(bp->bp_sname)) { 652299720Sarybchik if (strcmp(bp->bp_sname, hostname)) { 653299720Sarybchik if (debug) 654299720Sarybchik report(LOG_INFO, "\ 655299720Sarybchikignoring request for server %s from client at %s address %s", 656299720Sarybchik bp->bp_sname, netname(bp->bp_htype), 657299720Sarybchik haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 658299720Sarybchik /* XXX - Is it correct to ignore such a request? -gwr */ 659299720Sarybchik return; 660299720Sarybchik } 661299720Sarybchik } else { 662299720Sarybchik strcpy(bp->bp_sname, hostname); 663299720Sarybchik } 664299720Sarybchik 665299720Sarybchik /* Convert the request into a reply. */ 666299720Sarybchik bp->bp_op = BOOTREPLY; 667299720Sarybchik if (bp->bp_ciaddr.s_addr == 0) { 668299720Sarybchik /* 669299720Sarybchik * client doesnt know his IP address, 670299720Sarybchik * search by hardware address. 671299720Sarybchik */ 672299720Sarybchik if (debug > 1) { 673299720Sarybchik report(LOG_INFO, "request from %s address %s", 674299720Sarybchik netname(bp->bp_htype), 675299720Sarybchik haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 676299720Sarybchik } 677299720Sarybchik hlen = haddrlength(bp->bp_htype); 678299720Sarybchik if (hlen != bp->bp_hlen) { 679299720Sarybchik report(LOG_NOTICE, "bad addr len from from %s address %s", 680299720Sarybchik netname(bp->bp_htype), 681299720Sarybchik haddrtoa(bp->bp_chaddr, hlen)); 682299720Sarybchik } 683299720Sarybchik dummyhost.htype = bp->bp_htype; 684299720Sarybchik bcopy(bp->bp_chaddr, dummyhost.haddr, hlen); 685299720Sarybchik hashcode = hash_HashFunction(bp->bp_chaddr, hlen); 686299720Sarybchik hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp, 687299720Sarybchik &dummyhost); 688299720Sarybchik if (hp == NULL && 689299720Sarybchik bp->bp_htype == HTYPE_IEEE802) 690299720Sarybchik { 691299720Sarybchik /* Try again with address in "canonical" form. */ 692299720Sarybchik haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen); 693299720Sarybchik if (debug > 1) { 694299720Sarybchik report(LOG_INFO, "\ 695299720SarybchikHW addr type is IEEE 802. convert to %s and check again\n", 696299720Sarybchik haddrtoa(dummyhost.haddr, bp->bp_hlen)); 697299720Sarybchik } 698299720Sarybchik hashcode = hash_HashFunction(dummyhost.haddr, hlen); 699299720Sarybchik hp = (struct host *) hash_Lookup(hwhashtable, hashcode, 700299720Sarybchik hwlookcmp, &dummyhost); 701299720Sarybchik } 702299720Sarybchik if (hp == NULL) { 703299720Sarybchik /* 704299720Sarybchik * XXX - Add dynamic IP address assignment? 705299720Sarybchik */ 706299720Sarybchik if (debug) 707299720Sarybchik report(LOG_NOTICE, "unknown client %s address %s", 708299720Sarybchik netname(bp->bp_htype), 709299720Sarybchik haddrtoa(bp->bp_chaddr, bp->bp_hlen)); 710299720Sarybchik return; /* not found */ 711299720Sarybchik } 712299720Sarybchik (bp->bp_yiaddr).s_addr = hp->iaddr.s_addr; 713299720Sarybchik 714299720Sarybchik } else { 715299720Sarybchik 716299720Sarybchik /* 717299720Sarybchik * search by IP address. 718299720Sarybchik */ 719299720Sarybchik if (debug > 1) { 720299720Sarybchik report(LOG_INFO, "request from IP addr %s", 721299720Sarybchik inet_ntoa(bp->bp_ciaddr)); 722299720Sarybchik } 723299720Sarybchik dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr; 724299720Sarybchik hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4); 725299720Sarybchik hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp, 726299720Sarybchik &dummyhost); 727299720Sarybchik if (hp == NULL) { 728299720Sarybchik if (debug) { 729299720Sarybchik report(LOG_NOTICE, "IP address not found: %s", 730299720Sarybchik inet_ntoa(bp->bp_ciaddr)); 731299720Sarybchik } 732299720Sarybchik return; 733299720Sarybchik } 734299720Sarybchik } 735299720Sarybchik 736299720Sarybchik if (debug) { 737299720Sarybchik report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr), 738299720Sarybchik hp->hostname->string); 739299720Sarybchik } 740299720Sarybchik 741299720Sarybchik /* 742299720Sarybchik * If there is a response delay threshold, ignore requests 743299720Sarybchik * with a timestamp lower than the threshold. 744299720Sarybchik */ 745299720Sarybchik if (hp->flags.min_wait) { 746299720Sarybchik u_int32 t = (u_int32) ntohs(bp->bp_secs); 747299720Sarybchik if (t < hp->min_wait) { 748299720Sarybchik if (debug > 1) 749299720Sarybchik report(LOG_INFO, 750299720Sarybchik "ignoring request due to timestamp (%d < %d)", 751299720Sarybchik t, hp->min_wait); 752299720Sarybchik return; 753299720Sarybchik } 754299720Sarybchik } 755299720Sarybchik 756299720Sarybchik#ifdef YORK_EX_OPTION 757299720Sarybchik /* 758299720Sarybchik * The need for the "ex" tag arose out of the need to empty 759299720Sarybchik * shared networked drives on diskless PCs. This solution is 760299720Sarybchik * not very clean but it does work fairly well. 761299720Sarybchik * Written by Edmund J. Sutcliffe <edmund@york.ac.uk> 762299720Sarybchik * 763299720Sarybchik * XXX - This could compromise security if a non-trusted user 764299720Sarybchik * managed to write an entry in the bootptab with :ex=trojan: 765299720Sarybchik * so I would leave this turned off unless you need it. -gwr 766299720Sarybchik */ 767299720Sarybchik /* Run a program, passing the client name as a parameter. */ 768299720Sarybchik if (hp->flags.exec_file) { 769299720Sarybchik char tst[100]; 770299720Sarybchik /* XXX - Check string lengths? -gwr */ 771299720Sarybchik strcpy (tst, hp->exec_file->string); 772299720Sarybchik strcat (tst, " "); 773299720Sarybchik strcat (tst, hp->hostname->string); 774299720Sarybchik strcat (tst, " &"); 775299720Sarybchik if (debug) 776299720Sarybchik report(LOG_INFO, "executing %s", tst); 777299720Sarybchik system(tst); /* Hope this finishes soon... */ 778299720Sarybchik } 779299720Sarybchik#endif /* YORK_EX_OPTION */ 780299720Sarybchik 781299720Sarybchik /* 782299720Sarybchik * If a specific TFTP server address was specified in the bootptab file, 783299720Sarybchik * fill it in, otherwise zero it. 784299720Sarybchik * XXX - Rather than zero it, should it be the bootpd address? -gwr 785299720Sarybchik */ 786299720Sarybchik (bp->bp_siaddr).s_addr = (hp->flags.bootserver) ? 787299720Sarybchik hp->bootserver.s_addr : 0L; 788299720Sarybchik 789299720Sarybchik#ifdef STANFORD_PROM_COMPAT 790299720Sarybchik /* 791299720Sarybchik * Stanford bootp PROMs (for a Sun?) have no way to leave 792299720Sarybchik * the boot file name field blank (because the boot file 793299720Sarybchik * name is automatically generated from some index). 794299720Sarybchik * As a work-around, this little hack allows those PROMs to 795299720Sarybchik * specify "sunboot14" with the same effect as a NULL name. 796299720Sarybchik * (The user specifies boot device 14 or some such magic.) 797299720Sarybchik */ 798299720Sarybchik if (strcmp(bp->bp_file, "sunboot14") == 0) 799299720Sarybchik bp->bp_file[0] = '\0'; /* treat it as unspecified */ 800299720Sarybchik#endif 801299720Sarybchik 802299720Sarybchik /* 803299720Sarybchik * Fill in the client's proper bootfile. 804299720Sarybchik * 805299720Sarybchik * If the client specifies an absolute path, try that file with a 806299720Sarybchik * ".host" suffix and then without. If the file cannot be found, no 807299720Sarybchik * reply is made at all. 808299720Sarybchik * 809299720Sarybchik * If the client specifies a null or relative file, use the following 810299720Sarybchik * table to determine the appropriate action: 811299720Sarybchik * 812299720Sarybchik * Homedir Bootfile Client's file 813299720Sarybchik * specified? specified? specification Action 814299720Sarybchik * ------------------------------------------------------------------- 815299720Sarybchik * No No Null Send null filename 816299720Sarybchik * No No Relative Discard request 817299720Sarybchik * No Yes Null Send if absolute else null 818299720Sarybchik * No Yes Relative Discard request *XXX 819299720Sarybchik * Yes No Null Send null filename 820299720Sarybchik * Yes No Relative Lookup with ".host" 821299720Sarybchik * Yes Yes Null Send home/boot or bootfile 822299720Sarybchik * Yes Yes Relative Lookup with ".host" *XXX 823299720Sarybchik * 824299720Sarybchik */ 825299720Sarybchik 826299720Sarybchik /* 827299720Sarybchik * XXX - I don't like the policy of ignoring a client when the 828299720Sarybchik * boot file is not accessible. The TFTP server might not be 829299720Sarybchik * running on the same machine as the BOOTP server, in which 830299720Sarybchik * case checking accessibility of the boot file is pointless. 831299720Sarybchik * 832299720Sarybchik * Therefore, file accessibility is now demanded ONLY if you 833299720Sarybchik * define CHECK_FILE_ACCESS in the Makefile options. -gwr 834299720Sarybchik */ 835299720Sarybchik 836299720Sarybchik /* 837299720Sarybchik * The "real" path is as seen by the BOOTP daemon on this 838299720Sarybchik * machine, while the client path is relative to the TFTP 839299720Sarybchik * daemon chroot directory (i.e. /tftpboot). 840299720Sarybchik */ 841299720Sarybchik if (hp->flags.tftpdir) { 842299720Sarybchik snprintf(realpath, sizeof(realpath), "%s", hp->tftpdir->string); 843299720Sarybchik clntpath = &realpath[strlen(realpath)]; 844299720Sarybchik } else { 845299720Sarybchik realpath[0] = '\0'; 846299720Sarybchik clntpath = realpath; 847299720Sarybchik } 848299720Sarybchik 849299720Sarybchik /* 850299720Sarybchik * Determine client's requested homedir and bootfile. 851299720Sarybchik */ 852299720Sarybchik homedir = NULL; 853299720Sarybchik bootfile = NULL; 854299720Sarybchik if (bp->bp_file[0]) { 855299720Sarybchik homedir = bp->bp_file; 856299720Sarybchik bootfile = strrchr(homedir, '/'); 857299720Sarybchik if (bootfile) { 858299720Sarybchik if (homedir == bootfile) 859299720Sarybchik homedir = NULL; 860299720Sarybchik *bootfile++ = '\0'; 861299720Sarybchik } else { 862299720Sarybchik /* no "/" in the string */ 863299720Sarybchik bootfile = homedir; 864299720Sarybchik homedir = NULL; 865299720Sarybchik } 866299720Sarybchik if (debug > 2) { 867299720Sarybchik report(LOG_INFO, "requested path=\"%s\" file=\"%s\"", 868299720Sarybchik (homedir) ? homedir : "", 869299720Sarybchik (bootfile) ? bootfile : ""); 870299720Sarybchik } 871299720Sarybchik } 872299720Sarybchik 873299720Sarybchik /* 874299720Sarybchik * Specifications in bootptab override client requested values. 875299720Sarybchik */ 876299720Sarybchik if (hp->flags.homedir) 877299720Sarybchik homedir = hp->homedir->string; 878299720Sarybchik if (hp->flags.bootfile) 879299720Sarybchik bootfile = hp->bootfile->string; 880299720Sarybchik 881299720Sarybchik /* 882299720Sarybchik * Construct bootfile path. 883299720Sarybchik */ 884299720Sarybchik if (homedir) { 885299720Sarybchik if (homedir[0] != '/') 886299720Sarybchik strcat(clntpath, "/"); 887299720Sarybchik strcat(clntpath, homedir); 888299720Sarybchik homedir = NULL; 889299720Sarybchik } 890299720Sarybchik if (bootfile) { 891299720Sarybchik if (bootfile[0] != '/') 892299720Sarybchik strcat(clntpath, "/"); 893299720Sarybchik strcat(clntpath, bootfile); 894299720Sarybchik bootfile = NULL; 895299720Sarybchik } 896299720Sarybchik 897299720Sarybchik /* 898299720Sarybchik * First try to find the file with a ".host" suffix 899299720Sarybchik */ 900299720Sarybchik n = strlen(clntpath); 901299720Sarybchik strcat(clntpath, "."); 902299720Sarybchik strcat(clntpath, hp->hostname->string); 903299720Sarybchik if (chk_access(realpath, &bootsize) < 0) { 904299720Sarybchik clntpath[n] = 0; /* Try it without the suffix */ 905299720Sarybchik if (chk_access(realpath, &bootsize) < 0) { 906299720Sarybchik /* neither "file.host" nor "file" was found */ 907299720Sarybchik#ifdef CHECK_FILE_ACCESS 908299720Sarybchik 909299720Sarybchik if (bp->bp_file[0]) { 910299720Sarybchik /* 911299720Sarybchik * Client wanted specific file 912299720Sarybchik * and we didn't have it. 913299720Sarybchik */ 914299720Sarybchik report(LOG_NOTICE, 915299720Sarybchik "requested file not found: \"%s\"", clntpath); 916299720Sarybchik return; 917299720Sarybchik } 918299720Sarybchik /* 919299720Sarybchik * Client didn't ask for a specific file and we couldn't 920299720Sarybchik * access the default file, so just zero-out the bootfile 921299720Sarybchik * field in the packet and continue processing the reply. 922299720Sarybchik */ 923299720Sarybchik bzero(bp->bp_file, sizeof(bp->bp_file)); 924299720Sarybchik goto null_file_name; 925299720Sarybchik 926299720Sarybchik#else /* CHECK_FILE_ACCESS */ 927299720Sarybchik 928299720Sarybchik /* Complain only if boot file size was needed. */ 929299720Sarybchik if (hp->flags.bootsize_auto) { 930299720Sarybchik report(LOG_ERR, "can not determine size of file \"%s\"", 931299720Sarybchik clntpath); 932299720Sarybchik } 933299720Sarybchik 934299720Sarybchik#endif /* CHECK_FILE_ACCESS */ 935299720Sarybchik } 936299720Sarybchik } 937299720Sarybchik strncpy(bp->bp_file, clntpath, BP_FILE_LEN); 938299720Sarybchik if (debug > 2) 939299720Sarybchik report(LOG_INFO, "bootfile=\"%s\"", clntpath); 940299720Sarybchik 941299720Sarybchik#ifdef CHECK_FILE_ACCESS 942299720Sarybchiknull_file_name: 943299720Sarybchik#endif /* CHECK_FILE_ACCESS */ 944299720Sarybchik 945299720Sarybchik 946299720Sarybchik /* 947299720Sarybchik * Handle vendor options based on magic number. 948299720Sarybchik */ 949299720Sarybchik 950299720Sarybchik if (debug > 1) { 951299720Sarybchik report(LOG_INFO, "vendor magic field is %d.%d.%d.%d", 952299720Sarybchik (int) ((bp->bp_vend)[0]), 953299720Sarybchik (int) ((bp->bp_vend)[1]), 954299720Sarybchik (int) ((bp->bp_vend)[2]), 955299720Sarybchik (int) ((bp->bp_vend)[3])); 956299720Sarybchik } 957299720Sarybchik /* 958299720Sarybchik * If this host isn't set for automatic vendor info then copy the 959299720Sarybchik * specific cookie into the bootp packet, thus forcing a certain 960299720Sarybchik * reply format. Only force reply format if user specified it. 961299720Sarybchik */ 962299720Sarybchik if (hp->flags.vm_cookie) { 963299720Sarybchik /* Slam in the user specified magic number. */ 964299720Sarybchik bcopy(hp->vm_cookie, bp->bp_vend, 4); 965299720Sarybchik } 966299720Sarybchik /* 967299720Sarybchik * Figure out the format for the vendor-specific info. 968299720Sarybchik * Note that bp->bp_vend may have been set above. 969299720Sarybchik */ 970299720Sarybchik if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) { 971299720Sarybchik /* RFC1048 conformant bootp client */ 972299720Sarybchik dovend_rfc1048(bp, hp, bootsize); 973299720Sarybchik if (debug > 1) { 974299720Sarybchik report(LOG_INFO, "sending reply (with RFC1048 options)"); 975299720Sarybchik } 976299720Sarybchik } 977299720Sarybchik#ifdef VEND_CMU 978299720Sarybchik else if (!bcmp(bp->bp_vend, vm_cmu, 4)) { 979299720Sarybchik dovend_cmu(bp, hp); 980299720Sarybchik if (debug > 1) { 981299720Sarybchik report(LOG_INFO, "sending reply (with CMU options)"); 982299720Sarybchik } 983299720Sarybchik } 984299720Sarybchik#endif 985299720Sarybchik else { 986299720Sarybchik if (debug > 1) { 987299720Sarybchik report(LOG_INFO, "sending reply (with no options)"); 988310917Sarybchik } 989299720Sarybchik } 990299720Sarybchik 991299720Sarybchik dest = (hp->flags.reply_addr) ? 992310938Sarybchik hp->reply_addr.s_addr : 0L; 993299720Sarybchik 994299720Sarybchik /* not forwarded */ 995299720Sarybchik sendreply(0, dest); 996299720Sarybchik} 997299720Sarybchik 998299720Sarybchik 999299720Sarybchik/* 1000299720Sarybchik * Process BOOTREPLY packet. 1001299720Sarybchik */ 1002299720SarybchikPRIVATE void 1003299720Sarybchikhandle_reply() 1004299720Sarybchik{ 1005299720Sarybchik if (debug) { 1006299720Sarybchik report(LOG_INFO, "processing boot reply"); 1007299720Sarybchik } 1008299720Sarybchik /* forwarded, no destination override */ 1009299720Sarybchik sendreply(1, 0); 1010299720Sarybchik} 1011299720Sarybchik 1012299720Sarybchik 1013299720Sarybchik/* 1014299720Sarybchik * Send a reply packet to the client. 'forward' flag is set if we are 1015299720Sarybchik * not the originator of this reply packet. 1016299720Sarybchik */ 1017299720SarybchikPRIVATE void 1018299720Sarybchiksendreply(forward, dst_override) 1019299720Sarybchik int forward; 1020299720Sarybchik int32 dst_override; 1021299720Sarybchik{ 1022299720Sarybchik struct bootp *bp = (struct bootp *) pktbuf; 1023299720Sarybchik struct in_addr dst; 1024299720Sarybchik u_short port = bootpc_port; 1025299720Sarybchik unsigned char *ha; 1026299720Sarybchik int len, haf; 1027299720Sarybchik 1028299720Sarybchik /* 1029299720Sarybchik * XXX - Should honor bp_flags "broadcast" bit here. 1030299720Sarybchik * Temporary workaround: use the :ra=ADDR: option to 1031299720Sarybchik * set the reply address to the broadcast address. 1032299720Sarybchik */ 1033299720Sarybchik 1034299720Sarybchik /* 1035299720Sarybchik * If the destination address was specified explicitly 1036299720Sarybchik * (i.e. the broadcast address for HP compatiblity) 1037299720Sarybchik * then send the response to that address. Otherwise, 1038299720Sarybchik * act in accordance with RFC951: 1039299720Sarybchik * If the client IP address is specified, use that 1040299720Sarybchik * else if gateway IP address is specified, use that 1041299720Sarybchik * else make a temporary arp cache entry for the client's 1042299720Sarybchik * NEW IP/hardware address and use that. 1043299720Sarybchik */ 1044299720Sarybchik if (dst_override) { 1045299720Sarybchik dst.s_addr = dst_override; 1046299720Sarybchik if (debug > 1) { 1047299720Sarybchik report(LOG_INFO, "reply address override: %s", 1048299720Sarybchik inet_ntoa(dst)); 1049299720Sarybchik } 1050299720Sarybchik } else if (bp->bp_ciaddr.s_addr) { 1051299720Sarybchik dst = bp->bp_ciaddr; 1052299720Sarybchik } else if (bp->bp_giaddr.s_addr && forward == 0) { 1053299720Sarybchik dst = bp->bp_giaddr; 1054299720Sarybchik port = bootps_port; 1055299720Sarybchik if (debug > 1) { 1056299720Sarybchik report(LOG_INFO, "sending reply to gateway %s", 1057299720Sarybchik inet_ntoa(dst)); 1058299720Sarybchik } 1059299720Sarybchik } else { 1060299720Sarybchik dst = bp->bp_yiaddr; 1061299720Sarybchik ha = bp->bp_chaddr; 1062299720Sarybchik len = bp->bp_hlen; 1063299720Sarybchik if (len > MAXHADDRLEN) 1064299720Sarybchik len = MAXHADDRLEN; 1065299720Sarybchik haf = (int) bp->bp_htype; 1066299720Sarybchik if (haf == 0) 1067299720Sarybchik haf = HTYPE_ETHERNET; 1068299720Sarybchik 1069299720Sarybchik if (debug > 1) 1070299720Sarybchik report(LOG_INFO, "setarp %s - %s", 1071299720Sarybchik inet_ntoa(dst), haddrtoa(ha, len)); 1072293887Sarybchik setarp(s, &dst, haf, ha, len); 1073293887Sarybchik } 1074293887Sarybchik 1075293734Sarybchik if ((forward == 0) && 1076293887Sarybchik (bp->bp_siaddr.s_addr == 0)) 1077293887Sarybchik { 1078293887Sarybchik struct ifreq *ifr; 1079299904Sarybchik struct in_addr siaddr; 1080299904Sarybchik /* 1081293734Sarybchik * If we are originating this reply, we 1082293887Sarybchik * need to find our own interface address to 1083299904Sarybchik * put in the bp_siaddr field of the reply. 1084299904Sarybchik * If this server is multi-homed, pick the 1085299904Sarybchik * 'best' interface (the one on the same net 1086299904Sarybchik * as the client). Of course, the client may 1087299904Sarybchik * be on the other side of a BOOTP gateway... 1088293887Sarybchik */ 1089293887Sarybchik ifr = getif(s, &dst); 1090293887Sarybchik if (ifr) { 1091293887Sarybchik struct sockaddr_in *sip; 1092293887Sarybchik sip = (struct sockaddr_in *) &(ifr->ifr_addr); 1093293887Sarybchik siaddr = sip->sin_addr; 1094293887Sarybchik } else { 1095293887Sarybchik /* Just use my "official" IP address. */ 1096293887Sarybchik siaddr = my_ip_addr; 1097293887Sarybchik } 1098293887Sarybchik 1099293887Sarybchik /* XXX - No need to set bp_giaddr here. */ 1100300009Sarybchik 1101300009Sarybchik /* Finally, set the server address field. */ 1102293887Sarybchik bp->bp_siaddr = siaddr; 1103300009Sarybchik } 1104293887Sarybchik /* Set up socket address for send. */ 1105293887Sarybchik send_addr.sin_family = AF_INET; 1106293887Sarybchik send_addr.sin_port = htons(port); 1107293887Sarybchik send_addr.sin_addr = dst; 1108293887Sarybchik 1109293887Sarybchik /* Send reply with same size packet as request used. */ 1110293887Sarybchik if (sendto(s, pktbuf, pktlen, 0, 1111293887Sarybchik (struct sockaddr *) &send_addr, 1112293887Sarybchik sizeof(send_addr)) < 0) 1113293887Sarybchik { 1114293887Sarybchik report(LOG_ERR, "sendto: %s", get_network_errmsg()); 1115294079Sarybchik } 1116294079Sarybchik} /* sendreply */ 1117294079Sarybchik 1118294079Sarybchik 1119294079Sarybchik/* nmatch() - now in getif.c */ 1120293887Sarybchik/* setarp() - now in hwaddr.c */ 1121293887Sarybchik 1122293887Sarybchik 1123293887Sarybchik/* 1124293887Sarybchik * This call checks read access to a file. It returns 0 if the file given 1125293887Sarybchik * by "path" exists and is publically readable. A value of -1 is returned if 1126293887Sarybchik * access is not permitted or an error occurs. Successful calls also 1127293734Sarybchik * return the file size in bytes using the long pointer "filesize". 1128293734Sarybchik * 1129293734Sarybchik * The read permission bit for "other" users is checked. This bit must be 1130293734Sarybchik * set for tftpd(8) to allow clients to read the file. 1131293734Sarybchik */ 1132 1133PRIVATE int 1134chk_access(path, filesize) 1135 char *path; 1136 int32 *filesize; 1137{ 1138 struct stat st; 1139 1140 if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) { 1141 *filesize = (int32) st.st_size; 1142 return 0; 1143 } else { 1144 return -1; 1145 } 1146} 1147 1148 1149/* 1150 * Now in dumptab.c : 1151 * dumptab() 1152 * dump_host() 1153 * list_ipaddresses() 1154 */ 1155 1156#ifdef VEND_CMU 1157 1158/* 1159 * Insert the CMU "vendor" data for the host pointed to by "hp" into the 1160 * bootp packet pointed to by "bp". 1161 */ 1162 1163PRIVATE void 1164dovend_cmu(bp, hp) 1165 struct bootp *bp; 1166 struct host *hp; 1167{ 1168 struct cmu_vend *vendp; 1169 struct in_addr_list *taddr; 1170 1171 /* 1172 * Initialize the entire vendor field to zeroes. 1173 */ 1174 bzero(bp->bp_vend, sizeof(bp->bp_vend)); 1175 1176 /* 1177 * Fill in vendor information. Subnet mask, default gateway, 1178 * domain name server, ien name server, time server 1179 */ 1180 vendp = (struct cmu_vend *) bp->bp_vend; 1181 strcpy(vendp->v_magic, (char *)vm_cmu); 1182 if (hp->flags.subnet_mask) { 1183 (vendp->v_smask).s_addr = hp->subnet_mask.s_addr; 1184 (vendp->v_flags) |= VF_SMASK; 1185 if (hp->flags.gateway) { 1186 (vendp->v_dgate).s_addr = hp->gateway->addr->s_addr; 1187 } 1188 } 1189 if (hp->flags.domain_server) { 1190 taddr = hp->domain_server; 1191 if (taddr->addrcount > 0) { 1192 (vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr; 1193 if (taddr->addrcount > 1) { 1194 (vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr; 1195 } 1196 } 1197 } 1198 if (hp->flags.name_server) { 1199 taddr = hp->name_server; 1200 if (taddr->addrcount > 0) { 1201 (vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr; 1202 if (taddr->addrcount > 1) { 1203 (vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr; 1204 } 1205 } 1206 } 1207 if (hp->flags.time_server) { 1208 taddr = hp->time_server; 1209 if (taddr->addrcount > 0) { 1210 (vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr; 1211 if (taddr->addrcount > 1) { 1212 (vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr; 1213 } 1214 } 1215 } 1216 /* Log message now done by caller. */ 1217} /* dovend_cmu */ 1218 1219#endif /* VEND_CMU */ 1220 1221 1222 1223/* 1224 * Insert the RFC1048 vendor data for the host pointed to by "hp" into the 1225 * bootp packet pointed to by "bp". 1226 */ 1227#define NEED(LEN, MSG) do \ 1228 if (bytesleft < (LEN)) { \ 1229 report(LOG_NOTICE, noroom, \ 1230 hp->hostname->string, MSG); \ 1231 return; \ 1232 } while (0) 1233PRIVATE void 1234dovend_rfc1048(bp, hp, bootsize) 1235 struct bootp *bp; 1236 struct host *hp; 1237 int32 bootsize; 1238{ 1239 int bytesleft, len; 1240 byte *vp; 1241 1242 static char noroom[] = "%s: No room for \"%s\" option"; 1243 1244 vp = bp->bp_vend; 1245 1246 if (hp->flags.msg_size) { 1247 pktlen = hp->msg_size; 1248 } else { 1249 /* 1250 * If the request was longer than the official length, build 1251 * a response of that same length where the additional length 1252 * is assumed to be part of the bp_vend (options) area. 1253 */ 1254 if (pktlen > sizeof(*bp)) { 1255 if (debug > 1) 1256 report(LOG_INFO, "request message length=%d", pktlen); 1257 } 1258 /* 1259 * Check whether the request contains the option: 1260 * Maximum DHCP Message Size (RFC1533 sec. 9.8) 1261 * and if so, override the response length with its value. 1262 * This request must lie within the first BP_VEND_LEN 1263 * bytes of the option space. 1264 */ 1265 { 1266 byte *p, *ep; 1267 byte tag, len; 1268 short msgsz = 0; 1269 1270 p = vp + 4; 1271 ep = p + BP_VEND_LEN - 4; 1272 while (p < ep) { 1273 tag = *p++; 1274 /* Check for tags with no data first. */ 1275 if (tag == TAG_PAD) 1276 continue; 1277 if (tag == TAG_END) 1278 break; 1279 /* Now scan the length byte. */ 1280 len = *p++; 1281 switch (tag) { 1282 case TAG_MAX_MSGSZ: 1283 if (len == 2) { 1284 bcopy(p, (char*)&msgsz, 2); 1285 msgsz = ntohs(msgsz); 1286 } 1287 break; 1288 case TAG_SUBNET_MASK: 1289 /* XXX - Should preserve this if given... */ 1290 break; 1291 } /* swtich */ 1292 p += len; 1293 } 1294 1295 if (msgsz > sizeof(*bp) + BP_MSG_OVERHEAD) { 1296 if (debug > 1) 1297 report(LOG_INFO, "request has DHCP msglen=%d", msgsz); 1298 pktlen = msgsz - BP_MSG_OVERHEAD; 1299 } 1300 } 1301 } 1302 1303 if (pktlen < sizeof(*bp)) { 1304 report(LOG_ERR, "invalid response length=%d", pktlen); 1305 pktlen = sizeof(*bp); 1306 } 1307 bytesleft = ((byte*)bp + pktlen) - vp; 1308 if (pktlen > sizeof(*bp)) { 1309 if (debug > 1) 1310 report(LOG_INFO, "extended reply, length=%d, options=%d", 1311 pktlen, bytesleft); 1312 } 1313 1314 /* Copy in the magic cookie */ 1315 bcopy(vm_rfc1048, vp, 4); 1316 vp += 4; 1317 bytesleft -= 4; 1318 1319 if (hp->flags.subnet_mask) { 1320 /* always enough room here. */ 1321 *vp++ = TAG_SUBNET_MASK;/* -1 byte */ 1322 *vp++ = 4; /* -1 byte */ 1323 insert_u_long(hp->subnet_mask.s_addr, &vp); /* -4 bytes */ 1324 bytesleft -= 6; /* Fix real count */ 1325 if (hp->flags.gateway) { 1326 (void) insert_ip(TAG_GATEWAY, 1327 hp->gateway, 1328 &vp, &bytesleft); 1329 } 1330 } 1331 if (hp->flags.bootsize) { 1332 /* always enough room here */ 1333 bootsize = (hp->flags.bootsize_auto) ? 1334 ((bootsize + 511) / 512) : (hp->bootsize); /* Round up */ 1335 *vp++ = TAG_BOOT_SIZE; 1336 *vp++ = 2; 1337 *vp++ = (byte) ((bootsize >> 8) & 0xFF); 1338 *vp++ = (byte) (bootsize & 0xFF); 1339 bytesleft -= 4; /* Tag, length, and 16 bit blocksize */ 1340 } 1341 /* 1342 * This one is special: Remaining options go in the ext file. 1343 * Only the subnet_mask, bootsize, and gateway should precede. 1344 */ 1345 if (hp->flags.exten_file) { 1346 /* 1347 * Check for room for exten_file. Add 3 to account for 1348 * TAG_EXTEN_FILE, length, and TAG_END. 1349 */ 1350 len = strlen(hp->exten_file->string); 1351 NEED((len + 3), "ef"); 1352 *vp++ = TAG_EXTEN_FILE; 1353 *vp++ = (byte) (len & 0xFF); 1354 bcopy(hp->exten_file->string, vp, len); 1355 vp += len; 1356 *vp++ = TAG_END; 1357 bytesleft -= len + 3; 1358 return; /* no more options here. */ 1359 } 1360 /* 1361 * The remaining options are inserted by the following 1362 * function (which is shared with bootpef.c). 1363 * Keep back one byte for the TAG_END. 1364 */ 1365 len = dovend_rfc1497(hp, vp, bytesleft - 1); 1366 vp += len; 1367 bytesleft -= len; 1368 1369 /* There should be at least one byte left. */ 1370 NEED(1, "(end)"); 1371 *vp++ = TAG_END; 1372 bytesleft--; 1373 1374 /* Log message done by caller. */ 1375 if (bytesleft > 0) { 1376 /* 1377 * Zero out any remaining part of the vendor area. 1378 */ 1379 bzero(vp, bytesleft); 1380 } 1381} /* dovend_rfc1048 */ 1382#undef NEED 1383 1384 1385/* 1386 * Now in readfile.c: 1387 * hwlookcmp() 1388 * iplookcmp() 1389 */ 1390 1391/* haddrtoa() - now in hwaddr.c */ 1392/* 1393 * Now in dovend.c: 1394 * insert_ip() 1395 * insert_generic() 1396 * insert_u_long() 1397 */ 1398 1399/* get_errmsg() - now in report.c */ 1400 1401/* 1402 * Local Variables: 1403 * tab-width: 4 1404 * c-indent-level: 4 1405 * c-argdecl-indent: 4 1406 * c-continued-statement-offset: 4 1407 * c-continued-brace-offset: -4 1408 * c-label-offset: -4 1409 * c-brace-offset: 0 1410 * End: 1411 */ 1412