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