xref: /freebsd/libexec/bootpd/bootpd.c (revision 2e620256bd76c449c835c604e404483437743011)
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 #include <sys/types.h>
42 #include <sys/param.h>
43 #include <sys/socket.h>
44 #include <sys/ioctl.h>
45 #include <sys/file.h>
46 #include <sys/time.h>
47 #include <sys/stat.h>
48 #include <sys/utsname.h>
49 
50 #include <net/if.h>
51 #include <netinet/in.h>
52 #include <arpa/inet.h>	/* inet_ntoa */
53 
54 #ifndef	NO_UNISTD
55 #include <unistd.h>
56 #endif
57 
58 #include <stdlib.h>
59 #include <signal.h>
60 #include <stdio.h>
61 #include <string.h>
62 #include <errno.h>
63 #include <ctype.h>
64 #include <netdb.h>
65 #include <paths.h>
66 #include <syslog.h>
67 #include <assert.h>
68 #include <inttypes.h>
69 
70 #ifdef	NO_SETSID
71 # include <fcntl.h>		/* for O_RDONLY, etc */
72 #endif
73 
74 #include "bootp.h"
75 #include "hash.h"
76 #include "hwaddr.h"
77 #include "bootpd.h"
78 #include "dovend.h"
79 #include "getif.h"
80 #include "readfile.h"
81 #include "report.h"
82 #include "tzone.h"
83 #include "patchlevel.h"
84 
85 #ifndef CONFIG_FILE
86 #define CONFIG_FILE		"/etc/bootptab"
87 #endif
88 #ifndef DUMPTAB_FILE
89 #define DUMPTAB_FILE		"/tmp/bootpd.dump"
90 #endif
91 
92 
93 
94 /*
95  * Externals, forward declarations, and global variables
96  */
97 
98 extern void dumptab(char *);
99 
100 PRIVATE void catcher(int);
101 PRIVATE int chk_access(char *, int32 *);
102 #ifdef VEND_CMU
103 PRIVATE void dovend_cmu(struct bootp *, struct host *);
104 #endif
105 PRIVATE void dovend_rfc1048(struct bootp *, struct host *, int32);
106 PRIVATE void handle_reply(void);
107 PRIVATE void handle_request(void);
108 PRIVATE void sendreply(int forward, int32 dest_override);
109 PRIVATE void usage(void);
110 
111 /*
112  * IP port numbers for client and server obtained from /etc/services
113  */
114 
115 u_short bootps_port, bootpc_port;
116 
117 
118 /*
119  * Internet socket and interface config structures
120  */
121 
122 struct sockaddr_in bind_addr;	/* Listening */
123 struct sockaddr_in recv_addr;	/* Packet source */
124 struct sockaddr_in send_addr;	/*  destination */
125 
126 
127 /*
128  * option defaults
129  */
130 int debug = 0;					/* Debugging flag (level) */
131 struct timeval actualtimeout =
132 {								/* fifteen minutes */
133 	15 * 60L,					/* tv_sec */
134 	0							/* tv_usec */
135 };
136 int arpmod = TRUE;				/* modify the ARP table */
137 
138 /*
139  * General
140  */
141 
142 int s;							/* Socket file descriptor */
143 char *pktbuf;					/* Receive packet buffer */
144 int pktlen;
145 char *progname;
146 char *chdir_path;
147 struct in_addr my_ip_addr;
148 
149 static const char *hostname;
150 static char default_hostname[MAXHOSTNAMELEN];
151 
152 /* Flags set by signal catcher. */
153 PRIVATE int do_readtab = 0;
154 PRIVATE int do_dumptab = 0;
155 
156 /*
157  * Globals below are associated with the bootp database file (bootptab).
158  */
159 
160 char *bootptab = CONFIG_FILE;
161 char *bootpd_dump = DUMPTAB_FILE;
162 
163 
164 
165 /*
166  * Initialization such as command-line processing is done and then the
167  * main server loop is started.
168  */
169 
170 int
171 main(int argc, char **argv)
172 {
173 	struct timeval *timeout;
174 	struct bootp *bp;
175 	struct servent *servp;
176 	struct hostent *hep;
177 	char *stmp;
178 	socklen_t ba_len, ra_len;
179 	int n;
180 	int nfound;
181 	fd_set readfds;
182 	int standalone;
183 #ifdef	SA_NOCLDSTOP	/* Have POSIX sigaction(2). */
184 	struct sigaction sa;
185 #endif
186 
187 	progname = strrchr(argv[0], '/');
188 	if (progname) progname++;
189 	else progname = argv[0];
190 
191 	/*
192 	 * Initialize logging.
193 	 */
194 	report_init(0);				/* uses progname */
195 
196 	/*
197 	 * Log startup
198 	 */
199 	report(LOG_INFO, "version %s.%d", VERSION, PATCHLEVEL);
200 
201 	/* Debugging for compilers with struct padding. */
202 	assert(sizeof(struct bootp) == BP_MINPKTSZ);
203 
204 	/* Get space for receiving packets and composing replies. */
205 	pktbuf = malloc(MAX_MSG_SIZE);
206 	if (!pktbuf) {
207 		report(LOG_ERR, "malloc failed");
208 		exit(1);
209 	}
210 	bp = (struct bootp *) pktbuf;
211 
212 	/*
213 	 * Check to see if a socket was passed to us from inetd.
214 	 *
215 	 * Use getsockname() to determine if descriptor 0 is indeed a socket
216 	 * (and thus we are probably a child of inetd) or if it is instead
217 	 * something else and we are running standalone.
218 	 */
219 	s = 0;
220 	ba_len = sizeof(bind_addr);
221 	bzero((char *) &bind_addr, ba_len);
222 	errno = 0;
223 	standalone = TRUE;
224 	if (getsockname(s, (struct sockaddr *) &bind_addr, &ba_len) == 0) {
225 		/*
226 		 * Descriptor 0 is a socket.  Assume we are a child of inetd.
227 		 */
228 		if (bind_addr.sin_family == AF_INET) {
229 			standalone = FALSE;
230 			bootps_port = ntohs(bind_addr.sin_port);
231 		} else {
232 			/* Some other type of socket? */
233 			report(LOG_ERR, "getsockname: not an INET socket");
234 		}
235 	}
236 
237 	/*
238 	 * Set defaults that might be changed by option switches.
239 	 */
240 	stmp = NULL;
241 	timeout = &actualtimeout;
242 
243 	if (gethostname(default_hostname, sizeof(default_hostname) - 1) < 0) {
244 		report(LOG_ERR, "bootpd: can't get hostname\n");
245 		exit(1);
246 	}
247 	default_hostname[sizeof(default_hostname) - 1] = '\0';
248 	hostname = default_hostname;
249 
250 	/*
251 	 * Read switches.
252 	 */
253 	for (argc--, argv++; argc > 0; argc--, argv++) {
254 		if (argv[0][0] != '-')
255 			break;
256 		switch (argv[0][1]) {
257 
258 		case 'a':				/* don't modify the ARP table */
259 			arpmod = FALSE;
260 			break;
261 		case 'c':				/* chdir_path */
262 			if (argv[0][2]) {
263 				stmp = &(argv[0][2]);
264 			} else {
265 				argc--;
266 				argv++;
267 				stmp = argv[0];
268 			}
269 			if (!stmp || (stmp[0] != '/')) {
270 				report(LOG_ERR,
271 						"bootpd: invalid chdir specification\n");
272 				break;
273 			}
274 			chdir_path = stmp;
275 			break;
276 
277 		case 'd':				/* debug level */
278 			if (argv[0][2]) {
279 				stmp = &(argv[0][2]);
280 			} else if (argv[1] && argv[1][0] == '-') {
281 				/*
282 				 * Backwards-compatible behavior:
283 				 * no parameter, so just increment the debug flag.
284 				 */
285 				debug++;
286 				break;
287 			} else {
288 				argc--;
289 				argv++;
290 				stmp = argv[0];
291 			}
292 			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
293 				report(LOG_ERR,
294 						"%s: invalid debug level\n", progname);
295 				break;
296 			}
297 			debug = n;
298 			break;
299 
300 		case 'h':				/* override hostname */
301 			if (argv[0][2]) {
302 				stmp = &(argv[0][2]);
303 			} else {
304 				argc--;
305 				argv++;
306 				stmp = argv[0];
307 			}
308 			if (!stmp) {
309 				report(LOG_ERR,
310 						"bootpd: missing hostname\n");
311 				break;
312 			}
313 			hostname = stmp;
314 			break;
315 
316 		case 'i':				/* inetd mode */
317 			standalone = FALSE;
318 			break;
319 
320 		case 's':				/* standalone mode */
321 			standalone = TRUE;
322 			break;
323 
324 		case 't':				/* timeout */
325 			if (argv[0][2]) {
326 				stmp = &(argv[0][2]);
327 			} else {
328 				argc--;
329 				argv++;
330 				stmp = argv[0];
331 			}
332 			if (!stmp || (sscanf(stmp, "%d", &n) != 1) || (n < 0)) {
333 				report(LOG_ERR,
334 						"%s: invalid timeout specification\n", progname);
335 				break;
336 			}
337 			actualtimeout.tv_sec = (int32) (60 * n);
338 			/*
339 			 * If the actual timeout is zero, pass a NULL pointer
340 			 * to select so it blocks indefinitely, otherwise,
341 			 * point to the actual timeout value.
342 			 */
343 			timeout = (n > 0) ? &actualtimeout : NULL;
344 			break;
345 
346 		default:
347 			report(LOG_ERR, "%s: unknown switch: -%c\n",
348 					progname, argv[0][1]);
349 			usage();
350 			break;
351 
352 		} /* switch */
353 	} /* for args */
354 
355 	/*
356 	 * Override default file names if specified on the command line.
357 	 */
358 	if (argc > 0)
359 		bootptab = argv[0];
360 
361 	if (argc > 1)
362 		bootpd_dump = argv[1];
363 
364 	/*
365 	 * Get my hostname and IP address.
366 	 */
367 
368 	hep = gethostbyname(hostname);
369 	if (!hep) {
370 		report(LOG_ERR, "Can not get my IP address\n");
371 		exit(1);
372 	}
373 	bcopy(hep->h_addr, (char *)&my_ip_addr, sizeof(my_ip_addr));
374 
375 	if (standalone) {
376 		/*
377 		 * Go into background and disassociate from controlling terminal.
378 		 */
379 		if (debug < 3) {
380 			if (fork())
381 				exit(0);
382 #ifdef	NO_SETSID
383 			setpgrp(0,0);
384 #ifdef TIOCNOTTY
385 			n = open(_PATH_TTY, O_RDWR);
386 			if (n >= 0) {
387 				ioctl(n, TIOCNOTTY, (char *) 0);
388 				(void) close(n);
389 			}
390 #endif	/* TIOCNOTTY */
391 #else	/* SETSID */
392 			if (setsid() < 0)
393 				perror("setsid");
394 #endif	/* SETSID */
395 		} /* if debug < 3 */
396 
397 		/*
398 		 * Nuke any timeout value
399 		 */
400 		timeout = NULL;
401 
402 	} /* if standalone (1st) */
403 
404 	/* Set the cwd (i.e. to /tftpboot) */
405 	if (chdir_path) {
406 		if (chdir(chdir_path) < 0)
407 			report(LOG_ERR, "%s: chdir failed", chdir_path);
408 	}
409 
410 	/* Get the timezone. */
411 	tzone_init();
412 
413 	/* Allocate hash tables. */
414 	rdtab_init();
415 
416 	/*
417 	 * Read the bootptab file.
418 	 */
419 	readtab(1);					/* force read */
420 
421 	if (standalone) {
422 
423 		/*
424 		 * Create a socket.
425 		 */
426 		if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
427 			report(LOG_ERR, "socket: %s", get_network_errmsg());
428 			exit(1);
429 		}
430 
431 		/*
432 		 * Get server's listening port number
433 		 */
434 		servp = getservbyname("bootps", "udp");
435 		if (servp) {
436 			bootps_port = ntohs((u_short) servp->s_port);
437 		} else {
438 			bootps_port = (u_short) IPPORT_BOOTPS;
439 			report(LOG_ERR,
440 				"bootps/udp: unknown service -- using port %d",
441 				   bootps_port);
442 		}
443 
444 		/*
445 		 * Bind socket to BOOTPS port.
446 		 */
447 		bind_addr.sin_family = AF_INET;
448 		bind_addr.sin_addr.s_addr = INADDR_ANY;
449 		bind_addr.sin_port = htons(bootps_port);
450 		if (bind(s, (struct sockaddr *) &bind_addr,
451 				 sizeof(bind_addr)) < 0)
452 		{
453 			report(LOG_ERR, "bind: %s", get_network_errmsg());
454 			exit(1);
455 		}
456 	} /* if standalone (2nd)*/
457 
458 	/*
459 	 * Get destination port number so we can reply to client
460 	 */
461 	servp = getservbyname("bootpc", "udp");
462 	if (servp) {
463 		bootpc_port = ntohs(servp->s_port);
464 	} else {
465 		report(LOG_ERR,
466 			   "bootpc/udp: unknown service -- using port %d",
467 			   IPPORT_BOOTPC);
468 		bootpc_port = (u_short) IPPORT_BOOTPC;
469 	}
470 
471 	/*
472 	 * Set up signals to read or dump the table.
473 	 */
474 #ifdef	SA_NOCLDSTOP	/* Have POSIX sigaction(2). */
475 	sa.sa_handler = catcher;
476 	sigemptyset(&sa.sa_mask);
477 	sa.sa_flags = 0;
478 	if (sigaction(SIGHUP, &sa, NULL) < 0) {
479 		report(LOG_ERR, "sigaction: %s", get_errmsg());
480 		exit(1);
481 	}
482 	if (sigaction(SIGUSR1, &sa, NULL) < 0) {
483 		report(LOG_ERR, "sigaction: %s", get_errmsg());
484 		exit(1);
485 	}
486 #else	/* SA_NOCLDSTOP */
487 	/* Old-fashioned UNIX signals */
488 	if ((int) signal(SIGHUP, catcher) < 0) {
489 		report(LOG_ERR, "signal: %s", get_errmsg());
490 		exit(1);
491 	}
492 	if ((int) signal(SIGUSR1, catcher) < 0) {
493 		report(LOG_ERR, "signal: %s", get_errmsg());
494 		exit(1);
495 	}
496 #endif	/* SA_NOCLDSTOP */
497 
498 	/*
499 	 * Process incoming requests.
500 	 */
501 	FD_ZERO(&readfds);
502 	for (;;) {
503 		struct timeval tv;
504 
505 		FD_SET(s, &readfds);
506 		if (timeout)
507 			tv = *timeout;
508 
509 		nfound = select(s + 1, &readfds, NULL, NULL,
510 						(timeout) ? &tv : NULL);
511 		if (nfound < 0) {
512 			if (errno != EINTR) {
513 				report(LOG_ERR, "select: %s", get_errmsg());
514 			}
515 			/*
516 			 * Call readtab() or dumptab() here to avoid the
517 			 * dangers of doing I/O from a signal handler.
518 			 */
519 			if (do_readtab) {
520 				do_readtab = 0;
521 				readtab(1);		/* force read */
522 			}
523 			if (do_dumptab) {
524 				do_dumptab = 0;
525 				dumptab(bootpd_dump);
526 			}
527 			continue;
528 		}
529 		if (!FD_ISSET(s, &readfds)) {
530 			if (debug > 1)
531 				report(LOG_INFO, "exiting after %jd minutes of inactivity",
532 					   (intmax_t)actualtimeout.tv_sec / 60);
533 			exit(0);
534 		}
535 		ra_len = sizeof(recv_addr);
536 		n = recvfrom(s, pktbuf, MAX_MSG_SIZE, 0,
537 					 (struct sockaddr *) &recv_addr, &ra_len);
538 		if (n <= 0) {
539 			continue;
540 		}
541 		if (debug > 1) {
542 			report(LOG_INFO, "recvd pkt from IP addr %s",
543 				   inet_ntoa(recv_addr.sin_addr));
544 		}
545 		if (n < sizeof(struct bootp)) {
546 			if (debug) {
547 				report(LOG_NOTICE, "received short packet");
548 			}
549 			continue;
550 		}
551 		pktlen = n;
552 
553 		readtab(0);				/* maybe re-read bootptab */
554 
555 		switch (bp->bp_op) {
556 		case BOOTREQUEST:
557 			handle_request();
558 			break;
559 		case BOOTREPLY:
560 			handle_reply();
561 			break;
562 		}
563 	}
564 	return 0;
565 }
566 
567 
568 
569 
570 /*
571  * Print "usage" message and exit
572  */
573 
574 PRIVATE void
575 usage()
576 {
577 	fprintf(stderr,
578 		"usage: bootpd [-a] [-i | -s] [-c chdir-path] [-d level] [-h hostname]\n"
579 		"              [-t timeout] [bootptab [dumpfile]]\n");
580 	fprintf(stderr, "       -a\tdon't modify ARP table\n");
581 	fprintf(stderr, "       -c n\tset current directory\n");
582 	fprintf(stderr, "       -d n\tset debug level\n");
583 	fprintf(stderr, "       -h n\tset the hostname to listen on\n");
584 	fprintf(stderr, "       -i\tforce inetd mode (run as child of inetd)\n");
585 	fprintf(stderr, "       -s\tforce standalone mode (run without inetd)\n");
586 	fprintf(stderr, "       -t n\tset inetd exit timeout to n minutes\n");
587 	exit(1);
588 }
589 
590 /* Signal catchers */
591 PRIVATE void
592 catcher(int sig)
593 {
594 	if (sig == SIGHUP)
595 		do_readtab = 1;
596 	if (sig == SIGUSR1)
597 		do_dumptab = 1;
598 #if	!defined(SA_NOCLDSTOP) && defined(SYSV)
599 	/* For older "System V" derivatives with no sigaction(). */
600 	signal(sig, catcher);
601 #endif
602 }
603 
604 
605 
606 /*
607  * Process BOOTREQUEST packet.
608  *
609  * Note:  This version of the bootpd.c server never forwards
610  * a request to another server.  That is the job of a gateway
611  * program such as the "bootpgw" program included here.
612  *
613  * (Also this version does not interpret the hostname field of
614  * the request packet;  it COULD do a name->address lookup and
615  * forward the request there.)
616  */
617 PRIVATE void
618 handle_request(void)
619 {
620 	struct bootp *bp = (struct bootp *) pktbuf;
621 	struct host *hp = NULL;
622 	struct host dummyhost;
623 	int32 bootsize = 0;
624 	unsigned hlen, hashcode;
625 	int32 dest;
626 	char realpath[1024];
627 	char *clntpath;
628 	char *homedir, *bootfile;
629 	int n;
630 
631 	if (bp->bp_htype >= hwinfocnt) {
632 		report(LOG_NOTICE, "bad hw addr type %u", bp->bp_htype);
633 		return;
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 doesn't 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(void)
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(int forward, int32 dst_override)
1012 {
1013 	struct bootp *bp = (struct bootp *) pktbuf;
1014 	struct in_addr dst;
1015 	u_short port = bootpc_port;
1016 	unsigned char *ha;
1017 	int len, haf;
1018 
1019 	/*
1020 	 * XXX - Should honor bp_flags "broadcast" bit here.
1021 	 * Temporary workaround: use the :ra=ADDR: option to
1022 	 * set the reply address to the broadcast address.
1023 	 */
1024 
1025 	/*
1026 	 * If the destination address was specified explicitly
1027 	 * (i.e. the broadcast address for HP compatibility)
1028 	 * then send the response to that address.  Otherwise,
1029 	 * act in accordance with RFC951:
1030 	 *   If the client IP address is specified, use that
1031 	 * else if gateway IP address is specified, use that
1032 	 * else make a temporary arp cache entry for the client's
1033 	 * NEW IP/hardware address and use that.
1034 	 */
1035 	if (dst_override) {
1036 		dst.s_addr = dst_override;
1037 		if (debug > 1) {
1038 			report(LOG_INFO, "reply address override: %s",
1039 				   inet_ntoa(dst));
1040 		}
1041 	} else if (bp->bp_ciaddr.s_addr) {
1042 		dst = bp->bp_ciaddr;
1043 	} else if (bp->bp_giaddr.s_addr && forward == 0) {
1044 		dst = bp->bp_giaddr;
1045 		port = bootps_port;
1046 		if (debug > 1) {
1047 			report(LOG_INFO, "sending reply to gateway %s",
1048 				   inet_ntoa(dst));
1049 		}
1050 	} else {
1051 		dst = bp->bp_yiaddr;
1052 		ha = bp->bp_chaddr;
1053 		len = bp->bp_hlen;
1054 		if (len > MAXHADDRLEN)
1055 			len = MAXHADDRLEN;
1056 		haf = (int) bp->bp_htype;
1057 		if (haf == 0)
1058 			haf = HTYPE_ETHERNET;
1059 
1060 		if (arpmod) {
1061 			if (debug > 1)
1062 				report(LOG_INFO, "setarp %s - %s",
1063 					   inet_ntoa(dst), haddrtoa(ha, len));
1064 			setarp(s, &dst, haf, ha, len);
1065 		}
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 publicly 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(char *path, int32 *filesize)
1128 {
1129 	struct stat st;
1130 
1131 	if ((stat(path, &st) == 0) && (st.st_mode & (S_IREAD >> 6))) {
1132 		*filesize = (int32) st.st_size;
1133 		return 0;
1134 	} else {
1135 		return -1;
1136 	}
1137 }
1138 
1139 
1140 /*
1141  * Now in dumptab.c :
1142  *	dumptab()
1143  *	dump_host()
1144  *	list_ipaddresses()
1145  */
1146 
1147 #ifdef VEND_CMU
1148 
1149 /*
1150  * Insert the CMU "vendor" data for the host pointed to by "hp" into the
1151  * bootp packet pointed to by "bp".
1152  */
1153 
1154 PRIVATE void
1155 dovend_cmu(struct bootp *bp, struct host *hp)
1156 {
1157 	struct cmu_vend *vendp;
1158 	struct in_addr_list *taddr;
1159 
1160 	/*
1161 	 * Initialize the entire vendor field to zeroes.
1162 	 */
1163 	bzero(bp->bp_vend, sizeof(bp->bp_vend));
1164 
1165 	/*
1166 	 * Fill in vendor information. Subnet mask, default gateway,
1167 	 * domain name server, ien name server, time server
1168 	 */
1169 	vendp = (struct cmu_vend *) bp->bp_vend;
1170 	strcpy(vendp->v_magic, (char *)vm_cmu);
1171 	if (hp->flags.subnet_mask) {
1172 		(vendp->v_smask).s_addr = hp->subnet_mask.s_addr;
1173 		(vendp->v_flags) |= VF_SMASK;
1174 		if (hp->flags.gateway) {
1175 			(vendp->v_dgate).s_addr = hp->gateway->addr->s_addr;
1176 		}
1177 	}
1178 	if (hp->flags.domain_server) {
1179 		taddr = hp->domain_server;
1180 		if (taddr->addrcount > 0) {
1181 			(vendp->v_dns1).s_addr = (taddr->addr)[0].s_addr;
1182 			if (taddr->addrcount > 1) {
1183 				(vendp->v_dns2).s_addr = (taddr->addr)[1].s_addr;
1184 			}
1185 		}
1186 	}
1187 	if (hp->flags.name_server) {
1188 		taddr = hp->name_server;
1189 		if (taddr->addrcount > 0) {
1190 			(vendp->v_ins1).s_addr = (taddr->addr)[0].s_addr;
1191 			if (taddr->addrcount > 1) {
1192 				(vendp->v_ins2).s_addr = (taddr->addr)[1].s_addr;
1193 			}
1194 		}
1195 	}
1196 	if (hp->flags.time_server) {
1197 		taddr = hp->time_server;
1198 		if (taddr->addrcount > 0) {
1199 			(vendp->v_ts1).s_addr = (taddr->addr)[0].s_addr;
1200 			if (taddr->addrcount > 1) {
1201 				(vendp->v_ts2).s_addr = (taddr->addr)[1].s_addr;
1202 			}
1203 		}
1204 	}
1205 	/* Log message now done by caller. */
1206 } /* dovend_cmu */
1207 
1208 #endif /* VEND_CMU */
1209 
1210 
1211 
1212 /*
1213  * Insert the RFC1048 vendor data for the host pointed to by "hp" into the
1214  * bootp packet pointed to by "bp".
1215  */
1216 #define	NEED(LEN, MSG) do \
1217 	if (bytesleft < (LEN)) { \
1218 		report(LOG_NOTICE, noroom, \
1219 			   hp->hostname->string, MSG); \
1220 		return; \
1221 	} while (0)
1222 PRIVATE void
1223 dovend_rfc1048(struct bootp *bp, struct host *hp, int32 bootsize)
1224 {
1225 	int bytesleft, len;
1226 	byte *vp;
1227 
1228 	static const char noroom[] = "%s: No room for \"%s\" option";
1229 
1230 	vp = bp->bp_vend;
1231 
1232 	if (hp->flags.msg_size) {
1233 		pktlen = hp->msg_size;
1234 	} else {
1235 		/*
1236 		 * If the request was longer than the official length, build
1237 		 * a response of that same length where the additional length
1238 		 * is assumed to be part of the bp_vend (options) area.
1239 		 */
1240 		if (pktlen > sizeof(*bp)) {
1241 			if (debug > 1)
1242 				report(LOG_INFO, "request message length=%d", pktlen);
1243 		}
1244 		/*
1245 		 * Check whether the request contains the option:
1246 		 * Maximum DHCP Message Size (RFC1533 sec. 9.8)
1247 		 * and if so, override the response length with its value.
1248 		 * This request must lie within the first BP_VEND_LEN
1249 		 * bytes of the option space.
1250 		 */
1251 		{
1252 			byte *p, *ep;
1253 			byte tag, len;
1254 			short msgsz = 0;
1255 
1256 			p = vp + 4;
1257 			ep = p + BP_VEND_LEN - 4;
1258 			while (p < ep) {
1259 				tag = *p++;
1260 				/* Check for tags with no data first. */
1261 				if (tag == TAG_PAD)
1262 					continue;
1263 				if (tag == TAG_END)
1264 					break;
1265 				/* Now scan the length byte. */
1266 				len = *p++;
1267 				switch (tag) {
1268 				case TAG_MAX_MSGSZ:
1269 					if (len == 2) {
1270 						bcopy(p, (char*)&msgsz, 2);
1271 						msgsz = ntohs(msgsz);
1272 					}
1273 					break;
1274 				case TAG_SUBNET_MASK:
1275 					/* XXX - Should preserve this if given... */
1276 					break;
1277 				} /* swtich */
1278 				p += len;
1279 			}
1280 
1281 			if (msgsz > sizeof(*bp) + BP_MSG_OVERHEAD) {
1282 				if (debug > 1)
1283 					report(LOG_INFO, "request has DHCP msglen=%d", msgsz);
1284 				pktlen = msgsz - BP_MSG_OVERHEAD;
1285 			}
1286 		}
1287 	}
1288 
1289 	if (pktlen < sizeof(*bp)) {
1290 		report(LOG_ERR, "invalid response length=%d", pktlen);
1291 		pktlen = sizeof(*bp);
1292 	}
1293 	bytesleft = ((byte*)bp + pktlen) - vp;
1294 	if (pktlen > sizeof(*bp)) {
1295 		if (debug > 1)
1296 			report(LOG_INFO, "extended reply, length=%d, options=%d",
1297 				   pktlen, bytesleft);
1298 	}
1299 
1300 	/* Copy in the magic cookie */
1301 	bcopy(vm_rfc1048, vp, 4);
1302 	vp += 4;
1303 	bytesleft -= 4;
1304 
1305 	if (hp->flags.subnet_mask) {
1306 		/* always enough room here. */
1307 		*vp++ = TAG_SUBNET_MASK;/* -1 byte  */
1308 		*vp++ = 4;				/* -1 byte  */
1309 		insert_u_long(hp->subnet_mask.s_addr, &vp);	/* -4 bytes */
1310 		bytesleft -= 6;			/* Fix real count */
1311 		if (hp->flags.gateway) {
1312 			(void) insert_ip(TAG_GATEWAY,
1313 							 hp->gateway,
1314 							 &vp, &bytesleft);
1315 		}
1316 	}
1317 	if (hp->flags.bootsize) {
1318 		/* always enough room here */
1319 		bootsize = (hp->flags.bootsize_auto) ?
1320 			((bootsize + 511) / 512) : (hp->bootsize);	/* Round up */
1321 		*vp++ = TAG_BOOT_SIZE;
1322 		*vp++ = 2;
1323 		*vp++ = (byte) ((bootsize >> 8) & 0xFF);
1324 		*vp++ = (byte) (bootsize & 0xFF);
1325 		bytesleft -= 4;			/* Tag, length, and 16 bit blocksize */
1326 	}
1327 	/*
1328 	 * This one is special: Remaining options go in the ext file.
1329 	 * Only the subnet_mask, bootsize, and gateway should precede.
1330 	 */
1331 	if (hp->flags.exten_file) {
1332 		/*
1333 		 * Check for room for exten_file.  Add 3 to account for
1334 		 * TAG_EXTEN_FILE, length, and TAG_END.
1335 		 */
1336 		len = strlen(hp->exten_file->string);
1337 		NEED((len + 3), "ef");
1338 		*vp++ = TAG_EXTEN_FILE;
1339 		*vp++ = (byte) (len & 0xFF);
1340 		bcopy(hp->exten_file->string, vp, len);
1341 		vp += len;
1342 		*vp++ = TAG_END;
1343 		bytesleft -= len + 3;
1344 		return;					/* no more options here. */
1345 	}
1346 	/*
1347 	 * The remaining options are inserted by the following
1348 	 * function (which is shared with bootpef.c).
1349 	 * Keep back one byte for the TAG_END.
1350 	 */
1351 	len = dovend_rfc1497(hp, vp, bytesleft - 1);
1352 	vp += len;
1353 	bytesleft -= len;
1354 
1355 	/* There should be at least one byte left. */
1356 	NEED(1, "(end)");
1357 	*vp++ = TAG_END;
1358 	bytesleft--;
1359 
1360 	/* Log message done by caller. */
1361 	if (bytesleft > 0) {
1362 		/*
1363 		 * Zero out any remaining part of the vendor area.
1364 		 */
1365 		bzero(vp, bytesleft);
1366 	}
1367 } /* dovend_rfc1048 */
1368 #undef	NEED
1369 
1370 
1371 /*
1372  * Now in readfile.c:
1373  * 	hwlookcmp()
1374  *	iplookcmp()
1375  */
1376 
1377 /* haddrtoa() - now in hwaddr.c */
1378 /*
1379  * Now in dovend.c:
1380  * insert_ip()
1381  * insert_generic()
1382  * insert_u_long()
1383  */
1384 
1385 /* get_errmsg() - now in report.c */
1386 
1387 /*
1388  * Local Variables:
1389  * tab-width: 4
1390  * c-indent-level: 4
1391  * c-argdecl-indent: 4
1392  * c-continued-statement-offset: 4
1393  * c-continued-brace-offset: -4
1394  * c-label-offset: -4
1395  * c-brace-offset: 0
1396  * End:
1397  */
1398