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