Sync with trunk rev.61910 to get latest improvements and bugfixes.
[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 #include <winsvc.h>
59
60 #define PERIOD 0x2e
61 #define hyphenchar(c) ((c) == 0x2d)
62 #define bslashchar(c) ((c) == 0x5c)
63 #define periodchar(c) ((c) == PERIOD)
64 #define asterchar(c) ((c) == 0x2a)
65 #define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) || \
66 ((c) >= 0x61 && (c) <= 0x7a))
67 #define digitchar(c) ((c) >= 0x30 && (c) <= 0x39)
68
69 #define borderchar(c) (alphachar(c) || digitchar(c))
70 #define middlechar(c) (borderchar(c) || hyphenchar(c))
71 #define domainchar(c) ((c) > 0x20 && (c) < 0x7f)
72
73 unsigned long debug_trace_level = 0; /* DEBUG_ULTRA */
74
75 char *path_dhclient_conf = _PATH_DHCLIENT_CONF;
76 char *path_dhclient_db = NULL;
77
78 int log_perror = 1;
79 int privfd;
80 //int nullfd = -1;
81
82 struct iaddr iaddr_broadcast = { 4, { 255, 255, 255, 255 } };
83 struct in_addr inaddr_any;
84 struct sockaddr_in sockaddr_broadcast;
85
86 /*
87 * ASSERT_STATE() does nothing now; it used to be
88 * assert (state_is == state_shouldbe).
89 */
90 #define ASSERT_STATE(state_is, state_shouldbe) {}
91
92 #define TIME_MAX 2147483647
93
94 int log_priority;
95 int no_daemon;
96 int unknown_ok = 1;
97 int routefd;
98
99 void usage(void);
100 int check_option(struct client_lease *l, int option);
101 int ipv4addrs(char * buf);
102 int res_hnok(const char *dn);
103 char *option_as_string(unsigned int code, unsigned char *data, int len);
104 int fork_privchld(int, int);
105 int check_arp( struct interface_info *ip, struct client_lease *lp );
106
107 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
108
109 time_t scripttime;
110
111 static WCHAR ServiceName[] = L"DHCP";
112
113 SERVICE_STATUS_HANDLE ServiceStatusHandle = 0;
114 SERVICE_STATUS ServiceStatus;
115
116
117 /* XXX Implement me */
118 int check_arp( struct interface_info *ip, struct client_lease *lp ) {
119 return 1;
120 }
121
122
123 static VOID
124 UpdateServiceStatus(DWORD dwState)
125 {
126 ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
127 ServiceStatus.dwCurrentState = dwState;
128
129 ServiceStatus.dwControlsAccepted = 0;
130
131 ServiceStatus.dwWin32ExitCode = 0;
132 ServiceStatus.dwServiceSpecificExitCode = 0;
133 ServiceStatus.dwCheckPoint = 0;
134
135 if (dwState == SERVICE_START_PENDING ||
136 dwState == SERVICE_STOP_PENDING ||
137 dwState == SERVICE_PAUSE_PENDING ||
138 dwState == SERVICE_CONTINUE_PENDING)
139 ServiceStatus.dwWaitHint = 10000;
140 else
141 ServiceStatus.dwWaitHint = 0;
142
143 SetServiceStatus(ServiceStatusHandle,
144 &ServiceStatus);
145 }
146
147
148 static DWORD WINAPI
149 ServiceControlHandler(DWORD dwControl,
150 DWORD dwEventType,
151 LPVOID lpEventData,
152 LPVOID lpContext)
153 {
154 switch (dwControl)
155 {
156 case SERVICE_CONTROL_STOP:
157 UpdateServiceStatus(SERVICE_STOP_PENDING);
158 UpdateServiceStatus(SERVICE_STOPPED);
159 return ERROR_SUCCESS;
160
161 case SERVICE_CONTROL_PAUSE:
162 UpdateServiceStatus(SERVICE_PAUSED);
163 return ERROR_SUCCESS;
164
165 case SERVICE_CONTROL_CONTINUE:
166 UpdateServiceStatus(SERVICE_START_PENDING);
167 UpdateServiceStatus(SERVICE_RUNNING);
168 return ERROR_SUCCESS;
169
170 case SERVICE_CONTROL_INTERROGATE:
171 SetServiceStatus(ServiceStatusHandle,
172 &ServiceStatus);
173 return ERROR_SUCCESS;
174
175 case SERVICE_CONTROL_SHUTDOWN:
176 UpdateServiceStatus(SERVICE_STOP_PENDING);
177 UpdateServiceStatus(SERVICE_STOPPED);
178 return ERROR_SUCCESS;
179
180 default :
181 return ERROR_CALL_NOT_IMPLEMENTED;
182 }
183 }
184
185
186 VOID NTAPI
187 ServiceMain(DWORD argc, LPWSTR *argv)
188 {
189 ServiceStatusHandle = RegisterServiceCtrlHandlerExW(ServiceName,
190 ServiceControlHandler,
191 NULL);
192 if (!ServiceStatusHandle)
193 {
194 DbgPrint("DHCPCSVC: Unable to register service control handler (%lx)\n", GetLastError());
195 return;
196 }
197
198 UpdateServiceStatus(SERVICE_START_PENDING);
199
200 ApiInit();
201 AdapterInit();
202
203 tzset();
204
205 memset(&sockaddr_broadcast, 0, sizeof(sockaddr_broadcast));
206 sockaddr_broadcast.sin_family = AF_INET;
207 sockaddr_broadcast.sin_port = htons(REMOTE_PORT);
208 sockaddr_broadcast.sin_addr.s_addr = INADDR_BROADCAST;
209 inaddr_any.s_addr = INADDR_ANY;
210 bootp_packet_handler = do_packet;
211
212 if (PipeInit() == INVALID_HANDLE_VALUE)
213 {
214 DbgPrint("DHCPCSVC: PipeInit() failed!\n");
215 AdapterStop();
216 ApiFree();
217 UpdateServiceStatus(SERVICE_STOPPED);
218 }
219
220 DH_DbgPrint(MID_TRACE,("DHCP Service Started\n"));
221
222 UpdateServiceStatus(SERVICE_RUNNING);
223
224 DH_DbgPrint(MID_TRACE,("Going into dispatch()\n"));
225
226 DH_DbgPrint(MID_TRACE, ("DHCPCSVC: DHCP service is starting up\n"));
227
228 dispatch();
229
230 DbgPrint("DHCPCSVC: DHCP service is shutting down\n");
231
232 //AdapterStop();
233 //ApiFree();
234 /* FIXME: Close pipe and kill pipe thread */
235
236 UpdateServiceStatus(SERVICE_STOPPED);
237 }
238
239 /*
240 * Individual States:
241 *
242 * Each routine is called from the dhclient_state_machine() in one of
243 * these conditions:
244 * -> entering INIT state
245 * -> recvpacket_flag == 0: timeout in this state
246 * -> otherwise: received a packet in this state
247 *
248 * Return conditions as handled by dhclient_state_machine():
249 * Returns 1, sendpacket_flag = 1: send packet, reset timer.
250 * Returns 1, sendpacket_flag = 0: just reset the timer (wait for a milestone).
251 * Returns 0: finish the nap which was interrupted for no good reason.
252 *
253 * Several per-interface variables are used to keep track of the process:
254 * active_lease: the lease that is being used on the interface
255 * (null pointer if not configured yet).
256 * offered_leases: leases corresponding to DHCPOFFER messages that have
257 * been sent to us by DHCP servers.
258 * acked_leases: leases corresponding to DHCPACK messages that have been
259 * sent to us by DHCP servers.
260 * sendpacket: DHCP packet we're trying to send.
261 * destination: IP address to send sendpacket to
262 * In addition, there are several relevant per-lease variables.
263 * T1_expiry, T2_expiry, lease_expiry: lease milestones
264 * In the active lease, these control the process of renewing the lease;
265 * In leases on the acked_leases list, this simply determines when we
266 * can no longer legitimately use the lease.
267 */
268
269 void
270 state_reboot(void *ipp)
271 {
272 struct interface_info *ip = ipp;
273 ULONG foo = (ULONG) GetTickCount();
274
275 /* If we don't remember an active lease, go straight to INIT. */
276 if (!ip->client->active || ip->client->active->is_bootp) {
277 state_init(ip);
278 return;
279 }
280
281 /* We are in the rebooting state. */
282 ip->client->state = S_REBOOTING;
283
284 /* make_request doesn't initialize xid because it normally comes
285 from the DHCPDISCOVER, but we haven't sent a DHCPDISCOVER,
286 so pick an xid now. */
287 ip->client->xid = RtlRandom(&foo);
288
289 /* Make a DHCPREQUEST packet, and set appropriate per-interface
290 flags. */
291 make_request(ip, ip->client->active);
292 ip->client->destination = iaddr_broadcast;
293 time(&ip->client->first_sending);
294 ip->client->interval = ip->client->config->initial_interval;
295
296 /* Zap the medium list... */
297 ip->client->medium = NULL;
298
299 /* Send out the first DHCPREQUEST packet. */
300 send_request(ip);
301 }
302
303 /*
304 * Called when a lease has completely expired and we've
305 * been unable to renew it.
306 */
307 void
308 state_init(void *ipp)
309 {
310 struct interface_info *ip = ipp;
311
312 ASSERT_STATE(state, S_INIT);
313
314 /* Make a DHCPDISCOVER packet, and set appropriate per-interface
315 flags. */
316 make_discover(ip, ip->client->active);
317 ip->client->xid = ip->client->packet.xid;
318 ip->client->destination = iaddr_broadcast;
319 ip->client->state = S_SELECTING;
320 time(&ip->client->first_sending);
321 ip->client->interval = ip->client->config->initial_interval;
322
323 /* Add an immediate timeout to cause the first DHCPDISCOVER packet
324 to go out. */
325 send_discover(ip);
326 }
327
328 /*
329 * state_selecting is called when one or more DHCPOFFER packets
330 * have been received and a configurable period of time has passed.
331 */
332 void
333 state_selecting(void *ipp)
334 {
335 struct interface_info *ip = ipp;
336 struct client_lease *lp, *next, *picked;
337 time_t cur_time;
338
339 ASSERT_STATE(state, S_SELECTING);
340
341 time(&cur_time);
342
343 /* Cancel state_selecting and send_discover timeouts, since either
344 one could have got us here. */
345 cancel_timeout(state_selecting, ip);
346 cancel_timeout(send_discover, ip);
347
348 /* We have received one or more DHCPOFFER packets. Currently,
349 the only criterion by which we judge leases is whether or
350 not we get a response when we arp for them. */
351 picked = NULL;
352 for (lp = ip->client->offered_leases; lp; lp = next) {
353 next = lp->next;
354
355 /* Check to see if we got an ARPREPLY for the address
356 in this particular lease. */
357 if (!picked) {
358 if( !check_arp(ip,lp) ) goto freeit;
359 picked = lp;
360 picked->next = NULL;
361 } else {
362 freeit:
363 free_client_lease(lp);
364 }
365 }
366 ip->client->offered_leases = NULL;
367
368 /* If we just tossed all the leases we were offered, go back
369 to square one. */
370 if (!picked) {
371 ip->client->state = S_INIT;
372 state_init(ip);
373 return;
374 }
375
376 /* If it was a BOOTREPLY, we can just take the address right now. */
377 if (!picked->options[DHO_DHCP_MESSAGE_TYPE].len) {
378 ip->client->new = picked;
379
380 /* Make up some lease expiry times
381 XXX these should be configurable. */
382 ip->client->new->expiry = cur_time + 12000;
383 ip->client->new->renewal += cur_time + 8000;
384 ip->client->new->rebind += cur_time + 10000;
385
386 ip->client->state = S_REQUESTING;
387
388 /* Bind to the address we received. */
389 bind_lease(ip);
390 return;
391 }
392
393 /* Go to the REQUESTING state. */
394 ip->client->destination = iaddr_broadcast;
395 ip->client->state = S_REQUESTING;
396 ip->client->first_sending = cur_time;
397 ip->client->interval = ip->client->config->initial_interval;
398
399 /* Make a DHCPREQUEST packet from the lease we picked. */
400 make_request(ip, picked);
401 ip->client->xid = ip->client->packet.xid;
402
403 /* Toss the lease we picked - we'll get it back in a DHCPACK. */
404 free_client_lease(picked);
405
406 /* Add an immediate timeout to send the first DHCPREQUEST packet. */
407 send_request(ip);
408 }
409
410 /* state_requesting is called when we receive a DHCPACK message after
411 having sent out one or more DHCPREQUEST packets. */
412
413 void
414 dhcpack(struct packet *packet)
415 {
416 struct interface_info *ip = packet->interface;
417 struct client_lease *lease;
418 time_t cur_time;
419
420 time(&cur_time);
421
422 /* If we're not receptive to an offer right now, or if the offer
423 has an unrecognizable transaction id, then just drop it. */
424 if (packet->interface->client->xid != packet->raw->xid ||
425 (packet->interface->hw_address.hlen != packet->raw->hlen) ||
426 (memcmp(packet->interface->hw_address.haddr,
427 packet->raw->chaddr, packet->raw->hlen)))
428 return;
429
430 if (ip->client->state != S_REBOOTING &&
431 ip->client->state != S_REQUESTING &&
432 ip->client->state != S_RENEWING &&
433 ip->client->state != S_REBINDING)
434 return;
435
436 note("DHCPACK from %s", piaddr(packet->client_addr));
437
438 lease = packet_to_lease(packet);
439 if (!lease) {
440 note("packet_to_lease failed.");
441 return;
442 }
443
444 ip->client->new = lease;
445
446 /* Stop resending DHCPREQUEST. */
447 cancel_timeout(send_request, ip);
448
449 /* Figure out the lease time. */
450 if (ip->client->new->options[DHO_DHCP_LEASE_TIME].data)
451 ip->client->new->expiry = getULong(
452 ip->client->new->options[DHO_DHCP_LEASE_TIME].data);
453 else
454 ip->client->new->expiry = DHCP_DEFAULT_LEASE_TIME;
455 /* A number that looks negative here is really just very large,
456 because the lease expiry offset is unsigned. */
457 if (ip->client->new->expiry < 0)
458 ip->client->new->expiry = TIME_MAX;
459 /* XXX should be fixed by resetting the client state */
460 if (ip->client->new->expiry < 60)
461 ip->client->new->expiry = 60;
462
463 /* Take the server-provided renewal time if there is one;
464 otherwise figure it out according to the spec. */
465 if (ip->client->new->options[DHO_DHCP_RENEWAL_TIME].len)
466 ip->client->new->renewal = getULong(
467 ip->client->new->options[DHO_DHCP_RENEWAL_TIME].data);
468 else
469 ip->client->new->renewal = ip->client->new->expiry / 2;
470
471 /* Same deal with the rebind time. */
472 if (ip->client->new->options[DHO_DHCP_REBINDING_TIME].len)
473 ip->client->new->rebind = getULong(
474 ip->client->new->options[DHO_DHCP_REBINDING_TIME].data);
475 else
476 ip->client->new->rebind = ip->client->new->renewal +
477 ip->client->new->renewal / 2 + ip->client->new->renewal / 4;
478
479 #ifdef __REACTOS__
480 ip->client->new->obtained = cur_time;
481 #endif
482 ip->client->new->expiry += cur_time;
483 /* Lease lengths can never be negative. */
484 if (ip->client->new->expiry < cur_time)
485 ip->client->new->expiry = TIME_MAX;
486 ip->client->new->renewal += cur_time;
487 if (ip->client->new->renewal < cur_time)
488 ip->client->new->renewal = TIME_MAX;
489 ip->client->new->rebind += cur_time;
490 if (ip->client->new->rebind < cur_time)
491 ip->client->new->rebind = TIME_MAX;
492
493 bind_lease(ip);
494 }
495
496 void set_name_servers( PDHCP_ADAPTER Adapter, struct client_lease *new_lease ) {
497 CHAR Buffer[200] = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\";
498 HKEY RegKey;
499
500 strcat(Buffer, Adapter->DhclientInfo.name);
501 if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, Buffer, 0, KEY_WRITE, &RegKey ) != ERROR_SUCCESS)
502 return;
503
504
505 if( new_lease->options[DHO_DOMAIN_NAME_SERVERS].len ) {
506
507 struct iaddr nameserver;
508 char *nsbuf;
509 int i, addrs =
510 new_lease->options[DHO_DOMAIN_NAME_SERVERS].len / sizeof(ULONG);
511
512 nsbuf = malloc( addrs * sizeof(IP_ADDRESS_STRING) );
513
514 if( nsbuf) {
515 nsbuf[0] = 0;
516 for( i = 0; i < addrs; i++ ) {
517 nameserver.len = sizeof(ULONG);
518 memcpy( nameserver.iabuf,
519 new_lease->options[DHO_DOMAIN_NAME_SERVERS].data +
520 (i * sizeof(ULONG)), sizeof(ULONG) );
521 strcat( nsbuf, piaddr(nameserver) );
522 if( i != addrs-1 ) strcat( nsbuf, "," );
523 }
524
525 DH_DbgPrint(MID_TRACE,("Setting DhcpNameserver: %s\n", nsbuf));
526
527 RegSetValueExA( RegKey, "DhcpNameServer", 0, REG_SZ,
528 (LPBYTE)nsbuf, strlen(nsbuf) + 1 );
529 free( nsbuf );
530 }
531
532 } else {
533 RegDeleteValueW( RegKey, L"DhcpNameServer" );
534 }
535
536 RegCloseKey( RegKey );
537
538 }
539
540 void setup_adapter( PDHCP_ADAPTER Adapter, struct client_lease *new_lease ) {
541 CHAR Buffer[200] = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\";
542 struct iaddr netmask;
543 HKEY hkey;
544 int i;
545 DWORD dwEnableDHCP;
546
547 strcat(Buffer, Adapter->DhclientInfo.name);
548 if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, Buffer, 0, KEY_WRITE, &hkey) != ERROR_SUCCESS)
549 hkey = NULL;
550
551
552 if( Adapter->NteContext )
553 {
554 DeleteIPAddress( Adapter->NteContext );
555 Adapter->NteContext = 0;
556 }
557
558 /* Set up our default router if we got one from the DHCP server */
559 if( new_lease->options[DHO_SUBNET_MASK].len ) {
560 NTSTATUS Status;
561
562 memcpy( netmask.iabuf,
563 new_lease->options[DHO_SUBNET_MASK].data,
564 new_lease->options[DHO_SUBNET_MASK].len );
565 Status = AddIPAddress
566 ( *((ULONG*)new_lease->address.iabuf),
567 *((ULONG*)netmask.iabuf),
568 Adapter->IfMib.dwIndex,
569 &Adapter->NteContext,
570 &Adapter->NteInstance );
571 if (hkey) {
572 RegSetValueExA(hkey, "DhcpIPAddress", 0, REG_SZ, (LPBYTE)piaddr(new_lease->address), strlen(piaddr(new_lease->address))+1);
573 Buffer[0] = '\0';
574 for(i = 0; i < new_lease->options[DHO_SUBNET_MASK].len; i++)
575 {
576 sprintf(&Buffer[strlen(Buffer)], "%u", new_lease->options[DHO_SUBNET_MASK].data[i]);
577 if (i + 1 < new_lease->options[DHO_SUBNET_MASK].len)
578 strcat(Buffer, ".");
579 }
580 RegSetValueExA(hkey, "DhcpSubnetMask", 0, REG_SZ, (LPBYTE)Buffer, strlen(Buffer)+1);
581 dwEnableDHCP = 1;
582 RegSetValueExA(hkey, "EnableDHCP", 0, REG_DWORD, (LPBYTE)&dwEnableDHCP, sizeof(DWORD));
583 }
584
585 if( !NT_SUCCESS(Status) )
586 warning("AddIPAddress: %lx\n", Status);
587 }
588
589 if( new_lease->options[DHO_ROUTERS].len ) {
590 NTSTATUS Status;
591
592 Adapter->RouterMib.dwForwardDest = 0; /* Default route */
593 Adapter->RouterMib.dwForwardMask = 0;
594 Adapter->RouterMib.dwForwardMetric1 = 1;
595 Adapter->RouterMib.dwForwardIfIndex = Adapter->IfMib.dwIndex;
596
597 if( Adapter->RouterMib.dwForwardNextHop ) {
598 /* If we set a default route before, delete it before continuing */
599 DeleteIpForwardEntry( &Adapter->RouterMib );
600 }
601
602 Adapter->RouterMib.dwForwardNextHop =
603 *((ULONG*)new_lease->options[DHO_ROUTERS].data);
604
605 Status = CreateIpForwardEntry( &Adapter->RouterMib );
606
607 if( !NT_SUCCESS(Status) )
608 warning("CreateIpForwardEntry: %lx\n", Status);
609
610 if (hkey) {
611 Buffer[0] = '\0';
612 for(i = 0; i < new_lease->options[DHO_ROUTERS].len; i++)
613 {
614 sprintf(&Buffer[strlen(Buffer)], "%u", new_lease->options[DHO_ROUTERS].data[i]);
615 if (i + 1 < new_lease->options[DHO_ROUTERS].len)
616 strcat(Buffer, ".");
617 }
618 RegSetValueExA(hkey, "DhcpDefaultGateway", 0, REG_SZ, (LPBYTE)Buffer, strlen(Buffer)+1);
619 }
620 }
621
622 if (hkey)
623 RegCloseKey(hkey);
624 }
625
626
627 void
628 bind_lease(struct interface_info *ip)
629 {
630 PDHCP_ADAPTER Adapter;
631 struct client_lease *new_lease = ip->client->new;
632 time_t cur_time;
633
634 time(&cur_time);
635
636 /* Remember the medium. */
637 ip->client->new->medium = ip->client->medium;
638 ip->client->active = ip->client->new;
639 ip->client->new = NULL;
640
641 /* Set up a timeout to start the renewal process. */
642 /* Timeout of zero means no timeout (some implementations seem to use
643 * one day).
644 */
645 if( ip->client->active->renewal - cur_time )
646 add_timeout(ip->client->active->renewal, state_bound, ip);
647
648 note("bound to %s -- renewal in %ld seconds.",
649 piaddr(ip->client->active->address),
650 (long int)(ip->client->active->renewal - cur_time));
651
652 ip->client->state = S_BOUND;
653
654 Adapter = AdapterFindInfo( ip );
655
656 if( Adapter ) setup_adapter( Adapter, new_lease );
657 else {
658 warning("Could not find adapter for info %p\n", ip);
659 return;
660 }
661 set_name_servers( Adapter, new_lease );
662 }
663
664 /*
665 * state_bound is called when we've successfully bound to a particular
666 * lease, but the renewal time on that lease has expired. We are
667 * expected to unicast a DHCPREQUEST to the server that gave us our
668 * original lease.
669 */
670 void
671 state_bound(void *ipp)
672 {
673 struct interface_info *ip = ipp;
674
675 ASSERT_STATE(state, S_BOUND);
676
677 /* T1 has expired. */
678 make_request(ip, ip->client->active);
679 ip->client->xid = ip->client->packet.xid;
680
681 if (ip->client->active->options[DHO_DHCP_SERVER_IDENTIFIER].len == 4) {
682 memcpy(ip->client->destination.iabuf, ip->client->active->
683 options[DHO_DHCP_SERVER_IDENTIFIER].data, 4);
684 ip->client->destination.len = 4;
685 } else
686 ip->client->destination = iaddr_broadcast;
687
688 time(&ip->client->first_sending);
689 ip->client->interval = ip->client->config->initial_interval;
690 ip->client->state = S_RENEWING;
691
692 /* Send the first packet immediately. */
693 send_request(ip);
694 }
695
696 void
697 bootp(struct packet *packet)
698 {
699 struct iaddrlist *ap;
700
701 if (packet->raw->op != BOOTREPLY)
702 return;
703
704 /* If there's a reject list, make sure this packet's sender isn't
705 on it. */
706 for (ap = packet->interface->client->config->reject_list;
707 ap; ap = ap->next) {
708 if (addr_eq(packet->client_addr, ap->addr)) {
709 note("BOOTREPLY from %s rejected.", piaddr(ap->addr));
710 return;
711 }
712 }
713 dhcpoffer(packet);
714 }
715
716 void
717 dhcp(struct packet *packet)
718 {
719 struct iaddrlist *ap;
720 void (*handler)(struct packet *);
721 char *type;
722
723 switch (packet->packet_type) {
724 case DHCPOFFER:
725 handler = dhcpoffer;
726 type = "DHCPOFFER";
727 break;
728 case DHCPNAK:
729 handler = dhcpnak;
730 type = "DHCPNACK";
731 break;
732 case DHCPACK:
733 handler = dhcpack;
734 type = "DHCPACK";
735 break;
736 default:
737 return;
738 }
739
740 /* If there's a reject list, make sure this packet's sender isn't
741 on it. */
742 for (ap = packet->interface->client->config->reject_list;
743 ap; ap = ap->next) {
744 if (addr_eq(packet->client_addr, ap->addr)) {
745 note("%s from %s rejected.", type, piaddr(ap->addr));
746 return;
747 }
748 }
749 (*handler)(packet);
750 }
751
752 void
753 dhcpoffer(struct packet *packet)
754 {
755 struct interface_info *ip = packet->interface;
756 struct client_lease *lease, *lp;
757 int i;
758 int arp_timeout_needed = 0, stop_selecting;
759 char *name = packet->options[DHO_DHCP_MESSAGE_TYPE].len ?
760 "DHCPOFFER" : "BOOTREPLY";
761 time_t cur_time;
762
763 time(&cur_time);
764
765 /* If we're not receptive to an offer right now, or if the offer
766 has an unrecognizable transaction id, then just drop it. */
767 if (ip->client->state != S_SELECTING ||
768 packet->interface->client->xid != packet->raw->xid ||
769 (packet->interface->hw_address.hlen != packet->raw->hlen) ||
770 (memcmp(packet->interface->hw_address.haddr,
771 packet->raw->chaddr, packet->raw->hlen)))
772 return;
773
774 note("%s from %s", name, piaddr(packet->client_addr));
775
776
777 /* If this lease doesn't supply the minimum required parameters,
778 blow it off. */
779 for (i = 0; ip->client->config->required_options[i]; i++) {
780 if (!packet->options[ip->client->config->
781 required_options[i]].len) {
782 note("%s isn't satisfactory.", name);
783 return;
784 }
785 }
786
787 /* If we've already seen this lease, don't record it again. */
788 for (lease = ip->client->offered_leases;
789 lease; lease = lease->next) {
790 if (lease->address.len == sizeof(packet->raw->yiaddr) &&
791 !memcmp(lease->address.iabuf,
792 &packet->raw->yiaddr, lease->address.len)) {
793 debug("%s already seen.", name);
794 return;
795 }
796 }
797
798 lease = packet_to_lease(packet);
799 if (!lease) {
800 note("packet_to_lease failed.");
801 return;
802 }
803
804 /* If this lease was acquired through a BOOTREPLY, record that
805 fact. */
806 if (!packet->options[DHO_DHCP_MESSAGE_TYPE].len)
807 lease->is_bootp = 1;
808
809 /* Record the medium under which this lease was offered. */
810 lease->medium = ip->client->medium;
811
812 /* Send out an ARP Request for the offered IP address. */
813 if( !check_arp( ip, lease ) ) {
814 note("Arp check failed\n");
815 return;
816 }
817
818 /* Figure out when we're supposed to stop selecting. */
819 stop_selecting =
820 ip->client->first_sending + ip->client->config->select_interval;
821
822 /* If this is the lease we asked for, put it at the head of the
823 list, and don't mess with the arp request timeout. */
824 if (lease->address.len == ip->client->requested_address.len &&
825 !memcmp(lease->address.iabuf,
826 ip->client->requested_address.iabuf,
827 ip->client->requested_address.len)) {
828 lease->next = ip->client->offered_leases;
829 ip->client->offered_leases = lease;
830 } else {
831 /* If we already have an offer, and arping for this
832 offer would take us past the selection timeout,
833 then don't extend the timeout - just hope for the
834 best. */
835 if (ip->client->offered_leases &&
836 (cur_time + arp_timeout_needed) > stop_selecting)
837 arp_timeout_needed = 0;
838
839 /* Put the lease at the end of the list. */
840 lease->next = NULL;
841 if (!ip->client->offered_leases)
842 ip->client->offered_leases = lease;
843 else {
844 for (lp = ip->client->offered_leases; lp->next;
845 lp = lp->next)
846 ; /* nothing */
847 lp->next = lease;
848 }
849 }
850
851 /* If we're supposed to stop selecting before we've had time
852 to wait for the ARPREPLY, add some delay to wait for
853 the ARPREPLY. */
854 if (stop_selecting - cur_time < arp_timeout_needed)
855 stop_selecting = cur_time + arp_timeout_needed;
856
857 /* If the selecting interval has expired, go immediately to
858 state_selecting(). Otherwise, time out into
859 state_selecting at the select interval. */
860 if (stop_selecting <= 0)
861 state_selecting(ip);
862 else {
863 add_timeout(stop_selecting, state_selecting, ip);
864 cancel_timeout(send_discover, ip);
865 }
866 }
867
868 /* Allocate a client_lease structure and initialize it from the parameters
869 in the specified packet. */
870
871 struct client_lease *
872 packet_to_lease(struct packet *packet)
873 {
874 struct client_lease *lease;
875 int i;
876
877 lease = malloc(sizeof(struct client_lease));
878
879 if (!lease) {
880 warning("dhcpoffer: no memory to record lease.");
881 return (NULL);
882 }
883
884 memset(lease, 0, sizeof(*lease));
885
886 /* Copy the lease options. */
887 for (i = 0; i < 256; i++) {
888 if (packet->options[i].len) {
889 lease->options[i].data =
890 malloc(packet->options[i].len + 1);
891 if (!lease->options[i].data) {
892 warning("dhcpoffer: no memory for option %d", i);
893 free_client_lease(lease);
894 return (NULL);
895 } else {
896 memcpy(lease->options[i].data,
897 packet->options[i].data,
898 packet->options[i].len);
899 lease->options[i].len =
900 packet->options[i].len;
901 lease->options[i].data[lease->options[i].len] =
902 0;
903 }
904 if (!check_option(lease,i)) {
905 /* ignore a bogus lease offer */
906 warning("Invalid lease option - ignoring offer");
907 free_client_lease(lease);
908 return (NULL);
909 }
910 }
911 }
912
913 lease->address.len = sizeof(packet->raw->yiaddr);
914 memcpy(lease->address.iabuf, &packet->raw->yiaddr, lease->address.len);
915 #ifdef __REACTOS__
916 lease->serveraddress.len = sizeof(packet->raw->siaddr);
917 memcpy(lease->serveraddress.iabuf, &packet->raw->siaddr, lease->address.len);
918 #endif
919
920 /* If the server name was filled out, copy it. */
921 if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len ||
922 !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)) &&
923 packet->raw->sname[0]) {
924 lease->server_name = malloc(DHCP_SNAME_LEN + 1);
925 if (!lease->server_name) {
926 warning("dhcpoffer: no memory for server name.");
927 free_client_lease(lease);
928 return (NULL);
929 }
930 memcpy(lease->server_name, packet->raw->sname, DHCP_SNAME_LEN);
931 lease->server_name[DHCP_SNAME_LEN]='\0';
932 if (!res_hnok(lease->server_name) ) {
933 warning("Bogus server name %s", lease->server_name );
934 free_client_lease(lease);
935 return (NULL);
936 }
937
938 }
939
940 /* Ditto for the filename. */
941 if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len ||
942 !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)) &&
943 packet->raw->file[0]) {
944 /* Don't count on the NUL terminator. */
945 lease->filename = malloc(DHCP_FILE_LEN + 1);
946 if (!lease->filename) {
947 warning("dhcpoffer: no memory for filename.");
948 free_client_lease(lease);
949 return (NULL);
950 }
951 memcpy(lease->filename, packet->raw->file, DHCP_FILE_LEN);
952 lease->filename[DHCP_FILE_LEN]='\0';
953 }
954 return lease;
955 }
956
957 void
958 dhcpnak(struct packet *packet)
959 {
960 struct interface_info *ip = packet->interface;
961
962 /* If we're not receptive to an offer right now, or if the offer
963 has an unrecognizable transaction id, then just drop it. */
964 if (packet->interface->client->xid != packet->raw->xid ||
965 (packet->interface->hw_address.hlen != packet->raw->hlen) ||
966 (memcmp(packet->interface->hw_address.haddr,
967 packet->raw->chaddr, packet->raw->hlen)))
968 return;
969
970 if (ip->client->state != S_REBOOTING &&
971 ip->client->state != S_REQUESTING &&
972 ip->client->state != S_RENEWING &&
973 ip->client->state != S_REBINDING)
974 return;
975
976 note("DHCPNAK from %s", piaddr(packet->client_addr));
977
978 if (!ip->client->active) {
979 note("DHCPNAK with no active lease.\n");
980 return;
981 }
982
983 free_client_lease(ip->client->active);
984 ip->client->active = NULL;
985
986 /* Stop sending DHCPREQUEST packets... */
987 cancel_timeout(send_request, ip);
988
989 ip->client->state = S_INIT;
990 state_init(ip);
991 }
992
993 /* Send out a DHCPDISCOVER packet, and set a timeout to send out another
994 one after the right interval has expired. If we don't get an offer by
995 the time we reach the panic interval, call the panic function. */
996
997 void
998 send_discover(void *ipp)
999 {
1000 struct interface_info *ip = ipp;
1001 int interval, increase = 1;
1002 time_t cur_time;
1003
1004 DH_DbgPrint(MID_TRACE,("Doing discover on interface %p\n",ip));
1005
1006 time(&cur_time);
1007
1008 /* Figure out how long it's been since we started transmitting. */
1009 interval = cur_time - ip->client->first_sending;
1010
1011 /* If we're past the panic timeout, call the script and tell it
1012 we haven't found anything for this interface yet. */
1013 if (interval > ip->client->config->timeout) {
1014 state_panic(ip);
1015 ip->client->first_sending = cur_time;
1016 }
1017
1018 /* If we're selecting media, try the whole list before doing
1019 the exponential backoff, but if we've already received an
1020 offer, stop looping, because we obviously have it right. */
1021 if (!ip->client->offered_leases &&
1022 ip->client->config->media) {
1023 int fail = 0;
1024
1025 if (ip->client->medium) {
1026 ip->client->medium = ip->client->medium->next;
1027 increase = 0;
1028 }
1029 if (!ip->client->medium) {
1030 if (fail)
1031 error("No valid media types for %s!", ip->name);
1032 ip->client->medium = ip->client->config->media;
1033 increase = 1;
1034 }
1035
1036 note("Trying medium \"%s\" %d", ip->client->medium->string,
1037 increase);
1038 /* XXX Support other media types eventually */
1039 }
1040
1041 /*
1042 * If we're supposed to increase the interval, do so. If it's
1043 * currently zero (i.e., we haven't sent any packets yet), set
1044 * it to one; otherwise, add to it a random number between zero
1045 * and two times itself. On average, this means that it will
1046 * double with every transmission.
1047 */
1048 if (increase) {
1049 if (!ip->client->interval)
1050 ip->client->interval =
1051 ip->client->config->initial_interval;
1052 else {
1053 ip->client->interval += (rand() >> 2) %
1054 (2 * ip->client->interval);
1055 }
1056
1057 /* Don't backoff past cutoff. */
1058 if (ip->client->interval >
1059 ip->client->config->backoff_cutoff)
1060 ip->client->interval =
1061 ((ip->client->config->backoff_cutoff / 2)
1062 + ((rand() >> 2) %
1063 ip->client->config->backoff_cutoff));
1064 } else if (!ip->client->interval)
1065 ip->client->interval =
1066 ip->client->config->initial_interval;
1067
1068 /* If the backoff would take us to the panic timeout, just use that
1069 as the interval. */
1070 if (cur_time + ip->client->interval >
1071 ip->client->first_sending + ip->client->config->timeout)
1072 ip->client->interval =
1073 (ip->client->first_sending +
1074 ip->client->config->timeout) - cur_time + 1;
1075
1076 /* Record the number of seconds since we started sending. */
1077 if (interval < 65536)
1078 ip->client->packet.secs = htons(interval);
1079 else
1080 ip->client->packet.secs = htons(65535);
1081 ip->client->secs = ip->client->packet.secs;
1082
1083 note("DHCPDISCOVER on %s to %s port %d interval %ld",
1084 ip->name, inet_ntoa(sockaddr_broadcast.sin_addr),
1085 ntohs(sockaddr_broadcast.sin_port), (long int)ip->client->interval);
1086
1087 /* Send out a packet. */
1088 (void)send_packet(ip, &ip->client->packet, ip->client->packet_length,
1089 inaddr_any, &sockaddr_broadcast, NULL);
1090
1091 DH_DbgPrint(MID_TRACE,("discover timeout: now %x -> then %x\n",
1092 cur_time, cur_time + ip->client->interval));
1093
1094 add_timeout(cur_time + ip->client->interval, send_discover, ip);
1095 }
1096
1097 /*
1098 * state_panic gets called if we haven't received any offers in a preset
1099 * amount of time. When this happens, we try to use existing leases
1100 * that haven't yet expired, and failing that, we call the client script
1101 * and hope it can do something.
1102 */
1103 void
1104 state_panic(void *ipp)
1105 {
1106 struct interface_info *ip = ipp;
1107 PDHCP_ADAPTER Adapter = AdapterFindInfo(ip);
1108
1109 note("No DHCPOFFERS received.");
1110
1111 if (!Adapter->NteContext)
1112 {
1113 /* Generate an automatic private address */
1114 DbgPrint("DHCPCSVC: Failed to receive a response from a DHCP server. An automatic private address will be assigned.\n");
1115
1116 /* FIXME: The address generation code sucks */
1117 AddIPAddress(htonl(0xA9FE0000 | (rand() % 0xFFFF)), //169.254.X.X
1118 htonl(0xFFFF0000), //255.255.0.0
1119 Adapter->IfMib.dwIndex,
1120 &Adapter->NteContext,
1121 &Adapter->NteInstance);
1122 }
1123 }
1124
1125 void
1126 send_request(void *ipp)
1127 {
1128 struct interface_info *ip = ipp;
1129 struct sockaddr_in destination;
1130 struct in_addr from;
1131 int interval;
1132 time_t cur_time;
1133
1134 time(&cur_time);
1135
1136 /* Figure out how long it's been since we started transmitting. */
1137 interval = cur_time - ip->client->first_sending;
1138
1139 /* If we're in the INIT-REBOOT or REQUESTING state and we're
1140 past the reboot timeout, go to INIT and see if we can
1141 DISCOVER an address... */
1142 /* XXX In the INIT-REBOOT state, if we don't get an ACK, it
1143 means either that we're on a network with no DHCP server,
1144 or that our server is down. In the latter case, assuming
1145 that there is a backup DHCP server, DHCPDISCOVER will get
1146 us a new address, but we could also have successfully
1147 reused our old address. In the former case, we're hosed
1148 anyway. This is not a win-prone situation. */
1149 if ((ip->client->state == S_REBOOTING ||
1150 ip->client->state == S_REQUESTING) &&
1151 interval > ip->client->config->reboot_timeout) {
1152 ip->client->state = S_INIT;
1153 cancel_timeout(send_request, ip);
1154 state_init(ip);
1155 return;
1156 }
1157
1158 /* If we're in the reboot state, make sure the media is set up
1159 correctly. */
1160 if (ip->client->state == S_REBOOTING &&
1161 !ip->client->medium &&
1162 ip->client->active->medium ) {
1163 /* If the medium we chose won't fly, go to INIT state. */
1164 /* XXX Nothing for now */
1165
1166 /* Record the medium. */
1167 ip->client->medium = ip->client->active->medium;
1168 }
1169
1170 /* If the lease has expired, relinquish the address and go back
1171 to the INIT state. */
1172 if (ip->client->state != S_REQUESTING &&
1173 cur_time > ip->client->active->expiry) {
1174 PDHCP_ADAPTER Adapter = AdapterFindInfo( ip );
1175 /* Run the client script with the new parameters. */
1176 /* No script actions necessary in the expiry case */
1177 /* Now do a preinit on the interface so that we can
1178 discover a new address. */
1179
1180 if( Adapter )
1181 {
1182 DeleteIPAddress( Adapter->NteContext );
1183 Adapter->NteContext = 0;
1184 }
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