xref: /onnv-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/util.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include <unistd.h>
30*0Sstevel@tonic-gate #include <sys/types.h>
31*0Sstevel@tonic-gate #include <sys/stat.h>
32*0Sstevel@tonic-gate #include <stdlib.h>
33*0Sstevel@tonic-gate #include <netinet/in.h>		/* struct in_addr */
34*0Sstevel@tonic-gate #include <netinet/dhcp.h>
35*0Sstevel@tonic-gate #include <signal.h>
36*0Sstevel@tonic-gate #include <sys/dlpi.h>
37*0Sstevel@tonic-gate #include <sys/sockio.h>
38*0Sstevel@tonic-gate #include <sys/socket.h>
39*0Sstevel@tonic-gate #include <errno.h>
40*0Sstevel@tonic-gate #include <net/route.h>
41*0Sstevel@tonic-gate #include <net/if_arp.h>
42*0Sstevel@tonic-gate #include <string.h>
43*0Sstevel@tonic-gate #include <dhcpmsg.h>
44*0Sstevel@tonic-gate #include <ctype.h>
45*0Sstevel@tonic-gate #include <netdb.h>
46*0Sstevel@tonic-gate #include <fcntl.h>
47*0Sstevel@tonic-gate #include <stdio.h>
48*0Sstevel@tonic-gate 
49*0Sstevel@tonic-gate #include "states.h"
50*0Sstevel@tonic-gate #include "agent.h"
51*0Sstevel@tonic-gate #include "interface.h"
52*0Sstevel@tonic-gate #include "util.h"
53*0Sstevel@tonic-gate #include "packet.h"
54*0Sstevel@tonic-gate #include "defaults.h"
55*0Sstevel@tonic-gate 
56*0Sstevel@tonic-gate /*
57*0Sstevel@tonic-gate  * this file contains utility functions that have no real better home
58*0Sstevel@tonic-gate  * of their own.  they can largely be broken into six categories:
59*0Sstevel@tonic-gate  *
60*0Sstevel@tonic-gate  *  o  conversion functions -- functions to turn integers into strings,
61*0Sstevel@tonic-gate  *     or to convert between units of a similar measure.
62*0Sstevel@tonic-gate  *
63*0Sstevel@tonic-gate  *  o  ipc-related functions -- functions to simplify the generation of
64*0Sstevel@tonic-gate  *     ipc messages to the agent's clients.
65*0Sstevel@tonic-gate  *
66*0Sstevel@tonic-gate  *  o  signal-related functions -- functions to clean up the agent when
67*0Sstevel@tonic-gate  *     it receives a signal.
68*0Sstevel@tonic-gate  *
69*0Sstevel@tonic-gate  *  o  routing table manipulation functions
70*0Sstevel@tonic-gate  *
71*0Sstevel@tonic-gate  *  o  acknak handler functions
72*0Sstevel@tonic-gate  *
73*0Sstevel@tonic-gate  *  o  true miscellany -- anything else
74*0Sstevel@tonic-gate  */
75*0Sstevel@tonic-gate 
76*0Sstevel@tonic-gate /*
77*0Sstevel@tonic-gate  * pkt_type_to_string(): stringifies a packet type
78*0Sstevel@tonic-gate  *
79*0Sstevel@tonic-gate  *   input: uchar_t: a DHCP packet type value, as defined in RFC2131
80*0Sstevel@tonic-gate  *  output: const char *: the stringified packet type
81*0Sstevel@tonic-gate  */
82*0Sstevel@tonic-gate 
83*0Sstevel@tonic-gate const char *
84*0Sstevel@tonic-gate pkt_type_to_string(uchar_t type)
85*0Sstevel@tonic-gate {
86*0Sstevel@tonic-gate 	/*
87*0Sstevel@tonic-gate 	 * note: the ordering here allows direct indexing of the table
88*0Sstevel@tonic-gate 	 *	 based on the RFC2131 packet type value passed in.
89*0Sstevel@tonic-gate 	 */
90*0Sstevel@tonic-gate 
91*0Sstevel@tonic-gate 	static const char *types[] = {
92*0Sstevel@tonic-gate 		"BOOTP",  "DISCOVER", "OFFER",   "REQUEST", "DECLINE",
93*0Sstevel@tonic-gate 		"ACK",    "NAK",      "RELEASE", "INFORM"
94*0Sstevel@tonic-gate 	};
95*0Sstevel@tonic-gate 
96*0Sstevel@tonic-gate 	if (type >= (sizeof (types) / sizeof (*types)) || types[type] == NULL)
97*0Sstevel@tonic-gate 		return ("<unknown>");
98*0Sstevel@tonic-gate 
99*0Sstevel@tonic-gate 	return (types[type]);
100*0Sstevel@tonic-gate }
101*0Sstevel@tonic-gate 
102*0Sstevel@tonic-gate /*
103*0Sstevel@tonic-gate  * dlpi_to_arp(): converts DLPI datalink types into ARP datalink types
104*0Sstevel@tonic-gate  *
105*0Sstevel@tonic-gate  *   input: uchar_t: the DLPI datalink type
106*0Sstevel@tonic-gate  *  output: uchar_t: the ARP datalink type (0 if no corresponding code)
107*0Sstevel@tonic-gate  */
108*0Sstevel@tonic-gate 
109*0Sstevel@tonic-gate uchar_t
110*0Sstevel@tonic-gate dlpi_to_arp(uchar_t dlpi_type)
111*0Sstevel@tonic-gate {
112*0Sstevel@tonic-gate 	switch (dlpi_type) {
113*0Sstevel@tonic-gate 
114*0Sstevel@tonic-gate 	case DL_ETHER:
115*0Sstevel@tonic-gate 		return (1);
116*0Sstevel@tonic-gate 
117*0Sstevel@tonic-gate 	case DL_FRAME:
118*0Sstevel@tonic-gate 		return (15);
119*0Sstevel@tonic-gate 
120*0Sstevel@tonic-gate 	case DL_ATM:
121*0Sstevel@tonic-gate 		return (16);
122*0Sstevel@tonic-gate 
123*0Sstevel@tonic-gate 	case DL_HDLC:
124*0Sstevel@tonic-gate 		return (17);
125*0Sstevel@tonic-gate 
126*0Sstevel@tonic-gate 	case DL_FC:
127*0Sstevel@tonic-gate 		return (18);
128*0Sstevel@tonic-gate 
129*0Sstevel@tonic-gate 	case DL_CSMACD:				/* ieee 802 networks */
130*0Sstevel@tonic-gate 	case DL_TPB:
131*0Sstevel@tonic-gate 	case DL_TPR:
132*0Sstevel@tonic-gate 	case DL_METRO:
133*0Sstevel@tonic-gate 	case DL_FDDI:
134*0Sstevel@tonic-gate 		return (6);
135*0Sstevel@tonic-gate 	case DL_IB:
136*0Sstevel@tonic-gate 		return (ARPHRD_IB);
137*0Sstevel@tonic-gate 	}
138*0Sstevel@tonic-gate 
139*0Sstevel@tonic-gate 	return (0);
140*0Sstevel@tonic-gate }
141*0Sstevel@tonic-gate 
142*0Sstevel@tonic-gate /*
143*0Sstevel@tonic-gate  * monosec_to_string(): converts a monosec_t into a date string
144*0Sstevel@tonic-gate  *
145*0Sstevel@tonic-gate  *   input: monosec_t: the monosec_t to convert
146*0Sstevel@tonic-gate  *  output: const char *: the corresponding date string
147*0Sstevel@tonic-gate  */
148*0Sstevel@tonic-gate 
149*0Sstevel@tonic-gate const char *
150*0Sstevel@tonic-gate monosec_to_string(monosec_t monosec)
151*0Sstevel@tonic-gate {
152*0Sstevel@tonic-gate 	time_t	time = monosec_to_time(monosec);
153*0Sstevel@tonic-gate 	char	*time_string = ctime(&time);
154*0Sstevel@tonic-gate 
155*0Sstevel@tonic-gate 	/* strip off the newline -- ugh, why, why, why.. */
156*0Sstevel@tonic-gate 	time_string[strlen(time_string) - 1] = '\0';
157*0Sstevel@tonic-gate 	return (time_string);
158*0Sstevel@tonic-gate }
159*0Sstevel@tonic-gate 
160*0Sstevel@tonic-gate /*
161*0Sstevel@tonic-gate  * monosec(): returns a monotonically increasing time in seconds that
162*0Sstevel@tonic-gate  *            is not affected by stime(2) or adjtime(2).
163*0Sstevel@tonic-gate  *
164*0Sstevel@tonic-gate  *   input: void
165*0Sstevel@tonic-gate  *  output: monosec_t: the number of seconds since some time in the past
166*0Sstevel@tonic-gate  */
167*0Sstevel@tonic-gate 
168*0Sstevel@tonic-gate monosec_t
169*0Sstevel@tonic-gate monosec(void)
170*0Sstevel@tonic-gate {
171*0Sstevel@tonic-gate 	return (gethrtime() / NANOSEC);
172*0Sstevel@tonic-gate }
173*0Sstevel@tonic-gate 
174*0Sstevel@tonic-gate /*
175*0Sstevel@tonic-gate  * monosec_to_time(): converts a monosec_t into real wall time
176*0Sstevel@tonic-gate  *
177*0Sstevel@tonic-gate  *    input: monosec_t: the absolute monosec_t to convert
178*0Sstevel@tonic-gate  *   output: time_t: the absolute time that monosec_t represents in wall time
179*0Sstevel@tonic-gate  */
180*0Sstevel@tonic-gate 
181*0Sstevel@tonic-gate time_t
182*0Sstevel@tonic-gate monosec_to_time(monosec_t abs_monosec)
183*0Sstevel@tonic-gate {
184*0Sstevel@tonic-gate 	return (abs_monosec - monosec()) + time(NULL);
185*0Sstevel@tonic-gate }
186*0Sstevel@tonic-gate 
187*0Sstevel@tonic-gate /*
188*0Sstevel@tonic-gate  * send_ok_reply(): sends an "ok" reply to a request and closes the ipc
189*0Sstevel@tonic-gate  *		    connection
190*0Sstevel@tonic-gate  *
191*0Sstevel@tonic-gate  *   input: dhcp_ipc_request_t *: the request to reply to
192*0Sstevel@tonic-gate  *	    int *: the ipc connection file descriptor (set to -1 on return)
193*0Sstevel@tonic-gate  *  output: void
194*0Sstevel@tonic-gate  *    note: the request is freed (thus the request must be on the heap).
195*0Sstevel@tonic-gate  */
196*0Sstevel@tonic-gate 
197*0Sstevel@tonic-gate void
198*0Sstevel@tonic-gate send_ok_reply(dhcp_ipc_request_t *request, int *control_fd)
199*0Sstevel@tonic-gate {
200*0Sstevel@tonic-gate 	send_error_reply(request, 0, control_fd);
201*0Sstevel@tonic-gate }
202*0Sstevel@tonic-gate 
203*0Sstevel@tonic-gate /*
204*0Sstevel@tonic-gate  * send_error_reply(): sends an "error" reply to a request and closes the ipc
205*0Sstevel@tonic-gate  *		       connection
206*0Sstevel@tonic-gate  *
207*0Sstevel@tonic-gate  *   input: dhcp_ipc_request_t *: the request to reply to
208*0Sstevel@tonic-gate  *	    int: the error to send back on the ipc connection
209*0Sstevel@tonic-gate  *	    int *: the ipc connection file descriptor (set to -1 on return)
210*0Sstevel@tonic-gate  *  output: void
211*0Sstevel@tonic-gate  *    note: the request is freed (thus the request must be on the heap).
212*0Sstevel@tonic-gate  */
213*0Sstevel@tonic-gate 
214*0Sstevel@tonic-gate void
215*0Sstevel@tonic-gate send_error_reply(dhcp_ipc_request_t *request, int error, int *control_fd)
216*0Sstevel@tonic-gate {
217*0Sstevel@tonic-gate 	send_data_reply(request, control_fd, error, DHCP_TYPE_NONE, NULL, NULL);
218*0Sstevel@tonic-gate }
219*0Sstevel@tonic-gate 
220*0Sstevel@tonic-gate /*
221*0Sstevel@tonic-gate  * send_data_reply(): sends a reply to a request and closes the ipc connection
222*0Sstevel@tonic-gate  *
223*0Sstevel@tonic-gate  *   input: dhcp_ipc_request_t *: the request to reply to
224*0Sstevel@tonic-gate  *	    int *: the ipc connection file descriptor (set to -1 on return)
225*0Sstevel@tonic-gate  *	    int: the status to send back on the ipc connection (zero for
226*0Sstevel@tonic-gate  *		 success, DHCP_IPC_E_* otherwise).
227*0Sstevel@tonic-gate  *	    dhcp_data_type_t: the type of the payload in the reply
228*0Sstevel@tonic-gate  *	    void *: the payload for the reply, or NULL if there is no payload
229*0Sstevel@tonic-gate  *	    size_t: the size of the payload
230*0Sstevel@tonic-gate  *  output: void
231*0Sstevel@tonic-gate  *    note: the request is freed (thus the request must be on the heap).
232*0Sstevel@tonic-gate  */
233*0Sstevel@tonic-gate 
234*0Sstevel@tonic-gate void
235*0Sstevel@tonic-gate send_data_reply(dhcp_ipc_request_t *request, int *control_fd,
236*0Sstevel@tonic-gate     int error, dhcp_data_type_t type, void *buffer, size_t size)
237*0Sstevel@tonic-gate {
238*0Sstevel@tonic-gate 	dhcp_ipc_reply_t	*reply;
239*0Sstevel@tonic-gate 
240*0Sstevel@tonic-gate 	if (*control_fd == -1)
241*0Sstevel@tonic-gate 		return;
242*0Sstevel@tonic-gate 
243*0Sstevel@tonic-gate 	reply = dhcp_ipc_alloc_reply(request, error, buffer, size, type);
244*0Sstevel@tonic-gate 	if (reply == NULL)
245*0Sstevel@tonic-gate 		dhcpmsg(MSG_ERR, "send_data_reply: cannot allocate reply");
246*0Sstevel@tonic-gate 
247*0Sstevel@tonic-gate 	else if (dhcp_ipc_send_reply(*control_fd, reply) != 0)
248*0Sstevel@tonic-gate 		dhcpmsg(MSG_ERR, "send_data_reply: dhcp_ipc_send_reply");
249*0Sstevel@tonic-gate 
250*0Sstevel@tonic-gate 	/*
251*0Sstevel@tonic-gate 	 * free the request since we've now used it to send our reply.
252*0Sstevel@tonic-gate 	 * we can also close the socket since the reply has been sent.
253*0Sstevel@tonic-gate 	 */
254*0Sstevel@tonic-gate 
255*0Sstevel@tonic-gate 	free(reply);
256*0Sstevel@tonic-gate 	free(request);
257*0Sstevel@tonic-gate 	(void) dhcp_ipc_close(*control_fd);
258*0Sstevel@tonic-gate 	*control_fd = -1;
259*0Sstevel@tonic-gate }
260*0Sstevel@tonic-gate 
261*0Sstevel@tonic-gate /*
262*0Sstevel@tonic-gate  * print_server_msg(): prints a message from a DHCP server
263*0Sstevel@tonic-gate  *
264*0Sstevel@tonic-gate  *   input: struct ifslist *: the interface the message came in on
265*0Sstevel@tonic-gate  *	    DHCP_OPT *: the option containing the string to display
266*0Sstevel@tonic-gate  *  output: void
267*0Sstevel@tonic-gate  */
268*0Sstevel@tonic-gate 
269*0Sstevel@tonic-gate void
270*0Sstevel@tonic-gate print_server_msg(struct ifslist *ifsp, DHCP_OPT *p)
271*0Sstevel@tonic-gate {
272*0Sstevel@tonic-gate 	dhcpmsg(MSG_INFO, "%s: message from server: %.*s", ifsp->if_name,
273*0Sstevel@tonic-gate 	    p->len, p->value);
274*0Sstevel@tonic-gate }
275*0Sstevel@tonic-gate 
276*0Sstevel@tonic-gate /*
277*0Sstevel@tonic-gate  * alrm_exit(): Signal handler for SIGARLM. terminates grandparent.
278*0Sstevel@tonic-gate  *
279*0Sstevel@tonic-gate  *    input: int: signal the handler was called with.
280*0Sstevel@tonic-gate  *
281*0Sstevel@tonic-gate  *   output: void
282*0Sstevel@tonic-gate  */
283*0Sstevel@tonic-gate 
284*0Sstevel@tonic-gate static void
285*0Sstevel@tonic-gate alrm_exit(int sig)
286*0Sstevel@tonic-gate {
287*0Sstevel@tonic-gate 	int exitval;
288*0Sstevel@tonic-gate 
289*0Sstevel@tonic-gate 	if (sig == SIGALRM && grandparent != 0)
290*0Sstevel@tonic-gate 		exitval = EXIT_SUCCESS;
291*0Sstevel@tonic-gate 	else
292*0Sstevel@tonic-gate 		exitval = EXIT_FAILURE;
293*0Sstevel@tonic-gate 
294*0Sstevel@tonic-gate 	_exit(exitval);
295*0Sstevel@tonic-gate }
296*0Sstevel@tonic-gate 
297*0Sstevel@tonic-gate /*
298*0Sstevel@tonic-gate  * daemonize(): daemonizes the process
299*0Sstevel@tonic-gate  *
300*0Sstevel@tonic-gate  *   input: void
301*0Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
302*0Sstevel@tonic-gate  */
303*0Sstevel@tonic-gate 
304*0Sstevel@tonic-gate int
305*0Sstevel@tonic-gate daemonize(void)
306*0Sstevel@tonic-gate {
307*0Sstevel@tonic-gate 	/*
308*0Sstevel@tonic-gate 	 * We've found that adoption takes sufficiently long that
309*0Sstevel@tonic-gate 	 * a dhcpinfo run after dhcpagent -a is started may occur
310*0Sstevel@tonic-gate 	 * before the agent is ready to process the request.
311*0Sstevel@tonic-gate 	 * The result is an error message and an unhappy user.
312*0Sstevel@tonic-gate 	 *
313*0Sstevel@tonic-gate 	 * The initial process now sleeps for DHCP_ADOPT_SLEEP,
314*0Sstevel@tonic-gate 	 * unless interrupted by a SIGALRM, in which case it
315*0Sstevel@tonic-gate 	 * exits immediately. This has the effect that the
316*0Sstevel@tonic-gate 	 * grandparent doesn't exit until the dhcpagent is ready
317*0Sstevel@tonic-gate 	 * to process requests. This defers the the balance of
318*0Sstevel@tonic-gate 	 * the system start-up script processing until the
319*0Sstevel@tonic-gate 	 * dhcpagent is ready to field requests.
320*0Sstevel@tonic-gate 	 *
321*0Sstevel@tonic-gate 	 * grandparent is only set for the adopt case; other
322*0Sstevel@tonic-gate 	 * cases do not require the wait.
323*0Sstevel@tonic-gate 	 */
324*0Sstevel@tonic-gate 
325*0Sstevel@tonic-gate 	if (grandparent != 0)
326*0Sstevel@tonic-gate 		(void) signal(SIGALRM, alrm_exit);
327*0Sstevel@tonic-gate 
328*0Sstevel@tonic-gate 	switch (fork()) {
329*0Sstevel@tonic-gate 
330*0Sstevel@tonic-gate 	case -1:
331*0Sstevel@tonic-gate 		return (0);
332*0Sstevel@tonic-gate 
333*0Sstevel@tonic-gate 	case  0:
334*0Sstevel@tonic-gate 		if (grandparent != 0)
335*0Sstevel@tonic-gate 			(void) signal(SIGALRM, SIG_DFL);
336*0Sstevel@tonic-gate 
337*0Sstevel@tonic-gate 		/*
338*0Sstevel@tonic-gate 		 * setsid() makes us lose our controlling terminal,
339*0Sstevel@tonic-gate 		 * and become both a session leader and a process
340*0Sstevel@tonic-gate 		 * group leader.
341*0Sstevel@tonic-gate 		 */
342*0Sstevel@tonic-gate 
343*0Sstevel@tonic-gate 		(void) setsid();
344*0Sstevel@tonic-gate 
345*0Sstevel@tonic-gate 		/*
346*0Sstevel@tonic-gate 		 * under POSIX, a session leader can accidentally
347*0Sstevel@tonic-gate 		 * (through open(2)) acquire a controlling terminal if
348*0Sstevel@tonic-gate 		 * it does not have one.  just to be safe, fork again
349*0Sstevel@tonic-gate 		 * so we are not a session leader.
350*0Sstevel@tonic-gate 		 */
351*0Sstevel@tonic-gate 
352*0Sstevel@tonic-gate 		switch (fork()) {
353*0Sstevel@tonic-gate 
354*0Sstevel@tonic-gate 		case -1:
355*0Sstevel@tonic-gate 			return (0);
356*0Sstevel@tonic-gate 
357*0Sstevel@tonic-gate 		case 0:
358*0Sstevel@tonic-gate 			(void) signal(SIGHUP, SIG_IGN);
359*0Sstevel@tonic-gate 			(void) chdir("/");
360*0Sstevel@tonic-gate 			(void) umask(022);
361*0Sstevel@tonic-gate 			closefrom(0);
362*0Sstevel@tonic-gate 			break;
363*0Sstevel@tonic-gate 
364*0Sstevel@tonic-gate 		default:
365*0Sstevel@tonic-gate 			_exit(EXIT_SUCCESS);
366*0Sstevel@tonic-gate 		}
367*0Sstevel@tonic-gate 		break;
368*0Sstevel@tonic-gate 
369*0Sstevel@tonic-gate 	default:
370*0Sstevel@tonic-gate 		if (grandparent != 0) {
371*0Sstevel@tonic-gate 			(void) signal(SIGCHLD, SIG_IGN);
372*0Sstevel@tonic-gate 			dhcpmsg(MSG_DEBUG, "dhcpagent: daemonize: "
373*0Sstevel@tonic-gate 			    "waiting for adoption to complete.");
374*0Sstevel@tonic-gate 			if (sleep(DHCP_ADOPT_SLEEP) == 0) {
375*0Sstevel@tonic-gate 				dhcpmsg(MSG_WARNING, "dhcpagent: daemonize: "
376*0Sstevel@tonic-gate 				    "timed out awaiting adoption.");
377*0Sstevel@tonic-gate 			}
378*0Sstevel@tonic-gate 		}
379*0Sstevel@tonic-gate 		_exit(EXIT_SUCCESS);
380*0Sstevel@tonic-gate 	}
381*0Sstevel@tonic-gate 
382*0Sstevel@tonic-gate 	return (1);
383*0Sstevel@tonic-gate }
384*0Sstevel@tonic-gate 
385*0Sstevel@tonic-gate /*
386*0Sstevel@tonic-gate  * update_default_route(): update the interface's default route
387*0Sstevel@tonic-gate  *
388*0Sstevel@tonic-gate  *   input: int: the type of message; either RTM_ADD or RTM_DELETE
389*0Sstevel@tonic-gate  *	    struct in_addr: the default gateway to use
390*0Sstevel@tonic-gate  *	    const char *: the interface associated with the route
391*0Sstevel@tonic-gate  *	    int: any additional flags (besides RTF_STATIC and RTF_GATEWAY)
392*0Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
393*0Sstevel@tonic-gate  */
394*0Sstevel@tonic-gate 
395*0Sstevel@tonic-gate static int
396*0Sstevel@tonic-gate update_default_route(const char *ifname, int type, struct in_addr *gateway_nbo,
397*0Sstevel@tonic-gate     int flags)
398*0Sstevel@tonic-gate {
399*0Sstevel@tonic-gate 	static int rtsock_fd = -1;
400*0Sstevel@tonic-gate 	struct {
401*0Sstevel@tonic-gate 		struct rt_msghdr	rm_mh;
402*0Sstevel@tonic-gate 		struct sockaddr_in	rm_dst;
403*0Sstevel@tonic-gate 		struct sockaddr_in	rm_gw;
404*0Sstevel@tonic-gate 		struct sockaddr_in	rm_mask;
405*0Sstevel@tonic-gate 		struct sockaddr_dl	rm_ifp;
406*0Sstevel@tonic-gate 	} rtmsg;
407*0Sstevel@tonic-gate 
408*0Sstevel@tonic-gate 	if (rtsock_fd == -1) {
409*0Sstevel@tonic-gate 		rtsock_fd = socket(PF_ROUTE, SOCK_RAW, 0);
410*0Sstevel@tonic-gate 		if (rtsock_fd == -1) {
411*0Sstevel@tonic-gate 			dhcpmsg(MSG_ERR, "update_default_route: "
412*0Sstevel@tonic-gate 			    "cannot create routing socket");
413*0Sstevel@tonic-gate 			return (0);
414*0Sstevel@tonic-gate 		}
415*0Sstevel@tonic-gate 	}
416*0Sstevel@tonic-gate 
417*0Sstevel@tonic-gate 	(void) memset(&rtmsg, 0, sizeof (rtmsg));
418*0Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_version = RTM_VERSION;
419*0Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_msglen	= sizeof (rtmsg);
420*0Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_type	= type;
421*0Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_pid	= getpid();
422*0Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_flags	= RTF_GATEWAY | RTF_STATIC | flags;
423*0Sstevel@tonic-gate 	rtmsg.rm_mh.rtm_addrs	= RTA_GATEWAY | RTA_DST | RTA_NETMASK | RTA_IFP;
424*0Sstevel@tonic-gate 
425*0Sstevel@tonic-gate 	rtmsg.rm_gw.sin_family	= AF_INET;
426*0Sstevel@tonic-gate 	rtmsg.rm_gw.sin_addr	= *gateway_nbo;
427*0Sstevel@tonic-gate 
428*0Sstevel@tonic-gate 	rtmsg.rm_dst.sin_family = AF_INET;
429*0Sstevel@tonic-gate 	rtmsg.rm_dst.sin_addr.s_addr = htonl(INADDR_ANY);
430*0Sstevel@tonic-gate 
431*0Sstevel@tonic-gate 	rtmsg.rm_mask.sin_family = AF_INET;
432*0Sstevel@tonic-gate 	rtmsg.rm_mask.sin_addr.s_addr = htonl(0);
433*0Sstevel@tonic-gate 
434*0Sstevel@tonic-gate 	rtmsg.rm_ifp.sdl_family	= AF_LINK;
435*0Sstevel@tonic-gate 	rtmsg.rm_ifp.sdl_index	= if_nametoindex(ifname);
436*0Sstevel@tonic-gate 
437*0Sstevel@tonic-gate 	return (write(rtsock_fd, &rtmsg, sizeof (rtmsg)) == sizeof (rtmsg));
438*0Sstevel@tonic-gate }
439*0Sstevel@tonic-gate 
440*0Sstevel@tonic-gate /*
441*0Sstevel@tonic-gate  * add_default_route(): add the default route to the given gateway
442*0Sstevel@tonic-gate  *
443*0Sstevel@tonic-gate  *   input: const char *: the name of the interface associated with the route
444*0Sstevel@tonic-gate  *	    struct in_addr: the default gateway to add
445*0Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
446*0Sstevel@tonic-gate  */
447*0Sstevel@tonic-gate 
448*0Sstevel@tonic-gate int
449*0Sstevel@tonic-gate add_default_route(const char *ifname, struct in_addr *gateway_nbo)
450*0Sstevel@tonic-gate {
451*0Sstevel@tonic-gate 	if (strchr(ifname, ':') != NULL)	/* see README */
452*0Sstevel@tonic-gate 		return (1);
453*0Sstevel@tonic-gate 
454*0Sstevel@tonic-gate 	return (update_default_route(ifname, RTM_ADD, gateway_nbo, RTF_UP));
455*0Sstevel@tonic-gate }
456*0Sstevel@tonic-gate 
457*0Sstevel@tonic-gate /*
458*0Sstevel@tonic-gate  * del_default_route(): deletes the default route to the given gateway
459*0Sstevel@tonic-gate  *
460*0Sstevel@tonic-gate  *   input: const char *: the name of the interface associated with the route
461*0Sstevel@tonic-gate  *	    struct in_addr: if not INADDR_ANY, the default gateway to remove
462*0Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
463*0Sstevel@tonic-gate  */
464*0Sstevel@tonic-gate 
465*0Sstevel@tonic-gate int
466*0Sstevel@tonic-gate del_default_route(const char *ifname, struct in_addr *gateway_nbo)
467*0Sstevel@tonic-gate {
468*0Sstevel@tonic-gate 	if (strchr(ifname, ':') != NULL)
469*0Sstevel@tonic-gate 		return (1);
470*0Sstevel@tonic-gate 
471*0Sstevel@tonic-gate 	if (gateway_nbo->s_addr == htonl(INADDR_ANY)) /* no router */
472*0Sstevel@tonic-gate 		return (1);
473*0Sstevel@tonic-gate 
474*0Sstevel@tonic-gate 	return (update_default_route(ifname, RTM_DELETE, gateway_nbo, 0));
475*0Sstevel@tonic-gate }
476*0Sstevel@tonic-gate 
477*0Sstevel@tonic-gate /*
478*0Sstevel@tonic-gate  * inactivity_shutdown(): shuts down agent if there are no interfaces to manage
479*0Sstevel@tonic-gate  *
480*0Sstevel@tonic-gate  *   input: iu_tq_t *: unused
481*0Sstevel@tonic-gate  *	    void *: unused
482*0Sstevel@tonic-gate  *  output: void
483*0Sstevel@tonic-gate  */
484*0Sstevel@tonic-gate 
485*0Sstevel@tonic-gate /* ARGSUSED */
486*0Sstevel@tonic-gate void
487*0Sstevel@tonic-gate inactivity_shutdown(iu_tq_t *tqp, void *arg)
488*0Sstevel@tonic-gate {
489*0Sstevel@tonic-gate 	if (ifs_count() > 0)	/* shouldn't happen, but... */
490*0Sstevel@tonic-gate 		return;
491*0Sstevel@tonic-gate 
492*0Sstevel@tonic-gate 	iu_stop_handling_events(eh, DHCP_REASON_INACTIVITY, NULL, NULL);
493*0Sstevel@tonic-gate }
494*0Sstevel@tonic-gate 
495*0Sstevel@tonic-gate /*
496*0Sstevel@tonic-gate  * graceful_shutdown(): shuts down the agent gracefully
497*0Sstevel@tonic-gate  *
498*0Sstevel@tonic-gate  *   input: int: the signal that caused graceful_shutdown to be called
499*0Sstevel@tonic-gate  *  output: void
500*0Sstevel@tonic-gate  */
501*0Sstevel@tonic-gate 
502*0Sstevel@tonic-gate void
503*0Sstevel@tonic-gate graceful_shutdown(int sig)
504*0Sstevel@tonic-gate {
505*0Sstevel@tonic-gate 	iu_stop_handling_events(eh, sig, drain_script, NULL);
506*0Sstevel@tonic-gate }
507*0Sstevel@tonic-gate 
508*0Sstevel@tonic-gate /*
509*0Sstevel@tonic-gate  * register_acknak(): registers dhcp_acknak() to be called back when ACK or
510*0Sstevel@tonic-gate  *		      NAK packets are received on a given interface
511*0Sstevel@tonic-gate  *
512*0Sstevel@tonic-gate  *   input: struct ifslist *: the interface to register for
513*0Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
514*0Sstevel@tonic-gate  */
515*0Sstevel@tonic-gate 
516*0Sstevel@tonic-gate int
517*0Sstevel@tonic-gate register_acknak(struct ifslist *ifsp)
518*0Sstevel@tonic-gate {
519*0Sstevel@tonic-gate 	iu_event_id_t	ack_id, ack_bcast_id = -1;
520*0Sstevel@tonic-gate 
521*0Sstevel@tonic-gate 	/*
522*0Sstevel@tonic-gate 	 * having an acknak id already registered isn't impossible;
523*0Sstevel@tonic-gate 	 * handle the situation as gracefully as possible.
524*0Sstevel@tonic-gate 	 */
525*0Sstevel@tonic-gate 
526*0Sstevel@tonic-gate 	if (ifsp->if_acknak_id != -1) {
527*0Sstevel@tonic-gate 		dhcpmsg(MSG_DEBUG, "register_acknak: acknak id pending, "
528*0Sstevel@tonic-gate 		    "attempting to cancel");
529*0Sstevel@tonic-gate 		if (unregister_acknak(ifsp) == 0)
530*0Sstevel@tonic-gate 			return (0);
531*0Sstevel@tonic-gate 	}
532*0Sstevel@tonic-gate 
533*0Sstevel@tonic-gate 	switch (ifsp->if_state) {
534*0Sstevel@tonic-gate 
535*0Sstevel@tonic-gate 	case BOUND:
536*0Sstevel@tonic-gate 	case REBINDING:
537*0Sstevel@tonic-gate 	case RENEWING:
538*0Sstevel@tonic-gate 
539*0Sstevel@tonic-gate 		ack_bcast_id = iu_register_event(eh, ifsp->if_sock_fd, POLLIN,
540*0Sstevel@tonic-gate 		    dhcp_acknak, ifsp);
541*0Sstevel@tonic-gate 
542*0Sstevel@tonic-gate 		if (ack_bcast_id == -1) {
543*0Sstevel@tonic-gate 			dhcpmsg(MSG_WARNING, "register_acknak: cannot "
544*0Sstevel@tonic-gate 			    "register to receive socket broadcasts");
545*0Sstevel@tonic-gate 			return (0);
546*0Sstevel@tonic-gate 		}
547*0Sstevel@tonic-gate 
548*0Sstevel@tonic-gate 		ack_id = iu_register_event(eh, ifsp->if_sock_ip_fd, POLLIN,
549*0Sstevel@tonic-gate 		    dhcp_acknak, ifsp);
550*0Sstevel@tonic-gate 		break;
551*0Sstevel@tonic-gate 
552*0Sstevel@tonic-gate 	default:
553*0Sstevel@tonic-gate 		ack_id = iu_register_event(eh, ifsp->if_dlpi_fd, POLLIN,
554*0Sstevel@tonic-gate 		    dhcp_acknak, ifsp);
555*0Sstevel@tonic-gate 		break;
556*0Sstevel@tonic-gate 	}
557*0Sstevel@tonic-gate 
558*0Sstevel@tonic-gate 	if (ack_id == -1) {
559*0Sstevel@tonic-gate 		dhcpmsg(MSG_WARNING, "register_acknak: cannot register event");
560*0Sstevel@tonic-gate 		(void) iu_unregister_event(eh, ack_bcast_id, NULL);
561*0Sstevel@tonic-gate 		return (0);
562*0Sstevel@tonic-gate 	}
563*0Sstevel@tonic-gate 
564*0Sstevel@tonic-gate 	ifsp->if_acknak_id = ack_id;
565*0Sstevel@tonic-gate 	hold_ifs(ifsp);
566*0Sstevel@tonic-gate 
567*0Sstevel@tonic-gate 	ifsp->if_acknak_bcast_id = ack_bcast_id;
568*0Sstevel@tonic-gate 	if (ifsp->if_acknak_bcast_id != -1) {
569*0Sstevel@tonic-gate 		hold_ifs(ifsp);
570*0Sstevel@tonic-gate 		dhcpmsg(MSG_DEBUG, "register_acknak: registered broadcast id "
571*0Sstevel@tonic-gate 		    "%d", ack_bcast_id);
572*0Sstevel@tonic-gate 	}
573*0Sstevel@tonic-gate 
574*0Sstevel@tonic-gate 	dhcpmsg(MSG_DEBUG, "register_acknak: registered acknak id %d", ack_id);
575*0Sstevel@tonic-gate 	return (1);
576*0Sstevel@tonic-gate }
577*0Sstevel@tonic-gate 
578*0Sstevel@tonic-gate /*
579*0Sstevel@tonic-gate  * unregister_acknak(): unregisters dhcp_acknak() to be called back
580*0Sstevel@tonic-gate  *
581*0Sstevel@tonic-gate  *   input: struct ifslist *: the interface to unregister for
582*0Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
583*0Sstevel@tonic-gate  */
584*0Sstevel@tonic-gate 
585*0Sstevel@tonic-gate int
586*0Sstevel@tonic-gate unregister_acknak(struct ifslist *ifsp)
587*0Sstevel@tonic-gate {
588*0Sstevel@tonic-gate 	if (ifsp->if_acknak_id != -1) {
589*0Sstevel@tonic-gate 
590*0Sstevel@tonic-gate 		if (iu_unregister_event(eh, ifsp->if_acknak_id, NULL) == 0) {
591*0Sstevel@tonic-gate 			dhcpmsg(MSG_DEBUG, "unregister_acknak: cannot "
592*0Sstevel@tonic-gate 			    "unregister acknak id %d on %s",
593*0Sstevel@tonic-gate 			    ifsp->if_acknak_id, ifsp->if_name);
594*0Sstevel@tonic-gate 			return (0);
595*0Sstevel@tonic-gate 		}
596*0Sstevel@tonic-gate 
597*0Sstevel@tonic-gate 		dhcpmsg(MSG_DEBUG, "unregister_acknak: unregistered acknak id "
598*0Sstevel@tonic-gate 		    "%d", ifsp->if_acknak_id);
599*0Sstevel@tonic-gate 
600*0Sstevel@tonic-gate 		ifsp->if_acknak_id = -1;
601*0Sstevel@tonic-gate 		(void) release_ifs(ifsp);
602*0Sstevel@tonic-gate 	}
603*0Sstevel@tonic-gate 
604*0Sstevel@tonic-gate 	if (ifsp->if_acknak_bcast_id != -1) {
605*0Sstevel@tonic-gate 
606*0Sstevel@tonic-gate 		if (iu_unregister_event(eh, ifsp->if_acknak_bcast_id, NULL)
607*0Sstevel@tonic-gate 		    == 0) {
608*0Sstevel@tonic-gate 			dhcpmsg(MSG_DEBUG, "unregister_acknak: cannot "
609*0Sstevel@tonic-gate 			    "unregister broadcast id %d on %s",
610*0Sstevel@tonic-gate 			    ifsp->if_acknak_id, ifsp->if_name);
611*0Sstevel@tonic-gate 			return (0);
612*0Sstevel@tonic-gate 		}
613*0Sstevel@tonic-gate 
614*0Sstevel@tonic-gate 		dhcpmsg(MSG_DEBUG, "unregister_acknak: unregistered "
615*0Sstevel@tonic-gate 		    "broadcast id %d", ifsp->if_acknak_bcast_id);
616*0Sstevel@tonic-gate 
617*0Sstevel@tonic-gate 		ifsp->if_acknak_bcast_id = -1;
618*0Sstevel@tonic-gate 		(void) release_ifs(ifsp);
619*0Sstevel@tonic-gate 	}
620*0Sstevel@tonic-gate 
621*0Sstevel@tonic-gate 	return (1);
622*0Sstevel@tonic-gate }
623*0Sstevel@tonic-gate 
624*0Sstevel@tonic-gate /*
625*0Sstevel@tonic-gate  * bind_sock(): binds a socket to a given IP address and port number
626*0Sstevel@tonic-gate  *
627*0Sstevel@tonic-gate  *   input: int: the socket to bind
628*0Sstevel@tonic-gate  *	    in_port_t: the port number to bind to, host byte order
629*0Sstevel@tonic-gate  *	    in_addr_t: the address to bind to, host byte order
630*0Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
631*0Sstevel@tonic-gate  */
632*0Sstevel@tonic-gate 
633*0Sstevel@tonic-gate int
634*0Sstevel@tonic-gate bind_sock(int fd, in_port_t port_hbo, in_addr_t addr_hbo)
635*0Sstevel@tonic-gate {
636*0Sstevel@tonic-gate 	struct sockaddr_in	sin;
637*0Sstevel@tonic-gate 	int			on = 1;
638*0Sstevel@tonic-gate 
639*0Sstevel@tonic-gate 	(void) memset(&sin, 0, sizeof (struct sockaddr_in));
640*0Sstevel@tonic-gate 	sin.sin_family = AF_INET;
641*0Sstevel@tonic-gate 	sin.sin_port   = htons(port_hbo);
642*0Sstevel@tonic-gate 	sin.sin_addr.s_addr = htonl(addr_hbo);
643*0Sstevel@tonic-gate 
644*0Sstevel@tonic-gate 	(void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (int));
645*0Sstevel@tonic-gate 
646*0Sstevel@tonic-gate 	return (bind(fd, (struct sockaddr *)&sin, sizeof (sin)) == 0);
647*0Sstevel@tonic-gate }
648*0Sstevel@tonic-gate 
649*0Sstevel@tonic-gate /*
650*0Sstevel@tonic-gate  * valid_hostname(): check whether a string is a valid hostname
651*0Sstevel@tonic-gate  *
652*0Sstevel@tonic-gate  *   input: const char *: the string to verify as a hostname
653*0Sstevel@tonic-gate  *  output: boolean_t: B_TRUE if the string is a valid hostname
654*0Sstevel@tonic-gate  *
655*0Sstevel@tonic-gate  * Note that we accept both host names beginning with a digit and
656*0Sstevel@tonic-gate  * those containing hyphens.  Neither is strictly legal according
657*0Sstevel@tonic-gate  * to the RFCs, but both are in common practice, so we endeavour
658*0Sstevel@tonic-gate  * to not break what customers are using.
659*0Sstevel@tonic-gate  */
660*0Sstevel@tonic-gate 
661*0Sstevel@tonic-gate static boolean_t
662*0Sstevel@tonic-gate valid_hostname(const char *hostname)
663*0Sstevel@tonic-gate {
664*0Sstevel@tonic-gate 	unsigned int i;
665*0Sstevel@tonic-gate 
666*0Sstevel@tonic-gate 	for (i = 0; hostname[i] != '\0'; i++) {
667*0Sstevel@tonic-gate 
668*0Sstevel@tonic-gate 		if (isalpha(hostname[i]) || isdigit(hostname[i]) ||
669*0Sstevel@tonic-gate 		    (((hostname[i] == '-') || (hostname[i] == '.')) && (i > 0)))
670*0Sstevel@tonic-gate 			continue;
671*0Sstevel@tonic-gate 
672*0Sstevel@tonic-gate 		return (B_FALSE);
673*0Sstevel@tonic-gate 	}
674*0Sstevel@tonic-gate 
675*0Sstevel@tonic-gate 	return (i > 0);
676*0Sstevel@tonic-gate }
677*0Sstevel@tonic-gate 
678*0Sstevel@tonic-gate /*
679*0Sstevel@tonic-gate  * iffile_to_hostname(): return the hostname contained on a line of the form
680*0Sstevel@tonic-gate  *
681*0Sstevel@tonic-gate  * [ ^I]*inet[ ^I]+hostname[\n]*\0
682*0Sstevel@tonic-gate  *
683*0Sstevel@tonic-gate  * in the file located at the specified path
684*0Sstevel@tonic-gate  *
685*0Sstevel@tonic-gate  *   input: const char *: the path of the file to look in for the hostname
686*0Sstevel@tonic-gate  *  output: const char *: the hostname at that path, or NULL on failure
687*0Sstevel@tonic-gate  */
688*0Sstevel@tonic-gate 
689*0Sstevel@tonic-gate #define	IFLINE_MAX	1024	/* maximum length of a hostname.<if> line */
690*0Sstevel@tonic-gate 
691*0Sstevel@tonic-gate const char *
692*0Sstevel@tonic-gate iffile_to_hostname(const char *path)
693*0Sstevel@tonic-gate {
694*0Sstevel@tonic-gate 	FILE		*fp;
695*0Sstevel@tonic-gate 	static char	ifline[IFLINE_MAX];
696*0Sstevel@tonic-gate 
697*0Sstevel@tonic-gate 	fp = fopen(path, "r");
698*0Sstevel@tonic-gate 	if (fp == NULL)
699*0Sstevel@tonic-gate 		return (NULL);
700*0Sstevel@tonic-gate 
701*0Sstevel@tonic-gate 	/*
702*0Sstevel@tonic-gate 	 * /etc/hostname.<if> may contain multiple ifconfig commands, but each
703*0Sstevel@tonic-gate 	 * such command is on a separate line (see the "while read ifcmds" code
704*0Sstevel@tonic-gate 	 * in /etc/init.d/inetinit).  Thus we will read the file a line at a
705*0Sstevel@tonic-gate 	 * time, searching for a line of the form
706*0Sstevel@tonic-gate 	 *
707*0Sstevel@tonic-gate 	 * [ ^I]*inet[ ^I]+hostname[\n]*\0
708*0Sstevel@tonic-gate 	 *
709*0Sstevel@tonic-gate 	 * extract the host name from it, and check it for validity.
710*0Sstevel@tonic-gate 	 */
711*0Sstevel@tonic-gate 	while (fgets(ifline, sizeof (ifline), fp) != NULL) {
712*0Sstevel@tonic-gate 		char *p;
713*0Sstevel@tonic-gate 
714*0Sstevel@tonic-gate 		if ((p = strstr(ifline, "inet")) != NULL) {
715*0Sstevel@tonic-gate 			if ((p != ifline) && !isspace(p[-1])) {
716*0Sstevel@tonic-gate 				(void) fclose(fp);
717*0Sstevel@tonic-gate 				return (NULL);
718*0Sstevel@tonic-gate 			}
719*0Sstevel@tonic-gate 			p += 4;	/* skip over "inet" and expect spaces or tabs */
720*0Sstevel@tonic-gate 			if ((*p == '\n') || (*p == '\0')) {
721*0Sstevel@tonic-gate 				(void) fclose(fp);
722*0Sstevel@tonic-gate 				return (NULL);
723*0Sstevel@tonic-gate 			}
724*0Sstevel@tonic-gate 			if (isspace(*p)) {
725*0Sstevel@tonic-gate 				char *nlptr;
726*0Sstevel@tonic-gate 
727*0Sstevel@tonic-gate 				/* no need to read more of the file */
728*0Sstevel@tonic-gate 				(void) fclose(fp);
729*0Sstevel@tonic-gate 
730*0Sstevel@tonic-gate 				while (isspace(*p))
731*0Sstevel@tonic-gate 					p++;
732*0Sstevel@tonic-gate 				if ((nlptr = strrchr(p, '\n')) != NULL)
733*0Sstevel@tonic-gate 					*nlptr = '\0';
734*0Sstevel@tonic-gate 				if (strlen(p) > MAXHOSTNAMELEN) {
735*0Sstevel@tonic-gate 					dhcpmsg(MSG_WARNING,
736*0Sstevel@tonic-gate 					    "iffile_to_hostname:"
737*0Sstevel@tonic-gate 					    " host name too long");
738*0Sstevel@tonic-gate 					return (NULL);
739*0Sstevel@tonic-gate 				}
740*0Sstevel@tonic-gate 				if (valid_hostname(p)) {
741*0Sstevel@tonic-gate 					return (p);
742*0Sstevel@tonic-gate 				} else {
743*0Sstevel@tonic-gate 					dhcpmsg(MSG_WARNING,
744*0Sstevel@tonic-gate 					    "iffile_to_hostname:"
745*0Sstevel@tonic-gate 					    " host name not valid");
746*0Sstevel@tonic-gate 					return (NULL);
747*0Sstevel@tonic-gate 				}
748*0Sstevel@tonic-gate 			} else {
749*0Sstevel@tonic-gate 				(void) fclose(fp);
750*0Sstevel@tonic-gate 				return (NULL);
751*0Sstevel@tonic-gate 			}
752*0Sstevel@tonic-gate 		}
753*0Sstevel@tonic-gate 	}
754*0Sstevel@tonic-gate 
755*0Sstevel@tonic-gate 	(void) fclose(fp);
756*0Sstevel@tonic-gate 	return (NULL);
757*0Sstevel@tonic-gate }
758