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