1/*
2 * Copyright (c) 1999 - 2008, 2011, 2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24 * bootpd.c
25 * - BOOTP/DHCP server main
26 * - see RFC951, RFC2131, RFC2132 for details on the BOOTP protocol,
27 *   BOOTP extensions/DHCP options, and the DHCP protocol
28 */
29
30/*
31 * Modification History
32 * 01/22/86	Croft	created.
33 *
34 * 03/19/86	Lougheed  Converted to run under 4.3 BSD inetd.
35 *
36 * 09/06/88	King	Added NeXT interrim support.
37 *
38 * 02/23/98	Dieter Siegmund (dieter@apple.com)
39 *		- complete overhaul
40 *		- added specialized Mac NC support
41 *		- removed the NeXT "Sexy Net Init" code that performed
42 *		  a proprietary form of dynamic BOOTP, since this
43 *		  functionality is replaced by DHCP
44 *		- added ability to respond to requests originating from
45 *		  a specific set of interfaces
46 *		- added rfc2132 option handling
47 *
48 * June 5, 1998 	Dieter Siegmund (dieter@apple.com)
49 * - do lookups using netinfo calls directly to be able to read/write
50 *   entries and get subnet-specific bindings
51 *
52 * Oct 19, 1998		Dieter Siegmund (dieter@apple.com)
53 * - provide domain name servers for this server if not explicitly
54 *   configured otherwise
55 * Mar 29, 1999		Dieter Siegmund (dieter@apple.com)
56 * - added code to do ethernet lookups with or without leading zeroes
57 * April 27, 2000	Dieter Siegmund (dieter@apple.com)
58 * - added netinfo host caching to avoid denial of service
59 *   attacks and handle any valid format for the ethernet address
60 *   i.e. leading zeroes, capitalization, etc.
61 * - eliminated practice of supplying a default bootfile
62 * - eliminated ability to read host entries from a file
63 */
64
65#include <unistd.h>
66#include <stdlib.h>
67#include <sys/stat.h>
68#include <sys/socket.h>
69#include <sys/ioctl.h>
70#include <sys/file.h>
71#include <sys/time.h>
72#include <sys/types.h>
73#include <net/if.h>
74#include <netinet/in.h>
75#include <netinet/in_systm.h>
76#include <netinet/ip.h>
77#include <netinet/udp.h>
78#include <netinet/bootp.h>
79#include <netinet/if_ether.h>
80#include <net/if_arp.h>
81#include <mach/boolean.h>
82#include <signal.h>
83#include <stdio.h>
84#include <stdarg.h>
85#include <string.h>
86#include <errno.h>
87#include <ctype.h>
88#include <netdb.h>
89#include <syslog.h>
90#include <arpa/inet.h>
91#include <arpa/nameser.h>
92#include <sys/uio.h>
93#include <resolv.h>
94#include <CoreFoundation/CFString.h>
95#include <CoreFoundation/CFNumber.h>
96#include <CoreFoundation/CFArray.h>
97#include <CoreFoundation/CFDictionary.h>
98#include <SystemConfiguration/SCValidation.h>
99
100#include "arp.h"
101#include "netinfo.h"
102#include "interfaces.h"
103#include "inetroute.h"
104#include "subnets.h"
105#include "dhcp_options.h"
106#include "DNSNameList.h"
107#include "rfc_options.h"
108#include "macNC.h"
109#include "bsdpd.h"
110#include "NICache.h"
111#include "host_identifier.h"
112#include "dhcpd.h"
113#include "bootpd.h"
114#include "bsdp.h"
115#include "bootp_transmit.h"
116#include "util.h"
117#include "cfutil.h"
118#include "bootpd-plist.h"
119#include "bootpdfile.h"
120#include "bootplookup.h"
121
122#define CFGPROP_DHCP_IGNORE_CLIENT_IDENTIFIER	"dhcp_ignore_client_identifier"
123#define CFGPROP_DETECT_OTHER_DHCP_SERVER	"detect_other_dhcp_server"
124#define CFGPROP_BOOTP_ENABLED		"bootp_enabled"
125#define CFGPROP_DHCP_ENABLED		"dhcp_enabled"
126#if !TARGET_OS_EMBEDDED
127#define CFGPROP_OLD_NETBOOT_ENABLED	"old_netboot_enabled"
128#define CFGPROP_NETBOOT_ENABLED		"netboot_enabled"
129#define CFGPROP_USE_OPEN_DIRECTORY	"use_open_directory"
130#endif /* !TARGET_OS_EMBEDDED */
131#define CFGPROP_RELAY_ENABLED		"relay_enabled"
132#define CFGPROP_ALLOW			"allow"
133#define CFGPROP_DENY			"deny"
134#define CFGPROP_REPLY_THRESHOLD_SECONDS	"reply_threshold_seconds"
135#define CFGPROP_RELAY_IP_LIST		"relay_ip_list"
136#define CFGPROP_USE_SERVER_CONFIG_FOR_DHCP_OPTIONS "use_server_config_for_dhcp_options"
137
138/*
139 * On some platforms the root filesystem is mounted read-only;
140 * make sure that the plist points to a user-writeable location.
141 */
142#if TARGET_OS_EMBEDDED
143#define	BOOTPD_PLIST_ROOT	"/Library/Preferences/SystemConfiguration"
144#else
145#define	BOOTPD_PLIST_ROOT	"/etc"
146#endif /* TARGET_OS_EMBEDDED */
147#define	BOOTPD_PLIST_PATH		BOOTPD_PLIST_ROOT "/bootpd.plist"
148
149/* local defines */
150#define	MAXIDLE			(5*60)	/* we hang around for five minutes */
151#define SERVICE_BOOTP		0x00000001
152#define SERVICE_DHCP		0x00000002
153#define SERVICE_OLD_NETBOOT	0x00000004
154#define SERVICE_NETBOOT		0x00000008
155#define SERVICE_RELAY		0x00000010
156
157/* global variables: */
158char		boot_tftp_dir[128] = "/private/tftpboot";
159int		bootp_socket = -1;
160int		debug = 0;
161bool		detect_other_dhcp_server = FALSE;
162bool		dhcp_ignore_client_identifier = FALSE;
163int		quiet = 0;
164uint32_t	reply_threshold_seconds = 0;
165unsigned short	server_priority = BSDP_PRIORITY_BASE;
166char *		testing_control = "";
167char		server_name[MAXHOSTNAMELEN + 1];
168SubnetListRef	subnets;
169/*
170 * transmit_buffer is cast to some struct types containing short fields;
171 * force it to be aligned as much as an int
172 */
173static int	transmit_buffer_aligned[512];
174char *		transmit_buffer = (char *)transmit_buffer_aligned;
175
176#if ! TARGET_OS_EMBEDDED
177bool		use_open_directory = TRUE;
178#endif /* ! TARGET_OS_EMBEDDED */
179int		verbose = 0;
180
181/* local types */
182
183/* local variables */
184static boolean_t		S_bootfile_noexist_reply = TRUE;
185static boolean_t		S_do_bootp;
186#if !TARGET_OS_EMBEDDED
187static boolean_t		S_do_netboot;
188static boolean_t		S_do_old_netboot;
189#endif /* !TARGET_OS_EMBEDDED */
190static boolean_t		S_do_dhcp;
191static boolean_t		S_do_relay;
192static struct in_addr *		S_dns_servers = NULL;
193static int			S_dns_servers_count = 0;
194static char *			S_domain_name = NULL;
195static uint8_t *		S_domain_search = NULL;
196static int			S_domain_search_size = 0;
197static ptrlist_t		S_if_list;
198static interface_list_t *	S_interfaces;
199static inetroute_list_t *	S_inetroutes = NULL;
200static u_short			S_ipport_client = IPPORT_BOOTPC;
201static u_short			S_ipport_server = IPPORT_BOOTPS;
202static struct timeval		S_lastmsgtime;
203/* ALIGN: S_rxpkt is aligned to at least sizeof(uint32_t) bytes */
204static uint32_t 		S_rxpkt[2048/(sizeof(uint32_t))];/* receive packet buffer */
205static boolean_t		S_sighup = TRUE; /* fake the 1st sighup */
206static u_int32_t		S_which_services = 0;
207static struct ether_addr *	S_allow = NULL;
208static int			S_allow_count = 0;
209static struct ether_addr *	S_deny = NULL;
210static int			S_deny_count = 0;
211static int			S_persist = 0;
212static struct in_addr *		S_relay_ip_list = NULL;
213static int			S_relay_ip_list_count = 0;
214static int			S_max_hops = 4;
215static boolean_t		S_use_server_config_for_dhcp_options = TRUE;
216
217
218void
219my_log(int priority, const char *message, ...)
220{
221    va_list 		ap;
222
223    if (priority == LOG_DEBUG) {
224	if (verbose == FALSE)
225	    return;
226	priority = LOG_NOTICE;
227    }
228    else if (priority == LOG_INFO) {
229	priority = LOG_NOTICE;
230    }
231    if (quiet && (priority > LOG_ERR)) {
232	return;
233    }
234    va_start(ap, message);
235    vsyslog(priority, message, ap);
236    va_end(ap);
237    return;
238}
239
240/* forward function declarations */
241static int 		issock(int fd);
242static void		on_alarm(int sigraised);
243static void		on_sighup(int sigraised);
244static void		bootp_request(request_t * request);
245static void		S_server_loop();
246
247#define PID_FILE "/var/run/bootpd.pid"
248static void
249writepid(void)
250{
251    FILE *fp;
252
253    fp = fopen(PID_FILE, "w");
254    if (fp != NULL) {
255	fprintf(fp, "%d\n", getpid());
256	(void) fclose(fp);
257    }
258}
259
260/*
261 * Function: background
262 *
263 * Purpose:
264 *   Daemon-ize ourselves.
265 */
266static void
267background()
268{
269    if (fork())
270	exit(0);
271    {
272	int s;
273	for (s = 0; s < 10; s++)
274	    (void) close(s);
275    }
276    (void) open("/", O_RDONLY);
277    (void) dup2(0, 1);
278    (void) dup2(0, 2);
279    {
280	int tt = open("/dev/tty", O_RDWR);
281	if (tt > 0) {
282	    ioctl(tt, TIOCNOTTY, 0);
283	    close(tt);
284	}
285    }
286}
287
288static void
289S_get_dns()
290{
291    int		domain_search_count = 0;
292    int 	i;
293
294    res_init(); /* figure out the default dns servers */
295
296    S_domain_name = NULL;
297    if (S_dns_servers) {
298	free(S_dns_servers);
299	S_dns_servers = NULL;
300    }
301    if (S_domain_search != NULL) {
302	free(S_domain_search);
303	S_domain_search = NULL;
304    }
305    S_domain_search_size = 0;
306    S_dns_servers_count = 0;
307
308    /* create the DNS server address list */
309    if (_res.nscount != 0) {
310	S_dns_servers = (struct in_addr *)malloc(sizeof(*S_dns_servers) * _res.nscount);
311	for (i = 0; i < _res.nscount; i++) {
312	    in_addr_t	s_addr = _res.nsaddr_list[i].sin_addr.s_addr;
313
314	    /* exclude 0.0.0.0, 255.255.255.255, and 127/8 */
315	    if (s_addr == 0
316		|| s_addr == INADDR_BROADCAST
317		|| (((ntohl(s_addr) & IN_CLASSA_NET) >> IN_CLASSA_NSHIFT)
318		    == IN_LOOPBACKNET)) {
319		continue;
320	    }
321	    S_dns_servers[S_dns_servers_count++].s_addr = s_addr;
322	    if (debug) {
323		if (S_dns_servers_count == 1) {
324		    printf("DNS servers:");
325		}
326		printf(" %s",
327		       inet_ntoa(S_dns_servers[S_dns_servers_count - 1]));
328	    }
329	}
330	if (S_dns_servers_count == 0) {
331	    free(S_dns_servers);
332	    S_dns_servers = NULL;
333	}
334	else if (debug) {
335	    printf("\n");
336	}
337    }
338    if (S_dns_servers_count != 0) {
339	if (_res.defdname[0] && strcmp(_res.defdname, "local") != 0) {
340	    S_domain_name = _res.defdname;
341	    if (debug)
342		printf("DNS domain: %s\n", S_domain_name);
343	}
344	/* create the DNS search list */
345	for (i = 0; i < MAXDNSRCH; i++) {
346	    if (_res.dnsrch[i] == NULL) {
347		break;
348	    }
349	    domain_search_count++;
350	    if (debug) {
351		if (i == 0) {
352		    printf("DNS search:");
353		}
354		printf(" %s", _res.dnsrch[i]);
355	    }
356	}
357	if (domain_search_count != 0) {
358	    if (debug) {
359		printf("\n");
360	    }
361	    S_domain_search
362		= DNSNameListBufferCreate((const char * *)_res.dnsrch,
363					  domain_search_count,
364					  NULL, &S_domain_search_size);
365	}
366    }
367    return;
368}
369
370/*
371 * Function: S_string_in_list
372 *
373 * Purpose:
374 *   Given a List object, return boolean whether the C string is
375 *   in the list.
376 */
377static boolean_t
378S_string_in_list(ptrlist_t * list, const char * str)
379{
380    int i;
381
382    for (i = 0; i < ptrlist_count(list); i++) {
383	char * lstr = (char *)ptrlist_element(list, i);
384	if (strcmp(str, lstr) == 0)
385	    return (TRUE);
386    }
387    return (FALSE);
388}
389
390/*
391 * Function: S_log_interfaces
392 *
393 * Purpose:
394 *   Log which interfaces we will respond on.
395 */
396void
397S_log_interfaces()
398{
399    int i;
400    int count = 0;
401
402    for (i = 0; i < S_interfaces->count; i++) {
403	interface_t * 	if_p = S_interfaces->list + i;
404
405	if ((ptrlist_count(&S_if_list) == 0
406	     || S_string_in_list(&S_if_list, if_name(if_p)))
407	    && if_inet_valid(if_p) && !(if_flags(if_p) & IFF_LOOPBACK)) {
408	    int 		i;
409	    inet_addrinfo_t *	info;
410	    char 		ip[32];
411
412	    for (i = 0; i < if_inet_count(if_p); i++) {
413		info = if_inet_addr_at(if_p, i);
414		strcpy(ip, inet_ntoa(info->addr));
415		my_log(LOG_INFO, "interface %s: ip %s mask %s",
416		       if_name(if_p), ip, inet_ntoa(info->mask));
417	    }
418	    count++;
419	}
420    }
421    if (count == 0) {
422	my_log(LOG_INFO, "no available interfaces");
423	if (S_persist == 0) {
424	    exit(2);
425	}
426    }
427    return;
428}
429
430/*
431 * Function: S_get_interfaces
432 *
433 * Purpose:
434 *   Get the list of interfaces we will use.
435 */
436void
437S_get_interfaces()
438{
439    interface_list_t *	new_list;
440
441    new_list = ifl_init();
442    if (new_list == NULL) {
443	my_log(LOG_INFO, "interface list initialization failed");
444	exit(1);
445    }
446    ifl_free(&S_interfaces);
447    S_interfaces = new_list;
448    return;
449}
450
451/*
452 * Function: S_get_network_routes
453 *
454 * Purpose:
455 *   Get the list of network routes.
456 */
457void
458S_get_network_routes()
459{
460    inetroute_list_t * new_list;
461
462    new_list = inetroute_list_init();
463    if (new_list == NULL) {
464	my_log(LOG_INFO, "can't get inetroutes list");
465	exit(1);
466    }
467
468    inetroute_list_free(&S_inetroutes);
469    S_inetroutes = new_list;
470    if (debug)
471	inetroute_list_print(S_inetroutes);
472}
473
474static void
475S_service_enable(CFTypeRef prop, u_int32_t which)
476{
477    int 	i;
478    CFStringRef	ifname_cf = NULL;
479    int		count;
480
481    if (prop == NULL) {
482	return;
483    }
484    if (isA_CFBoolean(prop) != NULL) {
485	if (CFEqual(prop, kCFBooleanTrue)) {
486	    S_which_services |= which;
487	}
488	return;
489    }
490    if (isA_CFString(prop) != NULL) {
491	count = 1;
492	ifname_cf = prop;
493    }
494    else if (isA_CFArray(prop) != NULL) {
495	count = CFArrayGetCount(prop);
496	if (count == 0) {
497	    S_which_services |= which;
498	    return;
499	}
500    }
501    else {
502	/* invalid type */
503	return;
504    }
505    for (i = 0; i < count; i++) {
506	interface_t * 	if_p;
507	char		ifname[IFNAMSIZ + 1];
508
509	if (i != 0 || ifname_cf == NULL) {
510	    ifname_cf = CFArrayGetValueAtIndex(prop, i);
511	    if (isA_CFString(ifname_cf) == NULL) {
512		continue;
513	    }
514	}
515	if (CFStringGetCString(ifname_cf, ifname, sizeof(ifname),
516			       kCFStringEncodingASCII)
517	    == FALSE) {
518	    continue;
519	}
520	if (*ifname == '\0') {
521	    continue;
522	}
523	if_p = ifl_find_name(S_interfaces, ifname);
524	if (if_p == NULL) {
525	    continue;
526	}
527	if_p->user_defined |= which;
528    }
529    return;
530}
531
532#if !TARGET_OS_EMBEDDED
533static void
534S_service_disable(u_int32_t service)
535{
536    int i;
537
538    S_which_services &= ~service;
539
540    for (i = 0; i < S_interfaces->count; i++) {
541	interface_t * 	if_p = S_interfaces->list + i;
542	if_p->user_defined &= ~service;
543    }
544    return;
545}
546
547static boolean_t
548S_service_is_enabled(u_int32_t service)
549{
550    int i;
551
552    if (S_which_services & service)
553	return (TRUE);
554
555    for (i = 0; i < S_interfaces->count; i++) {
556	interface_t * 	if_p = S_interfaces->list + i;
557	if (if_p->user_defined & service)
558	    return (TRUE);
559    }
560    return (FALSE);
561}
562
563static void
564S_disable_netboot()
565{
566    S_do_netboot = FALSE;
567    S_do_old_netboot = FALSE;
568    S_service_disable(SERVICE_NETBOOT | SERVICE_OLD_NETBOOT);
569    return;
570}
571#endif /* !TARGET_OS_EMBEDDED */
572
573typedef int (*qsort_compare_func_t)(const void *, const void *);
574
575static struct ether_addr *
576S_make_ether_list(CFArrayRef array, int * count_p)
577{
578    int			array_count = CFArrayGetCount(array);
579    int			count = 0;
580    int			i;
581    struct ether_addr * list;
582
583    list = (struct ether_addr *)malloc(sizeof(*list) * array_count);
584    for (i = 0; i < array_count; i++) {
585	struct ether_addr * 	eaddr;
586	CFStringRef		str = CFArrayGetValueAtIndex(array, i);
587	char			val[64];
588
589	if (isA_CFString(str) == NULL) {
590	    continue;
591	}
592	if (CFStringGetCString(str, val, sizeof(val), kCFStringEncodingASCII)
593	    == FALSE) {
594	    continue;
595	}
596	if (strlen(val) < 2) {
597	    continue;
598	}
599	/* ignore ethernet hardware type, if present */
600	if (strncmp(val, "1,", 2) == 0) {
601	    eaddr = ether_aton(val + 2);
602	}
603	else {
604	    eaddr = ether_aton((char *)val);
605	}
606	if (eaddr == NULL) {
607	    continue;
608	}
609	list[count++] = *eaddr;
610    }
611    if (count == 0) {
612	free(list);
613	list = NULL;
614    }
615    else {
616	qsort(list, count, sizeof(*list), (qsort_compare_func_t)ether_cmp);
617    }
618    *count_p = count;
619    return (list);
620}
621
622static boolean_t
623S_ok_to_respond(int hwtype, void * hwaddr, int hwlen)
624{
625    struct ether_addr *	search;
626    boolean_t		respond = TRUE;
627
628    if (hwlen != ETHER_ADDR_LEN) {
629	return (TRUE);
630    }
631    if (S_deny != NULL) {
632	search = bsearch(hwaddr, S_deny, S_deny_count, sizeof(*S_deny),
633			 (qsort_compare_func_t)ether_cmp);
634	if (search != NULL) {
635	    my_log(LOG_DEBUG, "%s is in deny list, ignoring",
636		   ether_ntoa(hwaddr));
637	    respond = FALSE;
638	}
639    }
640    if (respond == TRUE && S_allow != NULL) {
641	search = bsearch(hwaddr, S_allow, S_allow_count, sizeof(*S_allow),
642			 (qsort_compare_func_t)ether_cmp);
643	if (search == NULL) {
644	    my_log(LOG_DEBUG, "%s is not in the allow list, ignoring",
645		   ether_ntoa(hwaddr));
646	    respond = FALSE;
647	}
648    }
649    return (respond);
650}
651
652static void
653S_refresh_allow_deny(CFDictionaryRef plist)
654{
655    CFArrayRef		prop;
656
657    if (S_allow != NULL) {
658	free(S_allow);
659	S_allow = NULL;
660    }
661    if (S_deny != NULL) {
662	free(S_deny);
663	S_deny = NULL;
664    }
665    S_allow_count = 0;
666    S_deny_count = 0;
667
668    if (plist == NULL) {
669	return;
670    }
671
672    /* allow */
673    prop = CFDictionaryGetValue(plist, CFSTR(CFGPROP_ALLOW));
674    if (isA_CFArray(prop) != NULL && CFArrayGetCount(prop) > 0) {
675	S_allow = S_make_ether_list(prop, &S_allow_count);
676    }
677    /* deny */
678    prop = CFDictionaryGetValue(plist, CFSTR(CFGPROP_DENY));
679    if (isA_CFArray(prop) != NULL && CFArrayGetCount(prop) > 0) {
680	S_deny = S_make_ether_list(prop, &S_deny_count);
681    }
682    return;
683}
684
685static boolean_t
686S_str_to_ip(const char * ip_str, struct in_addr * ret_ip)
687{
688    if (inet_aton(ip_str, ret_ip) == 0
689	|| ret_ip->s_addr == 0
690	|| ret_ip->s_addr == INADDR_BROADCAST) {
691	return (FALSE);
692    }
693    return (TRUE);
694}
695
696static void
697S_relay_ip_list_clear(void)
698{
699    if (S_relay_ip_list != NULL) {
700	free(S_relay_ip_list);
701	S_relay_ip_list = NULL;
702	S_relay_ip_list_count = 0;
703    }
704    return;
705}
706
707static void
708S_relay_ip_list_add(struct in_addr relay_ip)
709{
710    if (S_relay_ip_list == NULL) {
711	S_relay_ip_list
712	    = (struct in_addr *)malloc(sizeof(struct in_addr *));
713	S_relay_ip_list[0] = relay_ip;
714	S_relay_ip_list_count = 1;
715    }
716    else {
717	S_relay_ip_list_count++;
718	S_relay_ip_list = (struct in_addr *)
719	    realloc(S_relay_ip_list,
720		    sizeof(struct in_addr *) * S_relay_ip_list_count);
721	S_relay_ip_list[S_relay_ip_list_count - 1] = relay_ip;
722    }
723    return;
724}
725
726static void
727S_update_relay_ip_list(CFArrayRef list)
728{
729    int		count;
730    int		i;
731
732    count = CFArrayGetCount(list);
733    S_relay_ip_list_clear();
734    for (i = 0; i < count; i++) {
735	struct in_addr	relay_ip;
736	CFStringRef	str = CFArrayGetValueAtIndex(list, i);
737
738	if (isA_CFString(str) == NULL) {
739	    continue;
740	}
741	if (my_CFStringToIPAddress(str, &relay_ip) == FALSE) {
742	    my_log(LOG_NOTICE, "Invalid relay server ip address");
743	    continue;
744	}
745	if (relay_ip.s_addr == 0 || relay_ip.s_addr == INADDR_BROADCAST) {
746	    my_log(LOG_NOTICE,
747		   "Invalid relay server ip address %s",
748		   inet_ntoa(relay_ip));
749	    continue;
750	}
751	if (ifl_find_ip(S_interfaces, relay_ip) != NULL) {
752	    my_log(LOG_NOTICE,
753		   "Relay server ip address %s specifies this host",
754		   inet_ntoa(relay_ip));
755	    continue;
756	}
757	S_relay_ip_list_add(relay_ip);
758    }
759    return;
760}
761
762__private_extern__ void
763set_number_from_plist(CFDictionaryRef plist, CFStringRef prop_name_cf,
764		      const char * prop_name, uint32_t * val_p)
765{
766    CFTypeRef	prop;
767
768    if (plist == NULL) {
769	return;
770    }
771    prop = CFDictionaryGetValue(plist, prop_name_cf);
772    if (prop != NULL
773	&& my_CFTypeToNumber(prop, val_p) == FALSE) {
774	my_log(LOG_INFO, "Invalid '%s' property", prop_name);
775    }
776    return;
777}
778
779static boolean_t
780S_get_plist_boolean(CFDictionaryRef plist, CFStringRef prop_name_cf,
781		    const char * prop_name, boolean_t def_value)
782{
783    boolean_t	ret;
784
785    ret = def_value;
786    if (plist != NULL) {
787	CFBooleanRef	prop = CFDictionaryGetValue(plist, prop_name_cf);
788	uint32_t	val;
789
790	if (prop != NULL) {
791	    if (my_CFTypeToNumber(prop, &val) == FALSE) {
792		my_log(LOG_NOTICE, "Invalid '%s' property",
793		       prop_name);
794	    }
795	    else {
796		ret = (val != 0);
797	    }
798	}
799    }
800    return (ret);
801}
802
803static void
804S_update_services()
805{
806    uint32_t		num;
807    CFDictionaryRef	plist = NULL;
808    CFTypeRef		prop;
809
810    plist = my_CFPropertyListCreateFromFile(BOOTPD_PLIST_PATH);
811    if (plist != NULL) {
812	if (isA_CFDictionary(plist) == NULL) {
813	    CFRelease(plist);
814	    plist = NULL;
815	}
816    }
817    S_which_services = 0;
818
819    if (plist != NULL) {
820	/* BOOTP */
821	S_service_enable(CFDictionaryGetValue(plist,
822					      CFSTR(CFGPROP_BOOTP_ENABLED)),
823			 SERVICE_BOOTP);
824
825	/* DHCP */
826	S_service_enable(CFDictionaryGetValue(plist,
827					      CFSTR(CFGPROP_DHCP_ENABLED)),
828			 SERVICE_DHCP);
829#if !TARGET_OS_EMBEDDED
830	/* NetBoot (2.0) */
831	S_service_enable(CFDictionaryGetValue(plist,
832					      CFSTR(CFGPROP_NETBOOT_ENABLED)),
833			 SERVICE_NETBOOT);
834
835	/* NetBoot (old, pre 2.0) */
836	S_service_enable(CFDictionaryGetValue(plist,
837					      CFSTR(CFGPROP_OLD_NETBOOT_ENABLED)),
838			 SERVICE_OLD_NETBOOT);
839#endif /* !TARGET_OS_EMBEDDED */
840	/* Relay */
841	S_service_enable(CFDictionaryGetValue(plist,
842					      CFSTR(CFGPROP_RELAY_ENABLED)),
843			 SERVICE_RELAY);
844	prop = CFDictionaryGetValue(plist, CFSTR(CFGPROP_RELAY_IP_LIST));
845	if (isA_CFArray(prop) != NULL) {
846	    S_update_relay_ip_list(prop);
847	}
848    }
849    /* allow/deny list */
850    S_refresh_allow_deny(plist);
851
852    /* reply threshold */
853    reply_threshold_seconds = 0;
854    set_number_from_plist(plist, CFSTR(CFGPROP_REPLY_THRESHOLD_SECONDS),
855			  CFGPROP_REPLY_THRESHOLD_SECONDS,
856			  &reply_threshold_seconds);
857
858    /* detect other DHCP server */
859    detect_other_dhcp_server = FALSE;
860    num = 0;
861    set_number_from_plist(plist, CFSTR(CFGPROP_DETECT_OTHER_DHCP_SERVER),
862			  CFGPROP_DETECT_OTHER_DHCP_SERVER,
863			  &num);
864    if (num != 0) {
865	detect_other_dhcp_server = TRUE;
866    }
867
868    /* ignore the DHCP client identifier */
869    dhcp_ignore_client_identifier = FALSE;
870    num = 0;
871    set_number_from_plist(plist, CFSTR(CFGPROP_DHCP_IGNORE_CLIENT_IDENTIFIER),
872			  CFGPROP_DHCP_IGNORE_CLIENT_IDENTIFIER,
873			  &num);
874    if (num != 0) {
875	dhcp_ignore_client_identifier = TRUE;
876    }
877#if !TARGET_OS_EMBEDDED
878    /* use open directory [for bootpent queries] */
879    use_open_directory = TRUE;
880    num = 1;
881    set_number_from_plist(plist, CFSTR(CFGPROP_USE_OPEN_DIRECTORY),
882			  CFGPROP_USE_OPEN_DIRECTORY,
883			  &num);
884    if (num == 0) {
885	use_open_directory = FALSE;
886    }
887#endif /* !TARGET_OS_EMBEDDED */
888
889    /* check whether to supply our own configuration for missing dhcp options */
890    S_use_server_config_for_dhcp_options
891	= S_get_plist_boolean(plist,
892			      CFSTR(CFGPROP_USE_SERVER_CONFIG_FOR_DHCP_OPTIONS),
893			      CFGPROP_USE_SERVER_CONFIG_FOR_DHCP_OPTIONS,
894			      TRUE);
895
896    /* get the new list of subnets */
897    SubnetListFree(&subnets);
898    if (plist != NULL) {
899	prop = CFDictionaryGetValue(plist, BOOTPD_PLIST_SUBNETS);
900	if (isA_CFArray(prop) != NULL) {
901	    subnets = SubnetListCreateWithArray(prop);
902	    if (subnets != NULL) {
903		if (debug) {
904		    SubnetListPrint(subnets);
905		}
906	    }
907	}
908    }
909
910    dhcp_init();
911#if !TARGET_OS_EMBEDDED
912    if (S_do_netboot || S_do_old_netboot
913	|| S_service_is_enabled(SERVICE_NETBOOT | SERVICE_OLD_NETBOOT)) {
914	if (bsdp_init(plist) == FALSE) {
915	    my_log(LOG_INFO, "bootpd: NetBoot service turned off");
916	    S_disable_netboot();
917	}
918    }
919#endif /* !TARGET_OS_EMBEDDED */
920    if (plist != NULL) {
921	CFRelease(plist);
922    }
923    return;
924}
925
926static __inline__ boolean_t
927bootp_enabled(interface_t * if_p)
928{
929    u_int32_t 	which = (S_which_services | if_p->user_defined);
930
931    return (S_do_bootp || (which & SERVICE_BOOTP) != 0);
932}
933
934static __inline__ boolean_t
935dhcp_enabled(interface_t * if_p)
936{
937    u_int32_t 	which = (S_which_services | if_p->user_defined);
938
939    return (S_do_dhcp || (which & SERVICE_DHCP) != 0);
940}
941
942#if !TARGET_OS_EMBEDDED
943static __inline__ boolean_t
944netboot_enabled(interface_t * if_p)
945{
946    u_int32_t 	which = (S_which_services | if_p->user_defined);
947
948    return (S_do_netboot || (which & SERVICE_NETBOOT) != 0);
949}
950
951static __inline__ boolean_t
952old_netboot_enabled(interface_t * if_p)
953{
954    u_int32_t 	which = (S_which_services | if_p->user_defined);
955
956    return (S_do_old_netboot || (which & SERVICE_OLD_NETBOOT) != 0);
957}
958#endif /* !TARGET_OS_EMBEDDED */
959
960static __inline__ boolean_t
961relay_enabled(interface_t * if_p)
962{
963    u_int32_t 	which = (S_which_services | if_p->user_defined);
964
965    return (S_do_relay || (which & SERVICE_RELAY) != 0);
966}
967
968void
969usage()
970{
971    fprintf(stderr, "usage: bootpd <options>\n"
972	    "<options> are:\n"
973	    "[ -a ] 	support anonymous binding for BOOTP clients\n"
974	    "[ -D ]	be a DHCP server\n"
975	    "[ -B ]	don't service BOOTP requests\n"
976	    "[ -b ] 	bootfile must exist or we don't respond\n"
977	    "[ -d ]	debug mode, stay in foreground, extra printf's\n"
978	    "[ -I ]	disable re-initialization on IP address changes\n"
979	    "[ -i <interface> [ -i <interface> ... ] ]\n"
980#if !TARGET_OS_EMBEDDED
981	    "[ -m ] 	be an old NetBoot (1.0) server\n"
982#endif /* !TARGET_OS_EMBEDDED */
983	    "[ -n <domain> [ -n <domain> [...] ] ]\n"
984#if !TARGET_OS_EMBEDDED
985	    "[ -N ]	be a NetBoot 2.0 server\n"
986#endif /* !TARGET_OS_EMBEDDED */
987	    "[ -q ]	be quiet as possible\n"
988	    "[ -r <server ip> [ -o <max hops> ] ] relay packets to server, "
989	    "optionally set the hop count (default is 4 hops)\n"
990	    "[ -v ] 	verbose mode, extra information\n"
991	    );
992    exit(1);
993}
994
995static void
996S_add_ip_change_notifications();
997
998int
999main(int argc, char * argv[])
1000{
1001    int			ch;
1002    boolean_t		ip_change_notifications = TRUE;
1003    int			logopt = LOG_CONS;
1004    struct in_addr	relay_ip = { 0 };
1005
1006    debug = 0;			/* no debugging ie. go into the background */
1007    verbose = 0;		/* don't print extra information */
1008
1009    ptrlist_init(&S_if_list);
1010
1011    S_get_interfaces();
1012
1013    while ((ch =  getopt(argc, argv, "aBbc:DdhHi:I"
1014#if !TARGET_OS_EMBEDDED
1015        "mN"
1016#endif /* !TARGET_OS_EMBEDDED */
1017	"o:Pp:qr:St:v")) != EOF) {
1018	switch ((char)ch) {
1019	case 'a':
1020	    /* was: enable anonymous binding for BOOTP clients */
1021	    break;
1022	case 'B':
1023	    break;
1024	case 'S':
1025	    S_do_bootp = TRUE;
1026	    break;
1027	case 'b':
1028	    S_bootfile_noexist_reply = FALSE;
1029	    /* reply only if bootfile exists */
1030	    break;
1031	case 'c':		    /* was: cache check interval - seconds */
1032	    break;
1033	case 'D':		/* answer DHCP requests as a DHCP server */
1034	    S_do_dhcp = TRUE;
1035	    break;
1036	case 'd':		/* stay in the foreground, extra printf's */
1037	    debug = 1;
1038	    break;
1039	case 'h':
1040	case 'H':
1041	    usage();
1042	    exit(1);
1043	    break;
1044	case 'I':
1045	    ip_change_notifications = FALSE;
1046	    break;
1047	case 'i':	/* user specified interface(s) to use */
1048	    if (S_string_in_list(&S_if_list, optarg) == FALSE) {
1049		ptrlist_add(&S_if_list, optarg);
1050	    }
1051	    else {
1052		my_log(LOG_INFO, "interface %s already specified",
1053		       optarg);
1054	    }
1055	    break;
1056#if !TARGET_OS_EMBEDDED
1057	case 'm':
1058	    S_do_old_netboot = TRUE;
1059	    S_do_dhcp = TRUE;
1060	    break;
1061	case 'N':
1062	    S_do_netboot = TRUE;
1063	    break;
1064#endif /* !TARGET_OS_EMBEDDED */
1065	case 'o': {
1066	    int h;
1067	    h = atoi(optarg);
1068	    if (h > 16 || h < 1) {
1069		printf("max hops value %s must be in the range 1..16\n",
1070		       optarg);
1071		exit(1);
1072	    }
1073	    S_max_hops = h;
1074	    break;
1075	}
1076	case 'P':
1077	    S_persist = 1;
1078	    break;
1079	case 'p':
1080	    server_priority = strtoul(optarg, NULL, 0);
1081	    printf("Priority set to %d\n", server_priority);
1082	    break;
1083	case 'q':
1084	    quiet = 1;
1085	    break;
1086	case 'r':
1087	    S_do_relay = 1;
1088	    if (S_str_to_ip(optarg, &relay_ip) == FALSE) {
1089		printf("Invalid relay server ip address %s\n", optarg);
1090		exit(1);
1091	    }
1092	    if (ifl_find_ip(S_interfaces, relay_ip) != NULL) {
1093		printf("Relay server ip address %s specifies this host\n",
1094		       optarg);
1095		exit(1);
1096	    }
1097	    S_relay_ip_list_add(relay_ip);
1098	    break;
1099	case 't':
1100	    testing_control = optarg;
1101	    break;
1102	case 'v':	/* extra info to syslog */
1103	    verbose++;
1104	    break;
1105	default:
1106	    break;
1107	}
1108    }
1109    if (!issock(0)) { /* started by user */
1110	struct sockaddr_in Sin = { sizeof(Sin), AF_INET };
1111	int i;
1112
1113	if (!debug)
1114	    background();
1115
1116	if ((bootp_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
1117	    my_log(LOG_INFO, "socket call failed");
1118	    exit(1);
1119	}
1120	Sin.sin_port = htons(S_ipport_server);
1121	Sin.sin_addr.s_addr = htonl(INADDR_ANY);
1122	i = 0;
1123	while (bind(bootp_socket, (struct sockaddr *)&Sin, sizeof(Sin)) < 0) {
1124	    my_log(LOG_INFO, "bind call failed: %s", strerror(errno));
1125	    if (errno != EADDRINUSE)
1126		exit(1);
1127	    i++;
1128	    if (i == 10) {
1129		my_log(LOG_INFO, "exiting");
1130		exit(1);
1131	    }
1132	    sleep(10);
1133	}
1134    }
1135    else { /* started by inetd */
1136	bootp_socket = 0;
1137	gettimeofday(&S_lastmsgtime, 0);
1138	if (S_persist == 0) {
1139	    signal(SIGALRM, on_alarm);
1140	    alarm(15);
1141	}
1142    }
1143
1144    writepid();
1145
1146    if (debug)
1147	logopt = LOG_PERROR;
1148
1149    (void) openlog("bootpd", logopt | LOG_PID, LOG_DAEMON);
1150
1151    SubnetListLogErrors(LOG_NOTICE);
1152
1153    my_log(LOG_DEBUG, "server starting");
1154
1155    {
1156	int opt = 1;
1157
1158#if defined(IP_RECVIF)
1159	if (setsockopt(bootp_socket, IPPROTO_IP, IP_RECVIF, (caddr_t)&opt,
1160		       sizeof(opt)) < 0) {
1161	    my_log(LOG_INFO, "setsockopt(IP_RECVIF) failed: %s",
1162		   strerror(errno));
1163	    exit(1);
1164	}
1165#endif
1166
1167	if (setsockopt(bootp_socket, SOL_SOCKET, SO_BROADCAST, (caddr_t)&opt,
1168		       sizeof(opt)) < 0) {
1169	    my_log(LOG_INFO, "setsockopt(SO_BROADCAST) failed");
1170	    exit(1);
1171	}
1172	if (setsockopt(bootp_socket, IPPROTO_IP, IP_RECVDSTADDR, (caddr_t)&opt,
1173		       sizeof(opt)) < 0) {
1174	    my_log(LOG_INFO, "setsockopt(IPPROTO_IP, IP_RECVDSTADDR) failed");
1175	    exit(1);
1176	}
1177	if (setsockopt(bootp_socket, SOL_SOCKET, SO_REUSEADDR, (caddr_t)&opt,
1178		       sizeof(opt)) < 0) {
1179	    my_log(LOG_INFO, "setsockopt(SO_REUSEADDR) failed");
1180	    exit(1);
1181	}
1182    }
1183
1184    /* install our sighup handler */
1185    signal(SIGHUP, on_sighup);
1186
1187    if (ip_change_notifications) {
1188	S_add_ip_change_notifications();
1189    }
1190    S_server_loop();
1191    exit (0);
1192}
1193
1194/*
1195 * Function: subnetAddressAndMask
1196 *
1197 * Purpose:
1198 *   Given the gateway address field from the request and the
1199 *   interface the packet was received on, determine the subnet
1200 *   address and mask.
1201 * Note:
1202 *   This currently does not support "super-netting", in which
1203 *   more than one proper subnet shares the same physical subnet.
1204 */
1205boolean_t
1206subnetAddressAndMask(struct in_addr giaddr, interface_t * if_p,
1207		     struct in_addr * addr, struct in_addr * mask)
1208{
1209    /* gateway specified, find a subnet description on the same subnet */
1210    if (giaddr.s_addr) {
1211	SubnetRef	subnet;
1212
1213	/* find a subnet entry on the same subnet as the gateway */
1214	if (subnets == NULL) {
1215	    return (FALSE);
1216	}
1217	subnet = SubnetListGetSubnetForAddress(subnets, giaddr, FALSE);
1218	if (subnet == NULL) {
1219	    return (FALSE);
1220	}
1221	*addr = giaddr;
1222	*mask = SubnetGetMask(subnet);
1223    }
1224    else {
1225	*addr = if_inet_netaddr(if_p);
1226	*mask = if_inet_netmask(if_p);
1227    }
1228    return (TRUE);
1229}
1230
1231/*
1232 * Function: issock
1233 *
1234 * Purpose:
1235 *   Determine if a descriptor belongs to a socket or not
1236 */
1237static int
1238issock(fd)
1239     int fd;
1240{
1241    struct stat st;
1242
1243    if (fstat(fd, &st) < 0) {
1244	return (0);
1245    }
1246    /*
1247     * SunOS returns S_IFIFO for sockets, while 4.3 returns 0 and
1248     * does not even have an S_IFIFO mode.  Since there is confusion
1249     * about what the mode is, we check for what it is not instead of
1250     * what it is.
1251     */
1252    switch (st.st_mode & S_IFMT) {
1253      case S_IFCHR:
1254      case S_IFREG:
1255      case S_IFLNK:
1256      case S_IFDIR:
1257      case S_IFBLK:
1258	return (0);
1259      default:
1260	return (1);
1261    }
1262}
1263
1264
1265/*
1266 * Function: on_sighup
1267 *
1268 * Purpose:
1269 *   If we get a sighup, re-read the subnet descriptions.
1270 */
1271static void
1272on_sighup(int sigraised)
1273{
1274    if (sigraised == SIGHUP)
1275	S_sighup = TRUE;
1276    return;
1277}
1278
1279/*
1280 * Function: on_alarm
1281 *
1282 * Purpose:
1283 *   If we were started by inetd, we kill ourselves during periods of
1284 *   inactivity.  If we've been idle for MAXIDLE, exit.
1285 */
1286static void
1287on_alarm(int sigraised)
1288{
1289    struct timeval tv;
1290
1291    gettimeofday(&tv, 0);
1292
1293    if ((tv.tv_sec - S_lastmsgtime.tv_sec) >= MAXIDLE)
1294	exit(0);
1295    alarm(15);
1296    return;
1297}
1298
1299/*
1300 * Function: bootp_add_bootfile
1301 *
1302 * Purpose:
1303 *   Verify that the specified bootfile exists, and add it to the given
1304 *   packet.  Handle <bootfile>.<hostname> to allow a specific host to
1305 *   get its own version of the bootfile.
1306 */
1307boolean_t
1308bootp_add_bootfile(const char * request_file, const char * hostname,
1309		   const char * bootfile,
1310		   char * reply_file, int reply_file_size)
1311{
1312    boolean_t 	dothost = FALSE;	/* file.host was found */
1313    char 	file[PATH_MAX];
1314    int		len;
1315    char 	path[PATH_MAX];
1316
1317    if (request_file && request_file[0])
1318	strcpy(file, request_file);
1319    else if (bootfile && bootfile[0])
1320	strcpy(file, bootfile);
1321    else {
1322	my_log(LOG_DEBUG, "no replyfile", path);
1323	return (TRUE);
1324    }
1325
1326    if (file[0] == '/')	/* if absolute pathname */
1327	strcpy(path, file);
1328    else {
1329	strcpy(path, boot_tftp_dir);
1330	strcat(path, "/");
1331	strcat(path, file);
1332    }
1333
1334    /* see if file exists with a ".host" suffix */
1335    if (hostname) {
1336	int 	n;
1337
1338	n = strlen(path);
1339	strcat(path, ".");
1340	strcat(path, hostname);
1341	if (access(path, R_OK) >= 0)
1342	    dothost = TRUE;
1343	else
1344	    path[n] = 0;	/* try it without the suffix */
1345    }
1346
1347    if (dothost == FALSE) {
1348	if (access(path, R_OK) < 0) {
1349	    if (S_bootfile_noexist_reply == FALSE) {
1350		my_log(LOG_INFO,
1351		       "boot file %s* missing - not replying", path);
1352		return (FALSE);
1353	    }
1354	    my_log(LOG_DEBUG, "boot file %s* missing", path);
1355	}
1356    }
1357    len = strlen(path);
1358    if (len >= reply_file_size) {
1359	my_log(LOG_DEBUG, "boot file name too long %d >= %d",
1360	       len, reply_file_size);
1361	return (TRUE);
1362    }
1363    my_log(LOG_DEBUG, "replyfile %s", path);
1364    strcpy(reply_file, path);
1365    return (TRUE);
1366}
1367
1368#define NIPROP_IP_ADDRESS	"ip_address"
1369
1370/*
1371 * Function: ip_address_reachable
1372 *
1373 * Purpose:
1374 *   Determine whether the given ip address is directly reachable from
1375 *   the given interface or gateway.
1376 *
1377 *   Directly reachable means without using a router ie. share the same wire.
1378 */
1379boolean_t
1380ip_address_reachable(struct in_addr ip, struct in_addr giaddr,
1381		    interface_t * if_p)
1382{
1383    int i;
1384
1385    if (giaddr.s_addr) { /* gateway'd */
1386	/* find a subnet entry on the same subnet as the gateway */
1387	if (subnets == NULL) {
1388	    return (FALSE);
1389	}
1390	return (SubnetListAreAddressesOnSameSupernet(subnets, ip, giaddr));
1391    }
1392
1393    for (i = 0; i < S_inetroutes->count; i++) {
1394	inetroute_t * inr_p = S_inetroutes->list + i;
1395
1396	if (inr_p->mask.s_addr != 0
1397	    && inr_p->gateway.link.sdl_family == AF_LINK
1398	    && (ifl_find_link(S_interfaces, inr_p->gateway.link.sdl_index)
1399		== if_p)) {
1400	    /* reachable? */
1401	    if (in_subnet(inr_p->dest, inr_p->mask, ip))
1402		return (TRUE);
1403	}
1404    }
1405    return (FALSE);
1406}
1407
1408boolean_t
1409subnet_match(void * arg, struct in_addr iaddr)
1410{
1411    subnet_match_args_t *	s = (subnet_match_args_t *)arg;
1412
1413    if (iaddr.s_addr == 0) {
1414	/* make sure we never vend 0.0.0.0 */
1415	return (FALSE);
1416    }
1417    /* the binding may be invalid for this subnet, but it has one */
1418    s->has_binding = TRUE;
1419    if (iaddr.s_addr == s->ciaddr.s_addr
1420	|| ip_address_reachable(iaddr, s->giaddr, s->if_p)) {
1421	return (TRUE);
1422    }
1423    return (FALSE);
1424}
1425
1426/*
1427 * Function: bootp_request
1428 *
1429 * Purpose:
1430 *   Process BOOTREQUEST packet.
1431 *
1432 */
1433static void
1434bootp_request(request_t * request)
1435{
1436    char *		bootfile = NULL;
1437    char *		hostname = NULL;
1438    struct in_addr	iaddr;
1439    struct bootp 	rp;
1440    struct bootp *	rq = (struct bootp *)request->pkt;
1441    u_int16_t		secs;
1442
1443    if (request->pkt_length < sizeof(struct bootp))
1444	return;
1445
1446    secs = (u_int16_t)ntohs(rq->bp_secs);
1447    if (secs < reply_threshold_seconds) {
1448	if (debug) {
1449	    printf("rq->bp_secs %d < threshold %d\n",
1450		   secs, reply_threshold_seconds);
1451	}
1452	return;
1453    }
1454
1455    rp = *rq;	/* copy request into reply */
1456    rp.bp_op = BOOTREPLY;
1457
1458    if (rq->bp_ciaddr.s_addr == 0) { /* client doesn't specify ip */
1459	subnet_match_args_t	match;
1460
1461	bzero(&match, sizeof(match));
1462	match.if_p = request->if_p;
1463	match.giaddr = rq->bp_giaddr;
1464	if (bootp_getbyhw_file(rq->bp_htype, rq->bp_chaddr, rq->bp_hlen,
1465			       subnet_match, &match, &iaddr,
1466			       &hostname, &bootfile) == FALSE) {
1467#if !TARGET_OS_EMBEDDED
1468	    if (use_open_directory == FALSE
1469	        || bootp_getbyhw_ds(rq->bp_htype, rq->bp_chaddr, rq->bp_hlen,
1470				    subnet_match, &match, &iaddr,
1471				    &hostname, &bootfile) == FALSE) {
1472		return;
1473	    }
1474#else /* TARGET_OS_EMBEDDED */
1475	    return;
1476#endif /* TARGET_OS_EMBEDDED */
1477	}
1478	rp.bp_yiaddr = iaddr;
1479    }
1480    else { /* client specified ip address */
1481	iaddr = rq->bp_ciaddr;
1482	if (bootp_getbyip_file(iaddr, &hostname, &bootfile) == FALSE) {
1483#if !TARGET_OS_EMBEDDED
1484	    if (use_open_directory == FALSE
1485	        || bootp_getbyip_ds(iaddr, &hostname, &bootfile) == FALSE) {
1486		return;
1487	    }
1488#else /* TARGET_OS_EMBEDDED */
1489	    return;
1490#endif /* TARGET_OS_EMBEDDED */
1491	}
1492    }
1493    rq->bp_file[sizeof(rq->bp_file) - 1] = '\0';
1494    my_log(LOG_INFO,"BOOTP request [%s]: %s requested file '%s'",
1495	   if_name(request->if_p),
1496	   hostname ? hostname : inet_ntoa(iaddr),
1497	   rq->bp_file);
1498    if (bootp_add_bootfile((const char *)rq->bp_file, hostname, bootfile,
1499			   (char *)rp.bp_file,
1500			   sizeof(rp.bp_file)) == FALSE)
1501	/* client specified a bootfile but it did not exist */
1502	goto no_reply;
1503
1504    if (bcmp(rq->bp_vend, rfc_magic, sizeof(rfc_magic)) == 0) {
1505	/* insert the usual set of options/extensions if possible */
1506	dhcpoa_t	options;
1507
1508	dhcpoa_init(&options, rp.bp_vend + sizeof(rfc_magic),
1509		    sizeof(rp.bp_vend) - sizeof(rfc_magic));
1510
1511	add_subnet_options(hostname, iaddr,
1512			   request->if_p, &options, NULL, 0);
1513	my_log(LOG_DEBUG, "added vendor extensions");
1514	if (dhcpoa_add(&options, dhcptag_end_e, 0, NULL)
1515	    != dhcpoa_success_e) {
1516	    my_log(LOG_INFO, "couldn't add end tag");
1517	}
1518	else
1519	    bcopy(rfc_magic, rp.bp_vend, sizeof(rfc_magic));
1520    } /* if RFC magic number */
1521
1522    rp.bp_siaddr = if_inet_addr(request->if_p);
1523    strcpy((char *)rp.bp_sname, server_name);
1524    if (sendreply(request->if_p, &rp, sizeof(rp), FALSE, NULL)) {
1525	my_log(LOG_INFO, "reply sent %s %s pktsize %d",
1526	       hostname, inet_ntoa(iaddr), sizeof(rp));
1527    }
1528
1529  no_reply:
1530    if (hostname != NULL)
1531	free(hostname);
1532    if (bootfile != NULL)
1533	free(bootfile);
1534    return;
1535}
1536
1537
1538/*
1539 * Function: sendreply
1540 *
1541 * Purpose:
1542 *   Send a reply packet to the client.
1543 */
1544boolean_t
1545sendreply(interface_t * if_p, struct bootp * bp, int n,
1546	  boolean_t broadcast, struct in_addr * dest_p)
1547{
1548    struct in_addr 		dst;
1549    u_short			dest_port = S_ipport_client;
1550    void *			hwaddr = NULL;
1551    u_short			src_port = S_ipport_server;
1552
1553    /*
1554     * If the client IP address is specified, use that
1555     * else if gateway IP address is specified, use that
1556     * else make a temporary arp cache entry for the client's NEW
1557     * IP/hardware address and use that.
1558     */
1559    if (bp->bp_ciaddr.s_addr) {
1560	dst = bp->bp_ciaddr;
1561	my_log(LOG_DEBUG, "reply ciaddr %s", inet_ntoa(dst));
1562    }
1563    else if (bp->bp_giaddr.s_addr) {
1564	dst = bp->bp_giaddr;
1565	dest_port = S_ipport_server;
1566	src_port = S_ipport_client;
1567	my_log(LOG_DEBUG, "reply giaddr %s", inet_ntoa(dst));
1568	if (broadcast) /* tell the gateway to broadcast */
1569	    bp->bp_unused = htons(ntohs(bp->bp_unused | DHCP_FLAGS_BROADCAST));
1570    }
1571    else { /* local net request */
1572	if (broadcast || (ntohs(bp->bp_unused) & DHCP_FLAGS_BROADCAST)) {
1573	    my_log(LOG_DEBUG, "replying using broadcast IP address");
1574	    dst.s_addr = htonl(INADDR_BROADCAST);
1575	}
1576	else {
1577	    if (dest_p)
1578		dst = *dest_p;
1579	    else
1580		dst = bp->bp_yiaddr;
1581	    hwaddr = bp->bp_chaddr;
1582	}
1583	my_log(LOG_DEBUG, "replying to %s", inet_ntoa(dst));
1584    }
1585    if (bootp_transmit(bootp_socket, transmit_buffer, if_name(if_p),
1586		       if_link_arptype(if_p),
1587		       hwaddr,
1588		       bp->bp_hlen,
1589		       dst, if_inet_addr(if_p),
1590		       dest_port, src_port,
1591		       bp, n) < 0) {
1592	my_log(LOG_INFO, "transmit failed, %m");
1593	return (FALSE);
1594    }
1595    if (debug && verbose) {
1596	printf("\n=================== Server Reply ===="
1597	       "=================\n");
1598	dhcp_packet_print((struct dhcp *)bp, n);
1599    }
1600    return (TRUE);
1601}
1602
1603/*
1604 * Function: add_subnet_options
1605 *
1606 * Purpose:
1607 *   Given a list of tags, retrieve them from the subnet entry and
1608 *   insert them into the message options.
1609 */
1610int
1611add_subnet_options(char * hostname,
1612		   struct in_addr iaddr, interface_t * if_p,
1613		   dhcpoa_t * options, const uint8_t * tags, int n)
1614{
1615    inet_addrinfo_t *	info = if_inet_addr_at(if_p, 0);
1616    static const uint8_t default_tags[] = {
1617	dhcptag_subnet_mask_e,
1618	dhcptag_router_e,
1619	dhcptag_domain_name_server_e,
1620	dhcptag_domain_name_e,
1621	dhcptag_host_name_e,
1622    };
1623#define N_DEFAULT_TAGS	(sizeof(default_tags) / sizeof(default_tags[0]))
1624    int			number_before = dhcpoa_count(options);
1625    int			i;
1626    SubnetRef		subnet = NULL;
1627
1628    if (subnets != NULL) {
1629	/* try to find exact match */
1630	subnet = SubnetListGetSubnetForAddress(subnets, iaddr, TRUE);
1631	if (subnet == NULL) {
1632	    /* settle for inexact match */
1633	    subnet = SubnetListGetSubnetForAddress(subnets, iaddr, FALSE);
1634	}
1635    }
1636    if (tags == NULL) {
1637	tags = default_tags;
1638	n = N_DEFAULT_TAGS;
1639    }
1640
1641    for (i = 0; i < n; i++ ) {
1642	bool		handled = FALSE;
1643
1644	switch (tags[i]) {
1645	  case dhcptag_end_e:
1646	  case dhcptag_pad_e:
1647	  case dhcptag_requested_ip_address_e:
1648	  case dhcptag_lease_time_e:
1649	  case dhcptag_option_overload_e:
1650	  case dhcptag_dhcp_message_type_e:
1651	  case dhcptag_server_identifier_e:
1652	  case dhcptag_parameter_request_list_e:
1653	  case dhcptag_message_e:
1654	  case dhcptag_max_dhcp_message_size_e:
1655	  case dhcptag_renewal_t1_time_value_e:
1656	  case dhcptag_rebinding_t2_time_value_e:
1657	  case dhcptag_client_identifier_e:
1658	      continue; /* ignore these */
1659	  default:
1660	      break;
1661	}
1662	if (tags[i] == dhcptag_host_name_e) {
1663	    if (hostname) {
1664		if (dhcpoa_add(options, dhcptag_host_name_e,
1665			       strlen(hostname), hostname)
1666		    != dhcpoa_success_e) {
1667		    my_log(LOG_INFO, "couldn't add hostname: %s",
1668			   dhcpoa_err(options));
1669		}
1670	    }
1671	    handled = TRUE;
1672	}
1673	else if (subnet != NULL) {
1674	    const char *	opt;
1675	    int			opt_length;
1676
1677	    opt = SubnetGetOptionPtrAndLength(subnet, tags[i], &opt_length);
1678	    if (opt != NULL) {
1679		handled = TRUE;
1680		if (dhcpoa_add(options, tags[i], opt_length, opt)
1681		    != dhcpoa_success_e) {
1682		    my_log(LOG_INFO, "couldn't add option %d: %s",
1683			   tags[i], dhcpoa_err(options));
1684		}
1685	    }
1686	}
1687	if (handled == FALSE && S_use_server_config_for_dhcp_options) {
1688	    /* try to use defaults if no explicit configuration */
1689	    struct in_addr * def_route;
1690
1691	    switch (tags[i]) {
1692	      case dhcptag_subnet_mask_e: {
1693		if (ifl_find_subnet(S_interfaces, iaddr) != if_p)
1694		    continue;
1695		if (dhcpoa_add(options, dhcptag_subnet_mask_e,
1696			       sizeof(info->mask), &info->mask)
1697		    != dhcpoa_success_e) {
1698		    my_log(LOG_INFO, "couldn't add subnet_mask: %s",
1699			   dhcpoa_err(options));
1700		    continue;
1701		}
1702		my_log(LOG_DEBUG, "subnet mask %s derived from %s",
1703		       inet_ntoa(info->mask), if_name(if_p));
1704		break;
1705	      }
1706	      case dhcptag_router_e:
1707		def_route = inetroute_default(S_inetroutes);
1708		if (def_route == NULL
1709		    || in_subnet(info->netaddr, info->mask,
1710				 *def_route) == FALSE
1711		    || in_subnet(info->netaddr, info->mask,
1712				 iaddr) == FALSE)
1713		    /* don't respond if default route not on same subnet */
1714		    continue;
1715		if (dhcpoa_add(options, dhcptag_router_e, sizeof(*def_route),
1716			       def_route) != dhcpoa_success_e) {
1717		    my_log(LOG_INFO, "couldn't add router: %s",
1718			   dhcpoa_err(options));
1719		    continue;
1720		}
1721		my_log(LOG_DEBUG, "default route added as router");
1722		break;
1723	      case dhcptag_domain_name_server_e:
1724		if (S_dns_servers_count == 0)
1725		    continue;
1726		if (dhcpoa_add(options, dhcptag_domain_name_server_e,
1727			       S_dns_servers_count * sizeof(*S_dns_servers),
1728			       S_dns_servers) != dhcpoa_success_e) {
1729		    my_log(LOG_INFO, "couldn't add dns servers: %s",
1730			   dhcpoa_err(options));
1731		    continue;
1732		}
1733		if (verbose)
1734		    my_log(LOG_DEBUG, "default dns servers added");
1735		break;
1736	      case dhcptag_domain_name_e:
1737		if (S_domain_name) {
1738		    if (dhcpoa_add(options, dhcptag_domain_name_e,
1739				   strlen(S_domain_name), S_domain_name)
1740			!= dhcpoa_success_e) {
1741			my_log(LOG_INFO, "couldn't add domain name: %s",
1742			       dhcpoa_err(options));
1743			continue;
1744		    }
1745		    if (verbose)
1746			my_log(LOG_DEBUG, "default domain name added");
1747		}
1748		break;
1749	      case dhcptag_domain_search_e:
1750		if (S_domain_search) {
1751		    if (dhcpoa_add(options, dhcptag_domain_search_e,
1752				   S_domain_search_size, S_domain_search)
1753			!= dhcpoa_success_e) {
1754			my_log(LOG_INFO, "couldn't add domain search: %s",
1755			       dhcpoa_err(options));
1756			continue;
1757		    }
1758		    if (verbose)
1759			my_log(LOG_DEBUG, "domain search added");
1760		}
1761		break;
1762	      default:
1763		break;
1764	    }
1765	}
1766    }
1767    return (dhcpoa_count(options) - number_before);
1768}
1769
1770/**
1771 ** Server Main Loop
1772 **/
1773static char 		control[512];
1774static struct iovec  	iov;
1775static struct msghdr 	msg;
1776
1777static void
1778S_init_msg()
1779{
1780    msg.msg_name = 0;
1781    msg.msg_namelen = 0;
1782    msg.msg_iov = &iov;
1783    msg.msg_iovlen = 1;
1784    msg.msg_control = control;
1785    msg.msg_controllen = sizeof(control);
1786    msg.msg_flags = 0;
1787    iov.iov_base = (caddr_t)S_rxpkt;
1788    iov.iov_len = sizeof(S_rxpkt);
1789    return;
1790}
1791
1792static void
1793S_relay_packet(struct bootp * bp, int n, interface_t * if_p)
1794{
1795    boolean_t	clear_giaddr = FALSE;
1796    int		i;
1797    boolean_t	printed = FALSE;
1798    u_int16_t	secs;
1799
1800    if (n < sizeof(struct bootp))
1801	return;
1802
1803    switch (bp->bp_op) {
1804    case BOOTREQUEST:
1805	if (bp->bp_hops >= S_max_hops)
1806	    return;
1807	secs = (u_int16_t)ntohs(bp->bp_secs);
1808	if (secs < reply_threshold_seconds) {
1809	    /* don't bother yet */
1810	    break;
1811	}
1812	if (bp->bp_giaddr.s_addr == 0) {
1813	    /* fill it in with our interface address */
1814	    bp->bp_giaddr = if_inet_addr(if_p);
1815	    clear_giaddr = TRUE;
1816	}
1817	bp->bp_hops++;
1818	for (i = 0; i < S_relay_ip_list_count; i++) {
1819	    struct in_addr relay = S_relay_ip_list[i];
1820	    if (relay.s_addr == if_inet_broadcast(if_p).s_addr) {
1821		continue; /* don't rebroadcast */
1822	    }
1823	    if (debug && verbose && printed == FALSE) {
1824		printed = TRUE;
1825		printf("\n=================== Relayed Request ===="
1826		       "=================\n");
1827		dhcp_packet_print((struct dhcp *)bp, n);
1828	    }
1829
1830	    if (bootp_transmit(bootp_socket, transmit_buffer, if_name(if_p),
1831			       bp->bp_htype, NULL, 0,
1832			       relay, if_inet_addr(if_p),
1833			       S_ipport_server, S_ipport_client,
1834			       bp, n) < 0) {
1835		my_log(LOG_INFO, "send to %s failed, %m", inet_ntoa(relay));
1836	    }
1837	    else {
1838		my_log(LOG_INFO,
1839		       "Relayed Request [%s] to %s", if_name(if_p),
1840		       inet_ntoa(relay));
1841	    }
1842	}
1843	if (clear_giaddr) {
1844	    bp->bp_giaddr.s_addr = 0;
1845	}
1846	bp->bp_hops--;
1847	break;
1848    case BOOTREPLY: {
1849	interface_t * 	if_p;
1850	struct in_addr	dst;
1851
1852	if (bp->bp_giaddr.s_addr == 0) {
1853	    break;
1854	}
1855	if_p = ifl_find_ip(S_interfaces, bp->bp_giaddr);
1856	if (if_p == NULL) { /* we aren't the gateway - discard */
1857	    break;
1858	}
1859
1860	if ((ntohs(bp->bp_unused) & DHCP_FLAGS_BROADCAST)) {
1861	    my_log(LOG_DEBUG, "replying using broadcast IP address");
1862	    dst.s_addr = htonl(INADDR_BROADCAST);
1863	}
1864	else {
1865	    dst = bp->bp_yiaddr;
1866	}
1867	if (debug && verbose) {
1868	    if (debug) {
1869		printf("\n=================== Relayed Reply ===="
1870		       "=================\n");
1871		dhcp_packet_print((struct dhcp *)bp, n);
1872	    }
1873	}
1874	if (bootp_transmit(bootp_socket, transmit_buffer, if_name(if_p),
1875			   bp->bp_htype, bp->bp_chaddr, bp->bp_hlen,
1876			   dst, if_inet_addr(if_p),
1877			   S_ipport_client, S_ipport_server,
1878			   bp, n) < 0) {
1879	    my_log(LOG_INFO, "send %s failed, %m", inet_ntoa(dst));
1880	}
1881	else {
1882	    my_log(LOG_INFO,
1883		   "Relayed Response [%s] to %s", if_name(if_p),
1884		   inet_ntoa(dst));
1885	}
1886	break;
1887    }
1888
1889    default:
1890	break;
1891    }
1892    return;
1893}
1894
1895static void
1896S_dispatch_packet(struct bootp * bp, int n, interface_t * if_p,
1897		  struct in_addr * dstaddr_p)
1898{
1899#if !TARGET_OS_EMBEDDED
1900    boolean_t		bsdp_pkt = FALSE;
1901#endif /* !TARGET_OS_EMBEDDED */
1902    boolean_t		dhcp_pkt = FALSE;
1903    dhcp_msgtype_t	dhcp_msgtype = dhcp_msgtype_none_e;
1904
1905    switch (bp->bp_op) {
1906      case BOOTREQUEST: {
1907	boolean_t 	handled = FALSE;
1908	dhcpol_t	options;
1909	request_t	request;
1910
1911	request.if_p = if_p;
1912	request.pkt = (struct dhcp *)bp;
1913	request.pkt_length = n;
1914	request.options_p = NULL;
1915	request.dstaddr_p = dstaddr_p;
1916	request.time_in_p = &S_lastmsgtime;
1917
1918	dhcpol_init(&options);
1919
1920	/* get the packet options, check for dhcp */
1921	if (dhcpol_parse_packet(&options, (struct dhcp *)bp, n, NULL)) {
1922	    request.options_p = &options;
1923	    dhcp_pkt = is_dhcp_packet(&options, &dhcp_msgtype);
1924	}
1925
1926	if (debug && verbose) {
1927	    printf("\n---------------- Client Request --------------------\n");
1928	    dhcp_packet_print((struct dhcp *)bp, n);
1929	}
1930
1931	if (bp->bp_sname[0] != '\0'
1932	    && strcmp((char *)bp->bp_sname, server_name) != 0)
1933	    goto request_done;
1934
1935	if (bp->bp_siaddr.s_addr != 0
1936	    && ntohl(bp->bp_siaddr.s_addr) != ntohl(if_inet_addr(if_p).s_addr))
1937	    goto request_done;
1938
1939	if (dhcp_pkt) { /* this is a DHCP packet */
1940#if !TARGET_OS_EMBEDDED
1941	    if (netboot_enabled(if_p) || old_netboot_enabled(if_p)) {
1942		char		arch[256];
1943		bsdp_version_t	client_version;
1944		boolean_t	is_old_netboot = FALSE;
1945		char		sysid[256];
1946		dhcpol_t	rq_vsopt; /* is_bsdp_packet() initializes */
1947
1948		bsdp_pkt = is_bsdp_packet(request.options_p, arch, sysid,
1949					  &rq_vsopt, &client_version,
1950					  &is_old_netboot);
1951		if (bsdp_pkt) {
1952		    if (is_old_netboot == TRUE
1953			&& old_netboot_enabled(if_p) == FALSE) {
1954			/* ignore it */
1955		    }
1956		    else {
1957			bsdp_request(&request, dhcp_msgtype,
1958				     arch, sysid, &rq_vsopt, client_version,
1959				     is_old_netboot);
1960		    }
1961		}
1962		else {
1963		    bsdp_dhcp_request(&request, dhcp_msgtype);
1964		}
1965		dhcpol_free(&rq_vsopt);
1966	    }
1967#endif /* !TARGET_OS_EMBEDDED */
1968	    if (dhcp_enabled(if_p)
1969#if !TARGET_OS_EMBEDDED
1970	        || old_netboot_enabled(if_p)
1971#endif /* !TARGET_OS_EMBEDDED */
1972	        ) {
1973		handled = TRUE;
1974		dhcp_request(&request, dhcp_msgtype, dhcp_enabled(if_p));
1975	    }
1976	}
1977#if !TARGET_OS_EMBEDDED
1978	if (handled == FALSE && old_netboot_enabled(if_p)) {
1979	    handled = old_netboot_request(&request);
1980	}
1981#endif /* !TARGET_OS_EMBEDDED */
1982	if (handled == FALSE && bootp_enabled(if_p)) {
1983	    bootp_request(&request);
1984	}
1985      request_done:
1986	dhcpol_free(&options);
1987	break;
1988      }
1989
1990      case BOOTREPLY:
1991	break;
1992
1993      default:
1994	break;
1995    }
1996
1997    if (S_relay_ip_list != NULL && relay_enabled(if_p)) {
1998	/* ALIGN: S_rxpkt is aligned to uint32, cast safe */
1999	S_relay_packet((struct bootp *)(void *)S_rxpkt, n, if_p);
2000    }
2001
2002    if (verbose) {
2003	struct timeval now;
2004	struct timeval result;
2005
2006	gettimeofday(&now, 0);
2007	timeval_subtract(now, S_lastmsgtime, &result);
2008	my_log(LOG_INFO, "service time %d.%06d seconds",
2009	       result.tv_sec, result.tv_usec);
2010    }
2011    return;
2012}
2013
2014static void *
2015S_parse_control(int level, int type, int * len)
2016{
2017    struct cmsghdr *	cmsg;
2018
2019    *len = 0;
2020    for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
2021	if (cmsg->cmsg_level == level
2022	    && cmsg->cmsg_type == type) {
2023	    if (cmsg->cmsg_len < sizeof(*cmsg))
2024		return (NULL);
2025	    *len = cmsg->cmsg_len - sizeof(*cmsg);
2026	    return (CMSG_DATA(cmsg));
2027	}
2028    }
2029    return (NULL);
2030}
2031
2032#if defined(IP_RECVIF)
2033static interface_t *
2034S_which_interface()
2035{
2036    struct sockaddr_dl *dl_p;
2037    char		ifname[IFNAMSIZ + 1];
2038    interface_t *	if_p = NULL;
2039    int 		len = 0;
2040
2041    dl_p = (struct sockaddr_dl *)S_parse_control(IPPROTO_IP, IP_RECVIF, &len);
2042    if (dl_p == NULL || len == 0 || dl_p->sdl_nlen >= sizeof(ifname)) {
2043	return (NULL);
2044    }
2045
2046    bcopy(dl_p->sdl_data, ifname, dl_p->sdl_nlen);
2047    ifname[dl_p->sdl_nlen] = '\0';
2048    if_p = ifl_find_name(S_interfaces, ifname);
2049    if (if_p == NULL) {
2050	if (verbose)
2051	    my_log(LOG_DEBUG, "unknown interface %s", ifname);
2052	return (NULL);
2053    }
2054    if (if_inet_valid(if_p) == FALSE)
2055	return (NULL);
2056    if (ptrlist_count(&S_if_list) > 0
2057	&& S_string_in_list(&S_if_list, ifname) == FALSE) {
2058	if (verbose)
2059	    my_log(LOG_DEBUG, "ignoring request on %s", ifname);
2060	return (NULL);
2061    }
2062    return (if_p);
2063}
2064#endif
2065
2066static struct in_addr *
2067S_which_dstaddr()
2068{
2069    void *	data;
2070    int		len = 0;
2071
2072    data = S_parse_control(IPPROTO_IP, IP_RECVDSTADDR, &len);
2073    if (data && len == sizeof(struct in_addr))
2074	return ((struct in_addr *)data);
2075    return (NULL);
2076}
2077
2078/*
2079 * Function: S_server_loop
2080 *
2081 * Purpose:
2082 *   This is the main loop that dispatches a request according to
2083 *   whether it is BOOTP, DHCP, or NetBoot.
2084 */
2085static void
2086S_server_loop()
2087{
2088    struct in_addr * 	dstaddr_p = NULL;
2089    struct sockaddr_in 	from = { sizeof(from), AF_INET };
2090    interface_t *	if_p = NULL;
2091    int 		mask;
2092    int			n;
2093    /* ALIGN: S_rxpkt is aligned to uint32, hence cast safe */
2094    struct dhcp *	request = (struct dhcp *)(void *)S_rxpkt;
2095
2096    for (;;) {
2097	S_init_msg();
2098	msg.msg_name = (caddr_t)&from;
2099	msg.msg_namelen = sizeof(from);
2100	n = recvmsg(bootp_socket, &msg, 0);
2101	if (n < 0) {
2102	    my_log(LOG_DEBUG, "recvmsg failed, %m");
2103	    errno = 0;
2104	    continue;
2105	}
2106	if (S_sighup) {
2107	    bootp_readtab(NULL);
2108
2109	    if (gethostname(server_name, sizeof(server_name) - 1)) {
2110		server_name[0] = '\0';
2111		my_log(LOG_INFO, "gethostname() failed, %m");
2112	    }
2113	    else {
2114		my_log(LOG_INFO, "server name %s", server_name);
2115	    }
2116
2117	    S_get_interfaces();
2118	    S_log_interfaces();
2119	    S_get_network_routes();
2120	    S_update_services();
2121	    S_get_dns();
2122	    S_sighup = FALSE;
2123	}
2124
2125	if (n < sizeof(struct dhcp)) {
2126	    continue;
2127	}
2128	if (request->dp_hlen > sizeof(request->dp_chaddr)) {
2129	    continue;
2130	}
2131	if (S_ok_to_respond(request->dp_htype, request->dp_chaddr,
2132			    request->dp_hlen) == FALSE) {
2133	    continue;
2134	}
2135	dstaddr_p = S_which_dstaddr();
2136	if (debug) {
2137	    if (dstaddr_p == NULL)
2138		printf("no destination address\n");
2139	    else
2140		printf("destination address %s\n", inet_ntoa(*dstaddr_p));
2141	}
2142
2143#if defined(IP_RECVIF)
2144	if_p = S_which_interface();
2145	if (if_p == NULL) {
2146	    continue;
2147	}
2148#else
2149	if_p = if_first_broadcast_inet(S_interfaces);
2150#endif
2151
2152	gettimeofday(&S_lastmsgtime, 0);
2153        mask = sigblock(sigmask(SIGALRM));
2154	/* ALIGN: S_rxpkt is aligned, cast ok. */
2155	S_dispatch_packet((struct bootp *)(void *)S_rxpkt, n, if_p, dstaddr_p);
2156	sigsetmask(mask);
2157    }
2158    return;
2159}
2160
2161#include <SystemConfiguration/SystemConfiguration.h>
2162#include <SystemConfiguration/SCDynamicStorePrivate.h>
2163static SCDynamicStoreRef	store;
2164static void
2165S_add_ip_change_notifications()
2166{
2167    CFStringRef			key;
2168    CFMutableArrayRef		patterns;
2169
2170    store = SCDynamicStoreCreate(NULL,
2171				 CFSTR("com.apple.network.bootpd"),
2172				 NULL,
2173				 NULL);
2174    if (store == NULL) {
2175	return;
2176    }
2177    key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
2178							kSCDynamicStoreDomainState,
2179							kSCCompAnyRegex,
2180							kSCEntNetIPv4);
2181    patterns = CFArrayCreateMutable(NULL, 1, &kCFTypeArrayCallBacks);
2182    CFArrayAppendValue(patterns, key);
2183    CFRelease(key);
2184    SCDynamicStoreSetNotificationKeys(store, NULL, patterns);
2185    CFRelease(patterns);
2186    SCDynamicStoreNotifySignal(store, getpid(), SIGHUP);
2187    return;
2188}
2189