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