xref: /reactos/base/services/dhcpcsvc/dhcp/dhclient.c (revision 81f8bcea)
1 /*	$OpenBSD: dhclient.c,v 1.62 2004/12/05 18:35:51 deraadt Exp $	*/
2 
3 /*
4  * Copyright 2004 Henning Brauer <henning@openbsd.org>
5  * Copyright (c) 1995, 1996, 1997, 1998, 1999
6  * The Internet Software Consortium.    All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of The Internet Software Consortium nor the names
18  *    of its contributors may be used to endorse or promote products derived
19  *    from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
22  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
23  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
26  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
29  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  * This software has been written for the Internet Software Consortium
36  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
37  * Enterprises.  To learn more about the Internet Software Consortium,
38  * see ``http://www.vix.com/isc''.  To learn more about Vixie
39  * Enterprises, see ``http://www.vix.com''.
40  *
41  * This client was substantially modified and enhanced by Elliot Poger
42  * for use on Linux while he was working on the MosquitoNet project at
43  * Stanford.
44  *
45  * The current version owes much to Elliot's Linux enhancements, but
46  * was substantially reorganized and partially rewritten by Ted Lemon
47  * so as to use the same networking framework that the Internet Software
48  * Consortium DHCP server uses.   Much system-specific configuration code
49  * was moved into a shell script so that as support for more operating
50  * systems is added, it will not be necessary to port and maintain
51  * system-specific configuration code to these operating systems - instead,
52  * the shell script can invoke the native tools to accomplish the same
53  * purpose.
54  */
55 
56 #include <rosdhcp.h>
57 
58 #define	PERIOD 0x2e
59 #define	hyphenchar(c) ((c) == 0x2d)
60 #define	bslashchar(c) ((c) == 0x5c)
61 #define	periodchar(c) ((c) == PERIOD)
62 #define	asterchar(c) ((c) == 0x2a)
63 #define	alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) || \
64 	    ((c) >= 0x61 && (c) <= 0x7a))
65 #define	digitchar(c) ((c) >= 0x30 && (c) <= 0x39)
66 
67 #define	borderchar(c) (alphachar(c) || digitchar(c))
68 #define	middlechar(c) (borderchar(c) || hyphenchar(c))
69 #define	domainchar(c) ((c) > 0x20 && (c) < 0x7f)
70 
71 unsigned long debug_trace_level = 0; /* DEBUG_ULTRA */
72 
73 char *path_dhclient_conf = _PATH_DHCLIENT_CONF;
74 char *path_dhclient_db = NULL;
75 
76 int log_perror = 1;
77 int privfd;
78 //int nullfd = -1;
79 
80 struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } };
81 struct in_addr inaddr_any;
82 struct sockaddr_in sockaddr_broadcast;
83 
84 /*
85  * ASSERT_STATE() does nothing now; it used to be
86  * assert (state_is == state_shouldbe).
87  */
88 #define ASSERT_STATE(state_is, state_shouldbe) {}
89 
90 #define TIME_MAX 2147483647
91 
92 int		log_priority;
93 int		no_daemon;
94 int		unknown_ok = 1;
95 int		routefd;
96 
97 void		 usage(void);
98 int		 check_option(struct client_lease *l, int option);
99 int		 ipv4addrs(char * buf);
100 int		 res_hnok(const char *dn);
101 char		*option_as_string(unsigned int code, unsigned char *data, int len);
102 int		 fork_privchld(int, int);
103 int              check_arp( struct interface_info *ip, struct client_lease *lp );
104 
105 #define	ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
106 
107 time_t	scripttime;
108 
109 
110 int
111 init_client(void)
112 {
113     ApiInit();
114     AdapterInit();
115 
116     tzset();
117 
118     memset(&sockaddr_broadcast, 0, sizeof(sockaddr_broadcast));
119     sockaddr_broadcast.sin_family = AF_INET;
120     sockaddr_broadcast.sin_port = htons(REMOTE_PORT);
121     sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST;
122     inaddr_any.s_addr = INADDR_ANY;
123     bootp_packet_handler = do_packet;
124 
125     if (PipeInit() == INVALID_HANDLE_VALUE)
126     {
127         DbgPrint("DHCPCSVC: PipeInit() failed!\n");
128         AdapterStop();
129         ApiFree();
130         return 0; // FALSE
131     }
132 
133     return 1; // TRUE
134 }
135 
136 void
137 stop_client(void)
138 {
139     // AdapterStop();
140     // ApiFree();
141     /* FIXME: Close pipe and kill pipe thread */
142 }
143 
144 /* XXX Implement me */
145 int check_arp( struct interface_info *ip, struct client_lease *lp ) {
146     return 1;
147 }
148 
149 /*
150  * Individual States:
151  *
152  * Each routine is called from the dhclient_state_machine() in one of
153  * these conditions:
154  * -> entering INIT state
155  * -> recvpacket_flag == 0: timeout in this state
156  * -> otherwise: received a packet in this state
157  *
158  * Return conditions as handled by dhclient_state_machine():
159  * Returns 1, sendpacket_flag = 1: send packet, reset timer.
160  * Returns 1, sendpacket_flag = 0: just reset the timer (wait for a milestone).
161  * Returns 0: finish the nap which was interrupted for no good reason.
162  *
163  * Several per-interface variables are used to keep track of the process:
164  *   active_lease: the lease that is being used on the interface
165  *                 (null pointer if not configured yet).
166  *   offered_leases: leases corresponding to DHCPOFFER messages that have
167  *                   been sent to us by DHCP servers.
168  *   acked_leases: leases corresponding to DHCPACK messages that have been
169  *                 sent to us by DHCP servers.
170  *   sendpacket: DHCP packet we're trying to send.
171  *   destination: IP address to send sendpacket to
172  * In addition, there are several relevant per-lease variables.
173  *   T1_expiry, T2_expiry, lease_expiry: lease milestones
174  * In the active lease, these control the process of renewing the lease;
175  * In leases on the acked_leases list, this simply determines when we
176  * can no longer legitimately use the lease.
177  */
178 
179 void
180 state_reboot(void *ipp)
181 {
182 	struct interface_info *ip = ipp;
183 	ULONG foo = (ULONG) GetTickCount();
184 
185 	/* If we don't remember an active lease, go straight to INIT. */
186 	if (!ip->client->active || ip->client->active->is_bootp) {
187 		state_init(ip);
188 		return;
189 	}
190 
191 	/* We are in the rebooting state. */
192 	ip->client->state = S_REBOOTING;
193 
194 	/* make_request doesn't initialize xid because it normally comes
195 	   from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER,
196 	   so pick an xid now. */
197 	ip->client->xid = RtlRandom(&foo);
198 
199 	/* Make a DHCPREQUEST packet, and set appropriate per-interface
200 	   flags. */
201 	make_request(ip, ip->client->active);
202 	ip->client->destination = iaddr_broadcast;
203 	time(&ip->client->first_sending);
204 	ip->client->interval = ip->client->config->initial_interval;
205 
206 	/* Zap the medium list... */
207 	ip->client->medium = NULL;
208 
209 	/* Send out the first DHCPREQUEST packet. */
210 	send_request(ip);
211 }
212 
213 /*
214  * Called when a lease has completely expired and we've
215  * been unable to renew it.
216  */
217 void
218 state_init(void *ipp)
219 {
220 	struct interface_info *ip = ipp;
221 
222 	ASSERT_STATE(state, S_INIT);
223 
224 	/* Make a DHCPDISCOVER packet, and set appropriate per-interface
225 	   flags. */
226 	make_discover(ip, ip->client->active);
227 	ip->client->xid = ip->client->packet.xid;
228 	ip->client->destination = iaddr_broadcast;
229 	ip->client->state = S_SELECTING;
230 	time(&ip->client->first_sending);
231 	ip->client->interval = ip->client->config->initial_interval;
232 
233 	/* Add an immediate timeout to cause the first DHCPDISCOVER packet
234 	   to go out. */
235 	send_discover(ip);
236 }
237 
238 /*
239  * state_selecting is called when one or more DHCPOFFER packets
240  * have been received and a configurable period of time has passed.
241  */
242 void
243 state_selecting(void *ipp)
244 {
245 	struct interface_info *ip = ipp;
246 	struct client_lease *lp, *next, *picked;
247         time_t cur_time;
248 
249 	ASSERT_STATE(state, S_SELECTING);
250 
251         time(&cur_time);
252 
253 	/* Cancel state_selecting and send_discover timeouts, since either
254 	   one could have got us here. */
255 	cancel_timeout(state_selecting, ip);
256 	cancel_timeout(send_discover, ip);
257 
258 	/* We have received one or more DHCPOFFER packets.   Currently,
259 	   the only criterion by which we judge leases is whether or
260 	   not we get a response when we arp for them. */
261 	picked = NULL;
262 	for (lp = ip->client->offered_leases; lp; lp = next) {
263 		next = lp->next;
264 
265 		/* Check to see if we got an ARPREPLY for the address
266 		   in this particular lease. */
267 		if (!picked) {
268                     if( !check_arp(ip,lp) ) goto freeit;
269                     picked = lp;
270                     picked->next = NULL;
271 		} else {
272 freeit:
273 			free_client_lease(lp);
274 		}
275 	}
276 	ip->client->offered_leases = NULL;
277 
278 	/* If we just tossed all the leases we were offered, go back
279 	   to square one. */
280 	if (!picked) {
281 		ip->client->state = S_INIT;
282 		state_init(ip);
283 		return;
284 	}
285 
286 	/* If it was a BOOTREPLY, we can just take the address right now. */
287 	if (!picked->options[DHO_DHCP_MESSAGE_TYPE].len) {
288 		ip->client->new = picked;
289 
290 		/* Make up some lease expiry times
291 		   XXX these should be configurable. */
292 		ip->client->new->expiry = cur_time + 12000;
293 		ip->client->new->renewal += cur_time + 8000;
294 		ip->client->new->rebind += cur_time + 10000;
295 
296 		ip->client->state = S_REQUESTING;
297 
298 		/* Bind to the address we received. */
299 		bind_lease(ip);
300 		return;
301 	}
302 
303 	/* Go to the REQUESTING state. */
304 	ip->client->destination = iaddr_broadcast;
305 	ip->client->state = S_REQUESTING;
306 	ip->client->first_sending = cur_time;
307 	ip->client->interval = ip->client->config->initial_interval;
308 
309 	/* Make a DHCPREQUEST packet from the lease we picked. */
310 	make_request(ip, picked);
311 	ip->client->xid = ip->client->packet.xid;
312 
313 	/* Toss the lease we picked - we'll get it back in a DHCPACK. */
314 	free_client_lease(picked);
315 
316 	/* Add an immediate timeout to send the first DHCPREQUEST packet. */
317 	send_request(ip);
318 }
319 
320 /* state_requesting is called when we receive a DHCPACK message after
321    having sent out one or more DHCPREQUEST packets. */
322 
323 void
324 dhcpack(struct packet *packet)
325 {
326 	struct interface_info *ip = packet->interface;
327 	struct client_lease *lease;
328         time_t cur_time;
329 
330         time(&cur_time);
331 
332 	/* If we're not receptive to an offer right now, or if the offer
333 	   has an unrecognizable transaction id, then just drop it. */
334 	if (packet->interface->client->xid != packet->raw->xid ||
335 	    (packet->interface->hw_address.hlen != packet->raw->hlen) ||
336 	    (memcmp(packet->interface->hw_address.haddr,
337 	    packet->raw->chaddr, packet->raw->hlen)))
338 		return;
339 
340 	if (ip->client->state != S_REBOOTING &&
341 	    ip->client->state != S_REQUESTING &&
342 	    ip->client->state != S_RENEWING &&
343 	    ip->client->state != S_REBINDING)
344 		return;
345 
346 	note("DHCPACK from %s", piaddr(packet->client_addr));
347 
348 	lease = packet_to_lease(packet);
349 	if (!lease) {
350 		note("packet_to_lease failed.");
351 		return;
352 	}
353 
354 	ip->client->new = lease;
355 
356 	/* Stop resending DHCPREQUEST. */
357 	cancel_timeout(send_request, ip);
358 
359 	/* Figure out the lease time. */
360 	if (ip->client->new->options[DHO_DHCP_LEASE_TIME].data)
361 		ip->client->new->expiry = getULong(
362 		    ip->client->new->options[DHO_DHCP_LEASE_TIME].data);
363 	else
364 		ip->client->new->expiry = DHCP_DEFAULT_LEASE_TIME;
365 	/* A number that looks negative here is really just very large,
366 	   because the lease expiry offset is unsigned. */
367 	if (ip->client->new->expiry < 0)
368 		ip->client->new->expiry = TIME_MAX;
369 	/* XXX should be fixed by resetting the client state */
370 	if (ip->client->new->expiry < 60)
371 		ip->client->new->expiry = 60;
372 
373 	/* Take the server-provided renewal time if there is one;
374 	   otherwise figure it out according to the spec. */
375 	if (ip->client->new->options[DHO_DHCP_RENEWAL_TIME].len)
376 		ip->client->new->renewal = getULong(
377 		    ip->client->new->options[DHO_DHCP_RENEWAL_TIME].data);
378 	else
379 		ip->client->new->renewal = ip->client->new->expiry / 2;
380 
381 	/* Same deal with the rebind time. */
382 	if (ip->client->new->options[DHO_DHCP_REBINDING_TIME].len)
383 		ip->client->new->rebind = getULong(
384 		    ip->client->new->options[DHO_DHCP_REBINDING_TIME].data);
385 	else
386 		ip->client->new->rebind = ip->client->new->renewal +
387 		    ip->client->new->renewal / 2 + ip->client->new->renewal / 4;
388 
389 #ifdef __REACTOS__
390 	ip->client->new->obtained = cur_time;
391 #endif
392 	ip->client->new->expiry += cur_time;
393 	/* Lease lengths can never be negative. */
394 	if (ip->client->new->expiry < cur_time)
395 		ip->client->new->expiry = TIME_MAX;
396 	ip->client->new->renewal += cur_time;
397 	if (ip->client->new->renewal < cur_time)
398 		ip->client->new->renewal = TIME_MAX;
399 	ip->client->new->rebind += cur_time;
400 	if (ip->client->new->rebind < cur_time)
401 		ip->client->new->rebind = TIME_MAX;
402 
403 	bind_lease(ip);
404 }
405 
406 void set_name_servers( PDHCP_ADAPTER Adapter, struct client_lease *new_lease ) {
407     CHAR Buffer[200] = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\";
408     HKEY RegKey;
409 
410     strcat(Buffer, Adapter->DhclientInfo.name);
411     if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, Buffer, 0, KEY_WRITE, &RegKey ) != ERROR_SUCCESS)
412         return;
413 
414 
415     if( new_lease->options[DHO_DOMAIN_NAME_SERVERS].len ) {
416 
417         struct iaddr nameserver;
418         char *nsbuf;
419         int i, addrs =
420             new_lease->options[DHO_DOMAIN_NAME_SERVERS].len / sizeof(ULONG);
421 
422         nsbuf = malloc( addrs * sizeof(IP_ADDRESS_STRING) );
423 
424         if( nsbuf) {
425             nsbuf[0] = 0;
426             for( i = 0; i < addrs; i++ ) {
427                 nameserver.len = sizeof(ULONG);
428                 memcpy( nameserver.iabuf,
429                         new_lease->options[DHO_DOMAIN_NAME_SERVERS].data +
430                         (i * sizeof(ULONG)), sizeof(ULONG) );
431                 strcat( nsbuf, piaddr(nameserver) );
432                 if( i != addrs-1 ) strcat( nsbuf, "," );
433             }
434 
435             DH_DbgPrint(MID_TRACE,("Setting DhcpNameserver: %s\n", nsbuf));
436 
437             RegSetValueExA( RegKey, "DhcpNameServer", 0, REG_SZ,
438                            (LPBYTE)nsbuf, strlen(nsbuf) + 1 );
439             free( nsbuf );
440         }
441 
442     } else {
443             RegDeleteValueW( RegKey, L"DhcpNameServer" );
444     }
445 
446     RegCloseKey( RegKey );
447 
448 }
449 
450 void
451 set_domain(PDHCP_ADAPTER Adapter,
452            struct client_lease *new_lease)
453 {
454     CHAR Buffer1[MAX_PATH] = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\";
455     CHAR Buffer2[MAX_PATH] = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters";
456     HKEY RegKey1, RegKey2;
457 
458     strcat(Buffer1, Adapter->DhclientInfo.name);
459 
460     if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, Buffer1, 0, KEY_WRITE, &RegKey1 ) != ERROR_SUCCESS)
461     {
462         return;
463     }
464 
465     if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, Buffer2, 0, KEY_WRITE, &RegKey2 ) != ERROR_SUCCESS)
466     {
467         RegCloseKey(RegKey1);
468         return;
469     }
470 
471     if (new_lease->options[DHO_DOMAIN_NAME].len)
472     {
473         DH_DbgPrint(MID_TRACE, ("Setting DhcpDomain: %s\n", new_lease->options[DHO_DOMAIN_NAME].data));
474 
475         RegSetValueExA(RegKey1,
476                        "DhcpDomain",
477                        0,
478                        REG_SZ,
479                        (LPBYTE)new_lease->options[DHO_DOMAIN_NAME].data,
480                        new_lease->options[DHO_DOMAIN_NAME].len);
481 
482         RegSetValueExA(RegKey2,
483                        "DhcpDomain",
484                        0,
485                        REG_SZ,
486                        (LPBYTE)new_lease->options[DHO_DOMAIN_NAME].data,
487                        new_lease->options[DHO_DOMAIN_NAME].len);
488     }
489     else
490     {
491         RegDeleteValueW(RegKey1, L"DhcpDomain");
492         RegDeleteValueW(RegKey2, L"DhcpDomain");
493     }
494 
495     RegCloseKey(RegKey1);
496     RegCloseKey(RegKey2);
497 
498 }
499 
500 void setup_adapter( PDHCP_ADAPTER Adapter, struct client_lease *new_lease ) {
501     CHAR Buffer[200] = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\";
502     struct iaddr netmask;
503     HKEY hkey;
504     int i;
505     DWORD dwEnableDHCP;
506 
507     strcat(Buffer, Adapter->DhclientInfo.name);
508     if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, Buffer, 0, KEY_WRITE, &hkey) != ERROR_SUCCESS)
509         hkey = NULL;
510 
511 
512     if( Adapter->NteContext )
513     {
514         DeleteIPAddress( Adapter->NteContext );
515         Adapter->NteContext = 0;
516     }
517 
518     /* Set up our default router if we got one from the DHCP server */
519     if( new_lease->options[DHO_SUBNET_MASK].len ) {
520         NTSTATUS Status;
521 
522         memcpy( netmask.iabuf,
523                 new_lease->options[DHO_SUBNET_MASK].data,
524                 new_lease->options[DHO_SUBNET_MASK].len );
525         Status = AddIPAddress
526             ( *((ULONG*)new_lease->address.iabuf),
527               *((ULONG*)netmask.iabuf),
528               Adapter->IfMib.dwIndex,
529               &Adapter->NteContext,
530               &Adapter->NteInstance );
531         if (hkey) {
532             RegSetValueExA(hkey, "DhcpIPAddress", 0, REG_SZ, (LPBYTE)piaddr(new_lease->address), strlen(piaddr(new_lease->address))+1);
533             Buffer[0] = '\0';
534             for(i = 0; i < new_lease->options[DHO_SUBNET_MASK].len; i++)
535             {
536                 sprintf(&Buffer[strlen(Buffer)], "%u", new_lease->options[DHO_SUBNET_MASK].data[i]);
537                 if (i + 1 < new_lease->options[DHO_SUBNET_MASK].len)
538                     strcat(Buffer, ".");
539             }
540             RegSetValueExA(hkey, "DhcpSubnetMask", 0, REG_SZ, (LPBYTE)Buffer, strlen(Buffer)+1);
541             dwEnableDHCP = 1;
542             RegSetValueExA(hkey, "EnableDHCP", 0, REG_DWORD, (LPBYTE)&dwEnableDHCP, sizeof(DWORD));
543         }
544 
545         if( !NT_SUCCESS(Status) )
546             warning("AddIPAddress: %lx\n", Status);
547     }
548 
549     if( new_lease->options[DHO_ROUTERS].len ) {
550         NTSTATUS Status;
551 
552         Adapter->RouterMib.dwForwardDest = 0; /* Default route */
553         Adapter->RouterMib.dwForwardMask = 0;
554         Adapter->RouterMib.dwForwardMetric1 = 1;
555         Adapter->RouterMib.dwForwardIfIndex = Adapter->IfMib.dwIndex;
556 
557         if( Adapter->RouterMib.dwForwardNextHop ) {
558             /* If we set a default route before, delete it before continuing */
559             DeleteIpForwardEntry( &Adapter->RouterMib );
560         }
561 
562         Adapter->RouterMib.dwForwardNextHop =
563             *((ULONG*)new_lease->options[DHO_ROUTERS].data);
564 
565         Status = CreateIpForwardEntry( &Adapter->RouterMib );
566 
567         if( !NT_SUCCESS(Status) )
568             warning("CreateIpForwardEntry: %lx\n", Status);
569 
570         if (hkey) {
571             Buffer[0] = '\0';
572             for(i = 0; i < new_lease->options[DHO_ROUTERS].len; i++)
573             {
574                 sprintf(&Buffer[strlen(Buffer)], "%u", new_lease->options[DHO_ROUTERS].data[i]);
575                 if (i + 1 < new_lease->options[DHO_ROUTERS].len)
576                     strcat(Buffer, ".");
577             }
578             RegSetValueExA(hkey, "DhcpDefaultGateway", 0, REG_SZ, (LPBYTE)Buffer, strlen(Buffer)+1);
579         }
580     }
581 
582     if (hkey)
583         RegCloseKey(hkey);
584 }
585 
586 
587 void
588 bind_lease(struct interface_info *ip)
589 {
590     PDHCP_ADAPTER Adapter;
591     struct client_lease *new_lease = ip->client->new;
592     time_t cur_time;
593 
594     time(&cur_time);
595 
596     /* Remember the medium. */
597     ip->client->new->medium = ip->client->medium;
598 
599     /* Replace the old active lease with the new one. */
600     if (ip->client->active)
601         free_client_lease(ip->client->active);
602     ip->client->active = ip->client->new;
603     ip->client->new = NULL;
604 
605     /* Set up a timeout to start the renewal process. */
606     /* Timeout of zero means no timeout (some implementations seem to use
607      * one day).
608      */
609     if( ip->client->active->renewal - cur_time )
610         add_timeout(ip->client->active->renewal, state_bound, ip);
611 
612     note("bound to %s -- renewal in %ld seconds.",
613          piaddr(ip->client->active->address),
614          (long int)(ip->client->active->renewal - cur_time));
615 
616     ip->client->state = S_BOUND;
617 
618     Adapter = AdapterFindInfo( ip );
619 
620     if( Adapter )  setup_adapter( Adapter, new_lease );
621     else {
622         warning("Could not find adapter for info %p\n", ip);
623         return;
624     }
625     set_name_servers( Adapter, new_lease );
626     set_domain( Adapter, new_lease );
627 }
628 
629 /*
630  * state_bound is called when we've successfully bound to a particular
631  * lease, but the renewal time on that lease has expired.   We are
632  * expected to unicast a DHCPREQUEST to the server that gave us our
633  * original lease.
634  */
635 void
636 state_bound(void *ipp)
637 {
638 	struct interface_info *ip = ipp;
639 
640 	ASSERT_STATE(state, S_BOUND);
641 
642 	/* T1 has expired. */
643 	make_request(ip, ip->client->active);
644 	ip->client->xid = ip->client->packet.xid;
645 
646 	if (ip->client->active->options[DHO_DHCP_SERVER_IDENTIFIER].len == 4) {
647 		memcpy(ip->client->destination.iabuf, ip->client->active->
648 		    options[DHO_DHCP_SERVER_IDENTIFIER].data, 4);
649 		ip->client->destination.len = 4;
650 	} else
651 		ip->client->destination = iaddr_broadcast;
652 
653 	time(&ip->client->first_sending);
654 	ip->client->interval = ip->client->config->initial_interval;
655 	ip->client->state = S_RENEWING;
656 
657 	/* Send the first packet immediately. */
658 	send_request(ip);
659 }
660 
661 void
662 bootp(struct packet *packet)
663 {
664 	struct iaddrlist *ap;
665 
666 	if (packet->raw->op != BOOTREPLY)
667 		return;
668 
669 	/* If there's a reject list, make sure this packet's sender isn't
670 	   on it. */
671 	for (ap = packet->interface->client->config->reject_list;
672 	    ap; ap = ap->next) {
673 		if (addr_eq(packet->client_addr, ap->addr)) {
674 			note("BOOTREPLY from %s rejected.", piaddr(ap->addr));
675 			return;
676 		}
677 	}
678 	dhcpoffer(packet);
679 }
680 
681 void
682 dhcp(struct packet *packet)
683 {
684 	struct iaddrlist *ap;
685 	void (*handler)(struct packet *);
686 	char *type;
687 
688 	switch (packet->packet_type) {
689 	case DHCPOFFER:
690 		handler = dhcpoffer;
691 		type = "DHCPOFFER";
692 		break;
693 	case DHCPNAK:
694 		handler = dhcpnak;
695 		type = "DHCPNACK";
696 		break;
697 	case DHCPACK:
698 		handler = dhcpack;
699 		type = "DHCPACK";
700 		break;
701 	default:
702 		return;
703 	}
704 
705 	/* If there's a reject list, make sure this packet's sender isn't
706 	   on it. */
707 	for (ap = packet->interface->client->config->reject_list;
708 	    ap; ap = ap->next) {
709 		if (addr_eq(packet->client_addr, ap->addr)) {
710 			note("%s from %s rejected.", type, piaddr(ap->addr));
711 			return;
712 		}
713 	}
714 	(*handler)(packet);
715 }
716 
717 void
718 dhcpoffer(struct packet *packet)
719 {
720 	struct interface_info *ip = packet->interface;
721 	struct client_lease *lease, *lp;
722 	int i;
723 	int arp_timeout_needed = 0, stop_selecting;
724 	char *name = packet->options[DHO_DHCP_MESSAGE_TYPE].len ?
725 	    "DHCPOFFER" : "BOOTREPLY";
726         time_t cur_time;
727 
728         time(&cur_time);
729 
730 	/* If we're not receptive to an offer right now, or if the offer
731 	   has an unrecognizable transaction id, then just drop it. */
732 	if (ip->client->state != S_SELECTING ||
733             packet->interface->client->xid != packet->raw->xid ||
734             (packet->interface->hw_address.hlen != packet->raw->hlen) ||
735 	    (memcmp(packet->interface->hw_address.haddr,
736 	    packet->raw->chaddr, packet->raw->hlen)))
737 		return;
738 
739 	note("%s from %s", name, piaddr(packet->client_addr));
740 
741 
742 	/* If this lease doesn't supply the minimum required parameters,
743 	   blow it off. */
744 	for (i = 0; ip->client->config->required_options[i]; i++) {
745 		if (!packet->options[ip->client->config->
746 		    required_options[i]].len) {
747 			note("%s isn't satisfactory.", name);
748 			return;
749 		}
750 	}
751 
752 	/* If we've already seen this lease, don't record it again. */
753 	for (lease = ip->client->offered_leases;
754 	    lease; lease = lease->next) {
755 		if (lease->address.len == sizeof(packet->raw->yiaddr) &&
756 		    !memcmp(lease->address.iabuf,
757 		    &packet->raw->yiaddr, lease->address.len)) {
758 			debug("%s already seen.", name);
759 			return;
760 		}
761 	}
762 
763 	lease = packet_to_lease(packet);
764 	if (!lease) {
765 		note("packet_to_lease failed.");
766 		return;
767 	}
768 
769 	/* If this lease was acquired through a BOOTREPLY, record that
770 	   fact. */
771 	if (!packet->options[DHO_DHCP_MESSAGE_TYPE].len)
772 		lease->is_bootp = 1;
773 
774 	/* Record the medium under which this lease was offered. */
775 	lease->medium = ip->client->medium;
776 
777 	/* Send out an ARP Request for the offered IP address. */
778         if( !check_arp( ip, lease ) ) {
779             note("Arp check failed\n");
780             return;
781         }
782 
783 	/* Figure out when we're supposed to stop selecting. */
784 	stop_selecting =
785 	    ip->client->first_sending + ip->client->config->select_interval;
786 
787 	/* If this is the lease we asked for, put it at the head of the
788 	   list, and don't mess with the arp request timeout. */
789 	if (lease->address.len == ip->client->requested_address.len &&
790 	    !memcmp(lease->address.iabuf,
791 	    ip->client->requested_address.iabuf,
792 	    ip->client->requested_address.len)) {
793 		lease->next = ip->client->offered_leases;
794 		ip->client->offered_leases = lease;
795 	} else {
796 		/* If we already have an offer, and arping for this
797 		   offer would take us past the selection timeout,
798 		   then don't extend the timeout - just hope for the
799 		   best. */
800 		if (ip->client->offered_leases &&
801 		    (cur_time + arp_timeout_needed) > stop_selecting)
802 			arp_timeout_needed = 0;
803 
804 		/* Put the lease at the end of the list. */
805 		lease->next = NULL;
806 		if (!ip->client->offered_leases)
807 			ip->client->offered_leases = lease;
808 		else {
809 			for (lp = ip->client->offered_leases; lp->next;
810 			    lp = lp->next)
811 				;	/* nothing */
812 			lp->next = lease;
813 		}
814 	}
815 
816 	/* If we're supposed to stop selecting before we've had time
817 	   to wait for the ARPREPLY, add some delay to wait for
818 	   the ARPREPLY. */
819 	if (stop_selecting - cur_time < arp_timeout_needed)
820 		stop_selecting = cur_time + arp_timeout_needed;
821 
822 	/* If the selecting interval has expired, go immediately to
823 	   state_selecting().  Otherwise, time out into
824 	   state_selecting at the select interval. */
825 	if (stop_selecting <= 0)
826 		state_selecting(ip);
827 	else {
828 		add_timeout(stop_selecting, state_selecting, ip);
829 		cancel_timeout(send_discover, ip);
830 	}
831 }
832 
833 /* Allocate a client_lease structure and initialize it from the parameters
834    in the specified packet. */
835 
836 struct client_lease *
837 packet_to_lease(struct packet *packet)
838 {
839 	struct client_lease *lease;
840 	int i;
841 
842 	lease = malloc(sizeof(struct client_lease));
843 
844 	if (!lease) {
845 		warning("dhcpoffer: no memory to record lease.");
846 		return (NULL);
847 	}
848 
849 	memset(lease, 0, sizeof(*lease));
850 
851 	/* Copy the lease options. */
852 	for (i = 0; i < 256; i++) {
853 		if (packet->options[i].len) {
854 			lease->options[i].data =
855 			    malloc(packet->options[i].len + 1);
856 			if (!lease->options[i].data) {
857 				warning("dhcpoffer: no memory for option %d", i);
858 				free_client_lease(lease);
859 				return (NULL);
860 			} else {
861 				memcpy(lease->options[i].data,
862 				    packet->options[i].data,
863 				    packet->options[i].len);
864 				lease->options[i].len =
865 				    packet->options[i].len;
866 				lease->options[i].data[lease->options[i].len] =
867 				    0;
868 			}
869 			if (!check_option(lease,i)) {
870 				/* ignore a bogus lease offer */
871 				warning("Invalid lease option - ignoring offer");
872 				free_client_lease(lease);
873 				return (NULL);
874 			}
875 		}
876 	}
877 
878 	lease->address.len = sizeof(packet->raw->yiaddr);
879 	memcpy(lease->address.iabuf, &packet->raw->yiaddr, lease->address.len);
880 #ifdef __REACTOS__
881 	lease->serveraddress.len = sizeof(packet->raw->siaddr);
882 	memcpy(lease->serveraddress.iabuf, &packet->raw->siaddr, lease->address.len);
883 #endif
884 
885 	/* If the server name was filled out, copy it. */
886 	if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len ||
887 	    !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)) &&
888 	    packet->raw->sname[0]) {
889 		lease->server_name = malloc(DHCP_SNAME_LEN + 1);
890 		if (!lease->server_name) {
891 			warning("dhcpoffer: no memory for server name.");
892 			free_client_lease(lease);
893 			return (NULL);
894 		}
895 		memcpy(lease->server_name, packet->raw->sname, DHCP_SNAME_LEN);
896 		lease->server_name[DHCP_SNAME_LEN]='\0';
897 		if (!res_hnok(lease->server_name) ) {
898 			warning("Bogus server name %s",  lease->server_name );
899 			free_client_lease(lease);
900 			return (NULL);
901 		}
902 
903 	}
904 
905 	/* Ditto for the filename. */
906 	if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len ||
907 	    !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)) &&
908 	    packet->raw->file[0]) {
909 		/* Don't count on the NUL terminator. */
910 		lease->filename = malloc(DHCP_FILE_LEN + 1);
911 		if (!lease->filename) {
912 			warning("dhcpoffer: no memory for filename.");
913 			free_client_lease(lease);
914 			return (NULL);
915 		}
916 		memcpy(lease->filename, packet->raw->file, DHCP_FILE_LEN);
917 		lease->filename[DHCP_FILE_LEN]='\0';
918 	}
919 	return lease;
920 }
921 
922 void
923 dhcpnak(struct packet *packet)
924 {
925 	struct interface_info *ip = packet->interface;
926 
927 	/* If we're not receptive to an offer right now, or if the offer
928 	   has an unrecognizable transaction id, then just drop it. */
929 	if (packet->interface->client->xid != packet->raw->xid ||
930 	    (packet->interface->hw_address.hlen != packet->raw->hlen) ||
931 	    (memcmp(packet->interface->hw_address.haddr,
932 	    packet->raw->chaddr, packet->raw->hlen)))
933 		return;
934 
935 	if (ip->client->state != S_REBOOTING &&
936 	    ip->client->state != S_REQUESTING &&
937 	    ip->client->state != S_RENEWING &&
938 	    ip->client->state != S_REBINDING)
939 		return;
940 
941 	note("DHCPNAK from %s", piaddr(packet->client_addr));
942 
943 	if (!ip->client->active) {
944 		note("DHCPNAK with no active lease.\n");
945 		return;
946 	}
947 
948 	free_client_lease(ip->client->active);
949 	ip->client->active = NULL;
950 
951 	/* Stop sending DHCPREQUEST packets... */
952 	cancel_timeout(send_request, ip);
953 
954 	ip->client->state = S_INIT;
955 	state_init(ip);
956 }
957 
958 /* Send out a DHCPDISCOVER packet, and set a timeout to send out another
959    one after the right interval has expired.  If we don't get an offer by
960    the time we reach the panic interval, call the panic function. */
961 
962 void
963 send_discover(void *ipp)
964 {
965 	struct interface_info *ip = ipp;
966 	int interval, increase = 1;
967         time_t cur_time;
968 
969         DH_DbgPrint(MID_TRACE,("Doing discover on interface %p\n",ip));
970 
971         time(&cur_time);
972 
973 	/* Figure out how long it's been since we started transmitting. */
974 	interval = cur_time - ip->client->first_sending;
975 
976 	/* If we're past the panic timeout, call the script and tell it
977 	   we haven't found anything for this interface yet. */
978 	if (interval > ip->client->config->timeout) {
979 		state_panic(ip);
980         ip->client->first_sending = cur_time;
981 	}
982 
983 	/* If we're selecting media, try the whole list before doing
984 	   the exponential backoff, but if we've already received an
985 	   offer, stop looping, because we obviously have it right. */
986 	if (!ip->client->offered_leases &&
987 	    ip->client->config->media) {
988 		int fail = 0;
989 
990 		if (ip->client->medium) {
991 			ip->client->medium = ip->client->medium->next;
992 			increase = 0;
993 		}
994 		if (!ip->client->medium) {
995 			if (fail)
996 				error("No valid media types for %s!", ip->name);
997 			ip->client->medium = ip->client->config->media;
998 			increase = 1;
999 		}
1000 
1001 		note("Trying medium \"%s\" %d", ip->client->medium->string,
1002 		    increase);
1003                 /* XXX Support other media types eventually */
1004 	}
1005 
1006 	/*
1007 	 * If we're supposed to increase the interval, do so.  If it's
1008 	 * currently zero (i.e., we haven't sent any packets yet), set
1009 	 * it to one; otherwise, add to it a random number between zero
1010 	 * and two times itself.  On average, this means that it will
1011 	 * double with every transmission.
1012 	 */
1013 	if (increase) {
1014 		if (!ip->client->interval)
1015 			ip->client->interval =
1016 			    ip->client->config->initial_interval;
1017 		else {
1018 			ip->client->interval += (rand() >> 2) %
1019 			    (2 * ip->client->interval);
1020 		}
1021 
1022 		/* Don't backoff past cutoff. */
1023 		if (ip->client->interval >
1024 		    ip->client->config->backoff_cutoff)
1025 			ip->client->interval =
1026 				((ip->client->config->backoff_cutoff / 2)
1027 				 + ((rand() >> 2) %
1028 				    ip->client->config->backoff_cutoff));
1029 	} else if (!ip->client->interval)
1030 		ip->client->interval =
1031 			ip->client->config->initial_interval;
1032 
1033 	/* If the backoff would take us to the panic timeout, just use that
1034 	   as the interval. */
1035 	if (cur_time + ip->client->interval >
1036 	    ip->client->first_sending + ip->client->config->timeout)
1037 		ip->client->interval =
1038 			(ip->client->first_sending +
1039 			 ip->client->config->timeout) - cur_time + 1;
1040 
1041 	/* Record the number of seconds since we started sending. */
1042 	if (interval < 65536)
1043 		ip->client->packet.secs = htons(interval);
1044 	else
1045 		ip->client->packet.secs = htons(65535);
1046 	ip->client->secs = ip->client->packet.secs;
1047 
1048 	note("DHCPDISCOVER on %s to %s port %d interval %ld",
1049 	    ip->name, inet_ntoa(sockaddr_broadcast.sin_addr),
1050 	    ntohs(sockaddr_broadcast.sin_port), (long int)ip->client->interval);
1051 
1052 	/* Send out a packet. */
1053 	(void)send_packet(ip, &ip->client->packet, ip->client->packet_length,
1054 	    inaddr_any, &sockaddr_broadcast, NULL);
1055 
1056         DH_DbgPrint(MID_TRACE,("discover timeout: now %x -> then %x\n",
1057                                cur_time, cur_time + ip->client->interval));
1058 
1059 	add_timeout(cur_time + ip->client->interval, send_discover, ip);
1060 }
1061 
1062 /*
1063  * state_panic gets called if we haven't received any offers in a preset
1064  * amount of time.   When this happens, we try to use existing leases
1065  * that haven't yet expired, and failing that, we call the client script
1066  * and hope it can do something.
1067  */
1068 void
1069 state_panic(void *ipp)
1070 {
1071 	struct interface_info *ip = ipp;
1072 	uint16_t address_low;
1073 	int i;
1074     IPAddr IpAddress;
1075     ULONG Buffer[20];
1076     ULONG BufferSize;
1077     DWORD ret;
1078     PDHCP_ADAPTER Adapter = AdapterFindInfo(ip);
1079 
1080 	note("No DHCPOFFERS received.");
1081 
1082     if (!Adapter->NteContext)
1083     {
1084         /* Generate an automatic private address */
1085         DbgPrint("DHCPCSVC: Failed to receive a response from a DHCP server. An automatic private address will be assigned.\n");
1086 
1087         /* FIXME: The address generation code sucks */
1088         srand(0);
1089 
1090         for (;;)
1091         {
1092             address_low = rand();
1093             for (i = 0; i < ip->hw_address.hlen; i++)
1094                 address_low += ip->hw_address.haddr[i];
1095 
1096             IpAddress = htonl(0xA9FE0000 | address_low);  // 169.254.X.X
1097 
1098             /* Send an ARP request to check if the IP address is already in use */
1099             BufferSize = sizeof(Buffer);
1100             ret = SendARP(IpAddress,
1101                           IpAddress,
1102                           Buffer,
1103                           &BufferSize);
1104             DH_DbgPrint(MID_TRACE,("DHCPCSVC: SendARP returned %lu\n", ret));
1105             if (ret != 0)
1106             {
1107                 /* The IP address is not in use */
1108                 DH_DbgPrint(MID_TRACE,("DHCPCSVC: Using automatic private address\n"));
1109                 AddIPAddress(IpAddress,
1110                              htonl(0xFFFF0000), // 255.255.0.0
1111                              Adapter->IfMib.dwIndex,
1112                              &Adapter->NteContext,
1113                              &Adapter->NteInstance);
1114                 return;
1115             }
1116         }
1117     }
1118 }
1119 
1120 void
1121 send_request(void *ipp)
1122 {
1123 	struct interface_info *ip = ipp;
1124 	struct sockaddr_in destination;
1125 	struct in_addr from;
1126 	int interval;
1127         time_t cur_time;
1128 
1129         time(&cur_time);
1130 
1131 	/* Figure out how long it's been since we started transmitting. */
1132 	interval = cur_time - ip->client->first_sending;
1133 
1134 	/* If we're in the INIT-REBOOT or REQUESTING state and we're
1135 	   past the reboot timeout, go to INIT and see if we can
1136 	   DISCOVER an address... */
1137 	/* XXX In the INIT-REBOOT state, if we don't get an ACK, it
1138 	   means either that we're on a network with no DHCP server,
1139 	   or that our server is down.  In the latter case, assuming
1140 	   that there is a backup DHCP server, DHCPDISCOVER will get
1141 	   us a new address, but we could also have successfully
1142 	   reused our old address.  In the former case, we're hosed
1143 	   anyway.  This is not a win-prone situation. */
1144 	if ((ip->client->state == S_REBOOTING ||
1145 	    ip->client->state == S_REQUESTING) &&
1146 	    interval > ip->client->config->reboot_timeout) {
1147 		ip->client->state = S_INIT;
1148 		cancel_timeout(send_request, ip);
1149 		state_init(ip);
1150 		return;
1151 	}
1152 
1153 	/* If we're in the reboot state, make sure the media is set up
1154 	   correctly. */
1155 	if (ip->client->state == S_REBOOTING &&
1156 	    !ip->client->medium &&
1157 	    ip->client->active->medium ) {
1158 		/* If the medium we chose won't fly, go to INIT state. */
1159                 /* XXX Nothing for now */
1160 
1161 		/* Record the medium. */
1162 		ip->client->medium = ip->client->active->medium;
1163 	}
1164 
1165 	/* If the lease has expired, relinquish the address and go back
1166 	   to the INIT state. */
1167 	if (ip->client->state != S_REQUESTING &&
1168 	    cur_time > ip->client->active->expiry) {
1169             PDHCP_ADAPTER Adapter = AdapterFindInfo( ip );
1170             /* Run the client script with the new parameters. */
1171             /* No script actions necessary in the expiry case */
1172             /* Now do a preinit on the interface so that we can
1173                discover a new address. */
1174 
1175             if( Adapter )
1176             {
1177                 DeleteIPAddress( Adapter->NteContext );
1178                 Adapter->NteContext = 0;
1179             }
1180 
1181             ip->client->state = S_INIT;
1182             state_init(ip);
1183             return;
1184 	}
1185 
1186 	/* Do the exponential backoff... */
1187 	if (!ip->client->interval)
1188 		ip->client->interval = ip->client->config->initial_interval;
1189 	else
1190 		ip->client->interval += ((rand() >> 2) %
1191 		    (2 * ip->client->interval));
1192 
1193 	/* Don't backoff past cutoff. */
1194 	if (ip->client->interval >
1195 	    ip->client->config->backoff_cutoff)
1196 		ip->client->interval =
1197 		    ((ip->client->config->backoff_cutoff / 2) +
1198 		    ((rand() >> 2) % ip->client->interval));
1199 
1200 	/* If the backoff would take us to the expiry time, just set the
1201 	   timeout to the expiry time. */
1202 	if (ip->client->state != S_REQUESTING &&
1203 	    cur_time + ip->client->interval >
1204 	    ip->client->active->expiry)
1205 		ip->client->interval =
1206 		    ip->client->active->expiry - cur_time + 1;
1207 
1208 	/* If the lease T2 time has elapsed, or if we're not yet bound,
1209 	   broadcast the DHCPREQUEST rather than unicasting. */
1210 	memset(&destination, 0, sizeof(destination));
1211 	if (ip->client->state == S_REQUESTING ||
1212 	    ip->client->state == S_REBOOTING ||
1213 	    cur_time > ip->client->active->rebind)
1214 		destination.sin_addr.s_addr = INADDR_BROADCAST;
1215 	else
1216 		memcpy(&destination.sin_addr.s_addr,
1217 		    ip->client->destination.iabuf,
1218 		    sizeof(destination.sin_addr.s_addr));
1219 	destination.sin_port = htons(REMOTE_PORT);
1220 	destination.sin_family = AF_INET;
1221 //	destination.sin_len = sizeof(destination);
1222 
1223 	if (ip->client->state != S_REQUESTING)
1224 		memcpy(&from, ip->client->active->address.iabuf,
1225 		    sizeof(from));
1226 	else
1227 		from.s_addr = INADDR_ANY;
1228 
1229 	/* Record the number of seconds since we started sending. */
1230 	if (ip->client->state == S_REQUESTING)
1231 		ip->client->packet.secs = ip->client->secs;
1232 	else {
1233 		if (interval < 65536)
1234 			ip->client->packet.secs = htons(interval);
1235 		else
1236 			ip->client->packet.secs = htons(65535);
1237 	}
1238 
1239 	note("DHCPREQUEST on %s to %s port %d", ip->name,
1240 	    inet_ntoa(destination.sin_addr), ntohs(destination.sin_port));
1241 
1242 	/* Send out a packet. */
1243 	(void) send_packet(ip, &ip->client->packet, ip->client->packet_length,
1244 	    from, &destination, NULL);
1245 
1246 	add_timeout(cur_time + ip->client->interval, send_request, ip);
1247 }
1248 
1249 void
1250 send_decline(void *ipp)
1251 {
1252 	struct interface_info *ip = ipp;
1253 
1254 	note("DHCPDECLINE on %s to %s port %d", ip->name,
1255 	    inet_ntoa(sockaddr_broadcast.sin_addr),
1256 	    ntohs(sockaddr_broadcast.sin_port));
1257 
1258 	/* Send out a packet. */
1259 	(void) send_packet(ip, &ip->client->packet, ip->client->packet_length,
1260 	    inaddr_any, &sockaddr_broadcast, NULL);
1261 }
1262 
1263 void
1264 make_discover(struct interface_info *ip, struct client_lease *lease)
1265 {
1266 	unsigned char discover = DHCPDISCOVER;
1267 	struct tree_cache *options[256];
1268 	struct tree_cache option_elements[256];
1269 	int i;
1270 	ULONG foo = (ULONG) GetTickCount();
1271 
1272 	memset(option_elements, 0, sizeof(option_elements));
1273 	memset(options, 0, sizeof(options));
1274 	memset(&ip->client->packet, 0, sizeof(ip->client->packet));
1275 
1276 	/* Set DHCP_MESSAGE_TYPE to DHCPDISCOVER */
1277 	i = DHO_DHCP_MESSAGE_TYPE;
1278 	options[i] = &option_elements[i];
1279 	options[i]->value = &discover;
1280 	options[i]->len = sizeof(discover);
1281 	options[i]->buf_size = sizeof(discover);
1282 	options[i]->timeout = 0xFFFFFFFF;
1283 
1284 	/* Request the options we want */
1285 	i  = DHO_DHCP_PARAMETER_REQUEST_LIST;
1286 	options[i] = &option_elements[i];
1287 	options[i]->value = ip->client->config->requested_options;
1288 	options[i]->len = ip->client->config->requested_option_count;
1289 	options[i]->buf_size =
1290 		ip->client->config->requested_option_count;
1291 	options[i]->timeout = 0xFFFFFFFF;
1292 
1293 	/* If we had an address, try to get it again. */
1294 	if (lease) {
1295 		ip->client->requested_address = lease->address;
1296 		i = DHO_DHCP_REQUESTED_ADDRESS;
1297 		options[i] = &option_elements[i];
1298 		options[i]->value = lease->address.iabuf;
1299 		options[i]->len = lease->address.len;
1300 		options[i]->buf_size = lease->address.len;
1301 		options[i]->timeout = 0xFFFFFFFF;
1302 	} else
1303 		ip->client->requested_address.len = 0;
1304 
1305 	/* Send any options requested in the config file. */
1306 	for (i = 0; i < 256; i++)
1307 		if (!options[i] &&
1308 		    ip->client->config->send_options[i].data) {
1309 			options[i] = &option_elements[i];
1310 			options[i]->value =
1311 			    ip->client->config->send_options[i].data;
1312 			options[i]->len =
1313 			    ip->client->config->send_options[i].len;
1314 			options[i]->buf_size =
1315 			    ip->client->config->send_options[i].len;
1316 			options[i]->timeout = 0xFFFFFFFF;
1317 		}
1318 
1319 	/* Set up the option buffer... */
1320 	ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0,
1321 	    options);
1322 	if (ip->client->packet_length < BOOTP_MIN_LEN)
1323 		ip->client->packet_length = BOOTP_MIN_LEN;
1324 
1325 	ip->client->packet.op = BOOTREQUEST;
1326 	ip->client->packet.htype = ip->hw_address.htype;
1327 	ip->client->packet.hlen = ip->hw_address.hlen;
1328 	ip->client->packet.hops = 0;
1329 	ip->client->packet.xid = RtlRandom(&foo);
1330 	ip->client->packet.secs = 0; /* filled in by send_discover. */
1331 	ip->client->packet.flags = 0;
1332 
1333 	memset(&(ip->client->packet.ciaddr),
1334 	    0, sizeof(ip->client->packet.ciaddr));
1335 	memset(&(ip->client->packet.yiaddr),
1336 	    0, sizeof(ip->client->packet.yiaddr));
1337 	memset(&(ip->client->packet.siaddr),
1338 	    0, sizeof(ip->client->packet.siaddr));
1339 	memset(&(ip->client->packet.giaddr),
1340 	    0, sizeof(ip->client->packet.giaddr));
1341 	memcpy(ip->client->packet.chaddr,
1342 	    ip->hw_address.haddr, ip->hw_address.hlen);
1343 }
1344 
1345 
1346 void
1347 make_request(struct interface_info *ip, struct client_lease * lease)
1348 {
1349 	unsigned char request = DHCPREQUEST;
1350 	struct tree_cache *options[256];
1351 	struct tree_cache option_elements[256];
1352 	int i;
1353 
1354 	memset(options, 0, sizeof(options));
1355 	memset(&ip->client->packet, 0, sizeof(ip->client->packet));
1356 
1357 	/* Set DHCP_MESSAGE_TYPE to DHCPREQUEST */
1358 	i = DHO_DHCP_MESSAGE_TYPE;
1359 	options[i] = &option_elements[i];
1360 	options[i]->value = &request;
1361 	options[i]->len = sizeof(request);
1362 	options[i]->buf_size = sizeof(request);
1363 	options[i]->timeout = 0xFFFFFFFF;
1364 
1365 	/* Request the options we want */
1366 	i = DHO_DHCP_PARAMETER_REQUEST_LIST;
1367 	options[i] = &option_elements[i];
1368 	options[i]->value = ip->client->config->requested_options;
1369 	options[i]->len = ip->client->config->requested_option_count;
1370 	options[i]->buf_size =
1371 		ip->client->config->requested_option_count;
1372 	options[i]->timeout = 0xFFFFFFFF;
1373 
1374 	/* If we are requesting an address that hasn't yet been assigned
1375 	   to us, use the DHCP Requested Address option. */
1376 	if (ip->client->state == S_REQUESTING) {
1377 		/* Send back the server identifier... */
1378 		i = DHO_DHCP_SERVER_IDENTIFIER;
1379 		options[i] = &option_elements[i];
1380 		options[i]->value = lease->options[i].data;
1381 		options[i]->len = lease->options[i].len;
1382 		options[i]->buf_size = lease->options[i].len;
1383 		options[i]->timeout = 0xFFFFFFFF;
1384 	}
1385 	if (ip->client->state == S_REQUESTING ||
1386 	    ip->client->state == S_REBOOTING) {
1387 		ip->client->requested_address = lease->address;
1388 		i = DHO_DHCP_REQUESTED_ADDRESS;
1389 		options[i] = &option_elements[i];
1390 		options[i]->value = lease->address.iabuf;
1391 		options[i]->len = lease->address.len;
1392 		options[i]->buf_size = lease->address.len;
1393 		options[i]->timeout = 0xFFFFFFFF;
1394 	} else
1395 		ip->client->requested_address.len = 0;
1396 
1397 	/* Send any options requested in the config file. */
1398 	for (i = 0; i < 256; i++)
1399 		if (!options[i] &&
1400 		    ip->client->config->send_options[i].data) {
1401 			options[i] = &option_elements[i];
1402 			options[i]->value =
1403 			    ip->client->config->send_options[i].data;
1404 			options[i]->len =
1405 			    ip->client->config->send_options[i].len;
1406 			options[i]->buf_size =
1407 			    ip->client->config->send_options[i].len;
1408 			options[i]->timeout = 0xFFFFFFFF;
1409 		}
1410 
1411 	/* Set up the option buffer... */
1412 	ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0,
1413 	    options);
1414 	if (ip->client->packet_length < BOOTP_MIN_LEN)
1415 		ip->client->packet_length = BOOTP_MIN_LEN;
1416 
1417 	ip->client->packet.op = BOOTREQUEST;
1418 	ip->client->packet.htype = ip->hw_address.htype;
1419 	ip->client->packet.hlen = ip->hw_address.hlen;
1420 	ip->client->packet.hops = 0;
1421 	ip->client->packet.xid = ip->client->xid;
1422 	ip->client->packet.secs = 0; /* Filled in by send_request. */
1423 
1424 	/* If we own the address we're requesting, put it in ciaddr;
1425 	   otherwise set ciaddr to zero. */
1426 	if (ip->client->state == S_BOUND ||
1427 	    ip->client->state == S_RENEWING ||
1428 	    ip->client->state == S_REBINDING) {
1429 		memcpy(&ip->client->packet.ciaddr,
1430 		    lease->address.iabuf, lease->address.len);
1431 		ip->client->packet.flags = 0;
1432 	} else {
1433 		memset(&ip->client->packet.ciaddr, 0,
1434 		    sizeof(ip->client->packet.ciaddr));
1435 		ip->client->packet.flags = 0;
1436 	}
1437 
1438 	memset(&ip->client->packet.yiaddr, 0,
1439 	    sizeof(ip->client->packet.yiaddr));
1440 	memset(&ip->client->packet.siaddr, 0,
1441 	    sizeof(ip->client->packet.siaddr));
1442 	memset(&ip->client->packet.giaddr, 0,
1443 	    sizeof(ip->client->packet.giaddr));
1444 	memcpy(ip->client->packet.chaddr,
1445 	    ip->hw_address.haddr, ip->hw_address.hlen);
1446 }
1447 
1448 void
1449 make_decline(struct interface_info *ip, struct client_lease *lease)
1450 {
1451 	struct tree_cache *options[256], message_type_tree;
1452 	struct tree_cache requested_address_tree;
1453 	struct tree_cache server_id_tree, client_id_tree;
1454 	unsigned char decline = DHCPDECLINE;
1455 	int i;
1456 
1457 	memset(options, 0, sizeof(options));
1458 	memset(&ip->client->packet, 0, sizeof(ip->client->packet));
1459 
1460 	/* Set DHCP_MESSAGE_TYPE to DHCPDECLINE */
1461 	i = DHO_DHCP_MESSAGE_TYPE;
1462 	options[i] = &message_type_tree;
1463 	options[i]->value = &decline;
1464 	options[i]->len = sizeof(decline);
1465 	options[i]->buf_size = sizeof(decline);
1466 	options[i]->timeout = 0xFFFFFFFF;
1467 
1468 	/* Send back the server identifier... */
1469 	i = DHO_DHCP_SERVER_IDENTIFIER;
1470 	options[i] = &server_id_tree;
1471 	options[i]->value = lease->options[i].data;
1472 	options[i]->len = lease->options[i].len;
1473 	options[i]->buf_size = lease->options[i].len;
1474 	options[i]->timeout = 0xFFFFFFFF;
1475 
1476 	/* Send back the address we're declining. */
1477 	i = DHO_DHCP_REQUESTED_ADDRESS;
1478 	options[i] = &requested_address_tree;
1479 	options[i]->value = lease->address.iabuf;
1480 	options[i]->len = lease->address.len;
1481 	options[i]->buf_size = lease->address.len;
1482 	options[i]->timeout = 0xFFFFFFFF;
1483 
1484 	/* Send the uid if the user supplied one. */
1485 	i = DHO_DHCP_CLIENT_IDENTIFIER;
1486 	if (ip->client->config->send_options[i].len) {
1487 		options[i] = &client_id_tree;
1488 		options[i]->value = ip->client->config->send_options[i].data;
1489 		options[i]->len = ip->client->config->send_options[i].len;
1490 		options[i]->buf_size = ip->client->config->send_options[i].len;
1491 		options[i]->timeout = 0xFFFFFFFF;
1492 	}
1493 
1494 
1495 	/* Set up the option buffer... */
1496 	ip->client->packet_length = cons_options(NULL, &ip->client->packet, 0,
1497 	    options);
1498 	if (ip->client->packet_length < BOOTP_MIN_LEN)
1499 		ip->client->packet_length = BOOTP_MIN_LEN;
1500 
1501 	ip->client->packet.op = BOOTREQUEST;
1502 	ip->client->packet.htype = ip->hw_address.htype;
1503 	ip->client->packet.hlen = ip->hw_address.hlen;
1504 	ip->client->packet.hops = 0;
1505 	ip->client->packet.xid = ip->client->xid;
1506 	ip->client->packet.secs = 0; /* Filled in by send_request. */
1507 	ip->client->packet.flags = 0;
1508 
1509 	/* ciaddr must always be zero. */
1510 	memset(&ip->client->packet.ciaddr, 0,
1511 	    sizeof(ip->client->packet.ciaddr));
1512 	memset(&ip->client->packet.yiaddr, 0,
1513 	    sizeof(ip->client->packet.yiaddr));
1514 	memset(&ip->client->packet.siaddr, 0,
1515 	    sizeof(ip->client->packet.siaddr));
1516 	memset(&ip->client->packet.giaddr, 0,
1517 	    sizeof(ip->client->packet.giaddr));
1518 	memcpy(ip->client->packet.chaddr,
1519 	    ip->hw_address.haddr, ip->hw_address.hlen);
1520 }
1521 
1522 void
1523 free_client_lease(struct client_lease *lease)
1524 {
1525 	int i;
1526 
1527 	if (lease->server_name)
1528 		free(lease->server_name);
1529 	if (lease->filename)
1530 		free(lease->filename);
1531 	for (i = 0; i < 256; i++) {
1532 		if (lease->options[i].len)
1533 			free(lease->options[i].data);
1534 	}
1535 	free(lease);
1536 }
1537 
1538 FILE *leaseFile;
1539 
1540 void
1541 rewrite_client_leases(struct interface_info *ifi)
1542 {
1543 	struct client_lease *lp;
1544 
1545 	if (!leaseFile) {
1546 		leaseFile = fopen(path_dhclient_db, "w");
1547 		if (!leaseFile)
1548 			error("can't create %s", path_dhclient_db);
1549 	} else {
1550 		fflush(leaseFile);
1551 		rewind(leaseFile);
1552 	}
1553 
1554 	for (lp = ifi->client->leases; lp; lp = lp->next)
1555 		write_client_lease(ifi, lp, 1);
1556 	if (ifi->client->active)
1557 		write_client_lease(ifi, ifi->client->active, 1);
1558 
1559 	fflush(leaseFile);
1560 }
1561 
1562 void
1563 write_client_lease(struct interface_info *ip, struct client_lease *lease,
1564     int rewrite)
1565 {
1566 	static int leases_written;
1567 	struct tm *t;
1568 	int i;
1569 
1570 	if (!rewrite) {
1571 		if (leases_written++ > 20) {
1572 			rewrite_client_leases(ip);
1573 			leases_written = 0;
1574 		}
1575 	}
1576 
1577 	/* If the lease came from the config file, we don't need to stash
1578 	   a copy in the lease database. */
1579 	if (lease->is_static)
1580 		return;
1581 
1582 	if (!leaseFile) {	/* XXX */
1583 		leaseFile = fopen(path_dhclient_db, "w");
1584 		if (!leaseFile) {
1585 			error("can't create %s", path_dhclient_db);
1586                         return;
1587                 }
1588 	}
1589 
1590 	fprintf(leaseFile, "lease {\n");
1591 	if (lease->is_bootp)
1592 		fprintf(leaseFile, "  bootp;\n");
1593 	fprintf(leaseFile, "  interface \"%s\";\n", ip->name);
1594 	fprintf(leaseFile, "  fixed-address %s;\n", piaddr(lease->address));
1595 	if (lease->filename)
1596 		fprintf(leaseFile, "  filename \"%s\";\n", lease->filename);
1597 	if (lease->server_name)
1598 		fprintf(leaseFile, "  server-name \"%s\";\n",
1599 		    lease->server_name);
1600 	if (lease->medium)
1601 		fprintf(leaseFile, "  medium \"%s\";\n", lease->medium->string);
1602 	for (i = 0; i < 256; i++)
1603 		if (lease->options[i].len)
1604 			fprintf(leaseFile, "  option %s %s;\n",
1605 			    dhcp_options[i].name,
1606 			    pretty_print_option(i, lease->options[i].data,
1607 			    lease->options[i].len, 1, 1));
1608 
1609 	t = gmtime(&lease->renewal);
1610         if (t)
1611 	    fprintf(leaseFile, "  renew %d %d/%d/%d %02d:%02d:%02d;\n",
1612 	        t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
1613 	        t->tm_hour, t->tm_min, t->tm_sec);
1614 	t = gmtime(&lease->rebind);
1615         if (t)
1616 	    fprintf(leaseFile, "  rebind %d %d/%d/%d %02d:%02d:%02d;\n",
1617 	         t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
1618 	         t->tm_hour, t->tm_min, t->tm_sec);
1619 	t = gmtime(&lease->expiry);
1620         if (t)
1621 	    fprintf(leaseFile, "  expire %d %d/%d/%d %02d:%02d:%02d;\n",
1622 	        t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
1623 	        t->tm_hour, t->tm_min, t->tm_sec);
1624 	fprintf(leaseFile, "}\n");
1625 	fflush(leaseFile);
1626 }
1627 
1628 void
1629 priv_script_init(struct interface_info *ip, char *reason, char *medium)
1630 {
1631 	if (ip) {
1632             // XXX Do we need to do anything?
1633         }
1634 }
1635 
1636 void
1637 priv_script_write_params(struct interface_info *ip, char *prefix, struct client_lease *lease)
1638 {
1639 	u_int8_t dbuf[1500];
1640 	int i, len = 0;
1641 
1642 #if 0
1643 	script_set_env(ip->client, prefix, "ip_address",
1644 	    piaddr(lease->address));
1645 #endif
1646 
1647 	if (lease->options[DHO_SUBNET_MASK].len &&
1648 	    (lease->options[DHO_SUBNET_MASK].len <
1649 	    sizeof(lease->address.iabuf))) {
1650 		struct iaddr netmask, subnet, broadcast;
1651 
1652 		memcpy(netmask.iabuf, lease->options[DHO_SUBNET_MASK].data,
1653 		    lease->options[DHO_SUBNET_MASK].len);
1654 		netmask.len = lease->options[DHO_SUBNET_MASK].len;
1655 
1656 		subnet = subnet_number(lease->address, netmask);
1657 		if (subnet.len) {
1658 #if 0
1659 			script_set_env(ip->client, prefix, "network_number",
1660 			    piaddr(subnet));
1661 #endif
1662 			if (!lease->options[DHO_BROADCAST_ADDRESS].len) {
1663 				broadcast = broadcast_addr(subnet, netmask);
1664 				if (broadcast.len)
1665 #if 0
1666 					script_set_env(ip->client, prefix,
1667 					    "broadcast_address",
1668 					    piaddr(broadcast));
1669 #else
1670                                 ;
1671 #endif
1672 			}
1673 		}
1674 	}
1675 
1676 #if 0
1677 	if (lease->filename)
1678 		script_set_env(ip->client, prefix, "filename", lease->filename);
1679 	if (lease->server_name)
1680 		script_set_env(ip->client, prefix, "server_name",
1681 		    lease->server_name);
1682 #endif
1683 
1684 	for (i = 0; i < 256; i++) {
1685 		u_int8_t *dp = NULL;
1686 
1687 		if (ip->client->config->defaults[i].len) {
1688 			if (lease->options[i].len) {
1689 				switch (
1690 				    ip->client->config->default_actions[i]) {
1691 				case ACTION_DEFAULT:
1692 					dp = lease->options[i].data;
1693 					len = lease->options[i].len;
1694 					break;
1695 				case ACTION_SUPERSEDE:
1696 supersede:
1697 					dp = ip->client->
1698 						config->defaults[i].data;
1699 					len = ip->client->
1700 						config->defaults[i].len;
1701 					break;
1702 				case ACTION_PREPEND:
1703 					len = ip->client->
1704 					    config->defaults[i].len +
1705 					    lease->options[i].len;
1706 					if (len >= sizeof(dbuf)) {
1707 						warning("no space to %s %s",
1708 						    "prepend option",
1709 						    dhcp_options[i].name);
1710 						goto supersede;
1711 					}
1712 					dp = dbuf;
1713 					memcpy(dp,
1714 						ip->client->
1715 						config->defaults[i].data,
1716 						ip->client->
1717 						config->defaults[i].len);
1718 					memcpy(dp + ip->client->
1719 						config->defaults[i].len,
1720 						lease->options[i].data,
1721 						lease->options[i].len);
1722 					dp[len] = '\0';
1723 					break;
1724 				case ACTION_APPEND:
1725 					len = ip->client->
1726 					    config->defaults[i].len +
1727 					    lease->options[i].len + 1;
1728 					if (len > sizeof(dbuf)) {
1729 						warning("no space to %s %s",
1730 						    "append option",
1731 						    dhcp_options[i].name);
1732 						goto supersede;
1733 					}
1734 					dp = dbuf;
1735 					memcpy(dp,
1736 						lease->options[i].data,
1737 						lease->options[i].len);
1738 					memcpy(dp + lease->options[i].len,
1739 						ip->client->
1740 						config->defaults[i].data,
1741 						ip->client->
1742 						config->defaults[i].len);
1743 					dp[len-1] = '\0';
1744 				}
1745 			} else {
1746 				dp = ip->client->
1747 					config->defaults[i].data;
1748 				len = ip->client->
1749 					config->defaults[i].len;
1750 			}
1751 		} else if (lease->options[i].len) {
1752 			len = lease->options[i].len;
1753 			dp = lease->options[i].data;
1754 		} else {
1755 			len = 0;
1756 		}
1757 #if 0
1758 		if (len) {
1759 			char name[256];
1760 
1761 			if (dhcp_option_ev_name(name, sizeof(name),
1762 			    &dhcp_options[i]))
1763 				script_set_env(ip->client, prefix, name,
1764 				    pretty_print_option(i, dp, len, 0, 0));
1765 		}
1766 #endif
1767 	}
1768 #if 0
1769 	snprintf(tbuf, sizeof(tbuf), "%d", (int)lease->expiry);
1770 	script_set_env(ip->client, prefix, "expiry", tbuf);
1771 #endif
1772 }
1773 
1774 int
1775 dhcp_option_ev_name(char *buf, size_t buflen, struct dhcp_option *option)
1776 {
1777 	int i;
1778 
1779 	for (i = 0; option->name[i]; i++) {
1780 		if (i + 1 == buflen)
1781 			return 0;
1782 		if (option->name[i] == '-')
1783 			buf[i] = '_';
1784 		else
1785 			buf[i] = option->name[i];
1786 	}
1787 
1788 	buf[i] = 0;
1789 	return 1;
1790 }
1791 
1792 #if 0
1793 void
1794 go_daemon(void)
1795 {
1796 	static int state = 0;
1797 
1798 	if (no_daemon || state)
1799 		return;
1800 
1801 	state = 1;
1802 
1803 	/* Stop logging to stderr... */
1804 	log_perror = 0;
1805 
1806 	if (daemon(1, 0) == -1)
1807 		error("daemon");
1808 
1809 	/* we are chrooted, daemon(3) fails to open /dev/null */
1810 	if (nullfd != -1) {
1811 		dup2(nullfd, STDIN_FILENO);
1812 		dup2(nullfd, STDOUT_FILENO);
1813 		dup2(nullfd, STDERR_FILENO);
1814 		close(nullfd);
1815 		nullfd = -1;
1816 	}
1817 }
1818 #endif
1819 
1820 int
1821 check_option(struct client_lease *l, int option)
1822 {
1823 	char *opbuf;
1824 	char *sbuf;
1825 
1826 	/* we use this, since this is what gets passed to dhclient-script */
1827 
1828 	opbuf = pretty_print_option(option, l->options[option].data,
1829 	    l->options[option].len, 0, 0);
1830 
1831 	sbuf = option_as_string(option, l->options[option].data,
1832 	    l->options[option].len);
1833 
1834 	switch (option) {
1835 	case DHO_SUBNET_MASK:
1836 	case DHO_TIME_SERVERS:
1837 	case DHO_NAME_SERVERS:
1838 	case DHO_ROUTERS:
1839 	case DHO_DOMAIN_NAME_SERVERS:
1840 	case DHO_LOG_SERVERS:
1841 	case DHO_COOKIE_SERVERS:
1842 	case DHO_LPR_SERVERS:
1843 	case DHO_IMPRESS_SERVERS:
1844 	case DHO_RESOURCE_LOCATION_SERVERS:
1845 	case DHO_SWAP_SERVER:
1846 	case DHO_BROADCAST_ADDRESS:
1847 	case DHO_NIS_SERVERS:
1848 	case DHO_NTP_SERVERS:
1849 	case DHO_NETBIOS_NAME_SERVERS:
1850 	case DHO_NETBIOS_DD_SERVER:
1851 	case DHO_FONT_SERVERS:
1852 	case DHO_DHCP_SERVER_IDENTIFIER:
1853 		if (!ipv4addrs(opbuf)) {
1854                         warning("Invalid IP address in option(%d): %s", option, opbuf);
1855 			return (0);
1856 		}
1857 		return (1)  ;
1858 	case DHO_HOST_NAME:
1859 	case DHO_DOMAIN_NAME:
1860 	case DHO_NIS_DOMAIN:
1861 		if (!res_hnok(sbuf))
1862 			warning("Bogus Host Name option %d: %s (%s)", option,
1863 			    sbuf, opbuf);
1864 		return (1);
1865 	case DHO_PAD:
1866 	case DHO_TIME_OFFSET:
1867 	case DHO_BOOT_SIZE:
1868 	case DHO_MERIT_DUMP:
1869 	case DHO_ROOT_PATH:
1870 	case DHO_EXTENSIONS_PATH:
1871 	case DHO_IP_FORWARDING:
1872 	case DHO_NON_LOCAL_SOURCE_ROUTING:
1873 	case DHO_POLICY_FILTER:
1874 	case DHO_MAX_DGRAM_REASSEMBLY:
1875 	case DHO_DEFAULT_IP_TTL:
1876 	case DHO_PATH_MTU_AGING_TIMEOUT:
1877 	case DHO_PATH_MTU_PLATEAU_TABLE:
1878 	case DHO_INTERFACE_MTU:
1879 	case DHO_ALL_SUBNETS_LOCAL:
1880 	case DHO_PERFORM_MASK_DISCOVERY:
1881 	case DHO_MASK_SUPPLIER:
1882 	case DHO_ROUTER_DISCOVERY:
1883 	case DHO_ROUTER_SOLICITATION_ADDRESS:
1884 	case DHO_STATIC_ROUTES:
1885 	case DHO_TRAILER_ENCAPSULATION:
1886 	case DHO_ARP_CACHE_TIMEOUT:
1887 	case DHO_IEEE802_3_ENCAPSULATION:
1888 	case DHO_DEFAULT_TCP_TTL:
1889 	case DHO_TCP_KEEPALIVE_INTERVAL:
1890 	case DHO_TCP_KEEPALIVE_GARBAGE:
1891 	case DHO_VENDOR_ENCAPSULATED_OPTIONS:
1892 	case DHO_NETBIOS_NODE_TYPE:
1893 	case DHO_NETBIOS_SCOPE:
1894 	case DHO_X_DISPLAY_MANAGER:
1895 	case DHO_DHCP_REQUESTED_ADDRESS:
1896 	case DHO_DHCP_LEASE_TIME:
1897 	case DHO_DHCP_OPTION_OVERLOAD:
1898 	case DHO_DHCP_MESSAGE_TYPE:
1899 	case DHO_DHCP_PARAMETER_REQUEST_LIST:
1900 	case DHO_DHCP_MESSAGE:
1901 	case DHO_DHCP_MAX_MESSAGE_SIZE:
1902 	case DHO_DHCP_RENEWAL_TIME:
1903 	case DHO_DHCP_REBINDING_TIME:
1904 	case DHO_DHCP_CLASS_IDENTIFIER:
1905 	case DHO_DHCP_CLIENT_IDENTIFIER:
1906 	case DHO_DHCP_USER_CLASS_ID:
1907 	case DHO_END:
1908 		return (1);
1909 	default:
1910 		warning("unknown dhcp option value 0x%x", option);
1911 		return (unknown_ok);
1912 	}
1913 }
1914 
1915 int
1916 res_hnok(const char *dn)
1917 {
1918 	int pch = PERIOD, ch = *dn++;
1919 
1920 	while (ch != '\0') {
1921 		int nch = *dn++;
1922 
1923 		if (periodchar(ch)) {
1924 			;
1925 		} else if (periodchar(pch)) {
1926 			if (!borderchar(ch))
1927 				return (0);
1928 		} else if (periodchar(nch) || nch == '\0') {
1929 			if (!borderchar(ch))
1930 				return (0);
1931 		} else {
1932 			if (!middlechar(ch))
1933 				return (0);
1934 		}
1935 		pch = ch, ch = nch;
1936 	}
1937 	return (1);
1938 }
1939 
1940 /* Does buf consist only of dotted decimal ipv4 addrs?
1941  * return how many if so,
1942  * otherwise, return 0
1943  */
1944 int
1945 ipv4addrs(char * buf)
1946 {
1947     char *tmp;
1948     struct in_addr jnk;
1949     int i = 0;
1950 
1951     note("Input: %s", buf);
1952 
1953     do {
1954         tmp = strtok(buf, " ");
1955         note("got %s", tmp);
1956 		if( tmp && inet_aton(tmp, &jnk) ) i++;
1957         buf = NULL;
1958     } while( tmp );
1959 
1960     return (i);
1961 }
1962 
1963 
1964 char *
1965 option_as_string(unsigned int code, unsigned char *data, int len)
1966 {
1967 	static char optbuf[32768]; /* XXX */
1968 	char *op = optbuf;
1969 	int opleft = sizeof(optbuf);
1970 	unsigned char *dp = data;
1971 
1972 	if (code > 255)
1973 		error("option_as_string: bad code %d", code);
1974 
1975 	for (; dp < data + len; dp++) {
1976 		if (!isascii(*dp) || !isprint(*dp)) {
1977 			if (dp + 1 != data + len || *dp != 0) {
1978 				_snprintf(op, opleft, "\\%03o", *dp);
1979 				op += 4;
1980 				opleft -= 4;
1981 			}
1982 		} else if (*dp == '"' || *dp == '\'' || *dp == '$' ||
1983 		    *dp == '`' || *dp == '\\') {
1984 			*op++ = '\\';
1985 			*op++ = *dp;
1986 			opleft -= 2;
1987 		} else {
1988 			*op++ = *dp;
1989 			opleft--;
1990 		}
1991 	}
1992 	if (opleft < 1)
1993 		goto toobig;
1994 	*op = 0;
1995 	return optbuf;
1996 toobig:
1997 	warning("dhcp option too large");
1998 	return "<error>";
1999 }
2000 
2001