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