17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5842620c7Smeem  * Common Development and Distribution License (the "License").
6842620c7Smeem  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
220a3e1f6cSVasumathi Sundaram  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
23b31320a7SChris Fraire  * Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #include <unistd.h>
277c478bd9Sstevel@tonic-gate #include <sys/types.h>
287c478bd9Sstevel@tonic-gate #include <sys/stat.h>
29b31320a7SChris Fraire #include <sys/utsname.h>
307c478bd9Sstevel@tonic-gate #include <stdlib.h>
317c478bd9Sstevel@tonic-gate #include <netinet/in.h>		/* struct in_addr */
327c478bd9Sstevel@tonic-gate #include <netinet/dhcp.h>
337c478bd9Sstevel@tonic-gate #include <signal.h>
347c478bd9Sstevel@tonic-gate #include <sys/socket.h>
357c478bd9Sstevel@tonic-gate #include <net/route.h>
367c478bd9Sstevel@tonic-gate #include <net/if_arp.h>
377c478bd9Sstevel@tonic-gate #include <string.h>
387c478bd9Sstevel@tonic-gate #include <dhcpmsg.h>
397c478bd9Sstevel@tonic-gate #include <ctype.h>
40b31320a7SChris Fraire #include <arpa/inet.h>
41b31320a7SChris Fraire #include <arpa/nameser.h>
42b31320a7SChris Fraire #include <resolv.h>
437c478bd9Sstevel@tonic-gate #include <netdb.h>
447c478bd9Sstevel@tonic-gate #include <fcntl.h>
457c478bd9Sstevel@tonic-gate #include <stdio.h>
460a3e1f6cSVasumathi Sundaram #include <dhcp_hostconf.h>
47b31320a7SChris Fraire #include <dhcp_inittab.h>
48b31320a7SChris Fraire #include <dhcp_symbol.h>
49b31320a7SChris Fraire #include <limits.h>
50b31320a7SChris Fraire #include <strings.h>
51b31320a7SChris Fraire #include <libipadm.h>
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate #include "states.h"
547c478bd9Sstevel@tonic-gate #include "agent.h"
557c478bd9Sstevel@tonic-gate #include "interface.h"
567c478bd9Sstevel@tonic-gate #include "util.h"
577c478bd9Sstevel@tonic-gate #include "packet.h"
58b31320a7SChris Fraire #include "defaults.h"
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate /*
617c478bd9Sstevel@tonic-gate  * this file contains utility functions that have no real better home
627c478bd9Sstevel@tonic-gate  * of their own.  they can largely be broken into six categories:
637c478bd9Sstevel@tonic-gate  *
647c478bd9Sstevel@tonic-gate  *  o  conversion functions -- functions to turn integers into strings,
657c478bd9Sstevel@tonic-gate  *     or to convert between units of a similar measure.
667c478bd9Sstevel@tonic-gate  *
67d04ccbb3Scarlsonj  *  o  time and timer functions -- functions to handle time measurement
68d04ccbb3Scarlsonj  *     and events.
69d04ccbb3Scarlsonj  *
707c478bd9Sstevel@tonic-gate  *  o  ipc-related functions -- functions to simplify the generation of
717c478bd9Sstevel@tonic-gate  *     ipc messages to the agent's clients.
727c478bd9Sstevel@tonic-gate  *
737c478bd9Sstevel@tonic-gate  *  o  signal-related functions -- functions to clean up the agent when
747c478bd9Sstevel@tonic-gate  *     it receives a signal.
757c478bd9Sstevel@tonic-gate  *
767c478bd9Sstevel@tonic-gate  *  o  routing table manipulation functions
777c478bd9Sstevel@tonic-gate  *
787c478bd9Sstevel@tonic-gate  *  o  true miscellany -- anything else
797c478bd9Sstevel@tonic-gate  */
807c478bd9Sstevel@tonic-gate 
81b31320a7SChris Fraire #define	ETCNODENAME		"/etc/nodename"
82b31320a7SChris Fraire 
83b31320a7SChris Fraire static	boolean_t	is_fqdn(const char *);
84b31320a7SChris Fraire static	boolean_t	dhcp_assemble_fqdn(char *fqdnbuf, size_t buflen,
85b31320a7SChris Fraire 			    dhcp_smach_t *dsmp);
86b31320a7SChris Fraire 
877c478bd9Sstevel@tonic-gate /*
887c478bd9Sstevel@tonic-gate  * pkt_type_to_string(): stringifies a packet type
897c478bd9Sstevel@tonic-gate  *
90d04ccbb3Scarlsonj  *   input: uchar_t: a DHCP packet type value, RFC 2131 or 3315
91d04ccbb3Scarlsonj  *	    boolean_t: B_TRUE if IPv6
927c478bd9Sstevel@tonic-gate  *  output: const char *: the stringified packet type
937c478bd9Sstevel@tonic-gate  */
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate const char *
pkt_type_to_string(uchar_t type,boolean_t isv6)96d04ccbb3Scarlsonj pkt_type_to_string(uchar_t type, boolean_t isv6)
977c478bd9Sstevel@tonic-gate {
987c478bd9Sstevel@tonic-gate 	/*
99d04ccbb3Scarlsonj 	 * note: the ordering in these arrays allows direct indexing of the
100d04ccbb3Scarlsonj 	 *	 table based on the RFC packet type value passed in.
1017c478bd9Sstevel@tonic-gate 	 */
1027c478bd9Sstevel@tonic-gate 
103d04ccbb3Scarlsonj 	static const char *v4types[] = {
1047c478bd9Sstevel@tonic-gate 		"BOOTP",  "DISCOVER", "OFFER",   "REQUEST", "DECLINE",
1057c478bd9Sstevel@tonic-gate 		"ACK",    "NAK",      "RELEASE", "INFORM"
1067c478bd9Sstevel@tonic-gate 	};
107d04ccbb3Scarlsonj 	static const char *v6types[] = {
108d04ccbb3Scarlsonj 		NULL, "SOLICIT", "ADVERTISE", "REQUEST",
109d04ccbb3Scarlsonj 		"CONFIRM", "RENEW", "REBIND", "REPLY",
110d04ccbb3Scarlsonj 		"RELEASE", "DECLINE", "RECONFIGURE", "INFORMATION-REQUEST",
111d04ccbb3Scarlsonj 		"RELAY-FORW", "RELAY-REPL"
112d04ccbb3Scarlsonj 	};
1137c478bd9Sstevel@tonic-gate 
114d04ccbb3Scarlsonj 	if (isv6) {
115d04ccbb3Scarlsonj 		if (type >= sizeof (v6types) / sizeof (*v6types) ||
116d04ccbb3Scarlsonj 		    v6types[type] == NULL)
117d04ccbb3Scarlsonj 			return ("<unknown>");
118d04ccbb3Scarlsonj 		else
119d04ccbb3Scarlsonj 			return (v6types[type]);
120d04ccbb3Scarlsonj 	} else {
121d04ccbb3Scarlsonj 		if (type >= sizeof (v4types) / sizeof (*v4types) ||
122d04ccbb3Scarlsonj 		    v4types[type] == NULL)
123d04ccbb3Scarlsonj 			return ("<unknown>");
124d04ccbb3Scarlsonj 		else
125d04ccbb3Scarlsonj 			return (v4types[type]);
1267c478bd9Sstevel@tonic-gate 	}
1277c478bd9Sstevel@tonic-gate }
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate /*
1307c478bd9Sstevel@tonic-gate  * monosec_to_string(): converts a monosec_t into a date string
1317c478bd9Sstevel@tonic-gate  *
1327c478bd9Sstevel@tonic-gate  *   input: monosec_t: the monosec_t to convert
1337c478bd9Sstevel@tonic-gate  *  output: const char *: the corresponding date string
1347c478bd9Sstevel@tonic-gate  */
1357c478bd9Sstevel@tonic-gate 
1367c478bd9Sstevel@tonic-gate const char *
monosec_to_string(monosec_t monosec)1377c478bd9Sstevel@tonic-gate monosec_to_string(monosec_t monosec)
1387c478bd9Sstevel@tonic-gate {
1397c478bd9Sstevel@tonic-gate 	time_t	time = monosec_to_time(monosec);
1407c478bd9Sstevel@tonic-gate 	char	*time_string = ctime(&time);
1417c478bd9Sstevel@tonic-gate 
1427c478bd9Sstevel@tonic-gate 	/* strip off the newline -- ugh, why, why, why.. */
1437c478bd9Sstevel@tonic-gate 	time_string[strlen(time_string) - 1] = '\0';
1447c478bd9Sstevel@tonic-gate 	return (time_string);
1457c478bd9Sstevel@tonic-gate }
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate /*
1487c478bd9Sstevel@tonic-gate  * monosec(): returns a monotonically increasing time in seconds that
1497c478bd9Sstevel@tonic-gate  *            is not affected by stime(2) or adjtime(2).
1507c478bd9Sstevel@tonic-gate  *
1517c478bd9Sstevel@tonic-gate  *   input: void
1527c478bd9Sstevel@tonic-gate  *  output: monosec_t: the number of seconds since some time in the past
1537c478bd9Sstevel@tonic-gate  */
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate monosec_t
monosec(void)1567c478bd9Sstevel@tonic-gate monosec(void)
1577c478bd9Sstevel@tonic-gate {
1587c478bd9Sstevel@tonic-gate 	return (gethrtime() / NANOSEC);
1597c478bd9Sstevel@tonic-gate }
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate /*
1627c478bd9Sstevel@tonic-gate  * monosec_to_time(): converts a monosec_t into real wall time
1637c478bd9Sstevel@tonic-gate  *
1647c478bd9Sstevel@tonic-gate  *    input: monosec_t: the absolute monosec_t to convert
1657c478bd9Sstevel@tonic-gate  *   output: time_t: the absolute time that monosec_t represents in wall time
1667c478bd9Sstevel@tonic-gate  */
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate time_t
monosec_to_time(monosec_t abs_monosec)1697c478bd9Sstevel@tonic-gate monosec_to_time(monosec_t abs_monosec)
1707c478bd9Sstevel@tonic-gate {
1717c478bd9Sstevel@tonic-gate 	return (abs_monosec - monosec()) + time(NULL);
1727c478bd9Sstevel@tonic-gate }
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate /*
175d04ccbb3Scarlsonj  * hrtime_to_monosec(): converts a hrtime_t to monosec_t
1767c478bd9Sstevel@tonic-gate  *
177d04ccbb3Scarlsonj  *    input: hrtime_t: the time to convert
178d04ccbb3Scarlsonj  *   output: monosec_t: the time in monosec_t
1797c478bd9Sstevel@tonic-gate  */
1807c478bd9Sstevel@tonic-gate 
181d04ccbb3Scarlsonj monosec_t
hrtime_to_monosec(hrtime_t hrtime)182d04ccbb3Scarlsonj hrtime_to_monosec(hrtime_t hrtime)
1837c478bd9Sstevel@tonic-gate {
184d04ccbb3Scarlsonj 	return (hrtime / NANOSEC);
1857c478bd9Sstevel@tonic-gate }
1867c478bd9Sstevel@tonic-gate 
1877c478bd9Sstevel@tonic-gate /*
1887c478bd9Sstevel@tonic-gate  * print_server_msg(): prints a message from a DHCP server
1897c478bd9Sstevel@tonic-gate  *
190d04ccbb3Scarlsonj  *   input: dhcp_smach_t *: the state machine the message is associated with
191d04ccbb3Scarlsonj  *	    const char *: the string to display
192d04ccbb3Scarlsonj  *	    uint_t: length of string
1937c478bd9Sstevel@tonic-gate  *  output: void
1947c478bd9Sstevel@tonic-gate  */
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate void
print_server_msg(dhcp_smach_t * dsmp,const char * msg,uint_t msglen)197d04ccbb3Scarlsonj print_server_msg(dhcp_smach_t *dsmp, const char *msg, uint_t msglen)
1987c478bd9Sstevel@tonic-gate {
199d04ccbb3Scarlsonj 	if (msglen > 0) {
200d04ccbb3Scarlsonj 		dhcpmsg(MSG_INFO, "%s: message from server: %.*s",
201d04ccbb3Scarlsonj 		    dsmp->dsm_name, msglen, msg);
202d04ccbb3Scarlsonj 	}
2037c478bd9Sstevel@tonic-gate }
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate /*
2067c478bd9Sstevel@tonic-gate  * alrm_exit(): Signal handler for SIGARLM. terminates grandparent.
2077c478bd9Sstevel@tonic-gate  *
2087c478bd9Sstevel@tonic-gate  *    input: int: signal the handler was called with.
2097c478bd9Sstevel@tonic-gate  *
2107c478bd9Sstevel@tonic-gate  *   output: void
2117c478bd9Sstevel@tonic-gate  */
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate static void
alrm_exit(int sig)2147c478bd9Sstevel@tonic-gate alrm_exit(int sig)
2157c478bd9Sstevel@tonic-gate {
2167c478bd9Sstevel@tonic-gate 	int exitval;
2177c478bd9Sstevel@tonic-gate 
2187c478bd9Sstevel@tonic-gate 	if (sig == SIGALRM && grandparent != 0)
2197c478bd9Sstevel@tonic-gate 		exitval = EXIT_SUCCESS;
2207c478bd9Sstevel@tonic-gate 	else
2217c478bd9Sstevel@tonic-gate 		exitval = EXIT_FAILURE;
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate 	_exit(exitval);
2247c478bd9Sstevel@tonic-gate }
2257c478bd9Sstevel@tonic-gate 
2267c478bd9Sstevel@tonic-gate /*
2277c478bd9Sstevel@tonic-gate  * daemonize(): daemonizes the process
2287c478bd9Sstevel@tonic-gate  *
2297c478bd9Sstevel@tonic-gate  *   input: void
2307c478bd9Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
2317c478bd9Sstevel@tonic-gate  */
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate int
daemonize(void)2347c478bd9Sstevel@tonic-gate daemonize(void)
2357c478bd9Sstevel@tonic-gate {
2367c478bd9Sstevel@tonic-gate 	/*
2377c478bd9Sstevel@tonic-gate 	 * We've found that adoption takes sufficiently long that
2387c478bd9Sstevel@tonic-gate 	 * a dhcpinfo run after dhcpagent -a is started may occur
2397c478bd9Sstevel@tonic-gate 	 * before the agent is ready to process the request.
2407c478bd9Sstevel@tonic-gate 	 * The result is an error message and an unhappy user.
2417c478bd9Sstevel@tonic-gate 	 *
2427c478bd9Sstevel@tonic-gate 	 * The initial process now sleeps for DHCP_ADOPT_SLEEP,
2437c478bd9Sstevel@tonic-gate 	 * unless interrupted by a SIGALRM, in which case it
2447c478bd9Sstevel@tonic-gate 	 * exits immediately. This has the effect that the
2457c478bd9Sstevel@tonic-gate 	 * grandparent doesn't exit until the dhcpagent is ready
2467c478bd9Sstevel@tonic-gate 	 * to process requests. This defers the the balance of
2477c478bd9Sstevel@tonic-gate 	 * the system start-up script processing until the
2487c478bd9Sstevel@tonic-gate 	 * dhcpagent is ready to field requests.
2497c478bd9Sstevel@tonic-gate 	 *
2507c478bd9Sstevel@tonic-gate 	 * grandparent is only set for the adopt case; other
2517c478bd9Sstevel@tonic-gate 	 * cases do not require the wait.
2527c478bd9Sstevel@tonic-gate 	 */
2537c478bd9Sstevel@tonic-gate 
2547c478bd9Sstevel@tonic-gate 	if (grandparent != 0)
2557c478bd9Sstevel@tonic-gate 		(void) signal(SIGALRM, alrm_exit);
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate 	switch (fork()) {
2587c478bd9Sstevel@tonic-gate 
2597c478bd9Sstevel@tonic-gate 	case -1:
2607c478bd9Sstevel@tonic-gate 		return (0);
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate 	case  0:
2637c478bd9Sstevel@tonic-gate 		if (grandparent != 0)
2647c478bd9Sstevel@tonic-gate 			(void) signal(SIGALRM, SIG_DFL);
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate 		/*
2677c478bd9Sstevel@tonic-gate 		 * setsid() makes us lose our controlling terminal,
2687c478bd9Sstevel@tonic-gate 		 * and become both a session leader and a process
2697c478bd9Sstevel@tonic-gate 		 * group leader.
2707c478bd9Sstevel@tonic-gate 		 */
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate 		(void) setsid();
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate 		/*
2757c478bd9Sstevel@tonic-gate 		 * under POSIX, a session leader can accidentally
2767c478bd9Sstevel@tonic-gate 		 * (through open(2)) acquire a controlling terminal if
2777c478bd9Sstevel@tonic-gate 		 * it does not have one.  just to be safe, fork again
2787c478bd9Sstevel@tonic-gate 		 * so we are not a session leader.
2797c478bd9Sstevel@tonic-gate 		 */
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate 		switch (fork()) {
2827c478bd9Sstevel@tonic-gate 
2837c478bd9Sstevel@tonic-gate 		case -1:
2847c478bd9Sstevel@tonic-gate 			return (0);
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate 		case 0:
2877c478bd9Sstevel@tonic-gate 			(void) signal(SIGHUP, SIG_IGN);
2887c478bd9Sstevel@tonic-gate 			(void) chdir("/");
2897c478bd9Sstevel@tonic-gate 			(void) umask(022);
2907c478bd9Sstevel@tonic-gate 			closefrom(0);
2917c478bd9Sstevel@tonic-gate 			break;
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate 		default:
2947c478bd9Sstevel@tonic-gate 			_exit(EXIT_SUCCESS);
2957c478bd9Sstevel@tonic-gate 		}
2967c478bd9Sstevel@tonic-gate 		break;
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 	default:
2997c478bd9Sstevel@tonic-gate 		if (grandparent != 0) {
3007c478bd9Sstevel@tonic-gate 			(void) signal(SIGCHLD, SIG_IGN);
301d04ccbb3Scarlsonj 			/*
302d04ccbb3Scarlsonj 			 * Note that we're not the agent here, so the DHCP
303d04ccbb3Scarlsonj 			 * logging subsystem hasn't been configured yet.
304d04ccbb3Scarlsonj 			 */
305d04ccbb3Scarlsonj 			syslog(LOG_DEBUG | LOG_DAEMON, "dhcpagent: daemonize: "
3067c478bd9Sstevel@tonic-gate 			    "waiting for adoption to complete.");
3077c478bd9Sstevel@tonic-gate 			if (sleep(DHCP_ADOPT_SLEEP) == 0) {
308d04ccbb3Scarlsonj 				syslog(LOG_WARNING | LOG_DAEMON,
309d04ccbb3Scarlsonj 				    "dhcpagent: daemonize: timed out awaiting "
310d04ccbb3Scarlsonj 				    "adoption.");
3117c478bd9Sstevel@tonic-gate 			}
312d04ccbb3Scarlsonj 			syslog(LOG_DEBUG | LOG_DAEMON, "dhcpagent: daemonize: "
313d04ccbb3Scarlsonj 			    "wait finished");
3147c478bd9Sstevel@tonic-gate 		}
3157c478bd9Sstevel@tonic-gate 		_exit(EXIT_SUCCESS);
3167c478bd9Sstevel@tonic-gate 	}
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate 	return (1);
3197c478bd9Sstevel@tonic-gate }
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate /*
3227c478bd9Sstevel@tonic-gate  * update_default_route(): update the interface's default route
3237c478bd9Sstevel@tonic-gate  *
3247c478bd9Sstevel@tonic-gate  *   input: int: the type of message; either RTM_ADD or RTM_DELETE
3257c478bd9Sstevel@tonic-gate  *	    struct in_addr: the default gateway to use
3267c478bd9Sstevel@tonic-gate  *	    const char *: the interface associated with the route
3277c478bd9Sstevel@tonic-gate  *	    int: any additional flags (besides RTF_STATIC and RTF_GATEWAY)
328d04ccbb3Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
3297c478bd9Sstevel@tonic-gate  */
3307c478bd9Sstevel@tonic-gate 
331d04ccbb3Scarlsonj static boolean_t
update_default_route(uint32_t ifindex,int type,struct in_addr * gateway_nbo,int flags)332cfb9c9abScarlsonj update_default_route(uint32_t ifindex, int type, struct in_addr *gateway_nbo,
3337c478bd9Sstevel@tonic-gate     int flags)
3347c478bd9Sstevel@tonic-gate {
3357c478bd9Sstevel@tonic-gate 	struct {
3367c478bd9Sstevel@tonic-gate 		struct rt_msghdr	rm_mh;
3377c478bd9Sstevel@tonic-gate 		struct sockaddr_in	rm_dst;
3387c478bd9Sstevel@tonic-gate 		struct sockaddr_in	rm_gw;
3397c478bd9Sstevel@tonic-gate 		struct sockaddr_in	rm_mask;
3407c478bd9Sstevel@tonic-gate 		struct sockaddr_dl	rm_ifp;
3417c478bd9Sstevel@tonic-gate 	} rtmsg;
3427c478bd9Sstevel@tonic-gate 
3437c478bd9Sstevel@tonic-gate 	(void) memset(&rtmsg, 0, sizeof (rtmsg));
3447c478bd9Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_version = RTM_VERSION;
3457c478bd9Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_msglen	= sizeof (rtmsg);
3467c478bd9Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_type	= type;
3477c478bd9Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_pid	= getpid();
3487c478bd9Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_flags	= RTF_GATEWAY | RTF_STATIC | flags;
3497c478bd9Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_addrs	= RTA_GATEWAY | RTA_DST | RTA_NETMASK | RTA_IFP;
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate 	rtmsg.rm_gw.sin_family	= AF_INET;
3527c478bd9Sstevel@tonic-gate 	rtmsg.rm_gw.sin_addr	= *gateway_nbo;
3537c478bd9Sstevel@tonic-gate 
3547c478bd9Sstevel@tonic-gate 	rtmsg.rm_dst.sin_family = AF_INET;
3557c478bd9Sstevel@tonic-gate 	rtmsg.rm_dst.sin_addr.s_addr = htonl(INADDR_ANY);
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate 	rtmsg.rm_mask.sin_family = AF_INET;
3587c478bd9Sstevel@tonic-gate 	rtmsg.rm_mask.sin_addr.s_addr = htonl(0);
3597c478bd9Sstevel@tonic-gate 
3607c478bd9Sstevel@tonic-gate 	rtmsg.rm_ifp.sdl_family	= AF_LINK;
361cfb9c9abScarlsonj 	rtmsg.rm_ifp.sdl_index	= ifindex;
3627c478bd9Sstevel@tonic-gate 
3637c478bd9Sstevel@tonic-gate 	return (write(rtsock_fd, &rtmsg, sizeof (rtmsg)) == sizeof (rtmsg));
3647c478bd9Sstevel@tonic-gate }
3657c478bd9Sstevel@tonic-gate 
3667c478bd9Sstevel@tonic-gate /*
3677c478bd9Sstevel@tonic-gate  * add_default_route(): add the default route to the given gateway
3687c478bd9Sstevel@tonic-gate  *
3697c478bd9Sstevel@tonic-gate  *   input: const char *: the name of the interface associated with the route
3707c478bd9Sstevel@tonic-gate  *	    struct in_addr: the default gateway to add
371d04ccbb3Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE otherwise
3727c478bd9Sstevel@tonic-gate  */
3737c478bd9Sstevel@tonic-gate 
374d04ccbb3Scarlsonj boolean_t
add_default_route(uint32_t ifindex,struct in_addr * gateway_nbo)375cfb9c9abScarlsonj add_default_route(uint32_t ifindex, struct in_addr *gateway_nbo)
3767c478bd9Sstevel@tonic-gate {
377cfb9c9abScarlsonj 	return (update_default_route(ifindex, RTM_ADD, gateway_nbo, RTF_UP));
3787c478bd9Sstevel@tonic-gate }
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate /*
3817c478bd9Sstevel@tonic-gate  * del_default_route(): deletes the default route to the given gateway
3827c478bd9Sstevel@tonic-gate  *
3837c478bd9Sstevel@tonic-gate  *   input: const char *: the name of the interface associated with the route
3847c478bd9Sstevel@tonic-gate  *	    struct in_addr: if not INADDR_ANY, the default gateway to remove
385d04ccbb3Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
3867c478bd9Sstevel@tonic-gate  */
3877c478bd9Sstevel@tonic-gate 
388d04ccbb3Scarlsonj boolean_t
del_default_route(uint32_t ifindex,struct in_addr * gateway_nbo)389cfb9c9abScarlsonj del_default_route(uint32_t ifindex, struct in_addr *gateway_nbo)
3907c478bd9Sstevel@tonic-gate {
3917c478bd9Sstevel@tonic-gate 	if (gateway_nbo->s_addr == htonl(INADDR_ANY)) /* no router */
392d04ccbb3Scarlsonj 		return (B_TRUE);
3937c478bd9Sstevel@tonic-gate 
394cfb9c9abScarlsonj 	return (update_default_route(ifindex, RTM_DELETE, gateway_nbo, 0));
3957c478bd9Sstevel@tonic-gate }
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate /*
398d04ccbb3Scarlsonj  * inactivity_shutdown(): shuts down agent if there are no state machines left
399d04ccbb3Scarlsonj  *			  to manage
4007c478bd9Sstevel@tonic-gate  *
4017c478bd9Sstevel@tonic-gate  *   input: iu_tq_t *: unused
4027c478bd9Sstevel@tonic-gate  *	    void *: unused
4037c478bd9Sstevel@tonic-gate  *  output: void
4047c478bd9Sstevel@tonic-gate  */
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate /* ARGSUSED */
4077c478bd9Sstevel@tonic-gate void
inactivity_shutdown(iu_tq_t * tqp,void * arg)4087c478bd9Sstevel@tonic-gate inactivity_shutdown(iu_tq_t *tqp, void *arg)
4097c478bd9Sstevel@tonic-gate {
410d04ccbb3Scarlsonj 	if (smach_count() > 0)	/* shouldn't happen, but... */
4117c478bd9Sstevel@tonic-gate 		return;
4127c478bd9Sstevel@tonic-gate 
413d04ccbb3Scarlsonj 	dhcpmsg(MSG_VERBOSE, "inactivity_shutdown: timed out");
414d04ccbb3Scarlsonj 
4157c478bd9Sstevel@tonic-gate 	iu_stop_handling_events(eh, DHCP_REASON_INACTIVITY, NULL, NULL);
4167c478bd9Sstevel@tonic-gate }
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate /*
4197c478bd9Sstevel@tonic-gate  * graceful_shutdown(): shuts down the agent gracefully
4207c478bd9Sstevel@tonic-gate  *
4217c478bd9Sstevel@tonic-gate  *   input: int: the signal that caused graceful_shutdown to be called
4227c478bd9Sstevel@tonic-gate  *  output: void
4237c478bd9Sstevel@tonic-gate  */
4247c478bd9Sstevel@tonic-gate 
4257c478bd9Sstevel@tonic-gate void
graceful_shutdown(int sig)4267c478bd9Sstevel@tonic-gate graceful_shutdown(int sig)
4277c478bd9Sstevel@tonic-gate {
428842620c7Smeem 	iu_stop_handling_events(eh, (sig == SIGTERM ? DHCP_REASON_TERMINATE :
429842620c7Smeem 	    DHCP_REASON_SIGNAL), drain_script, NULL);
4307c478bd9Sstevel@tonic-gate }
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate /*
433d04ccbb3Scarlsonj  * bind_sock(): binds a socket to a given IP address and port number
4347c478bd9Sstevel@tonic-gate  *
435d04ccbb3Scarlsonj  *   input: int: the socket to bind
436d04ccbb3Scarlsonj  *	    in_port_t: the port number to bind to, host byte order
437d04ccbb3Scarlsonj  *	    in_addr_t: the address to bind to, host byte order
438d04ccbb3Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
4397c478bd9Sstevel@tonic-gate  */
4407c478bd9Sstevel@tonic-gate 
441d04ccbb3Scarlsonj boolean_t
bind_sock(int fd,in_port_t port_hbo,in_addr_t addr_hbo)442d04ccbb3Scarlsonj bind_sock(int fd, in_port_t port_hbo, in_addr_t addr_hbo)
4437c478bd9Sstevel@tonic-gate {
444d04ccbb3Scarlsonj 	struct sockaddr_in	sin;
445d04ccbb3Scarlsonj 	int			on = 1;
4467c478bd9Sstevel@tonic-gate 
447d04ccbb3Scarlsonj 	(void) memset(&sin, 0, sizeof (struct sockaddr_in));
448d04ccbb3Scarlsonj 	sin.sin_family = AF_INET;
449d04ccbb3Scarlsonj 	sin.sin_port   = htons(port_hbo);
450d04ccbb3Scarlsonj 	sin.sin_addr.s_addr = htonl(addr_hbo);
4517c478bd9Sstevel@tonic-gate 
452d04ccbb3Scarlsonj 	(void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (int));
4537c478bd9Sstevel@tonic-gate 
454d04ccbb3Scarlsonj 	return (bind(fd, (struct sockaddr *)&sin, sizeof (sin)) == 0);
4557c478bd9Sstevel@tonic-gate }
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate /*
458d04ccbb3Scarlsonj  * bind_sock_v6(): binds a socket to a given IP address and port number
4597c478bd9Sstevel@tonic-gate  *
4607c478bd9Sstevel@tonic-gate  *   input: int: the socket to bind
4617c478bd9Sstevel@tonic-gate  *	    in_port_t: the port number to bind to, host byte order
462d04ccbb3Scarlsonj  *	    in6_addr_t: the address to bind to, network byte order
463d04ccbb3Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure
4647c478bd9Sstevel@tonic-gate  */
4657c478bd9Sstevel@tonic-gate 
466d04ccbb3Scarlsonj boolean_t
bind_sock_v6(int fd,in_port_t port_hbo,const in6_addr_t * addr_nbo)467d04ccbb3Scarlsonj bind_sock_v6(int fd, in_port_t port_hbo, const in6_addr_t *addr_nbo)
4687c478bd9Sstevel@tonic-gate {
469d04ccbb3Scarlsonj 	struct sockaddr_in6	sin6;
4707c478bd9Sstevel@tonic-gate 	int			on = 1;
4717c478bd9Sstevel@tonic-gate 
472d04ccbb3Scarlsonj 	(void) memset(&sin6, 0, sizeof (struct sockaddr_in6));
473d04ccbb3Scarlsonj 	sin6.sin6_family = AF_INET6;
474d04ccbb3Scarlsonj 	sin6.sin6_port   = htons(port_hbo);
475d04ccbb3Scarlsonj 	if (addr_nbo != NULL) {
476d04ccbb3Scarlsonj 		(void) memcpy(&sin6.sin6_addr, addr_nbo,
477d04ccbb3Scarlsonj 		    sizeof (sin6.sin6_addr));
478d04ccbb3Scarlsonj 	}
4797c478bd9Sstevel@tonic-gate 
4807c478bd9Sstevel@tonic-gate 	(void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (int));
4817c478bd9Sstevel@tonic-gate 
482d04ccbb3Scarlsonj 	return (bind(fd, (struct sockaddr *)&sin6, sizeof (sin6)) == 0);
4837c478bd9Sstevel@tonic-gate }
4847c478bd9Sstevel@tonic-gate 
4857c478bd9Sstevel@tonic-gate /*
4867c478bd9Sstevel@tonic-gate  * iffile_to_hostname(): return the hostname contained on a line of the form
4877c478bd9Sstevel@tonic-gate  *
4887c478bd9Sstevel@tonic-gate  * [ ^I]*inet[ ^I]+hostname[\n]*\0
4897c478bd9Sstevel@tonic-gate  *
4907c478bd9Sstevel@tonic-gate  * in the file located at the specified path
4917c478bd9Sstevel@tonic-gate  *
4927c478bd9Sstevel@tonic-gate  *   input: const char *: the path of the file to look in for the hostname
4937c478bd9Sstevel@tonic-gate  *  output: const char *: the hostname at that path, or NULL on failure
4947c478bd9Sstevel@tonic-gate  */
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate #define	IFLINE_MAX	1024	/* maximum length of a hostname.<if> line */
4977c478bd9Sstevel@tonic-gate 
4987c478bd9Sstevel@tonic-gate const char *
iffile_to_hostname(const char * path)4997c478bd9Sstevel@tonic-gate iffile_to_hostname(const char *path)
5007c478bd9Sstevel@tonic-gate {
5017c478bd9Sstevel@tonic-gate 	FILE		*fp;
5027c478bd9Sstevel@tonic-gate 	static char	ifline[IFLINE_MAX];
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate 	fp = fopen(path, "r");
5057c478bd9Sstevel@tonic-gate 	if (fp == NULL)
5067c478bd9Sstevel@tonic-gate 		return (NULL);
5077c478bd9Sstevel@tonic-gate 
5087c478bd9Sstevel@tonic-gate 	/*
5097c478bd9Sstevel@tonic-gate 	 * /etc/hostname.<if> may contain multiple ifconfig commands, but each
5107c478bd9Sstevel@tonic-gate 	 * such command is on a separate line (see the "while read ifcmds" code
5117c478bd9Sstevel@tonic-gate 	 * in /etc/init.d/inetinit).  Thus we will read the file a line at a
5127c478bd9Sstevel@tonic-gate 	 * time, searching for a line of the form
5137c478bd9Sstevel@tonic-gate 	 *
5147c478bd9Sstevel@tonic-gate 	 * [ ^I]*inet[ ^I]+hostname[\n]*\0
5157c478bd9Sstevel@tonic-gate 	 *
5167c478bd9Sstevel@tonic-gate 	 * extract the host name from it, and check it for validity.
5177c478bd9Sstevel@tonic-gate 	 */
5187c478bd9Sstevel@tonic-gate 	while (fgets(ifline, sizeof (ifline), fp) != NULL) {
5197c478bd9Sstevel@tonic-gate 		char *p;
5207c478bd9Sstevel@tonic-gate 
5217c478bd9Sstevel@tonic-gate 		if ((p = strstr(ifline, "inet")) != NULL) {
5227c478bd9Sstevel@tonic-gate 			if ((p != ifline) && !isspace(p[-1])) {
5237c478bd9Sstevel@tonic-gate 				(void) fclose(fp);
5247c478bd9Sstevel@tonic-gate 				return (NULL);
5257c478bd9Sstevel@tonic-gate 			}
5267c478bd9Sstevel@tonic-gate 			p += 4;	/* skip over "inet" and expect spaces or tabs */
5277c478bd9Sstevel@tonic-gate 			if ((*p == '\n') || (*p == '\0')) {
5287c478bd9Sstevel@tonic-gate 				(void) fclose(fp);
5297c478bd9Sstevel@tonic-gate 				return (NULL);
5307c478bd9Sstevel@tonic-gate 			}
5317c478bd9Sstevel@tonic-gate 			if (isspace(*p)) {
5327c478bd9Sstevel@tonic-gate 				char *nlptr;
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate 				/* no need to read more of the file */
5357c478bd9Sstevel@tonic-gate 				(void) fclose(fp);
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 				while (isspace(*p))
5387c478bd9Sstevel@tonic-gate 					p++;
5397c478bd9Sstevel@tonic-gate 				if ((nlptr = strrchr(p, '\n')) != NULL)
5407c478bd9Sstevel@tonic-gate 					*nlptr = '\0';
5417c478bd9Sstevel@tonic-gate 				if (strlen(p) > MAXHOSTNAMELEN) {
5427c478bd9Sstevel@tonic-gate 					dhcpmsg(MSG_WARNING,
5437c478bd9Sstevel@tonic-gate 					    "iffile_to_hostname:"
5447c478bd9Sstevel@tonic-gate 					    " host name too long");
5457c478bd9Sstevel@tonic-gate 					return (NULL);
5467c478bd9Sstevel@tonic-gate 				}
547b31320a7SChris Fraire 				if (ipadm_is_valid_hostname(p)) {
5487c478bd9Sstevel@tonic-gate 					return (p);
5497c478bd9Sstevel@tonic-gate 				} else {
5507c478bd9Sstevel@tonic-gate 					dhcpmsg(MSG_WARNING,
5517c478bd9Sstevel@tonic-gate 					    "iffile_to_hostname:"
5527c478bd9Sstevel@tonic-gate 					    " host name not valid");
5537c478bd9Sstevel@tonic-gate 					return (NULL);
5547c478bd9Sstevel@tonic-gate 				}
5557c478bd9Sstevel@tonic-gate 			} else {
5567c478bd9Sstevel@tonic-gate 				(void) fclose(fp);
5577c478bd9Sstevel@tonic-gate 				return (NULL);
5587c478bd9Sstevel@tonic-gate 			}
5597c478bd9Sstevel@tonic-gate 		}
5607c478bd9Sstevel@tonic-gate 	}
5617c478bd9Sstevel@tonic-gate 
5627c478bd9Sstevel@tonic-gate 	(void) fclose(fp);
5637c478bd9Sstevel@tonic-gate 	return (NULL);
5647c478bd9Sstevel@tonic-gate }
565d04ccbb3Scarlsonj 
566d04ccbb3Scarlsonj /*
567d04ccbb3Scarlsonj  * init_timer(): set up a DHCP timer
568d04ccbb3Scarlsonj  *
569d04ccbb3Scarlsonj  *   input: dhcp_timer_t *: the timer to set up
570d04ccbb3Scarlsonj  *  output: void
571d04ccbb3Scarlsonj  */
572d04ccbb3Scarlsonj 
573d04ccbb3Scarlsonj void
init_timer(dhcp_timer_t * dt,lease_t startval)574d04ccbb3Scarlsonj init_timer(dhcp_timer_t *dt, lease_t startval)
575d04ccbb3Scarlsonj {
576d04ccbb3Scarlsonj 	dt->dt_id = -1;
577d04ccbb3Scarlsonj 	dt->dt_start = startval;
578d04ccbb3Scarlsonj }
579d04ccbb3Scarlsonj 
580d04ccbb3Scarlsonj /*
581d04ccbb3Scarlsonj  * cancel_timer(): cancel a DHCP timer
582d04ccbb3Scarlsonj  *
583d04ccbb3Scarlsonj  *   input: dhcp_timer_t *: the timer to cancel
584d04ccbb3Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE otherwise
585d04ccbb3Scarlsonj  */
586d04ccbb3Scarlsonj 
587d04ccbb3Scarlsonj boolean_t
cancel_timer(dhcp_timer_t * dt)588d04ccbb3Scarlsonj cancel_timer(dhcp_timer_t *dt)
589d04ccbb3Scarlsonj {
590d04ccbb3Scarlsonj 	if (dt->dt_id == -1)
591d04ccbb3Scarlsonj 		return (B_TRUE);
592d04ccbb3Scarlsonj 
593d04ccbb3Scarlsonj 	if (iu_cancel_timer(tq, dt->dt_id, NULL) == 1) {
594d04ccbb3Scarlsonj 		dt->dt_id = -1;
595d04ccbb3Scarlsonj 		return (B_TRUE);
596d04ccbb3Scarlsonj 	}
597d04ccbb3Scarlsonj 
598d04ccbb3Scarlsonj 	return (B_FALSE);
599d04ccbb3Scarlsonj }
600d04ccbb3Scarlsonj 
601d04ccbb3Scarlsonj /*
602d04ccbb3Scarlsonj  * schedule_timer(): schedule a DHCP timer.  Note that it must not be already
603d04ccbb3Scarlsonj  *		     running, and that we can't cancel here.  If it were, and
604d04ccbb3Scarlsonj  *		     we did, we'd leak a reference to the callback argument.
605d04ccbb3Scarlsonj  *
606d04ccbb3Scarlsonj  *   input: dhcp_timer_t *: the timer to schedule
607d04ccbb3Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE otherwise
608d04ccbb3Scarlsonj  */
609d04ccbb3Scarlsonj 
610d04ccbb3Scarlsonj boolean_t
schedule_timer(dhcp_timer_t * dt,iu_tq_callback_t * cbfunc,void * arg)611d04ccbb3Scarlsonj schedule_timer(dhcp_timer_t *dt, iu_tq_callback_t *cbfunc, void *arg)
612d04ccbb3Scarlsonj {
613d04ccbb3Scarlsonj 	if (dt->dt_id != -1)
614d04ccbb3Scarlsonj 		return (B_FALSE);
615d04ccbb3Scarlsonj 	dt->dt_id = iu_schedule_timer(tq, dt->dt_start, cbfunc, arg);
616d04ccbb3Scarlsonj 	return (dt->dt_id != -1);
617d04ccbb3Scarlsonj }
618d04ccbb3Scarlsonj 
619d04ccbb3Scarlsonj /*
620d04ccbb3Scarlsonj  * dhcpv6_status_code(): report on a DHCPv6 status code found in an option
621d04ccbb3Scarlsonj  *			 buffer.
622d04ccbb3Scarlsonj  *
623d04ccbb3Scarlsonj  *   input: const dhcpv6_option_t *: pointer to option
624d04ccbb3Scarlsonj  *	    uint_t: option length
625d04ccbb3Scarlsonj  *	    const char **: error string (nul-terminated)
626d04ccbb3Scarlsonj  *	    const char **: message from server (unterminated)
627d04ccbb3Scarlsonj  *	    uint_t *: length of server message
628d04ccbb3Scarlsonj  *  output: int: -1 on error, or >= 0 for a DHCPv6 status code
629d04ccbb3Scarlsonj  */
630d04ccbb3Scarlsonj 
631d04ccbb3Scarlsonj int
dhcpv6_status_code(const dhcpv6_option_t * d6o,uint_t olen,const char ** estr,const char ** msg,uint_t * msglenp)632d04ccbb3Scarlsonj dhcpv6_status_code(const dhcpv6_option_t *d6o, uint_t olen, const char **estr,
633d04ccbb3Scarlsonj     const char **msg, uint_t *msglenp)
634d04ccbb3Scarlsonj {
635d04ccbb3Scarlsonj 	uint16_t status;
636d04ccbb3Scarlsonj 	static const char *v6_status[] = {
637d04ccbb3Scarlsonj 		NULL,
638d04ccbb3Scarlsonj 		"Unknown reason",
639d04ccbb3Scarlsonj 		"Server has no addresses available",
640d04ccbb3Scarlsonj 		"Client record unavailable",
641d04ccbb3Scarlsonj 		"Prefix inappropriate for link",
642d04ccbb3Scarlsonj 		"Client must use multicast",
643d04ccbb3Scarlsonj 		"No prefix available"
644d04ccbb3Scarlsonj 	};
645d04ccbb3Scarlsonj 	static char sbuf[32];
646d04ccbb3Scarlsonj 
647d04ccbb3Scarlsonj 	*estr = "";
648d04ccbb3Scarlsonj 	*msg = "";
649d04ccbb3Scarlsonj 	*msglenp = 0;
650d04ccbb3Scarlsonj 	if (d6o == NULL)
651d04ccbb3Scarlsonj 		return (0);
652d04ccbb3Scarlsonj 	olen -= sizeof (*d6o);
653d04ccbb3Scarlsonj 	if (olen < 2) {
654d04ccbb3Scarlsonj 		*estr = "garbled status code";
655d04ccbb3Scarlsonj 		return (-1);
656d04ccbb3Scarlsonj 	}
657d04ccbb3Scarlsonj 
658d04ccbb3Scarlsonj 	*msg = (const char *)(d6o + 1) + 2;
659d04ccbb3Scarlsonj 	*msglenp = olen - 2;
660d04ccbb3Scarlsonj 
661d04ccbb3Scarlsonj 	(void) memcpy(&status, d6o + 1, sizeof (status));
662d04ccbb3Scarlsonj 	status = ntohs(status);
663d04ccbb3Scarlsonj 	if (status > 0) {
664d04ccbb3Scarlsonj 		if (status > DHCPV6_STAT_NOPREFIX) {
665d04ccbb3Scarlsonj 			(void) snprintf(sbuf, sizeof (sbuf), "status %u",
666d04ccbb3Scarlsonj 			    status);
667d04ccbb3Scarlsonj 			*estr = sbuf;
668d04ccbb3Scarlsonj 		} else {
669d04ccbb3Scarlsonj 			*estr = v6_status[status];
670d04ccbb3Scarlsonj 		}
671d04ccbb3Scarlsonj 	}
672d04ccbb3Scarlsonj 	return (status);
673d04ccbb3Scarlsonj }
6740a3e1f6cSVasumathi Sundaram 
6750a3e1f6cSVasumathi Sundaram void
write_lease_to_hostconf(dhcp_smach_t * dsmp)6760a3e1f6cSVasumathi Sundaram write_lease_to_hostconf(dhcp_smach_t *dsmp)
6770a3e1f6cSVasumathi Sundaram {
6780a3e1f6cSVasumathi Sundaram 	PKT_LIST *plp[2];
6790a3e1f6cSVasumathi Sundaram 	const char *hcfile;
6800a3e1f6cSVasumathi Sundaram 
6810a3e1f6cSVasumathi Sundaram 	hcfile = ifname_to_hostconf(dsmp->dsm_name, dsmp->dsm_isv6);
6820a3e1f6cSVasumathi Sundaram 	plp[0] = dsmp->dsm_ack;
6830a3e1f6cSVasumathi Sundaram 	plp[1] = dsmp->dsm_orig_ack;
6840a3e1f6cSVasumathi Sundaram 	if (write_hostconf(dsmp->dsm_name, plp, 2,
6850a3e1f6cSVasumathi Sundaram 	    monosec_to_time(dsmp->dsm_curstart_monosec),
6860a3e1f6cSVasumathi Sundaram 	    dsmp->dsm_isv6) != -1) {
6870a3e1f6cSVasumathi Sundaram 		dhcpmsg(MSG_DEBUG, "wrote lease to %s", hcfile);
6880a3e1f6cSVasumathi Sundaram 	} else if (errno == EROFS) {
6890a3e1f6cSVasumathi Sundaram 		dhcpmsg(MSG_DEBUG, "%s is on a read-only file "
6900a3e1f6cSVasumathi Sundaram 		    "system; not saving lease", hcfile);
6910a3e1f6cSVasumathi Sundaram 	} else {
6920a3e1f6cSVasumathi Sundaram 		dhcpmsg(MSG_ERR, "cannot write %s (reboot will "
6930a3e1f6cSVasumathi Sundaram 		    "not use cached configuration)", hcfile);
6940a3e1f6cSVasumathi Sundaram 	}
6950a3e1f6cSVasumathi Sundaram }
696b31320a7SChris Fraire 
697b31320a7SChris Fraire /*
698b31320a7SChris Fraire  * Try to get a string from the first line of a file, up to but not
699b31320a7SChris Fraire  * including any space (0x20) or newline.
700b31320a7SChris Fraire  *
701b31320a7SChris Fraire  *   input: const char *: file name;
702b31320a7SChris Fraire  *	    char *: allocated buffer space;
703b31320a7SChris Fraire  *	    size_t: space available in buf;
704b31320a7SChris Fraire  *  output: boolean_t: B_TRUE if a non-empty string was written to buf;
705b31320a7SChris Fraire  *		       B_FALSE otherwise.
706b31320a7SChris Fraire  */
707b31320a7SChris Fraire 
708b31320a7SChris Fraire static boolean_t
dhcp_get_oneline(const char * filename,char * buf,size_t buflen)709b31320a7SChris Fraire dhcp_get_oneline(const char *filename, char *buf, size_t buflen)
710b31320a7SChris Fraire {
711b31320a7SChris Fraire 	char	value[SYS_NMLN], *c;
712b31320a7SChris Fraire 	int	fd, i;
713b31320a7SChris Fraire 
714b31320a7SChris Fraire 	if ((fd = open(filename, O_RDONLY)) <= 0) {
715b31320a7SChris Fraire 		dhcpmsg(MSG_DEBUG, "dhcp_get_oneline: could not open %s",
716b31320a7SChris Fraire 		    filename);
717b31320a7SChris Fraire 		*buf = '\0';
718b31320a7SChris Fraire 	} else {
719b31320a7SChris Fraire 		if ((i = read(fd, value, SYS_NMLN - 1)) <= 0) {
720b31320a7SChris Fraire 			dhcpmsg(MSG_WARNING, "dhcp_get_oneline: no line in %s",
721b31320a7SChris Fraire 			    filename);
722b31320a7SChris Fraire 			*buf = '\0';
723b31320a7SChris Fraire 		} else {
724b31320a7SChris Fraire 			value[i] = '\0';
725b31320a7SChris Fraire 			if ((c = strchr(value, '\n')) != NULL)
726b31320a7SChris Fraire 				*c = '\0';
727b31320a7SChris Fraire 			if ((c = strchr(value, ' ')) != NULL)
728b31320a7SChris Fraire 				*c = '\0';
729b31320a7SChris Fraire 
730b31320a7SChris Fraire 			if (strlcpy(buf, value, buflen) >= buflen) {
731b31320a7SChris Fraire 				dhcpmsg(MSG_WARNING, "dhcp_get_oneline: too"
732b31320a7SChris Fraire 				    " long value, %s", value);
733b31320a7SChris Fraire 				*buf = '\0';
734b31320a7SChris Fraire 			}
735b31320a7SChris Fraire 		}
736b31320a7SChris Fraire 		(void) close(fd);
737b31320a7SChris Fraire 	}
738b31320a7SChris Fraire 
739b31320a7SChris Fraire 	return (*buf != '\0');
740b31320a7SChris Fraire }
741b31320a7SChris Fraire 
742b31320a7SChris Fraire /*
743b31320a7SChris Fraire  * Try to get the hostname from the /etc/nodename file. uname(2) cannot
744b31320a7SChris Fraire  * be used, because that is initialized after DHCP has solicited, in order
745b31320a7SChris Fraire  * to allow for the possibility that utsname.nodename can be set from
746b31320a7SChris Fraire  * DHCP Hostname. Here, though, we want to send a value specified
747b31320a7SChris Fraire  * advance of DHCP, so read /etc/nodename directly.
748b31320a7SChris Fraire  *
749b31320a7SChris Fraire  *   input: char *: allocated buffer space;
750b31320a7SChris Fraire  *	    size_t: space available in buf;
751b31320a7SChris Fraire  *  output: boolean_t: B_TRUE if a non-empty string was written to buf;
752b31320a7SChris Fraire  *		       B_FALSE otherwise.
753b31320a7SChris Fraire  */
754b31320a7SChris Fraire 
755b31320a7SChris Fraire static boolean_t
dhcp_get_nodename(char * buf,size_t buflen)756b31320a7SChris Fraire dhcp_get_nodename(char *buf, size_t buflen)
757b31320a7SChris Fraire {
758b31320a7SChris Fraire 	return (dhcp_get_oneline(ETCNODENAME, buf, buflen));
759b31320a7SChris Fraire }
760b31320a7SChris Fraire 
761b31320a7SChris Fraire /*
762b31320a7SChris Fraire  * dhcp_add_hostname_opt(): Set CD_HOSTNAME option if REQUEST_HOSTNAME is
763b31320a7SChris Fraire  *			    affirmative and if 1) dsm_msg_reqhost is available;
764b31320a7SChris Fraire  *			    or 2) hostname is read from an extant
765b31320a7SChris Fraire  *			    /etc/hostname.<ifname> file; or 3) interface is
766*bbf21555SRichard Lowe  *			    primary and nodename(5) is defined.
767b31320a7SChris Fraire  *
768b31320a7SChris Fraire  *   input: dhcp_pkt_t *: pointer to DHCP message being constructed;
769b31320a7SChris Fraire  *	    dhcp_smach_t *: pointer to interface DHCP state machine;
770b31320a7SChris Fraire  *  output: B_TRUE if a client hostname was added; B_FALSE otherwise.
771b31320a7SChris Fraire  */
772b31320a7SChris Fraire 
773b31320a7SChris Fraire boolean_t
dhcp_add_hostname_opt(dhcp_pkt_t * dpkt,dhcp_smach_t * dsmp)774b31320a7SChris Fraire dhcp_add_hostname_opt(dhcp_pkt_t *dpkt, dhcp_smach_t *dsmp)
775b31320a7SChris Fraire {
776b31320a7SChris Fraire 	const char	*reqhost;
777b31320a7SChris Fraire 	char		nodename[MAXNAMELEN];
778b31320a7SChris Fraire 
779b31320a7SChris Fraire 	if (!df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, DF_REQUEST_HOSTNAME))
780b31320a7SChris Fraire 		return (B_FALSE);
781b31320a7SChris Fraire 
782b31320a7SChris Fraire 	dhcpmsg(MSG_DEBUG, "dhcp_add_hostname_opt: DF_REQUEST_HOSTNAME");
783b31320a7SChris Fraire 
784b31320a7SChris Fraire 	if (dsmp->dsm_msg_reqhost != NULL &&
785b31320a7SChris Fraire 	    ipadm_is_valid_hostname(dsmp->dsm_msg_reqhost)) {
786b31320a7SChris Fraire 		reqhost = dsmp->dsm_msg_reqhost;
787b31320a7SChris Fraire 	} else {
788b31320a7SChris Fraire 		char		hostfile[PATH_MAX + 1];
789b31320a7SChris Fraire 
790b31320a7SChris Fraire 		(void) snprintf(hostfile, sizeof (hostfile),
791b31320a7SChris Fraire 		    "/etc/hostname.%s", dsmp->dsm_name);
792b31320a7SChris Fraire 		reqhost = iffile_to_hostname(hostfile);
793b31320a7SChris Fraire 	}
794b31320a7SChris Fraire 
795b31320a7SChris Fraire 	if (reqhost == NULL && (dsmp->dsm_dflags & DHCP_IF_PRIMARY) &&
796b31320a7SChris Fraire 	    dhcp_get_nodename(nodename, sizeof (nodename))) {
797b31320a7SChris Fraire 		reqhost = nodename;
798b31320a7SChris Fraire 	}
799b31320a7SChris Fraire 
800b31320a7SChris Fraire 	if (reqhost != NULL) {
801b31320a7SChris Fraire 		free(dsmp->dsm_reqhost);
802b31320a7SChris Fraire 		if ((dsmp->dsm_reqhost = strdup(reqhost)) == NULL)
803b31320a7SChris Fraire 			dhcpmsg(MSG_WARNING, "dhcp_add_hostname_opt: cannot"
804b31320a7SChris Fraire 			    " allocate memory for host name option");
805b31320a7SChris Fraire 	}
806b31320a7SChris Fraire 
807b31320a7SChris Fraire 	if (dsmp->dsm_reqhost != NULL) {
808b31320a7SChris Fraire 		dhcpmsg(MSG_DEBUG, "dhcp_add_hostname_opt: host %s for %s",
809b31320a7SChris Fraire 		    dsmp->dsm_reqhost, dsmp->dsm_name);
810b31320a7SChris Fraire 		(void) add_pkt_opt(dpkt, CD_HOSTNAME, dsmp->dsm_reqhost,
811b31320a7SChris Fraire 		    strlen(dsmp->dsm_reqhost));
812b31320a7SChris Fraire 		return (B_FALSE);
813b31320a7SChris Fraire 	} else {
814b31320a7SChris Fraire 		dhcpmsg(MSG_DEBUG, "dhcp_add_hostname_opt: no hostname for %s",
815b31320a7SChris Fraire 		    dsmp->dsm_name);
816b31320a7SChris Fraire 	}
817b31320a7SChris Fraire 
818b31320a7SChris Fraire 	return (B_TRUE);
819b31320a7SChris Fraire }
820b31320a7SChris Fraire 
821b31320a7SChris Fraire /*
822b31320a7SChris Fraire  * dhcp_add_fqdn_opt(): Set client FQDN option if dhcp_assemble_fqdn()
823b31320a7SChris Fraire  *			initializes an FQDN, or else do nothing.
824b31320a7SChris Fraire  *
825b31320a7SChris Fraire  *   input: dhcp_pkt_t *: pointer to DHCP message being constructed;
826b31320a7SChris Fraire  *	    dhcp_smach_t *: pointer to interface DHCP state machine;
827b31320a7SChris Fraire  *  output: B_TRUE if a client FQDN was added; B_FALSE otherwise.
828b31320a7SChris Fraire  */
829b31320a7SChris Fraire 
830b31320a7SChris Fraire boolean_t
dhcp_add_fqdn_opt(dhcp_pkt_t * dpkt,dhcp_smach_t * dsmp)831b31320a7SChris Fraire dhcp_add_fqdn_opt(dhcp_pkt_t *dpkt, dhcp_smach_t *dsmp)
832b31320a7SChris Fraire {
833b31320a7SChris Fraire 	/*
834b31320a7SChris Fraire 	 * RFC 4702 section 2:
835b31320a7SChris Fraire 	 *
836b31320a7SChris Fraire 	 * The format of the Client FQDN option is:
837b31320a7SChris Fraire 	 *
838b31320a7SChris Fraire 	 *  Code   Len    Flags  RCODE1 RCODE2   Domain Name
839b31320a7SChris Fraire 	 * +------+------+------+------+------+------+--
840b31320a7SChris Fraire 	 * |  81  |   n  |      |      |      |       ...
841b31320a7SChris Fraire 	 * +------+------+------+------+------+------+--
842b31320a7SChris Fraire 	 *
843b31320a7SChris Fraire 	 * Code and Len are distinct, and the remainder is in a single buffer,
844b31320a7SChris Fraire 	 * opt81, for Flags + (unused) RCODE1 and RCODE2 (all octets) and a
845b31320a7SChris Fraire 	 * potentially maximum-length domain name.
846b31320a7SChris Fraire 	 *
847b31320a7SChris Fraire 	 * The format of the Flags field is:
848b31320a7SChris Fraire 	 *
849b31320a7SChris Fraire 	 *  0 1 2 3 4 5 6 7
850b31320a7SChris Fraire 	 * +-+-+-+-+-+-+-+-+
851b31320a7SChris Fraire 	 * |  MBZ  |N|E|O|S|
852b31320a7SChris Fraire 	 * +-+-+-+-+-+-+-+-+
853b31320a7SChris Fraire 	 *
854b31320a7SChris Fraire 	 * where MBZ is ignored and NEOS are:
855b31320a7SChris Fraire 	 *
856b31320a7SChris Fraire 	 * S = 1 to request that "the server SHOULD perform the A RR (FQDN-to-
857b31320a7SChris Fraire 	 * address) DNS updates;
858b31320a7SChris Fraire 	 *
859b31320a7SChris Fraire 	 * O = 0, for a server-only response bit;
860b31320a7SChris Fraire 	 *
861b31320a7SChris Fraire 	 * E = 1 to indicate the domain name is in "canonical wire format,
862b31320a7SChris Fraire 	 * without compression (i.e., ns_name_pton2) ....  This encoding SHOULD
863b31320a7SChris Fraire 	 * be used by clients ....";
864b31320a7SChris Fraire 	 *
865b31320a7SChris Fraire 	 * N = 0 to request that "the server SHALL perform DNS updates [of the
866b31320a7SChris Fraire 	 * PTR RR]." (1 would request SHALL NOT update).
867b31320a7SChris Fraire 	 */
868b31320a7SChris Fraire 
869b31320a7SChris Fraire 	const uint8_t	S_BIT_POS = 7;
870b31320a7SChris Fraire 	const uint8_t	E_BIT_POS = 5;
871b31320a7SChris Fraire 	const uint8_t	S_BIT = 1 << (7 - S_BIT_POS);
872b31320a7SChris Fraire 	const uint8_t	E_BIT = 1 << (7 - E_BIT_POS);
873b31320a7SChris Fraire 	const size_t	OPT_FQDN_METALEN = 3;
874b31320a7SChris Fraire 	char		fqdnbuf[MAXNAMELEN];
875b31320a7SChris Fraire 	uchar_t		enc_fqdnbuf[MAXNAMELEN];
876b31320a7SChris Fraire 	uint8_t		fqdnopt[MAXNAMELEN + OPT_FQDN_METALEN];
877b31320a7SChris Fraire 	uint_t		fqdncode;
878b31320a7SChris Fraire 	size_t		len, metalen;
879b31320a7SChris Fraire 
880b31320a7SChris Fraire 	if (dsmp->dsm_isv6)
881b31320a7SChris Fraire 		return (B_FALSE);
882b31320a7SChris Fraire 
883b31320a7SChris Fraire 	if (!dhcp_assemble_fqdn(fqdnbuf, sizeof (fqdnbuf), dsmp))
884b31320a7SChris Fraire 		return (B_FALSE);
885b31320a7SChris Fraire 
886b31320a7SChris Fraire 	/* encode the FQDN in canonical wire format */
887b31320a7SChris Fraire 
888b31320a7SChris Fraire 	if (ns_name_pton2(fqdnbuf, enc_fqdnbuf, sizeof (enc_fqdnbuf),
889b31320a7SChris Fraire 	    &len) < 0) {
890b31320a7SChris Fraire 		dhcpmsg(MSG_WARNING, "dhcp_add_fqdn_opt: error encoding domain"
891b31320a7SChris Fraire 		    " name %s", fqdnbuf);
892b31320a7SChris Fraire 		return (B_FALSE);
893b31320a7SChris Fraire 	}
894b31320a7SChris Fraire 
895b31320a7SChris Fraire 	dhcpmsg(MSG_DEBUG, "dhcp_add_fqdn_opt: interface FQDN is %s"
896b31320a7SChris Fraire 	    " for %s", fqdnbuf, dsmp->dsm_name);
897b31320a7SChris Fraire 
898b31320a7SChris Fraire 	bzero(fqdnopt, sizeof (fqdnopt));
899b31320a7SChris Fraire 	fqdncode = CD_CLIENTFQDN;
900b31320a7SChris Fraire 	metalen = OPT_FQDN_METALEN;
901b31320a7SChris Fraire 	*fqdnopt = S_BIT | E_BIT;
902b31320a7SChris Fraire 	(void) memcpy(fqdnopt + metalen, enc_fqdnbuf, len);
903b31320a7SChris Fraire 	(void) add_pkt_opt(dpkt, fqdncode, fqdnopt, metalen + len);
904b31320a7SChris Fraire 
905b31320a7SChris Fraire 	return (B_TRUE);
906b31320a7SChris Fraire }
907b31320a7SChris Fraire 
908b31320a7SChris Fraire /*
909b31320a7SChris Fraire  * dhcp_adopt_domainname(): Set namebuf if either dsm_dhcp_domainname or
910b31320a7SChris Fraire  *			    resolv's "default domain (deprecated)" is defined.
911b31320a7SChris Fraire  *
912b31320a7SChris Fraire  *   input: char *: pointer to buffer to which domain name will be written;
913b31320a7SChris Fraire  *	    size_t length of buffer;
914b31320a7SChris Fraire  *	    dhcp_smach_t *: pointer to interface DHCP state machine;
915b31320a7SChris Fraire  *  output: B_TRUE if namebuf was set to a valid domain name; B_FALSE
916b31320a7SChris Fraire  *	    otherwise.
917b31320a7SChris Fraire  */
918b31320a7SChris Fraire 
919b31320a7SChris Fraire static boolean_t
dhcp_adopt_domainname(char * namebuf,size_t buflen,dhcp_smach_t * dsmp)920b31320a7SChris Fraire dhcp_adopt_domainname(char *namebuf, size_t buflen, dhcp_smach_t *dsmp)
921b31320a7SChris Fraire {
922b31320a7SChris Fraire 	const char		*domainname;
923b31320a7SChris Fraire 	struct __res_state	res_state;
924b31320a7SChris Fraire 	int			lasterrno;
925b31320a7SChris Fraire 
926b31320a7SChris Fraire 	domainname = dsmp->dsm_dhcp_domainname;
927b31320a7SChris Fraire 
928b31320a7SChris Fraire 	if (ipadm_is_nil_hostname(domainname)) {
929b31320a7SChris Fraire 		/*
930b31320a7SChris Fraire 		 * fall back to resolv's "default domain (deprecated)"
931b31320a7SChris Fraire 		 */
932b31320a7SChris Fraire 		bzero(&res_state, sizeof (struct __res_state));
933b31320a7SChris Fraire 
934b31320a7SChris Fraire 		if ((lasterrno = res_ninit(&res_state)) != 0) {
935b31320a7SChris Fraire 			dhcpmsg(MSG_WARNING, "dhcp_adopt_domainname: error %d"
936b31320a7SChris Fraire 			    " initializing resolver", lasterrno);
937b31320a7SChris Fraire 			return (B_FALSE);
938b31320a7SChris Fraire 		}
939b31320a7SChris Fraire 
940b31320a7SChris Fraire 		domainname = NULL;
941b31320a7SChris Fraire 		if (!ipadm_is_nil_hostname(res_state.defdname))
942b31320a7SChris Fraire 			domainname = res_state.defdname;
943b31320a7SChris Fraire 
944b31320a7SChris Fraire 		/* N.b. res_state.defdname survives the following call */
945b31320a7SChris Fraire 		res_ndestroy(&res_state);
946b31320a7SChris Fraire 	}
947b31320a7SChris Fraire 
948b31320a7SChris Fraire 	if (domainname == NULL)
949b31320a7SChris Fraire 		return (B_FALSE);
950b31320a7SChris Fraire 
951b31320a7SChris Fraire 	if (strlcpy(namebuf, domainname, buflen) >= buflen) {
952b31320a7SChris Fraire 		dhcpmsg(MSG_WARNING,
953b31320a7SChris Fraire 		    "dhcp_adopt_domainname: too long adopted domain"
954b31320a7SChris Fraire 		    " name %s for %s", domainname, dsmp->dsm_name);
955b31320a7SChris Fraire 		return (B_FALSE);
956b31320a7SChris Fraire 	}
957b31320a7SChris Fraire 
958b31320a7SChris Fraire 	return (B_TRUE);
959b31320a7SChris Fraire }
960b31320a7SChris Fraire 
961b31320a7SChris Fraire /*
962b31320a7SChris Fraire  * dhcp_pick_domainname(): Set namebuf if DNS_DOMAINNAME is defined in
963b31320a7SChris Fraire  *			   /etc/default/dhcpagent or if dhcp_adopt_domainname()
964b31320a7SChris Fraire  *			   succeeds.
965b31320a7SChris Fraire  *
966b31320a7SChris Fraire  *   input: char *: pointer to buffer to which domain name will be written;
967b31320a7SChris Fraire  *	    size_t length of buffer;
968b31320a7SChris Fraire  *	    dhcp_smach_t *: pointer to interface DHCP state machine;
969b31320a7SChris Fraire  *  output: B_TRUE if namebuf was set to a valid domain name; B_FALSE
970b31320a7SChris Fraire  *	    otherwise.
971b31320a7SChris Fraire  */
972b31320a7SChris Fraire 
973b31320a7SChris Fraire static boolean_t
dhcp_pick_domainname(char * namebuf,size_t buflen,dhcp_smach_t * dsmp)974b31320a7SChris Fraire dhcp_pick_domainname(char *namebuf, size_t buflen, dhcp_smach_t *dsmp)
975b31320a7SChris Fraire {
976b31320a7SChris Fraire 	const char	*domainname;
977b31320a7SChris Fraire 
978b31320a7SChris Fraire 	/*
979b31320a7SChris Fraire 	 * Try to use a static DNS_DOMAINNAME if defined in
980b31320a7SChris Fraire 	 * /etc/default/dhcpagent.
981b31320a7SChris Fraire 	 */
982b31320a7SChris Fraire 	domainname = df_get_string(dsmp->dsm_name, dsmp->dsm_isv6,
983b31320a7SChris Fraire 	    DF_DNS_DOMAINNAME);
984b31320a7SChris Fraire 	if (!ipadm_is_nil_hostname(domainname)) {
985b31320a7SChris Fraire 		if (strlcpy(namebuf, domainname, buflen) >= buflen) {
986b31320a7SChris Fraire 			dhcpmsg(MSG_WARNING, "dhcp_pick_domainname: too long"
987b31320a7SChris Fraire 			    " DNS_DOMAINNAME %s for %s", domainname,
988b31320a7SChris Fraire 			    dsmp->dsm_name);
989b31320a7SChris Fraire 			return (B_FALSE);
990b31320a7SChris Fraire 		}
991b31320a7SChris Fraire 		return (B_TRUE);
992b31320a7SChris Fraire 	} else if (df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6,
993b31320a7SChris Fraire 	    DF_ADOPT_DOMAINNAME)) {
994b31320a7SChris Fraire 		return (dhcp_adopt_domainname(namebuf, buflen, dsmp));
995b31320a7SChris Fraire 	} else {
996b31320a7SChris Fraire 		return (B_FALSE);
997b31320a7SChris Fraire 	}
998b31320a7SChris Fraire }
999b31320a7SChris Fraire 
1000b31320a7SChris Fraire /*
1001b31320a7SChris Fraire  * dhcp_assemble_fqdn(): Set fqdnbuf if REQUEST_FQDN is set and
1002b31320a7SChris Fraire  *			 either a host name was sent in the IPC message (e.g.,
1003*bbf21555SRichard Lowe  *			 from ipadm(8) -h,--reqhost) or the interface is
1004*bbf21555SRichard Lowe  *			 primary and a nodename(5) is defined. If the host
1005b31320a7SChris Fraire  *			 name is not already fully qualified per is_fqdn(),
1006b31320a7SChris Fraire  *			 then dhcp_pick_domainname() is tried to select a
1007b31320a7SChris Fraire  *			 domain to be used to construct an FQDN.
1008b31320a7SChris Fraire  *
1009b31320a7SChris Fraire  *   input: char *: pointer to buffer to which FQDN will be written;
1010b31320a7SChris Fraire  *	    size_t length of buffer;
1011b31320a7SChris Fraire  *	    dhcp_smach_t *: pointer to interface DHCP state machine;
1012b31320a7SChris Fraire  *  output: B_TRUE if fqdnbuf was assigned a valid FQDN; B_FALSE otherwise.
1013b31320a7SChris Fraire  */
1014b31320a7SChris Fraire 
1015b31320a7SChris Fraire static boolean_t
dhcp_assemble_fqdn(char * fqdnbuf,size_t buflen,dhcp_smach_t * dsmp)1016b31320a7SChris Fraire dhcp_assemble_fqdn(char *fqdnbuf, size_t buflen, dhcp_smach_t *dsmp)
1017b31320a7SChris Fraire {
1018b31320a7SChris Fraire 	char		nodename[MAXNAMELEN], *reqhost;
1019b31320a7SChris Fraire 	size_t		pos, len;
1020b31320a7SChris Fraire 
1021b31320a7SChris Fraire 
1022b31320a7SChris Fraire 	if (!df_get_bool(dsmp->dsm_name, dsmp->dsm_isv6, DF_REQUEST_FQDN))
1023b31320a7SChris Fraire 		return (B_FALSE);
1024b31320a7SChris Fraire 
1025b31320a7SChris Fraire 	dhcpmsg(MSG_DEBUG, "dhcp_assemble_fqdn: DF_REQUEST_FQDN");
1026b31320a7SChris Fraire 
1027b31320a7SChris Fraire 	/* It's convenient to ensure fqdnbuf is always null-terminated */
1028b31320a7SChris Fraire 	bzero(fqdnbuf, buflen);
1029b31320a7SChris Fraire 
1030b31320a7SChris Fraire 	reqhost = dsmp->dsm_msg_reqhost;
1031b31320a7SChris Fraire 	if (ipadm_is_nil_hostname(reqhost) &&
1032b31320a7SChris Fraire 	    (dsmp->dsm_dflags & DHCP_IF_PRIMARY) &&
1033b31320a7SChris Fraire 	    dhcp_get_nodename(nodename, sizeof (nodename))) {
1034b31320a7SChris Fraire 		reqhost = nodename;
1035b31320a7SChris Fraire 	}
1036b31320a7SChris Fraire 
1037b31320a7SChris Fraire 	if (ipadm_is_nil_hostname(reqhost)) {
1038b31320a7SChris Fraire 		dhcpmsg(MSG_DEBUG,
1039b31320a7SChris Fraire 		    "dhcp_assemble_fqdn: no interface reqhost for %s",
1040b31320a7SChris Fraire 		    dsmp->dsm_name);
1041b31320a7SChris Fraire 		return (B_FALSE);
1042b31320a7SChris Fraire 	}
1043b31320a7SChris Fraire 
1044b31320a7SChris Fraire 	if ((pos = strlcpy(fqdnbuf, reqhost, buflen)) >= buflen) {
1045b31320a7SChris Fraire 		dhcpmsg(MSG_WARNING, "dhcp_assemble_fqdn: too long reqhost %s"
1046b31320a7SChris Fraire 		    " for %s", reqhost, dsmp->dsm_name);
1047b31320a7SChris Fraire 		return (B_FALSE);
1048b31320a7SChris Fraire 	}
1049b31320a7SChris Fraire 
1050b31320a7SChris Fraire 	/*
1051b31320a7SChris Fraire 	 * If not yet FQDN, construct if possible
1052b31320a7SChris Fraire 	 */
1053b31320a7SChris Fraire 	if (!is_fqdn(reqhost)) {
1054b31320a7SChris Fraire 		char		domainname[MAXNAMELEN];
1055b31320a7SChris Fraire 		size_t		needdots;
1056b31320a7SChris Fraire 
1057b31320a7SChris Fraire 		if (!dhcp_pick_domainname(domainname, sizeof (domainname),
1058b31320a7SChris Fraire 		    dsmp)) {
1059b31320a7SChris Fraire 			dhcpmsg(MSG_DEBUG,
1060b31320a7SChris Fraire 			    "dhcp_assemble_fqdn: no domain name for %s",
1061b31320a7SChris Fraire 			    dsmp->dsm_name);
1062b31320a7SChris Fraire 			return (B_FALSE);
1063b31320a7SChris Fraire 		}
1064b31320a7SChris Fraire 
1065b31320a7SChris Fraire 		/*
1066b31320a7SChris Fraire 		 * Finish constructing FQDN. Account for space needed to hold a
1067b31320a7SChris Fraire 		 * separator '.' and a terminating '.'.
1068b31320a7SChris Fraire 		 */
1069b31320a7SChris Fraire 		len = strlen(domainname);
1070b31320a7SChris Fraire 		needdots = 1 + (domainname[len - 1] != '.');
1071b31320a7SChris Fraire 
1072b31320a7SChris Fraire 		if (pos + len + needdots >= buflen) {
1073b31320a7SChris Fraire 			dhcpmsg(MSG_WARNING, "dhcp_assemble_fqdn: too long"
1074b31320a7SChris Fraire 			    " FQDN %s.%s for %s", fqdnbuf, domainname,
1075b31320a7SChris Fraire 			    dsmp->dsm_name);
1076b31320a7SChris Fraire 			return (B_FALSE);
1077b31320a7SChris Fraire 		}
1078b31320a7SChris Fraire 
1079b31320a7SChris Fraire 		/* add separator and then domain name */
1080b31320a7SChris Fraire 		fqdnbuf[pos++] = '.';
1081b31320a7SChris Fraire 		if (strlcpy(fqdnbuf + pos, domainname, buflen - pos) >=
1082b31320a7SChris Fraire 		    buflen - pos) {
1083b31320a7SChris Fraire 			/* shouldn't get here as we checked above */
1084b31320a7SChris Fraire 			return (B_FALSE);
1085b31320a7SChris Fraire 		}
1086b31320a7SChris Fraire 		pos += len;
1087b31320a7SChris Fraire 
1088b31320a7SChris Fraire 		/* ensure the final character is '.' */
1089b31320a7SChris Fraire 		if (needdots > 1)
1090b31320a7SChris Fraire 			fqdnbuf[pos++] = '.'; /* following is already zeroed */
1091b31320a7SChris Fraire 	}
1092b31320a7SChris Fraire 
1093b31320a7SChris Fraire 	if (!ipadm_is_valid_hostname(fqdnbuf)) {
1094b31320a7SChris Fraire 		dhcpmsg(MSG_WARNING, "dhcp_assemble_fqdn: invalid FQDN %s"
1095b31320a7SChris Fraire 		    " for %s", fqdnbuf, dsmp->dsm_name);
1096b31320a7SChris Fraire 		return (B_FALSE);
1097b31320a7SChris Fraire 	}
1098b31320a7SChris Fraire 
1099b31320a7SChris Fraire 	return (B_TRUE);
1100b31320a7SChris Fraire }
1101b31320a7SChris Fraire 
1102b31320a7SChris Fraire /*
1103b31320a7SChris Fraire  * is_fqdn() : Determine if the `hostname' can be considered as a Fully
1104b31320a7SChris Fraire  *	       Qualified Domain Name by being "rooted" (i.e., ending in '.')
1105b31320a7SChris Fraire  *	       or by containing at least three DNS labels (e.g.,
1106b31320a7SChris Fraire  *	       srv.example.com).
1107b31320a7SChris Fraire  *
1108b31320a7SChris Fraire  *   input: const char *: the hostname to inspect;
1109b31320a7SChris Fraire  *  output: boolean_t: B_TRUE if `hostname' is not NULL satisfies the
1110b31320a7SChris Fraire  *	    criteria above; otherwise, B_FALSE;
1111b31320a7SChris Fraire  */
1112b31320a7SChris Fraire 
1113b31320a7SChris Fraire boolean_t
is_fqdn(const char * hostname)1114b31320a7SChris Fraire is_fqdn(const char *hostname)
1115b31320a7SChris Fraire {
1116b31320a7SChris Fraire 	const char *c;
1117b31320a7SChris Fraire 	size_t i;
1118b31320a7SChris Fraire 
1119b31320a7SChris Fraire 	if (hostname == NULL)
1120b31320a7SChris Fraire 		return (B_FALSE);
1121b31320a7SChris Fraire 
1122b31320a7SChris Fraire 	i = strlen(hostname);
1123b31320a7SChris Fraire 	if (i > 0 && hostname[i - 1] == '.')
1124b31320a7SChris Fraire 		return (B_TRUE);
1125b31320a7SChris Fraire 
1126b31320a7SChris Fraire 	c = hostname;
1127b31320a7SChris Fraire 	i = 0;
1128b31320a7SChris Fraire 	while ((c = strchr(c, '.')) != NULL) {
1129b31320a7SChris Fraire 		++i;
1130b31320a7SChris Fraire 		++c;
1131b31320a7SChris Fraire 	}
1132b31320a7SChris Fraire 
1133b31320a7SChris Fraire 	/* at least two separators is inferred to be fully-qualified */
1134b31320a7SChris Fraire 	return (i >= 2);
1135b31320a7SChris Fraire }
1136b31320a7SChris Fraire 
1137b31320a7SChris Fraire /*
1138b31320a7SChris Fraire  * terminate_at_space(): Reset the first space, 0x20, to 0x0 in the
1139b31320a7SChris Fraire  *			 specified string.
1140b31320a7SChris Fraire  *
1141b31320a7SChris Fraire  *   input: char *: NULL or a null-terminated string;
1142b31320a7SChris Fraire  *  output: void.
1143b31320a7SChris Fraire  */
1144b31320a7SChris Fraire 
1145b31320a7SChris Fraire static void
terminate_at_space(char * value)1146b31320a7SChris Fraire terminate_at_space(char *value)
1147b31320a7SChris Fraire {
1148b31320a7SChris Fraire 	if (value != NULL) {
1149b31320a7SChris Fraire 		char	*sp;
1150b31320a7SChris Fraire 
1151b31320a7SChris Fraire 		sp = strchr(value, ' ');
1152b31320a7SChris Fraire 		if (sp != NULL)
1153b31320a7SChris Fraire 			*sp = '\0';
1154b31320a7SChris Fraire 	}
1155b31320a7SChris Fraire }
1156b31320a7SChris Fraire 
1157b31320a7SChris Fraire /*
1158b31320a7SChris Fraire  * get_offered_domainname_v4(): decode a defined v4 DNSdmain value if it
1159b31320a7SChris Fraire  *				exists to return a copy of the domain
1160b31320a7SChris Fraire  *				name.
1161b31320a7SChris Fraire  *
1162b31320a7SChris Fraire  *   input: dhcp_smach_t *: the state machine REQUESTs are being sent from;
1163b31320a7SChris Fraire  *	    PKT_LIST *: the best packet to be used to construct a REQUEST;
1164b31320a7SChris Fraire  *  output: char *: NULL or a copy of the domain name ('\0' terminated);
1165b31320a7SChris Fraire  */
1166b31320a7SChris Fraire 
1167b31320a7SChris Fraire static char *
get_offered_domainname_v4(PKT_LIST * offer)1168b31320a7SChris Fraire get_offered_domainname_v4(PKT_LIST *offer)
1169b31320a7SChris Fraire {
1170b31320a7SChris Fraire 	char		*domainname = NULL;
1171b31320a7SChris Fraire 	DHCP_OPT	*opt;
1172b31320a7SChris Fraire 
1173b31320a7SChris Fraire 	if ((opt = offer->opts[CD_DNSDOMAIN]) != NULL) {
1174b31320a7SChris Fraire 		uchar_t		*valptr;
1175b31320a7SChris Fraire 		dhcp_symbol_t	*symp;
1176b31320a7SChris Fraire 
1177b31320a7SChris Fraire 		valptr = (uchar_t *)opt + DHCP_OPT_META_LEN;
1178b31320a7SChris Fraire 
1179b31320a7SChris Fraire 		symp = inittab_getbycode(
1180b31320a7SChris Fraire 		    ITAB_CAT_STANDARD, ITAB_CONS_INFO, opt->code);
1181b31320a7SChris Fraire 		if (symp != NULL) {
1182b31320a7SChris Fraire 			domainname = inittab_decode(symp, valptr,
1183b31320a7SChris Fraire 			    opt->len, B_TRUE);
1184b31320a7SChris Fraire 			terminate_at_space(domainname);
1185b31320a7SChris Fraire 			free(symp);
1186b31320a7SChris Fraire 		}
1187b31320a7SChris Fraire 	}
1188b31320a7SChris Fraire 
1189b31320a7SChris Fraire 	return (domainname);
1190b31320a7SChris Fraire }
1191b31320a7SChris Fraire 
1192b31320a7SChris Fraire /*
1193b31320a7SChris Fraire  * save_domainname(): assign dsm_dhcp_domainname from
1194b31320a7SChris Fraire  *		      get_offered_domainname_v4 or leave the field NULL if no
1195b31320a7SChris Fraire  *		      option is present.
1196b31320a7SChris Fraire  *
1197b31320a7SChris Fraire  *   input: dhcp_smach_t *: the state machine REQUESTs are being sent from;
1198b31320a7SChris Fraire  *	    PKT_LIST *: the best packet to be used to construct a REQUEST;
1199b31320a7SChris Fraire  *  output: void
1200b31320a7SChris Fraire  */
1201b31320a7SChris Fraire 
1202b31320a7SChris Fraire void
save_domainname(dhcp_smach_t * dsmp,PKT_LIST * offer)1203b31320a7SChris Fraire save_domainname(dhcp_smach_t *dsmp, PKT_LIST *offer)
1204b31320a7SChris Fraire {
1205b31320a7SChris Fraire 	char	*domainname = NULL;
1206b31320a7SChris Fraire 
1207b31320a7SChris Fraire 	free(dsmp->dsm_dhcp_domainname);
1208b31320a7SChris Fraire 	dsmp->dsm_dhcp_domainname = NULL;
1209b31320a7SChris Fraire 
1210b31320a7SChris Fraire 	if (!dsmp->dsm_isv6) {
1211b31320a7SChris Fraire 		domainname = get_offered_domainname_v4(offer);
1212b31320a7SChris Fraire 	}
1213b31320a7SChris Fraire 
1214b31320a7SChris Fraire 	dsmp->dsm_dhcp_domainname = domainname;
1215b31320a7SChris Fraire }
1216