xref: /freebsd/libexec/bootpd/bootpd.c (revision 3c4ba5f55438f7afd4f4b0b56f88f2bb505fd6a6)
1 /************************************************************************
2           Copyright 1988, 1991 by Carnegie Mellon University
3 
4                           All Rights Reserved
5 
6 Permission to use, copy, modify, and distribute this software and its
7 documentation for any purpose and without fee is hereby granted, provided
8 that the above copyright notice appear in all copies and that both that
9 copyright notice and this permission notice appear in supporting
10 documentation, and that the name of Carnegie Mellon University not be used
11 in advertising or publicity pertaining to distribution of the software
12 without specific, written prior permission.
13 
14 CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
15 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
16 IN NO EVENT SHALL CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
17 DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
18 PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
19 ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20 SOFTWARE.
21 
22 ************************************************************************/
23 
24 /*
25  * BOOTP (bootstrap protocol) server daemon.
26  *
27  * Answers BOOTP request packets from booting client machines.
28  * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol.
29  * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions.
30  * See RFC 1395 for option tags 14-17.
31  * See accompanying man page -- bootpd.8
32  *
33  * HISTORY
34  *	See ./Changes
35  *
36  * BUGS
37  *	See ./ToDo
38  */
39 
40 #include <sys/cdefs.h>
41 __FBSDID("$FreeBSD$");
42 
43 #include <sys/types.h>
44 #include <sys/param.h>
45 #include <sys/socket.h>
46 #include <sys/ioctl.h>
47 #include <sys/file.h>
48 #include <sys/time.h>
49 #include <sys/stat.h>
50 #include <sys/utsname.h>
51 
52 #include <net/if.h>
53 #include <netinet/in.h>
54 #include <arpa/inet.h>	/* inet_ntoa */
55 
56 #ifndef	NO_UNISTD
57 #include <unistd.h>
58 #endif
59 
60 #include <stdlib.h>
61 #include <signal.h>
62 #include <stdio.h>
63 #include <string.h>
64 #include <errno.h>
65 #include <ctype.h>
66 #include <netdb.h>
67 #include <paths.h>
68 #include <syslog.h>
69 #include <assert.h>
70 #include <inttypes.h>
71 
72 #ifdef	NO_SETSID
73 # include <fcntl.h>		/* for O_RDONLY, etc */
74 #endif
75 
76 #include "bootp.h"
77 #include "hash.h"
78 #include "hwaddr.h"
79 #include "bootpd.h"
80 #include "dovend.h"
81 #include "getif.h"
82 #include "readfile.h"
83 #include "report.h"
84 #include "tzone.h"
85 #include "patchlevel.h"
86 
87 #ifndef CONFIG_FILE
88 #define CONFIG_FILE		"/etc/bootptab"
89 #endif
90 #ifndef DUMPTAB_FILE
91 #define DUMPTAB_FILE		"/tmp/bootpd.dump"
92 #endif
93 
94 
95 
96 /*
97  * Externals, forward declarations, and global variables
98  */
99 
100 extern void dumptab(char *);
101 
102 PRIVATE void catcher(int);
103 PRIVATE int chk_access(char *, int32 *);
104 #ifdef VEND_CMU
105 PRIVATE void dovend_cmu(struct bootp *, struct host *);
106 #endif
107 PRIVATE void dovend_rfc1048(struct bootp *, struct host *, int32);
108 PRIVATE void handle_reply(void);
109 PRIVATE void handle_request(void);
110 PRIVATE void sendreply(int forward, int32 dest_override);
111 PRIVATE void usage(void);
112 
113 /*
114  * IP port numbers for client and server obtained from /etc/services
115  */
116 
117 u_short bootps_port, bootpc_port;
118 
119 
120 /*
121  * Internet socket and interface config structures
122  */
123 
124 struct sockaddr_in bind_addr;	/* Listening */
125 struct sockaddr_in recv_addr;	/* Packet source */
126 struct sockaddr_in send_addr;	/*  destination */
127 
128 
129 /*
130  * option defaults
131  */
132 int debug = 0;					/* Debugging flag (level) */
133 struct timeval actualtimeout =
134 {								/* fifteen minutes */
135 	15 * 60L,					/* tv_sec */
136 	0							/* tv_usec */
137 };
138 int arpmod = TRUE;				/* modify the ARP table */
139 
140 /*
141  * General
142  */
143 
144 int s;							/* Socket file descriptor */
145 char *pktbuf;					/* Receive packet buffer */
146 int pktlen;
147 char *progname;
148 char *chdir_path;
149 struct in_addr my_ip_addr;
150 
151 static const char *hostname;
152 static char default_hostname[MAXHOSTNAMELEN];
153 
154 /* Flags set by signal catcher. */
155 PRIVATE int do_readtab = 0;
156 PRIVATE int do_dumptab = 0;
157 
158 /*
159  * Globals below are associated with the bootp database file (bootptab).
160  */
161 
162 char *bootptab = CONFIG_FILE;
163 char *bootpd_dump = DUMPTAB_FILE;
164 
165 
166 
167 /*
168  * Initialization such as command-line processing is done and then the
169  * main server loop is started.
170  */
171 
172 int
173 main(int argc, char **argv)
174 {
175 	struct timeval *timeout;
176 	struct bootp *bp;
177 	struct servent *servp;
178 	struct hostent *hep;
179 	char *stmp;
180 	socklen_t ba_len, ra_len;
181 	int n;
182 	int nfound;
183 	fd_set readfds;
184 	int standalone;
185 #ifdef	SA_NOCLDSTOP	/* Have POSIX sigaction(2). */
186 	struct sigaction sa;
187 #endif
188 
189 	progname = strrchr(argv[0], '/');
190 	if (progname) progname++;
191 	else progname = argv[0];
192 
193 	/*
194 	 * Initialize logging.
195 	 */
196 	report_init(0);				/* uses progname */
197 
198 	/*
199 	 * Log startup
200 	 */
201 	report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
202 
203 	/* Debugging for compilers with struct padding. */
204 	assert(sizeof(struct bootp) == BP_MINPKTSZ);
205 
206 	/* Get space for receiving packets and composing replies. */
207 	pktbuf = malloc(MAX_MSG_SIZE);
208 	if (!pktbuf) {
209 		report(LOG_ERR, "malloc failed");
210 		exit(1);
211 	}
212 	bp = (struct bootp *) pktbuf;
213 
214 	/*
215 	 * Check to see if a socket was passed to us from inetd.
216 	 *
217 	 * Use getsockname() to determine if descriptor 0 is indeed a socket
218 	 * (and thus we are probably a child of inetd) or if it is instead
219 	 * something else and we are running standalone.
220 	 */
221 	s = 0;
222 	ba_len = sizeof(bind_addr);
223 	bzero((char *) &bind_addr, ba_len);
224 	errno = 0;
225 	standalone = TRUE;
226 	if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
227 		/*
228 		 * Descriptor 0 is a socket.  Assume we are a child of inetd.
229 		 */
230 		if (bind_addr.sin_family == AF_INET) {
231 			standalone = FALSE;
232 			bootps_port = ntohs(bind_addr.sin_port);
233 		} else {
234 			/* Some other type of socket? */
235 			report(LOG_ERR, "getsockname: not an INET socket");
236 		}
237 	}
238 
239 	/*
240 	 * Set defaults that might be changed by option switches.
241 	 */
242 	stmp = NULL;
243 	timeout = &actualtimeout;
244 
245 	if (gethostname(default_hostname, sizeof(default_hostname) - 1) < 0) {
246 		report(LOG_ERR, "bootpd: can't get hostname\n");
247 		exit(1);
248 	}
249 	default_hostname[sizeof(default_hostname) - 1] = '\0';
250 	hostname = default_hostname;
251 
252 	/*
253 	 * Read switches.
254 	 */
255 	for (argc--, argv++; argc > 0; argc--, argv++) {
256 		if (argv[0][0] != '-')
257 			break;
258 		switch (argv[0][1]) {
259 
260 		case 'a':				/* don't modify the ARP table */
261 			arpmod = FALSE;
262 			break;
263 		case 'c':				/* chdir_path */
264 			if (argv[0][2]) {
265 				stmp = &(argv[0][2]);
266 			} else {
267 				argc--;
268 				argv++;
269 				stmp = argv[0];
270 			}
271 			if (!stmp || (stmp[0] != '/')) {
272 				report(LOG_ERR,
273 						"bootpd: invalid chdir specification\n");
274 				break;
275 			}
276 			chdir_path = stmp;
277 			break;
278 
279 		case 'd':				/* debug level */
280 			if (argv[0][2]) {
281 				stmp = &(argv[0][2]);
282 			} else if (argv[1] && argv[1][0] == '-') {
283 				/*
284 				 * Backwards-compatible behavior:
285 				 * no parameter, so just increment the debug flag.
286 				 */
287 				debug++;
288 				break;
289 			} else {
290 				argc--;
291 				argv++;
292 				stmp = argv[0];
293 			}
294 			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
295 				report(LOG_ERR,
296 						"%s: invalid debug level\n", progname);
297 				break;
298 			}
299 			debug = n;
300 			break;
301 
302 		case 'h':				/* override hostname */
303 			if (argv[0][2]) {
304 				stmp = &(argv[0][2]);
305 			} else {
306 				argc--;
307 				argv++;
308 				stmp = argv[0];
309 			}
310 			if (!stmp) {
311 				report(LOG_ERR,
312 						"bootpd: missing hostname\n");
313 				break;
314 			}
315 			hostname = stmp;
316 			break;
317 
318 		case 'i':				/* inetd mode */
319 			standalone = FALSE;
320 			break;
321 
322 		case 's':				/* standalone mode */
323 			standalone = TRUE;
324 			break;
325 
326 		case 't':				/* timeout */
327 			if (argv[0][2]) {
328 				stmp = &(argv[0][2]);
329 			} else {
330 				argc--;
331 				argv++;
332 				stmp = argv[0];
333 			}
334 			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
335 				report(LOG_ERR,
336 						"%s: invalid timeout specification\n", progname);
337 				break;
338 			}
339 			actualtimeout.tv_sec = (int32) (60 * n);
340 			/*
341 			 * If the actual timeout is zero, pass a NULL pointer
342 			 * to select so it blocks indefinitely, otherwise,
343 			 * point to the actual timeout value.
344 			 */
345 			timeout = (n > 0) ? &actualtimeout : NULL;
346 			break;
347 
348 		default:
349 			report(LOG_ERR, "%s: unknown switch: -%c\n",
350 					progname, argv[0][1]);
351 			usage();
352 			break;
353 
354 		} /* switch */
355 	} /* for args */
356 
357 	/*
358 	 * Override default file names if specified on the command line.
359 	 */
360 	if (argc > 0)
361 		bootptab = argv[0];
362 
363 	if (argc > 1)
364 		bootpd_dump = argv[1];
365 
366 	/*
367 	 * Get my hostname and IP address.
368 	 */
369 
370 	hep = gethostbyname(hostname);
371 	if (!hep) {
372 		report(LOG_ERR, "Can not get my IP address\n");
373 		exit(1);
374 	}
375 	bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
376 
377 	if (standalone) {
378 		/*
379 		 * Go into background and disassociate from controlling terminal.
380 		 */
381 		if (debug < 3) {
382 			if (fork())
383 				exit(0);
384 #ifdef	NO_SETSID
385 			setpgrp(0,0);
386 #ifdef TIOCNOTTY
387 			n = open(_PATH_TTY, O_RDWR);
388 			if (n >= 0) {
389 				ioctl(n, TIOCNOTTY, (char *) 0);
390 				(void) close(n);
391 			}
392 #endif	/* TIOCNOTTY */
393 #else	/* SETSID */
394 			if (setsid() < 0)
395 				perror("setsid");
396 #endif	/* SETSID */
397 		} /* if debug < 3 */
398 
399 		/*
400 		 * Nuke any timeout value
401 		 */
402 		timeout = NULL;
403 
404 	} /* if standalone (1st) */
405 
406 	/* Set the cwd (i.e. to /tftpboot) */
407 	if (chdir_path) {
408 		if (chdir(chdir_path) < 0)
409 			report(LOG_ERR, "%s: chdir failed", chdir_path);
410 	}
411 
412 	/* Get the timezone. */
413 	tzone_init();
414 
415 	/* Allocate hash tables. */
416 	rdtab_init();
417 
418 	/*
419 	 * Read the bootptab file.
420 	 */
421 	readtab(1);					/* force read */
422 
423 	if (standalone) {
424 
425 		/*
426 		 * Create a socket.
427 		 */
428 		if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
429 			report(LOG_ERR, "socket: %s", get_network_errmsg());
430 			exit(1);
431 		}
432 
433 		/*
434 		 * Get server's listening port number
435 		 */
436 		servp = getservbyname("bootps", "udp");
437 		if (servp) {
438 			bootps_port = ntohs((u_short) servp->s_port);
439 		} else {
440 			bootps_port = (u_short) IPPORT_BOOTPS;
441 			report(LOG_ERR,
442 				"bootps/udp: unknown service -- using port %d",
443 				   bootps_port);
444 		}
445 
446 		/*
447 		 * Bind socket to BOOTPS port.
448 		 */
449 		bind_addr.sin_family = AF_INET;
450 		bind_addr.sin_addr.s_addr = INADDR_ANY;
451 		bind_addr.sin_port = htons(bootps_port);
452 		if (bind(s, (struct sockaddr *) &bind_addr,
453 				 sizeof(bind_addr)) < 0)
454 		{
455 			report(LOG_ERR, "bind: %s", get_network_errmsg());
456 			exit(1);
457 		}
458 	} /* if standalone (2nd)*/
459 
460 	/*
461 	 * Get destination port number so we can reply to client
462 	 */
463 	servp = getservbyname("bootpc", "udp");
464 	if (servp) {
465 		bootpc_port = ntohs(servp->s_port);
466 	} else {
467 		report(LOG_ERR,
468 			   "bootpc/udp: unknown service -- using port %d",
469 			   IPPORT_BOOTPC);
470 		bootpc_port = (u_short) IPPORT_BOOTPC;
471 	}
472 
473 	/*
474 	 * Set up signals to read or dump the table.
475 	 */
476 #ifdef	SA_NOCLDSTOP	/* Have POSIX sigaction(2). */
477 	sa.sa_handler = catcher;
478 	sigemptyset(&sa.sa_mask);
479 	sa.sa_flags = 0;
480 	if (sigaction(SIGHUP, &sa, NULL) < 0) {
481 		report(LOG_ERR, "sigaction: %s", get_errmsg());
482 		exit(1);
483 	}
484 	if (sigaction(SIGUSR1, &sa, NULL) < 0) {
485 		report(LOG_ERR, "sigaction: %s", get_errmsg());
486 		exit(1);
487 	}
488 #else	/* SA_NOCLDSTOP */
489 	/* Old-fashioned UNIX signals */
490 	if ((int) signal(SIGHUP, catcher) < 0) {
491 		report(LOG_ERR, "signal: %s", get_errmsg());
492 		exit(1);
493 	}
494 	if ((int) signal(SIGUSR1, catcher) < 0) {
495 		report(LOG_ERR, "signal: %s", get_errmsg());
496 		exit(1);
497 	}
498 #endif	/* SA_NOCLDSTOP */
499 
500 	/*
501 	 * Process incoming requests.
502 	 */
503 	FD_ZERO(&readfds);
504 	for (;;) {
505 		struct timeval tv;
506 
507 		FD_SET(s, &readfds);
508 		if (timeout)
509 			tv = *timeout;
510 
511 		nfound = select(s + 1, &readfds, NULL, NULL,
512 						(timeout) ? &tv : NULL);
513 		if (nfound < 0) {
514 			if (errno != EINTR) {
515 				report(LOG_ERR, "select: %s", get_errmsg());
516 			}
517 			/*
518 			 * Call readtab() or dumptab() here to avoid the
519 			 * dangers of doing I/O from a signal handler.
520 			 */
521 			if (do_readtab) {
522 				do_readtab = 0;
523 				readtab(1);		/* force read */
524 			}
525 			if (do_dumptab) {
526 				do_dumptab = 0;
527 				dumptab(bootpd_dump);
528 			}
529 			continue;
530 		}
531 		if (!FD_ISSET(s, &readfds)) {
532 			if (debug > 1)
533 				report(LOG_INFO, "exiting after %jd minutes of inactivity",
534 					   (intmax_t)actualtimeout.tv_sec / 60);
535 			exit(0);
536 		}
537 		ra_len = sizeof(recv_addr);
538 		n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
539 					 (struct sockaddr *) &recv_addr, &ra_len);
540 		if (n <= 0) {
541 			continue;
542 		}
543 		if (debug > 1) {
544 			report(LOG_INFO, "recvd pkt from IP addr %s",
545 				   inet_ntoa(recv_addr.sin_addr));
546 		}
547 		if (n < sizeof(struct bootp)) {
548 			if (debug) {
549 				report(LOG_NOTICE, "received short packet");
550 			}
551 			continue;
552 		}
553 		pktlen = n;
554 
555 		readtab(0);				/* maybe re-read bootptab */
556 
557 		switch (bp->bp_op) {
558 		case BOOTREQUEST:
559 			handle_request();
560 			break;
561 		case BOOTREPLY:
562 			handle_reply();
563 			break;
564 		}
565 	}
566 	return 0;
567 }
568 
569 
570 
571 
572 /*
573  * Print "usage" message and exit
574  */
575 
576 PRIVATE void
577 usage()
578 {
579 	fprintf(stderr,
580 		"usage: bootpd [-a] [-i | -s] [-c chdir-path] [-d level] [-h hostname]\n"
581 		"              [-t timeout] [bootptab [dumpfile]]\n");
582 	fprintf(stderr, "       -a\tdon't modify ARP table\n");
583 	fprintf(stderr, "       -c n\tset current directory\n");
584 	fprintf(stderr, "       -d n\tset debug level\n");
585 	fprintf(stderr, "       -h n\tset the hostname to listen on\n");
586 	fprintf(stderr, "       -i\tforce inetd mode (run as child of inetd)\n");
587 	fprintf(stderr, "       -s\tforce standalone mode (run without inetd)\n");
588 	fprintf(stderr, "       -t n\tset inetd exit timeout to n minutes\n");
589 	exit(1);
590 }
591 
592 /* Signal catchers */
593 PRIVATE void
594 catcher(int sig)
595 {
596 	if (sig == SIGHUP)
597 		do_readtab = 1;
598 	if (sig == SIGUSR1)
599 		do_dumptab = 1;
600 #if	!defined(SA_NOCLDSTOP) && defined(SYSV)
601 	/* For older "System V" derivatives with no sigaction(). */
602 	signal(sig, catcher);
603 #endif
604 }
605 
606 
607 
608 /*
609  * Process BOOTREQUEST packet.
610  *
611  * Note:  This version of the bootpd.c server never forwards
612  * a request to another server.  That is the job of a gateway
613  * program such as the "bootpgw" program included here.
614  *
615  * (Also this version does not interpret the hostname field of
616  * the request packet;  it COULD do a name->address lookup and
617  * forward the request there.)
618  */
619 PRIVATE void
620 handle_request(void)
621 {
622 	struct bootp *bp = (struct bootp *) pktbuf;
623 	struct host *hp = NULL;
624 	struct host dummyhost;
625 	int32 bootsize = 0;
626 	unsigned hlen, hashcode;
627 	int32 dest;
628 	char realpath[1024];
629 	char *clntpath;
630 	char *homedir, *bootfile;
631 	int n;
632 
633 	if (bp->bp_htype >= hwinfocnt) {
634 		report(LOG_NOTICE, "bad hw addr type %u", bp->bp_htype);
635 		return;
636 	}
637 	bp->bp_file[sizeof(bp->bp_file)-1] = '\0';
638 
639 	/* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
640 
641 	/*
642 	 * If the servername field is set, compare it against us.
643 	 * If we're not being addressed, ignore this request.
644 	 * If the server name field is null, throw in our name.
645 	 */
646 	if (strlen(bp->bp_sname)) {
647 		if (strcmp(bp->bp_sname, hostname)) {
648 			if (debug)
649 				report(LOG_INFO, "\
650 ignoring request for server %s from client at %s address %s",
651 					   bp->bp_sname, netname(bp->bp_htype),
652 					   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
653 			/* XXX - Is it correct to ignore such a request? -gwr */
654 			return;
655 		}
656 	} else {
657 		strcpy(bp->bp_sname, hostname);
658 	}
659 
660 	/* Convert the request into a reply. */
661 	bp->bp_op = BOOTREPLY;
662 	if (bp->bp_ciaddr.s_addr == 0) {
663 		/*
664 		 * client doesn't know his IP address,
665 		 * search by hardware address.
666 		 */
667 		if (debug > 1) {
668 			report(LOG_INFO, "request from %s address %s",
669 				   netname(bp->bp_htype),
670 				   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
671 		}
672 		hlen = haddrlength(bp->bp_htype);
673 		if (hlen != bp->bp_hlen) {
674 			report(LOG_NOTICE, "bad addr len from %s address %s",
675 				   netname(bp->bp_htype),
676 				   haddrtoa(bp->bp_chaddr, hlen));
677 		}
678 		dummyhost.htype = bp->bp_htype;
679 		bcopy(bp->bp_chaddr, dummyhost.haddr, hlen);
680 		hashcode = hash_HashFunction(bp->bp_chaddr, hlen);
681 		hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp,
682 										 &dummyhost);
683 		if (hp == NULL &&
684 			bp->bp_htype == HTYPE_IEEE802)
685 		{
686 			/* Try again with address in "canonical" form. */
687 			haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen);
688 			if (debug > 1) {
689 				report(LOG_INFO, "\
690 HW addr type is IEEE 802.  convert to %s and check again\n",
691 					   haddrtoa(dummyhost.haddr, bp->bp_hlen));
692 			}
693 			hashcode = hash_HashFunction(dummyhost.haddr, hlen);
694 			hp = (struct host *) hash_Lookup(hwhashtable, hashcode,
695 											 hwlookcmp, &dummyhost);
696 		}
697 		if (hp == NULL) {
698 			/*
699 			 * XXX - Add dynamic IP address assignment?
700 			 */
701 			if (debug)
702 				report(LOG_NOTICE, "unknown client %s address %s",
703 					   netname(bp->bp_htype),
704 					   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
705 			return; /* not found */
706 		}
707 		(bp->bp_yiaddr).s_addr = hp->iaddr.s_addr;
708 
709 	} else {
710 
711 		/*
712 		 * search by IP address.
713 		 */
714 		if (debug > 1) {
715 			report(LOG_INFO, "request from IP addr %s",
716 				   inet_ntoa(bp->bp_ciaddr));
717 		}
718 		dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr;
719 		hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4);
720 		hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp,
721 										 &dummyhost);
722 		if (hp == NULL) {
723 			if (debug) {
724 				report(LOG_NOTICE, "IP address not found: %s",
725 					   inet_ntoa(bp->bp_ciaddr));
726 			}
727 			return;
728 		}
729 	}
730 
731 	if (debug) {
732 		report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr),
733 			   hp->hostname->string);
734 	}
735 
736 	/*
737 	 * If there is a response delay threshold, ignore requests
738 	 * with a timestamp lower than the threshold.
739 	 */
740 	if (hp->flags.min_wait) {
741 		u_int32 t = (u_int32) ntohs(bp->bp_secs);
742 		if (t < hp->min_wait) {
743 			if (debug > 1)
744 				report(LOG_INFO,
745 					   "ignoring request due to timestamp (%d < %d)",
746 					   t, hp->min_wait);
747 			return;
748 		}
749 	}
750 
751 #ifdef	YORK_EX_OPTION
752 	/*
753 	 * The need for the "ex" tag arose out of the need to empty
754 	 * shared networked drives on diskless PCs.  This solution is
755 	 * not very clean but it does work fairly well.
756 	 * Written by Edmund J. Sutcliffe <edmund@york.ac.uk>
757 	 *
758 	 * XXX - This could compromise security if a non-trusted user
759 	 * managed to write an entry in the bootptab with :ex=trojan:
760 	 * so I would leave this turned off unless you need it. -gwr
761 	 */
762 	/* Run a program, passing the client name as a parameter. */
763 	if (hp->flags.exec_file) {
764 		char tst[100];
765 		/* XXX - Check string lengths? -gwr */
766 		strcpy (tst, hp->exec_file->string);
767 		strcat (tst, " ");
768 		strcat (tst, hp->hostname->string);
769 		strcat (tst, " &");
770 		if (debug)
771 			report(LOG_INFO, "executing %s", tst);
772 		system(tst);	/* Hope this finishes soon... */
773 	}
774 #endif	/* YORK_EX_OPTION */
775 
776 	/*
777 	 * If a specific TFTP server address was specified in the bootptab file,
778 	 * fill it in, otherwise zero it.
779 	 * XXX - Rather than zero it, should it be the bootpd address? -gwr
780 	 */
781 	(bp->bp_siaddr).s_addr = (hp->flags.bootserver) ?
782 		hp->bootserver.s_addr : 0L;
783 
784 #ifdef	STANFORD_PROM_COMPAT
785 	/*
786 	 * Stanford bootp PROMs (for a Sun?) have no way to leave
787 	 * the boot file name field blank (because the boot file
788 	 * name is automatically generated from some index).
789 	 * As a work-around, this little hack allows those PROMs to
790 	 * specify "sunboot14" with the same effect as a NULL name.
791 	 * (The user specifies boot device 14 or some such magic.)
792 	 */
793 	if (strcmp(bp->bp_file, "sunboot14") == 0)
794 		bp->bp_file[0] = '\0';	/* treat it as unspecified */
795 #endif
796 
797 	/*
798 	 * Fill in the client's proper bootfile.
799 	 *
800 	 * If the client specifies an absolute path, try that file with a
801 	 * ".host" suffix and then without.  If the file cannot be found, no
802 	 * reply is made at all.
803 	 *
804 	 * If the client specifies a null or relative file, use the following
805 	 * table to determine the appropriate action:
806 	 *
807 	 *  Homedir      Bootfile    Client's file
808 	 * specified?   specified?   specification   Action
809 	 * -------------------------------------------------------------------
810 	 *      No          No          Null         Send null filename
811 	 *      No          No          Relative     Discard request
812 	 *      No          Yes         Null         Send if absolute else null
813 	 *      No          Yes         Relative     Discard request     *XXX
814 	 *      Yes         No          Null         Send null filename
815 	 *      Yes         No          Relative     Lookup with ".host"
816 	 *      Yes         Yes         Null         Send home/boot or bootfile
817 	 *      Yes         Yes         Relative     Lookup with ".host" *XXX
818 	 *
819 	 */
820 
821 	/*
822 	 * XXX - I don't like the policy of ignoring a client when the
823 	 * boot file is not accessible.  The TFTP server might not be
824 	 * running on the same machine as the BOOTP server, in which
825 	 * case checking accessibility of the boot file is pointless.
826 	 *
827 	 * Therefore, file accessibility is now demanded ONLY if you
828 	 * define CHECK_FILE_ACCESS in the Makefile options. -gwr
829 	 */
830 
831 	/*
832 	 * The "real" path is as seen by the BOOTP daemon on this
833 	 * machine, while the client path is relative to the TFTP
834 	 * daemon chroot directory (i.e. /tftpboot).
835 	 */
836 	if (hp->flags.tftpdir) {
837 		snprintf(realpath, sizeof(realpath), "%s", hp->tftpdir->string);
838 		clntpath = &realpath[strlen(realpath)];
839 	} else {
840 		realpath[0] = '\0';
841 		clntpath = realpath;
842 	}
843 
844 	/*
845 	 * Determine client's requested homedir and bootfile.
846 	 */
847 	homedir = NULL;
848 	bootfile = NULL;
849 	if (bp->bp_file[0]) {
850 		homedir = bp->bp_file;
851 		bootfile = strrchr(homedir, '/');
852 		if (bootfile) {
853 			if (homedir == bootfile)
854 				homedir = NULL;
855 			*bootfile++ = '\0';
856 		} else {
857 			/* no "/" in the string */
858 			bootfile = homedir;
859 			homedir = NULL;
860 		}
861 		if (debug > 2) {
862 			report(LOG_INFO, "requested path=\"%s\"  file=\"%s\"",
863 				   (homedir) ? homedir : "",
864 				   (bootfile) ? bootfile : "");
865 		}
866 	}
867 
868 	/*
869 	 * Specifications in bootptab override client requested values.
870 	 */
871 	if (hp->flags.homedir)
872 		homedir = hp->homedir->string;
873 	if (hp->flags.bootfile)
874 		bootfile = hp->bootfile->string;
875 
876 	/*
877 	 * Construct bootfile path.
878 	 */
879 	if (homedir) {
880 		if (homedir[0] != '/')
881 			strcat(clntpath, "/");
882 		strcat(clntpath, homedir);
883 		homedir = NULL;
884 	}
885 	if (bootfile) {
886 		if (bootfile[0] != '/')
887 			strcat(clntpath, "/");
888 		strcat(clntpath, bootfile);
889 		bootfile = NULL;
890 	}
891 
892 	/*
893 	 * First try to find the file with a ".host" suffix
894 	 */
895 	n = strlen(clntpath);
896 	strcat(clntpath, ".");
897 	strcat(clntpath, hp->hostname->string);
898 	if (chk_access(realpath, &bootsize) < 0) {
899 		clntpath[n] = 0;			/* Try it without the suffix */
900 		if (chk_access(realpath, &bootsize) < 0) {
901 			/* neither "file.host" nor "file" was found */
902 #ifdef	CHECK_FILE_ACCESS
903 
904 			if (bp->bp_file[0]) {
905 				/*
906 				 * Client wanted specific file
907 				 * and we didn't have it.
908 				 */
909 				report(LOG_NOTICE,
910 					   "requested file not found: \"%s\"", clntpath);
911 				return;
912 			}
913 			/*
914 			 * Client didn't ask for a specific file and we couldn't
915 			 * access the default file, so just zero-out the bootfile
916 			 * field in the packet and continue processing the reply.
917 			 */
918 			bzero(bp->bp_file, sizeof(bp->bp_file));
919 			goto null_file_name;
920 
921 #else	/* CHECK_FILE_ACCESS */
922 
923 			/* Complain only if boot file size was needed. */
924 			if (hp->flags.bootsize_auto) {
925 				report(LOG_ERR, "can not determine size of file \"%s\"",
926 					   clntpath);
927 			}
928 
929 #endif	/* CHECK_FILE_ACCESS */
930 		}
931 	}
932 	strncpy(bp->bp_file, clntpath, BP_FILE_LEN);
933 	if (debug > 2)
934 		report(LOG_INFO, "bootfile=\"%s\"", clntpath);
935 
936 #ifdef	CHECK_FILE_ACCESS
937 null_file_name:
938 #endif	/* CHECK_FILE_ACCESS */
939 
940 
941 	/*
942 	 * Handle vendor options based on magic number.
943 	 */
944 
945 	if (debug > 1) {
946 		report(LOG_INFO, "vendor magic field is %d.%d.%d.%d",
947 			   (int) ((bp->bp_vend)[0]),
948 			   (int) ((bp->bp_vend)[1]),
949 			   (int) ((bp->bp_vend)[2]),
950 			   (int) ((bp->bp_vend)[3]));
951 	}
952 	/*
953 	 * If this host isn't set for automatic vendor info then copy the
954 	 * specific cookie into the bootp packet, thus forcing a certain
955 	 * reply format.  Only force reply format if user specified it.
956 	 */
957 	if (hp->flags.vm_cookie) {
958 		/* Slam in the user specified magic number. */
959 		bcopy(hp->vm_cookie, bp->bp_vend, 4);
960 	}
961 	/*
962 	 * Figure out the format for the vendor-specific info.
963 	 * Note that bp->bp_vend may have been set above.
964 	 */
965 	if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) {
966 		/* RFC1048 conformant bootp client */
967 		dovend_rfc1048(bp, hp, bootsize);
968 		if (debug > 1) {
969 			report(LOG_INFO, "sending reply (with RFC1048 options)");
970 		}
971 	}
972 #ifdef VEND_CMU
973 	else if (!bcmp(bp->bp_vend, vm_cmu, 4)) {
974 		dovend_cmu(bp, hp);
975 		if (debug > 1) {
976 			report(LOG_INFO, "sending reply (with CMU options)");
977 		}
978 	}
979 #endif
980 	else {
981 		if (debug > 1) {
982 			report(LOG_INFO, "sending reply (with no options)");
983 		}
984 	}
985 
986 	dest = (hp->flags.reply_addr) ?
987 		hp->reply_addr.s_addr : 0L;
988 
989 	/* not forwarded */
990 	sendreply(0, dest);
991 }
992 
993 
994 /*
995  * Process BOOTREPLY packet.
996  */
997 PRIVATE void
998 handle_reply(void)
999 {
1000 	if (debug) {
1001 		report(LOG_INFO, "processing boot reply");
1002 	}
1003 	/* forwarded, no destination override */
1004 	sendreply(1, 0);
1005 }
1006 
1007 
1008 /*
1009  * Send a reply packet to the client.  'forward' flag is set if we are
1010  * not the originator of this reply packet.
1011  */
1012 PRIVATE void
1013 sendreply(int forward, int32 dst_override)
1014 {
1015 	struct bootp *bp = (struct bootp *) pktbuf;
1016 	struct in_addr dst;
1017 	u_short port = bootpc_port;
1018 	unsigned char *ha;
1019 	int len, haf;
1020 
1021 	/*
1022 	 * XXX - Should honor bp_flags "broadcast" bit here.
1023 	 * Temporary workaround: use the :ra=ADDR: option to
1024 	 * set the reply address to the broadcast address.
1025 	 */
1026 
1027 	/*
1028 	 * If the destination address was specified explicitly
1029 	 * (i.e. the broadcast address for HP compatibility)
1030 	 * then send the response to that address.  Otherwise,
1031 	 * act in accordance with RFC951:
1032 	 *   If the client IP address is specified, use that
1033 	 * else if gateway IP address is specified, use that
1034 	 * else make a temporary arp cache entry for the client's
1035 	 * NEW IP/hardware address and use that.
1036 	 */
1037 	if (dst_override) {
1038 		dst.s_addr = dst_override;
1039 		if (debug > 1) {
1040 			report(LOG_INFO, "reply address override: %s",
1041 				   inet_ntoa(dst));
1042 		}
1043 	} else if (bp->bp_ciaddr.s_addr) {
1044 		dst = bp->bp_ciaddr;
1045 	} else if (bp->bp_giaddr.s_addr && forward == 0) {
1046 		dst = bp->bp_giaddr;
1047 		port = bootps_port;
1048 		if (debug > 1) {
1049 			report(LOG_INFO, "sending reply to gateway %s",
1050 				   inet_ntoa(dst));
1051 		}
1052 	} else {
1053 		dst = bp->bp_yiaddr;
1054 		ha = bp->bp_chaddr;
1055 		len = bp->bp_hlen;
1056 		if (len > MAXHADDRLEN)
1057 			len = MAXHADDRLEN;
1058 		haf = (int) bp->bp_htype;
1059 		if (haf == 0)
1060 			haf = HTYPE_ETHERNET;
1061 
1062 		if (arpmod) {
1063 			if (debug > 1)
1064 				report(LOG_INFO, "setarp %s - %s",
1065 					   inet_ntoa(dst), haddrtoa(ha, len));
1066 			setarp(s, &dst, haf, ha, len);
1067 		}
1068 	}
1069 
1070 	if ((forward == 0) &&
1071 		(bp->bp_siaddr.s_addr == 0))
1072 	{
1073 		struct ifreq *ifr;
1074 		struct in_addr siaddr;
1075 		/*
1076 		 * If we are originating this reply, we
1077 		 * need to find our own interface address to
1078 		 * put in the bp_siaddr field of the reply.
1079 		 * If this server is multi-homed, pick the
1080 		 * 'best' interface (the one on the same net
1081 		 * as the client).  Of course, the client may
1082 		 * be on the other side of a BOOTP gateway...
1083 		 */
1084 		ifr = getif(s, &dst);
1085 		if (ifr) {
1086 			struct sockaddr_in *sip;
1087 			sip = (struct sockaddr_in *) &(ifr->ifr_addr);
1088 			siaddr = sip->sin_addr;
1089 		} else {
1090 			/* Just use my "official" IP address. */
1091 			siaddr = my_ip_addr;
1092 		}
1093 
1094 		/* XXX - No need to set bp_giaddr here. */
1095 
1096 		/* Finally, set the server address field. */
1097 		bp->bp_siaddr = siaddr;
1098 	}
1099 	/* Set up socket address for send. */
1100 	send_addr.sin_family = AF_INET;
1101 	send_addr.sin_port = htons(port);
1102 	send_addr.sin_addr = dst;
1103 
1104 	/* Send reply with same size packet as request used. */
1105 	if (sendto(s, pktbuf, pktlen, 0,
1106 			   (struct sockaddr *) &send_addr,
1107 			   sizeof(send_addr)) < 0)
1108 	{
1109 		report(LOG_ERR, "sendto: %s", get_network_errmsg());
1110 	}
1111 } /* sendreply */
1112 
1113 
1114 /* nmatch() - now in getif.c */
1115 /* setarp() - now in hwaddr.c */
1116 
1117 
1118 /*
1119  * This call checks read access to a file.  It returns 0 if the file given
1120  * by "path" exists and is publicly readable.  A value of -1 is returned if
1121  * access is not permitted or an error occurs.  Successful calls also
1122  * return the file size in bytes using the long pointer "filesize".
1123  *
1124  * The read permission bit for "other" users is checked.  This bit must be
1125  * set for tftpd(8) to allow clients to read the file.
1126  */
1127 
1128 PRIVATE int
1129 chk_access(char *path, int32 *filesize)
1130 {
1131 	struct stat st;
1132 
1133 	if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {
1134 		*filesize = (int32) st.st_size;
1135 		return 0;
1136 	} else {
1137 		return -1;
1138 	}
1139 }
1140 
1141 
1142 /*
1143  * Now in dumptab.c :
1144  *	dumptab()
1145  *	dump_host()
1146  *	list_ipaddresses()
1147  */
1148 
1149 #ifdef VEND_CMU
1150 
1151 /*
1152  * Insert the CMU "vendor" data for the host pointed to by "hp" into the
1153  * bootp packet pointed to by "bp".
1154  */
1155 
1156 PRIVATE void
1157 dovend_cmu(struct bootp *bp, struct host *hp)
1158 {
1159 	struct cmu_vend *vendp;
1160 	struct in_addr_list *taddr;
1161 
1162 	/*
1163 	 * Initialize the entire vendor field to zeroes.
1164 	 */
1165 	bzero(bp->bp_vend, sizeof(bp->bp_vend));
1166 
1167 	/*
1168 	 * Fill in vendor information. Subnet mask, default gateway,
1169 	 * domain name server, ien name server, time server
1170 	 */
1171 	vendp = (struct cmu_vend *) bp->bp_vend;
1172 	strcpy(vendp->v_magic, (char *)vm_cmu);
1173 	if (hp->flags.subnet_mask) {
1174 		(vendp->v_smask).s_addr = hp->subnet_mask.s_addr;
1175 		(vendp->v_flags) |= VF_SMASK;
1176 		if (hp->flags.gateway) {
1177 			(vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;
1178 		}
1179 	}
1180 	if (hp->flags.domain_server) {
1181 		taddr = hp->domain_server;
1182 		if (taddr->addrcount > 0) {
1183 			(vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr;
1184 			if (taddr->addrcount > 1) {
1185 				(vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr;
1186 			}
1187 		}
1188 	}
1189 	if (hp->flags.name_server) {
1190 		taddr = hp->name_server;
1191 		if (taddr->addrcount > 0) {
1192 			(vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr;
1193 			if (taddr->addrcount > 1) {
1194 				(vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr;
1195 			}
1196 		}
1197 	}
1198 	if (hp->flags.time_server) {
1199 		taddr = hp->time_server;
1200 		if (taddr->addrcount > 0) {
1201 			(vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr;
1202 			if (taddr->addrcount > 1) {
1203 				(vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr;
1204 			}
1205 		}
1206 	}
1207 	/* Log message now done by caller. */
1208 } /* dovend_cmu */
1209 
1210 #endif /* VEND_CMU */
1211 
1212 
1213 
1214 /*
1215  * Insert the RFC1048 vendor data for the host pointed to by "hp" into the
1216  * bootp packet pointed to by "bp".
1217  */
1218 #define	NEED(LEN, MSG) do \
1219 	if (bytesleft < (LEN)) { \
1220 		report(LOG_NOTICE, noroom, \
1221 			   hp->hostname->string, MSG); \
1222 		return; \
1223 	} while (0)
1224 PRIVATE void
1225 dovend_rfc1048(struct bootp *bp, struct host *hp, int32 bootsize)
1226 {
1227 	int bytesleft, len;
1228 	byte *vp;
1229 
1230 	static const char noroom[] = "%s: No room for \"%s\" option";
1231 
1232 	vp = bp->bp_vend;
1233 
1234 	if (hp->flags.msg_size) {
1235 		pktlen = hp->msg_size;
1236 	} else {
1237 		/*
1238 		 * If the request was longer than the official length, build
1239 		 * a response of that same length where the additional length
1240 		 * is assumed to be part of the bp_vend (options) area.
1241 		 */
1242 		if (pktlen > sizeof(*bp)) {
1243 			if (debug > 1)
1244 				report(LOG_INFO, "request message length=%d", pktlen);
1245 		}
1246 		/*
1247 		 * Check whether the request contains the option:
1248 		 * Maximum DHCP Message Size (RFC1533 sec. 9.8)
1249 		 * and if so, override the response length with its value.
1250 		 * This request must lie within the first BP_VEND_LEN
1251 		 * bytes of the option space.
1252 		 */
1253 		{
1254 			byte *p, *ep;
1255 			byte tag, len;
1256 			short msgsz = 0;
1257 
1258 			p = vp + 4;
1259 			ep = p + BP_VEND_LEN - 4;
1260 			while (p < ep) {
1261 				tag = *p++;
1262 				/* Check for tags with no data first. */
1263 				if (tag == TAG_PAD)
1264 					continue;
1265 				if (tag == TAG_END)
1266 					break;
1267 				/* Now scan the length byte. */
1268 				len = *p++;
1269 				switch (tag) {
1270 				case TAG_MAX_MSGSZ:
1271 					if (len == 2) {
1272 						bcopy(p, (char*)&msgsz, 2);
1273 						msgsz = ntohs(msgsz);
1274 					}
1275 					break;
1276 				case TAG_SUBNET_MASK:
1277 					/* XXX - Should preserve this if given... */
1278 					break;
1279 				} /* swtich */
1280 				p += len;
1281 			}
1282 
1283 			if (msgsz > sizeof(*bp) + BP_MSG_OVERHEAD) {
1284 				if (debug > 1)
1285 					report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
1286 				pktlen = msgsz - BP_MSG_OVERHEAD;
1287 			}
1288 		}
1289 	}
1290 
1291 	if (pktlen < sizeof(*bp)) {
1292 		report(LOG_ERR, "invalid response length=%d", pktlen);
1293 		pktlen = sizeof(*bp);
1294 	}
1295 	bytesleft = ((byte*)bp + pktlen) - vp;
1296 	if (pktlen > sizeof(*bp)) {
1297 		if (debug > 1)
1298 			report(LOG_INFO, "extended reply, length=%d, options=%d",
1299 				   pktlen, bytesleft);
1300 	}
1301 
1302 	/* Copy in the magic cookie */
1303 	bcopy(vm_rfc1048, vp, 4);
1304 	vp += 4;
1305 	bytesleft -= 4;
1306 
1307 	if (hp->flags.subnet_mask) {
1308 		/* always enough room here. */
1309 		*vp++ = TAG_SUBNET_MASK;/* -1 byte  */
1310 		*vp++ = 4;				/* -1 byte  */
1311 		insert_u_long(hp->subnet_mask.s_addr, &vp);	/* -4 bytes */
1312 		bytesleft -= 6;			/* Fix real count */
1313 		if (hp->flags.gateway) {
1314 			(void) insert_ip(TAG_GATEWAY,
1315 							 hp->gateway,
1316 							 &vp, &bytesleft);
1317 		}
1318 	}
1319 	if (hp->flags.bootsize) {
1320 		/* always enough room here */
1321 		bootsize = (hp->flags.bootsize_auto) ?
1322 			((bootsize + 511) / 512) : (hp->bootsize);	/* Round up */
1323 		*vp++ = TAG_BOOT_SIZE;
1324 		*vp++ = 2;
1325 		*vp++ = (byte) ((bootsize >> 8) & 0xFF);
1326 		*vp++ = (byte) (bootsize & 0xFF);
1327 		bytesleft -= 4;			/* Tag, length, and 16 bit blocksize */
1328 	}
1329 	/*
1330 	 * This one is special: Remaining options go in the ext file.
1331 	 * Only the subnet_mask, bootsize, and gateway should precede.
1332 	 */
1333 	if (hp->flags.exten_file) {
1334 		/*
1335 		 * Check for room for exten_file.  Add 3 to account for
1336 		 * TAG_EXTEN_FILE, length, and TAG_END.
1337 		 */
1338 		len = strlen(hp->exten_file->string);
1339 		NEED((len + 3), "ef");
1340 		*vp++ = TAG_EXTEN_FILE;
1341 		*vp++ = (byte) (len & 0xFF);
1342 		bcopy(hp->exten_file->string, vp, len);
1343 		vp += len;
1344 		*vp++ = TAG_END;
1345 		bytesleft -= len + 3;
1346 		return;					/* no more options here. */
1347 	}
1348 	/*
1349 	 * The remaining options are inserted by the following
1350 	 * function (which is shared with bootpef.c).
1351 	 * Keep back one byte for the TAG_END.
1352 	 */
1353 	len = dovend_rfc1497(hp, vp, bytesleft - 1);
1354 	vp += len;
1355 	bytesleft -= len;
1356 
1357 	/* There should be at least one byte left. */
1358 	NEED(1, "(end)");
1359 	*vp++ = TAG_END;
1360 	bytesleft--;
1361 
1362 	/* Log message done by caller. */
1363 	if (bytesleft > 0) {
1364 		/*
1365 		 * Zero out any remaining part of the vendor area.
1366 		 */
1367 		bzero(vp, bytesleft);
1368 	}
1369 } /* dovend_rfc1048 */
1370 #undef	NEED
1371 
1372 
1373 /*
1374  * Now in readfile.c:
1375  * 	hwlookcmp()
1376  *	iplookcmp()
1377  */
1378 
1379 /* haddrtoa() - now in hwaddr.c */
1380 /*
1381  * Now in dovend.c:
1382  * insert_ip()
1383  * insert_generic()
1384  * insert_u_long()
1385  */
1386 
1387 /* get_errmsg() - now in report.c */
1388 
1389 /*
1390  * Local Variables:
1391  * tab-width: 4
1392  * c-indent-level: 4
1393  * c-argdecl-indent: 4
1394  * c-continued-statement-offset: 4
1395  * c-continued-brace-offset: -4
1396  * c-label-offset: -4
1397  * c-brace-offset: 0
1398  * End:
1399  */
1400