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