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