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