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