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