xref: /freebsd/libexec/bootpd/bootpd.c (revision eacee0ff7ec955b32e09515246bd97b6edcd2b0f)
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  $FreeBSD$
23 
24 ************************************************************************/
25 
26 /*
27  * BOOTP (bootstrap protocol) server daemon.
28  *
29  * Answers BOOTP request packets from booting client machines.
30  * See [SRI-NIC]<RFC>RFC951.TXT for a description of the protocol.
31  * See [SRI-NIC]<RFC>RFC1048.TXT for vendor-information extensions.
32  * See RFC 1395 for option tags 14-17.
33  * See accompanying man page -- bootpd.8
34  *
35  * HISTORY
36  *	See ./Changes
37  *
38  * BUGS
39  *	See ./ToDo
40  */
41 
42 
43 
44 #include <sys/types.h>
45 #include <sys/param.h>
46 #include <sys/socket.h>
47 #include <sys/ioctl.h>
48 #include <sys/file.h>
49 #include <sys/time.h>
50 #include <sys/stat.h>
51 #include <sys/utsname.h>
52 
53 #include <net/if.h>
54 #include <netinet/in.h>
55 #include <arpa/inet.h>	/* inet_ntoa */
56 
57 #ifndef	NO_UNISTD
58 #include <unistd.h>
59 #endif
60 
61 #include <stdlib.h>
62 #include <signal.h>
63 #include <stdio.h>
64 #include <string.h>
65 #include <errno.h>
66 #include <ctype.h>
67 #include <netdb.h>
68 #include <paths.h>
69 #include <syslog.h>
70 #include <assert.h>
71 
72 #ifdef	NO_SETSID
73 # include <fcntl.h>		/* for O_RDONLY, etc */
74 #endif
75 
76 #ifndef	USE_BFUNCS
77 # include <memory.h>
78 /* Yes, memcpy is OK here (no overlapped copies). */
79 # define bcopy(a,b,c)    memcpy(b,a,c)
80 # define bzero(p,l)      memset(p,0,l)
81 # define bcmp(a,b,c)     memcmp(a,b,c)
82 #endif
83 
84 #include "bootp.h"
85 #include "hash.h"
86 #include "hwaddr.h"
87 #include "bootpd.h"
88 #include "dovend.h"
89 #include "getif.h"
90 #include "readfile.h"
91 #include "report.h"
92 #include "tzone.h"
93 #include "patchlevel.h"
94 
95 #ifndef CONFIG_FILE
96 #define CONFIG_FILE		"/etc/bootptab"
97 #endif
98 #ifndef DUMPTAB_FILE
99 #define DUMPTAB_FILE		"/tmp/bootpd.dump"
100 #endif
101 
102 
103 
104 /*
105  * Externals, forward declarations, and global variables
106  */
107 
108 #ifdef	__STDC__
109 #define P(args) args
110 #else
111 #define P(args) ()
112 #endif
113 
114 extern void dumptab P((char *));
115 
116 PRIVATE void catcher P((int));
117 PRIVATE int chk_access P((char *, int32 *));
118 #ifdef VEND_CMU
119 PRIVATE void dovend_cmu P((struct bootp *, struct host *));
120 #endif
121 PRIVATE void dovend_rfc1048 P((struct bootp *, struct host *, int32));
122 PRIVATE void handle_reply P((void));
123 PRIVATE void handle_request P((void));
124 PRIVATE void sendreply P((int forward, int32 dest_override));
125 PRIVATE void usage P((void));
126 
127 #undef	P
128 
129 /*
130  * IP port numbers for client and server obtained from /etc/services
131  */
132 
133 u_short bootps_port, bootpc_port;
134 
135 
136 /*
137  * Internet socket and interface config structures
138  */
139 
140 struct sockaddr_in bind_addr;	/* Listening */
141 struct sockaddr_in recv_addr;	/* Packet source */
142 struct sockaddr_in send_addr;	/*  destination */
143 
144 
145 /*
146  * option defaults
147  */
148 int debug = 0;					/* Debugging flag (level) */
149 struct timeval actualtimeout =
150 {								/* fifteen minutes */
151 	15 * 60L,					/* tv_sec */
152 	0							/* tv_usec */
153 };
154 
155 /*
156  * General
157  */
158 
159 int s;							/* Socket file descriptor */
160 char *pktbuf;					/* Receive packet buffer */
161 int pktlen;
162 char *progname;
163 char *chdir_path;
164 struct in_addr my_ip_addr;
165 
166 static const char *hostname;
167 static char default_hostname[MAXHOSTNAMELEN];
168 
169 /* Flags set by signal catcher. */
170 PRIVATE int do_readtab = 0;
171 PRIVATE int do_dumptab = 0;
172 
173 /*
174  * Globals below are associated with the bootp database file (bootptab).
175  */
176 
177 char *bootptab = CONFIG_FILE;
178 char *bootpd_dump = DUMPTAB_FILE;
179 
180 
181 
182 /*
183  * Initialization such as command-line processing is done and then the
184  * main server loop is started.
185  */
186 
187 int
188 main(argc, argv)
189 	int argc;
190 	char **argv;
191 {
192 	struct timeval *timeout;
193 	struct bootp *bp;
194 	struct servent *servp;
195 	struct hostent *hep;
196 	char *stmp;
197 	int n, ba_len, ra_len;
198 	int nfound, readfds;
199 	int standalone;
200 #ifdef	SA_NOCLDSTOP	/* Have POSIX sigaction(2). */
201 	struct sigaction sa;
202 #endif
203 
204 	progname = strrchr(argv[0], '/');
205 	if (progname) progname++;
206 	else progname = argv[0];
207 
208 	/*
209 	 * Initialize logging.
210 	 */
211 	report_init(0);				/* uses progname */
212 
213 	/*
214 	 * Log startup
215 	 */
216 	report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
217 
218 	/* Debugging for compilers with struct padding. */
219 	assert(sizeof(struct bootp) == BP_MINPKTSZ);
220 
221 	/* Get space for receiving packets and composing replies. */
222 	pktbuf = malloc(MAX_MSG_SIZE);
223 	if (!pktbuf) {
224 		report(LOG_ERR, "malloc failed");
225 		exit(1);
226 	}
227 	bp = (struct bootp *) pktbuf;
228 
229 	/*
230 	 * Check to see if a socket was passed to us from inetd.
231 	 *
232 	 * Use getsockname() to determine if descriptor 0 is indeed a socket
233 	 * (and thus we are probably a child of inetd) or if it is instead
234 	 * something else and we are running standalone.
235 	 */
236 	s = 0;
237 	ba_len = sizeof(bind_addr);
238 	bzero((char *) &bind_addr, ba_len);
239 	errno = 0;
240 	standalone = TRUE;
241 	if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
242 		/*
243 		 * Descriptor 0 is a socket.  Assume we are a child of inetd.
244 		 */
245 		if (bind_addr.sin_family == AF_INET) {
246 			standalone = FALSE;
247 			bootps_port = ntohs(bind_addr.sin_port);
248 		} else {
249 			/* Some other type of socket? */
250 			report(LOG_ERR, "getsockname: not an INET socket");
251 		}
252 	}
253 
254 	/*
255 	 * Set defaults that might be changed by option switches.
256 	 */
257 	stmp = NULL;
258 	timeout = &actualtimeout;
259 
260 	if (gethostname(default_hostname, sizeof(default_hostname) - 1) < 0) {
261 		report(LOG_ERR, "bootpd: can't get hostname\n");
262 		exit(1);
263 	}
264 	default_hostname[sizeof(default_hostname) - 1] = '\0';
265 	hostname = default_hostname;
266 
267 	/*
268 	 * Read switches.
269 	 */
270 	for (argc--, argv++; argc > 0; argc--, argv++) {
271 		if (argv[0][0] != '-')
272 			break;
273 		switch (argv[0][1]) {
274 
275 		case 'c':				/* chdir_path */
276 			if (argv[0][2]) {
277 				stmp = &(argv[0][2]);
278 			} else {
279 				argc--;
280 				argv++;
281 				stmp = argv[0];
282 			}
283 			if (!stmp || (stmp[0] != '/')) {
284 				report(LOG_ERR,
285 						"bootpd: invalid chdir specification\n");
286 				break;
287 			}
288 			chdir_path = stmp;
289 			break;
290 
291 		case 'd':				/* debug level */
292 			if (argv[0][2]) {
293 				stmp = &(argv[0][2]);
294 			} else if (argv[1] && argv[1][0] == '-') {
295 				/*
296 				 * Backwards-compatible behavior:
297 				 * no parameter, so just increment the debug flag.
298 				 */
299 				debug++;
300 				break;
301 			} else {
302 				argc--;
303 				argv++;
304 				stmp = argv[0];
305 			}
306 			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
307 				report(LOG_ERR,
308 						"%s: invalid debug level\n", progname);
309 				break;
310 			}
311 			debug = n;
312 			break;
313 
314 		case 'h':				/* override hostname */
315 			if (argv[0][2]) {
316 				stmp = &(argv[0][2]);
317 			} else {
318 				argc--;
319 				argv++;
320 				stmp = argv[0];
321 			}
322 			if (!stmp) {
323 				report(LOG_ERR,
324 						"bootpd: missing hostname\n");
325 				break;
326 			}
327 			hostname = stmp;
328 			break;
329 
330 		case 'i':				/* inetd mode */
331 			standalone = FALSE;
332 			break;
333 
334 		case 's':				/* standalone mode */
335 			standalone = TRUE;
336 			break;
337 
338 		case 't':				/* timeout */
339 			if (argv[0][2]) {
340 				stmp = &(argv[0][2]);
341 			} else {
342 				argc--;
343 				argv++;
344 				stmp = argv[0];
345 			}
346 			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
347 				report(LOG_ERR,
348 						"%s: invalid timeout specification\n", progname);
349 				break;
350 			}
351 			actualtimeout.tv_sec = (int32) (60 * n);
352 			/*
353 			 * If the actual timeout is zero, pass a NULL pointer
354 			 * to select so it blocks indefinitely, otherwise,
355 			 * point to the actual timeout value.
356 			 */
357 			timeout = (n > 0) ? &actualtimeout : NULL;
358 			break;
359 
360 		default:
361 			report(LOG_ERR, "%s: unknown switch: -%c\n",
362 					progname, argv[0][1]);
363 			usage();
364 			break;
365 
366 		} /* switch */
367 	} /* for args */
368 
369 	/*
370 	 * Override default file names if specified on the command line.
371 	 */
372 	if (argc > 0)
373 		bootptab = argv[0];
374 
375 	if (argc > 1)
376 		bootpd_dump = argv[1];
377 
378 	/*
379 	 * Get my hostname and IP address.
380 	 */
381 
382 	hep = gethostbyname(hostname);
383 	if (!hep) {
384 		report(LOG_ERR, "Can not get my IP address\n");
385 		exit(1);
386 	}
387 	bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
388 
389 	if (standalone) {
390 		/*
391 		 * Go into background and disassociate from controlling terminal.
392 		 */
393 		if (debug < 3) {
394 			if (fork())
395 				exit(0);
396 #ifdef	NO_SETSID
397 			setpgrp(0,0);
398 #ifdef TIOCNOTTY
399 			n = open(_PATH_TTY, O_RDWR);
400 			if (n >= 0) {
401 				ioctl(n, TIOCNOTTY, (char *) 0);
402 				(void) close(n);
403 			}
404 #endif	/* TIOCNOTTY */
405 #else	/* SETSID */
406 			if (setsid() < 0)
407 				perror("setsid");
408 #endif	/* SETSID */
409 		} /* if debug < 3 */
410 
411 		/*
412 		 * Nuke any timeout value
413 		 */
414 		timeout = NULL;
415 
416 	} /* if standalone (1st) */
417 
418 	/* Set the cwd (i.e. to /tftpboot) */
419 	if (chdir_path) {
420 		if (chdir(chdir_path) < 0)
421 			report(LOG_ERR, "%s: chdir failed", chdir_path);
422 	}
423 
424 	/* Get the timezone. */
425 	tzone_init();
426 
427 	/* Allocate hash tables. */
428 	rdtab_init();
429 
430 	/*
431 	 * Read the bootptab file.
432 	 */
433 	readtab(1);					/* force read */
434 
435 	if (standalone) {
436 
437 		/*
438 		 * Create a socket.
439 		 */
440 		if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
441 			report(LOG_ERR, "socket: %s", get_network_errmsg());
442 			exit(1);
443 		}
444 
445 		/*
446 		 * Get server's listening port number
447 		 */
448 		servp = getservbyname("bootps", "udp");
449 		if (servp) {
450 			bootps_port = ntohs((u_short) servp->s_port);
451 		} else {
452 			bootps_port = (u_short) IPPORT_BOOTPS;
453 			report(LOG_ERR,
454 				   "udp/bootps: unknown service -- assuming port %d",
455 				   bootps_port);
456 		}
457 
458 		/*
459 		 * Bind socket to BOOTPS port.
460 		 */
461 		bind_addr.sin_family = AF_INET;
462 		bind_addr.sin_addr.s_addr = INADDR_ANY;
463 		bind_addr.sin_port = htons(bootps_port);
464 		if (bind(s, (struct sockaddr *) &bind_addr,
465 				 sizeof(bind_addr)) < 0)
466 		{
467 			report(LOG_ERR, "bind: %s", get_network_errmsg());
468 			exit(1);
469 		}
470 	} /* if standalone (2nd)*/
471 
472 	/*
473 	 * Get destination port number so we can reply to client
474 	 */
475 	servp = getservbyname("bootpc", "udp");
476 	if (servp) {
477 		bootpc_port = ntohs(servp->s_port);
478 	} else {
479 		report(LOG_ERR,
480 			   "udp/bootpc: unknown service -- assuming port %d",
481 			   IPPORT_BOOTPC);
482 		bootpc_port = (u_short) IPPORT_BOOTPC;
483 	}
484 
485 	/*
486 	 * Set up signals to read or dump the table.
487 	 */
488 #ifdef	SA_NOCLDSTOP	/* Have POSIX sigaction(2). */
489 	sa.sa_handler = catcher;
490 	sigemptyset(&sa.sa_mask);
491 	sa.sa_flags = 0;
492 	if (sigaction(SIGHUP, &sa, NULL) < 0) {
493 		report(LOG_ERR, "sigaction: %s", get_errmsg());
494 		exit(1);
495 	}
496 	if (sigaction(SIGUSR1, &sa, NULL) < 0) {
497 		report(LOG_ERR, "sigaction: %s", get_errmsg());
498 		exit(1);
499 	}
500 #else	/* SA_NOCLDSTOP */
501 	/* Old-fashioned UNIX signals */
502 	if ((int) signal(SIGHUP, catcher) < 0) {
503 		report(LOG_ERR, "signal: %s", get_errmsg());
504 		exit(1);
505 	}
506 	if ((int) signal(SIGUSR1, catcher) < 0) {
507 		report(LOG_ERR, "signal: %s", get_errmsg());
508 		exit(1);
509 	}
510 #endif	/* SA_NOCLDSTOP */
511 
512 	/*
513 	 * Process incoming requests.
514 	 */
515 	for (;;) {
516 		struct timeval tv;
517 
518 		readfds = 1 << s;
519 		if (timeout)
520 			tv = *timeout;
521 
522 		nfound = select(s + 1, (fd_set *)&readfds, NULL, NULL,
523 						(timeout) ? &tv : NULL);
524 		if (nfound < 0) {
525 			if (errno != EINTR) {
526 				report(LOG_ERR, "select: %s", get_errmsg());
527 			}
528 			/*
529 			 * Call readtab() or dumptab() here to avoid the
530 			 * dangers of doing I/O from a signal handler.
531 			 */
532 			if (do_readtab) {
533 				do_readtab = 0;
534 				readtab(1);		/* force read */
535 			}
536 			if (do_dumptab) {
537 				do_dumptab = 0;
538 				dumptab(bootpd_dump);
539 			}
540 			continue;
541 		}
542 		if (!(readfds & (1 << s))) {
543 			if (debug > 1)
544 				report(LOG_INFO, "exiting after %ld minutes of inactivity",
545 					   actualtimeout.tv_sec / 60);
546 			exit(0);
547 		}
548 		ra_len = sizeof(recv_addr);
549 		n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
550 					 (struct sockaddr *) &recv_addr, &ra_len);
551 		if (n <= 0) {
552 			continue;
553 		}
554 		if (debug > 1) {
555 			report(LOG_INFO, "recvd pkt from IP addr %s",
556 				   inet_ntoa(recv_addr.sin_addr));
557 		}
558 		if (n < sizeof(struct bootp)) {
559 			if (debug) {
560 				report(LOG_NOTICE, "received short packet");
561 			}
562 			continue;
563 		}
564 		pktlen = n;
565 
566 		readtab(0);				/* maybe re-read bootptab */
567 
568 		switch (bp->bp_op) {
569 		case BOOTREQUEST:
570 			handle_request();
571 			break;
572 		case BOOTREPLY:
573 			handle_reply();
574 			break;
575 		}
576 	}
577 	return 0;
578 }
579 
580 
581 
582 
583 /*
584  * Print "usage" message and exit
585  */
586 
587 PRIVATE void
588 usage()
589 {
590 	fprintf(stderr,
591 			"usage:  bootpd [-d level] [-i] [-s] [-t timeout] [configfile [dumpfile]]\n");
592 	fprintf(stderr, "\t -c n\tset current directory\n");
593 	fprintf(stderr, "\t -d n\tset debug level\n");
594 	fprintf(stderr, "\t -i\tforce inetd mode (run as child of inetd)\n");
595 	fprintf(stderr, "\t -s\tforce standalone mode (run without inetd)\n");
596 	fprintf(stderr, "\t -t n\tset inetd exit timeout to n minutes\n");
597 	exit(1);
598 }
599 
600 /* Signal catchers */
601 PRIVATE void
602 catcher(sig)
603 	int sig;
604 {
605 	if (sig == SIGHUP)
606 		do_readtab = 1;
607 	if (sig == SIGUSR1)
608 		do_dumptab = 1;
609 #if	!defined(SA_NOCLDSTOP) && defined(SYSV)
610 	/* For older "System V" derivatives with no sigaction(). */
611 	signal(sig, catcher);
612 #endif
613 }
614 
615 
616 
617 /*
618  * Process BOOTREQUEST packet.
619  *
620  * Note:  This version of the bootpd.c server never forwards
621  * a request to another server.  That is the job of a gateway
622  * program such as the "bootpgw" program included here.
623  *
624  * (Also this version does not interpret the hostname field of
625  * the request packet;  it COULD do a name->address lookup and
626  * forward the request there.)
627  */
628 PRIVATE void
629 handle_request()
630 {
631 	struct bootp *bp = (struct bootp *) pktbuf;
632 	struct host *hp = NULL;
633 	struct host dummyhost;
634 	int32 bootsize = 0;
635 	unsigned hlen, hashcode;
636 	int32 dest;
637 	char realpath[1024];
638 	char *clntpath;
639 	char *homedir, *bootfile;
640 	int n;
641 
642 	bp->bp_file[sizeof(bp->bp_file)-1] = '\0';
643 
644 	/* XXX - SLIP init: Set bp_ciaddr = recv_addr here? */
645 
646 	/*
647 	 * If the servername field is set, compare it against us.
648 	 * If we're not being addressed, ignore this request.
649 	 * If the server name field is null, throw in our name.
650 	 */
651 	if (strlen(bp->bp_sname)) {
652 		if (strcmp(bp->bp_sname, hostname)) {
653 			if (debug)
654 				report(LOG_INFO, "\
655 ignoring request for server %s from client at %s address %s",
656 					   bp->bp_sname, netname(bp->bp_htype),
657 					   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
658 			/* XXX - Is it correct to ignore such a request? -gwr */
659 			return;
660 		}
661 	} else {
662 		strcpy(bp->bp_sname, hostname);
663 	}
664 
665 	/* Convert the request into a reply. */
666 	bp->bp_op = BOOTREPLY;
667 	if (bp->bp_ciaddr.s_addr == 0) {
668 		/*
669 		 * client doesnt know his IP address,
670 		 * search by hardware address.
671 		 */
672 		if (debug > 1) {
673 			report(LOG_INFO, "request from %s address %s",
674 				   netname(bp->bp_htype),
675 				   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
676 		}
677 		hlen = haddrlength(bp->bp_htype);
678 		if (hlen != bp->bp_hlen) {
679 			report(LOG_NOTICE, "bad addr len from from %s address %s",
680 				   netname(bp->bp_htype),
681 				   haddrtoa(bp->bp_chaddr, hlen));
682 		}
683 		dummyhost.htype = bp->bp_htype;
684 		bcopy(bp->bp_chaddr, dummyhost.haddr, hlen);
685 		hashcode = hash_HashFunction(bp->bp_chaddr, hlen);
686 		hp = (struct host *) hash_Lookup(hwhashtable, hashcode, hwlookcmp,
687 										 &dummyhost);
688 		if (hp == NULL &&
689 			bp->bp_htype == HTYPE_IEEE802)
690 		{
691 			/* Try again with address in "canonical" form. */
692 			haddr_conv802(bp->bp_chaddr, dummyhost.haddr, hlen);
693 			if (debug > 1) {
694 				report(LOG_INFO, "\
695 HW addr type is IEEE 802.  convert to %s and check again\n",
696 					   haddrtoa(dummyhost.haddr, bp->bp_hlen));
697 			}
698 			hashcode = hash_HashFunction(dummyhost.haddr, hlen);
699 			hp = (struct host *) hash_Lookup(hwhashtable, hashcode,
700 											 hwlookcmp, &dummyhost);
701 		}
702 		if (hp == NULL) {
703 			/*
704 			 * XXX - Add dynamic IP address assignment?
705 			 */
706 			if (debug)
707 				report(LOG_NOTICE, "unknown client %s address %s",
708 					   netname(bp->bp_htype),
709 					   haddrtoa(bp->bp_chaddr, bp->bp_hlen));
710 			return; /* not found */
711 		}
712 		(bp->bp_yiaddr).s_addr = hp->iaddr.s_addr;
713 
714 	} else {
715 
716 		/*
717 		 * search by IP address.
718 		 */
719 		if (debug > 1) {
720 			report(LOG_INFO, "request from IP addr %s",
721 				   inet_ntoa(bp->bp_ciaddr));
722 		}
723 		dummyhost.iaddr.s_addr = bp->bp_ciaddr.s_addr;
724 		hashcode = hash_HashFunction((u_char *) &(bp->bp_ciaddr.s_addr), 4);
725 		hp = (struct host *) hash_Lookup(iphashtable, hashcode, iplookcmp,
726 										 &dummyhost);
727 		if (hp == NULL) {
728 			if (debug) {
729 				report(LOG_NOTICE, "IP address not found: %s",
730 					   inet_ntoa(bp->bp_ciaddr));
731 			}
732 			return;
733 		}
734 	}
735 
736 	if (debug) {
737 		report(LOG_INFO, "found %s (%s)", inet_ntoa(hp->iaddr),
738 			   hp->hostname->string);
739 	}
740 
741 	/*
742 	 * If there is a response delay threshold, ignore requests
743 	 * with a timestamp lower than the threshold.
744 	 */
745 	if (hp->flags.min_wait) {
746 		u_int32 t = (u_int32) ntohs(bp->bp_secs);
747 		if (t < hp->min_wait) {
748 			if (debug > 1)
749 				report(LOG_INFO,
750 					   "ignoring request due to timestamp (%d < %d)",
751 					   t, hp->min_wait);
752 			return;
753 		}
754 	}
755 
756 #ifdef	YORK_EX_OPTION
757 	/*
758 	 * The need for the "ex" tag arose out of the need to empty
759 	 * shared networked drives on diskless PCs.  This solution is
760 	 * not very clean but it does work fairly well.
761 	 * Written by Edmund J. Sutcliffe <edmund@york.ac.uk>
762 	 *
763 	 * XXX - This could compromise security if a non-trusted user
764 	 * managed to write an entry in the bootptab with :ex=trojan:
765 	 * so I would leave this turned off unless you need it. -gwr
766 	 */
767 	/* Run a program, passing the client name as a parameter. */
768 	if (hp->flags.exec_file) {
769 		char tst[100];
770 		/* XXX - Check string lengths? -gwr */
771 		strcpy (tst, hp->exec_file->string);
772 		strcat (tst, " ");
773 		strcat (tst, hp->hostname->string);
774 		strcat (tst, " &");
775 		if (debug)
776 			report(LOG_INFO, "executing %s", tst);
777 		system(tst);	/* Hope this finishes soon... */
778 	}
779 #endif	/* YORK_EX_OPTION */
780 
781 	/*
782 	 * If a specific TFTP server address was specified in the bootptab file,
783 	 * fill it in, otherwise zero it.
784 	 * XXX - Rather than zero it, should it be the bootpd address? -gwr
785 	 */
786 	(bp->bp_siaddr).s_addr = (hp->flags.bootserver) ?
787 		hp->bootserver.s_addr : 0L;
788 
789 #ifdef	STANFORD_PROM_COMPAT
790 	/*
791 	 * Stanford bootp PROMs (for a Sun?) have no way to leave
792 	 * the boot file name field blank (because the boot file
793 	 * name is automatically generated from some index).
794 	 * As a work-around, this little hack allows those PROMs to
795 	 * specify "sunboot14" with the same effect as a NULL name.
796 	 * (The user specifies boot device 14 or some such magic.)
797 	 */
798 	if (strcmp(bp->bp_file, "sunboot14") == 0)
799 		bp->bp_file[0] = '\0';	/* treat it as unspecified */
800 #endif
801 
802 	/*
803 	 * Fill in the client's proper bootfile.
804 	 *
805 	 * If the client specifies an absolute path, try that file with a
806 	 * ".host" suffix and then without.  If the file cannot be found, no
807 	 * reply is made at all.
808 	 *
809 	 * If the client specifies a null or relative file, use the following
810 	 * table to determine the appropriate action:
811 	 *
812 	 *  Homedir      Bootfile    Client's file
813 	 * specified?   specified?   specification   Action
814 	 * -------------------------------------------------------------------
815 	 *      No          No          Null         Send null filename
816 	 *      No          No          Relative     Discard request
817 	 *      No          Yes         Null         Send if absolute else null
818 	 *      No          Yes         Relative     Discard request     *XXX
819 	 *      Yes         No          Null         Send null filename
820 	 *      Yes         No          Relative     Lookup with ".host"
821 	 *      Yes         Yes         Null         Send home/boot or bootfile
822 	 *      Yes         Yes         Relative     Lookup with ".host" *XXX
823 	 *
824 	 */
825 
826 	/*
827 	 * XXX - I don't like the policy of ignoring a client when the
828 	 * boot file is not accessible.  The TFTP server might not be
829 	 * running on the same machine as the BOOTP server, in which
830 	 * case checking accessibility of the boot file is pointless.
831 	 *
832 	 * Therefore, file accessibility is now demanded ONLY if you
833 	 * define CHECK_FILE_ACCESS in the Makefile options. -gwr
834 	 */
835 
836 	/*
837 	 * The "real" path is as seen by the BOOTP daemon on this
838 	 * machine, while the client path is relative to the TFTP
839 	 * daemon chroot directory (i.e. /tftpboot).
840 	 */
841 	if (hp->flags.tftpdir) {
842 		snprintf(realpath, sizeof(realpath), "%s", hp->tftpdir->string);
843 		clntpath = &realpath[strlen(realpath)];
844 	} else {
845 		realpath[0] = '\0';
846 		clntpath = realpath;
847 	}
848 
849 	/*
850 	 * Determine client's requested homedir and bootfile.
851 	 */
852 	homedir = NULL;
853 	bootfile = NULL;
854 	if (bp->bp_file[0]) {
855 		homedir = bp->bp_file;
856 		bootfile = strrchr(homedir, '/');
857 		if (bootfile) {
858 			if (homedir == bootfile)
859 				homedir = NULL;
860 			*bootfile++ = '\0';
861 		} else {
862 			/* no "/" in the string */
863 			bootfile = homedir;
864 			homedir = NULL;
865 		}
866 		if (debug > 2) {
867 			report(LOG_INFO, "requested path=\"%s\"  file=\"%s\"",
868 				   (homedir) ? homedir : "",
869 				   (bootfile) ? bootfile : "");
870 		}
871 	}
872 
873 	/*
874 	 * Specifications in bootptab override client requested values.
875 	 */
876 	if (hp->flags.homedir)
877 		homedir = hp->homedir->string;
878 	if (hp->flags.bootfile)
879 		bootfile = hp->bootfile->string;
880 
881 	/*
882 	 * Construct bootfile path.
883 	 */
884 	if (homedir) {
885 		if (homedir[0] != '/')
886 			strcat(clntpath, "/");
887 		strcat(clntpath, homedir);
888 		homedir = NULL;
889 	}
890 	if (bootfile) {
891 		if (bootfile[0] != '/')
892 			strcat(clntpath, "/");
893 		strcat(clntpath, bootfile);
894 		bootfile = NULL;
895 	}
896 
897 	/*
898 	 * First try to find the file with a ".host" suffix
899 	 */
900 	n = strlen(clntpath);
901 	strcat(clntpath, ".");
902 	strcat(clntpath, hp->hostname->string);
903 	if (chk_access(realpath, &bootsize) < 0) {
904 		clntpath[n] = 0;			/* Try it without the suffix */
905 		if (chk_access(realpath, &bootsize) < 0) {
906 			/* neither "file.host" nor "file" was found */
907 #ifdef	CHECK_FILE_ACCESS
908 
909 			if (bp->bp_file[0]) {
910 				/*
911 				 * Client wanted specific file
912 				 * and we didn't have it.
913 				 */
914 				report(LOG_NOTICE,
915 					   "requested file not found: \"%s\"", clntpath);
916 				return;
917 			}
918 			/*
919 			 * Client didn't ask for a specific file and we couldn't
920 			 * access the default file, so just zero-out the bootfile
921 			 * field in the packet and continue processing the reply.
922 			 */
923 			bzero(bp->bp_file, sizeof(bp->bp_file));
924 			goto null_file_name;
925 
926 #else	/* CHECK_FILE_ACCESS */
927 
928 			/* Complain only if boot file size was needed. */
929 			if (hp->flags.bootsize_auto) {
930 				report(LOG_ERR, "can not determine size of file \"%s\"",
931 					   clntpath);
932 			}
933 
934 #endif	/* CHECK_FILE_ACCESS */
935 		}
936 	}
937 	strncpy(bp->bp_file, clntpath, BP_FILE_LEN);
938 	if (debug > 2)
939 		report(LOG_INFO, "bootfile=\"%s\"", clntpath);
940 
941 #ifdef	CHECK_FILE_ACCESS
942 null_file_name:
943 #endif	/* CHECK_FILE_ACCESS */
944 
945 
946 	/*
947 	 * Handle vendor options based on magic number.
948 	 */
949 
950 	if (debug > 1) {
951 		report(LOG_INFO, "vendor magic field is %d.%d.%d.%d",
952 			   (int) ((bp->bp_vend)[0]),
953 			   (int) ((bp->bp_vend)[1]),
954 			   (int) ((bp->bp_vend)[2]),
955 			   (int) ((bp->bp_vend)[3]));
956 	}
957 	/*
958 	 * If this host isn't set for automatic vendor info then copy the
959 	 * specific cookie into the bootp packet, thus forcing a certain
960 	 * reply format.  Only force reply format if user specified it.
961 	 */
962 	if (hp->flags.vm_cookie) {
963 		/* Slam in the user specified magic number. */
964 		bcopy(hp->vm_cookie, bp->bp_vend, 4);
965 	}
966 	/*
967 	 * Figure out the format for the vendor-specific info.
968 	 * Note that bp->bp_vend may have been set above.
969 	 */
970 	if (!bcmp(bp->bp_vend, vm_rfc1048, 4)) {
971 		/* RFC1048 conformant bootp client */
972 		dovend_rfc1048(bp, hp, bootsize);
973 		if (debug > 1) {
974 			report(LOG_INFO, "sending reply (with RFC1048 options)");
975 		}
976 	}
977 #ifdef VEND_CMU
978 	else if (!bcmp(bp->bp_vend, vm_cmu, 4)) {
979 		dovend_cmu(bp, hp);
980 		if (debug > 1) {
981 			report(LOG_INFO, "sending reply (with CMU options)");
982 		}
983 	}
984 #endif
985 	else {
986 		if (debug > 1) {
987 			report(LOG_INFO, "sending reply (with no options)");
988 		}
989 	}
990 
991 	dest = (hp->flags.reply_addr) ?
992 		hp->reply_addr.s_addr : 0L;
993 
994 	/* not forwarded */
995 	sendreply(0, dest);
996 }
997 
998 
999 /*
1000  * Process BOOTREPLY packet.
1001  */
1002 PRIVATE void
1003 handle_reply()
1004 {
1005 	if (debug) {
1006 		report(LOG_INFO, "processing boot reply");
1007 	}
1008 	/* forwarded, no destination override */
1009 	sendreply(1, 0);
1010 }
1011 
1012 
1013 /*
1014  * Send a reply packet to the client.  'forward' flag is set if we are
1015  * not the originator of this reply packet.
1016  */
1017 PRIVATE void
1018 sendreply(forward, dst_override)
1019 	int forward;
1020 	int32 dst_override;
1021 {
1022 	struct bootp *bp = (struct bootp *) pktbuf;
1023 	struct in_addr dst;
1024 	u_short port = bootpc_port;
1025 	unsigned char *ha;
1026 	int len, haf;
1027 
1028 	/*
1029 	 * XXX - Should honor bp_flags "broadcast" bit here.
1030 	 * Temporary workaround: use the :ra=ADDR: option to
1031 	 * set the reply address to the broadcast address.
1032 	 */
1033 
1034 	/*
1035 	 * If the destination address was specified explicitly
1036 	 * (i.e. the broadcast address for HP compatiblity)
1037 	 * then send the response to that address.  Otherwise,
1038 	 * act in accordance with RFC951:
1039 	 *   If the client IP address is specified, use that
1040 	 * else if gateway IP address is specified, use that
1041 	 * else make a temporary arp cache entry for the client's
1042 	 * NEW IP/hardware address and use that.
1043 	 */
1044 	if (dst_override) {
1045 		dst.s_addr = dst_override;
1046 		if (debug > 1) {
1047 			report(LOG_INFO, "reply address override: %s",
1048 				   inet_ntoa(dst));
1049 		}
1050 	} else if (bp->bp_ciaddr.s_addr) {
1051 		dst = bp->bp_ciaddr;
1052 	} else if (bp->bp_giaddr.s_addr && forward == 0) {
1053 		dst = bp->bp_giaddr;
1054 		port = bootps_port;
1055 		if (debug > 1) {
1056 			report(LOG_INFO, "sending reply to gateway %s",
1057 				   inet_ntoa(dst));
1058 		}
1059 	} else {
1060 		dst = bp->bp_yiaddr;
1061 		ha = bp->bp_chaddr;
1062 		len = bp->bp_hlen;
1063 		if (len > MAXHADDRLEN)
1064 			len = MAXHADDRLEN;
1065 		haf = (int) bp->bp_htype;
1066 		if (haf == 0)
1067 			haf = HTYPE_ETHERNET;
1068 
1069 		if (debug > 1)
1070 			report(LOG_INFO, "setarp %s - %s",
1071 				   inet_ntoa(dst), haddrtoa(ha, len));
1072 		setarp(s, &dst, haf, ha, len);
1073 	}
1074 
1075 	if ((forward == 0) &&
1076 		(bp->bp_siaddr.s_addr == 0))
1077 	{
1078 		struct ifreq *ifr;
1079 		struct in_addr siaddr;
1080 		/*
1081 		 * If we are originating this reply, we
1082 		 * need to find our own interface address to
1083 		 * put in the bp_siaddr field of the reply.
1084 		 * If this server is multi-homed, pick the
1085 		 * 'best' interface (the one on the same net
1086 		 * as the client).  Of course, the client may
1087 		 * be on the other side of a BOOTP gateway...
1088 		 */
1089 		ifr = getif(s, &dst);
1090 		if (ifr) {
1091 			struct sockaddr_in *sip;
1092 			sip = (struct sockaddr_in *) &(ifr->ifr_addr);
1093 			siaddr = sip->sin_addr;
1094 		} else {
1095 			/* Just use my "official" IP address. */
1096 			siaddr = my_ip_addr;
1097 		}
1098 
1099 		/* XXX - No need to set bp_giaddr here. */
1100 
1101 		/* Finally, set the server address field. */
1102 		bp->bp_siaddr = siaddr;
1103 	}
1104 	/* Set up socket address for send. */
1105 	send_addr.sin_family = AF_INET;
1106 	send_addr.sin_port = htons(port);
1107 	send_addr.sin_addr = dst;
1108 
1109 	/* Send reply with same size packet as request used. */
1110 	if (sendto(s, pktbuf, pktlen, 0,
1111 			   (struct sockaddr *) &send_addr,
1112 			   sizeof(send_addr)) < 0)
1113 	{
1114 		report(LOG_ERR, "sendto: %s", get_network_errmsg());
1115 	}
1116 } /* sendreply */
1117 
1118 
1119 /* nmatch() - now in getif.c */
1120 /* setarp() - now in hwaddr.c */
1121 
1122 
1123 /*
1124  * This call checks read access to a file.  It returns 0 if the file given
1125  * by "path" exists and is publically readable.  A value of -1 is returned if
1126  * access is not permitted or an error occurs.  Successful calls also
1127  * return the file size in bytes using the long pointer "filesize".
1128  *
1129  * The read permission bit for "other" users is checked.  This bit must be
1130  * set for tftpd(8) to allow clients to read the file.
1131  */
1132 
1133 PRIVATE int
1134 chk_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 
1163 PRIVATE void
1164 dovend_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)
1233 PRIVATE void
1234 dovend_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 const 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