[DHCPCSVC] Fix a memory leak in bind_lease introduced in r14645 (!). CORE-14353
[reactos.git] / 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
451 set_domain(PDHCP_ADAPTER Adapter,
452 struct client_lease *new_lease)
453 {
454 CHAR Buffer1[MAX_PATH] = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\";
455 CHAR Buffer2[MAX_PATH] = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters";
456 HKEY RegKey1, RegKey2;
457
458 strcat(Buffer1, Adapter->DhclientInfo.name);
459
460 if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, Buffer1, 0, KEY_WRITE, &RegKey1 ) != ERROR_SUCCESS)
461 {
462 return;
463 }
464
465 if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, Buffer2, 0, KEY_WRITE, &RegKey2 ) != ERROR_SUCCESS)
466 {
467 RegCloseKey(RegKey1);
468 return;
469 }
470
471 if (new_lease->options[DHO_DOMAIN_NAME].len)
472 {
473 DH_DbgPrint(MID_TRACE, ("Setting DhcpDomain: %s\n", new_lease->options[DHO_DOMAIN_NAME].data));
474
475 RegSetValueExA(RegKey1,
476 "DhcpDomain",
477 0,
478 REG_SZ,
479 (LPBYTE)new_lease->options[DHO_DOMAIN_NAME].data,
480 new_lease->options[DHO_DOMAIN_NAME].len);
481
482 RegSetValueExA(RegKey2,
483 "DhcpDomain",
484 0,
485 REG_SZ,
486 (LPBYTE)new_lease->options[DHO_DOMAIN_NAME].data,
487 new_lease->options[DHO_DOMAIN_NAME].len);
488 }
489 else
490 {
491 RegDeleteValueW(RegKey1, L"DhcpDomain");
492 RegDeleteValueW(RegKey2, L"DhcpDomain");
493 }
494
495 RegCloseKey(RegKey1);
496 RegCloseKey(RegKey2);
497
498 }
499
500 void setup_adapter( PDHCP_ADAPTER Adapter, struct client_lease *new_lease ) {
501 CHAR Buffer[200] = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\";
502 struct iaddr netmask;
503 HKEY hkey;
504 int i;
505 DWORD dwEnableDHCP;
506
507 strcat(Buffer, Adapter->DhclientInfo.name);
508 if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, Buffer, 0, KEY_WRITE, &hkey) != ERROR_SUCCESS)
509 hkey = NULL;
510
511
512 if( Adapter->NteContext )
513 {
514 DeleteIPAddress( Adapter->NteContext );
515 Adapter->NteContext = 0;
516 }
517
518 /* Set up our default router if we got one from the DHCP server */
519 if( new_lease->options[DHO_SUBNET_MASK].len ) {
520 NTSTATUS Status;
521
522 memcpy( netmask.iabuf,
523 new_lease->options[DHO_SUBNET_MASK].data,
524 new_lease->options[DHO_SUBNET_MASK].len );
525 Status = AddIPAddress
526 ( *((ULONG*)new_lease->address.iabuf),
527 *((ULONG*)netmask.iabuf),
528 Adapter->IfMib.dwIndex,
529 &Adapter->NteContext,
530 &Adapter->NteInstance );
531 if (hkey) {
532 RegSetValueExA(hkey, "DhcpIPAddress", 0, REG_SZ, (LPBYTE)piaddr(new_lease->address), strlen(piaddr(new_lease->address))+1);
533 Buffer[0] = '\0';
534 for(i = 0; i < new_lease->options[DHO_SUBNET_MASK].len; i++)
535 {
536 sprintf(&Buffer[strlen(Buffer)], "%u", new_lease->options[DHO_SUBNET_MASK].data[i]);
537 if (i + 1 < new_lease->options[DHO_SUBNET_MASK].len)
538 strcat(Buffer, ".");
539 }
540 RegSetValueExA(hkey, "DhcpSubnetMask", 0, REG_SZ, (LPBYTE)Buffer, strlen(Buffer)+1);
541 dwEnableDHCP = 1;
542 RegSetValueExA(hkey, "EnableDHCP", 0, REG_DWORD, (LPBYTE)&dwEnableDHCP, sizeof(DWORD));
543 }
544
545 if( !NT_SUCCESS(Status) )
546 warning("AddIPAddress: %lx\n", Status);
547 }
548
549 if( new_lease->options[DHO_ROUTERS].len ) {
550 NTSTATUS Status;
551
552 Adapter->RouterMib.dwForwardDest = 0; /* Default route */
553 Adapter->RouterMib.dwForwardMask = 0;
554 Adapter->RouterMib.dwForwardMetric1 = 1;
555 Adapter->RouterMib.dwForwardIfIndex = Adapter->IfMib.dwIndex;
556
557 if( Adapter->RouterMib.dwForwardNextHop ) {
558 /* If we set a default route before, delete it before continuing */
559 DeleteIpForwardEntry( &Adapter->RouterMib );
560 }
561
562 Adapter->RouterMib.dwForwardNextHop =
563 *((ULONG*)new_lease->options[DHO_ROUTERS].data);
564
565 Status = CreateIpForwardEntry( &Adapter->RouterMib );
566
567 if( !NT_SUCCESS(Status) )
568 warning("CreateIpForwardEntry: %lx\n", Status);
569
570 if (hkey) {
571 Buffer[0] = '\0';
572 for(i = 0; i < new_lease->options[DHO_ROUTERS].len; i++)
573 {
574 sprintf(&Buffer[strlen(Buffer)], "%u", new_lease->options[DHO_ROUTERS].data[i]);
575 if (i + 1 < new_lease->options[DHO_ROUTERS].len)
576 strcat(Buffer, ".");
577 }
578 RegSetValueExA(hkey, "DhcpDefaultGateway", 0, REG_SZ, (LPBYTE)Buffer, strlen(Buffer)+1);
579 }
580 }
581
582 if (hkey)
583 RegCloseKey(hkey);
584 }
585
586
587 void
588 bind_lease(struct interface_info *ip)
589 {
590 PDHCP_ADAPTER Adapter;
591 struct client_lease *new_lease = ip->client->new;
592 time_t cur_time;
593
594 time(&cur_time);
595
596 /* Remember the medium. */
597 ip->client->new->medium = ip->client->medium;
598
599 /* Replace the old active lease with the new one. */
600 if (ip->client->active)
601 free_client_lease(ip->client->active);
602 ip->client->active = ip->client->new;
603 ip->client->new = NULL;
604
605 /* Set up a timeout to start the renewal process. */
606 /* Timeout of zero means no timeout (some implementations seem to use
607 * one day).
608 */
609 if( ip->client->active->renewal - cur_time )
610 add_timeout(ip->client->active->renewal, state_bound, ip);
611
612 note("bound to %s -- renewal in %ld seconds.",
613 piaddr(ip->client->active->address),
614 (long int)(ip->client->active->renewal - cur_time));
615
616 ip->client->state = S_BOUND;
617
618 Adapter = AdapterFindInfo( ip );
619
620 if( Adapter ) setup_adapter( Adapter, new_lease );
621 else {
622 warning("Could not find adapter for info %p\n", ip);
623 return;
624 }
625 set_name_servers( Adapter, new_lease );
626 set_domain( Adapter, new_lease );
627 }
628
629 /*
630 * state_bound is called when we've successfully bound to a particular
631 * lease, but the renewal time on that lease has expired. We are
632 * expected to unicast a DHCPREQUEST to the server that gave us our
633 * original lease.
634 */
635 void
636 state_bound(void *ipp)
637 {
638 struct interface_info *ip = ipp;
639
640 ASSERT_STATE(state, S_BOUND);
641
642 /* T1 has expired. */
643 make_request(ip, ip->client->active);
644 ip->client->xid = ip->client->packet.xid;
645
646 if (ip->client->active->options[DHO_DHCP_SERVER_IDENTIFIER].len == 4) {
647 memcpy(ip->client->destination.iabuf, ip->client->active->
648 options[DHO_DHCP_SERVER_IDENTIFIER].data, 4);
649 ip->client->destination.len = 4;
650 } else
651 ip->client->destination = iaddr_broadcast;
652
653 time(&ip->client->first_sending);
654 ip->client->interval = ip->client->config->initial_interval;
655 ip->client->state = S_RENEWING;
656
657 /* Send the first packet immediately. */
658 send_request(ip);
659 }
660
661 void
662 bootp(struct packet *packet)
663 {
664 struct iaddrlist *ap;
665
666 if (packet->raw->op != BOOTREPLY)
667 return;
668
669 /* If there's a reject list, make sure this packet's sender isn't
670 on it. */
671 for (ap = packet->interface->client->config->reject_list;
672 ap; ap = ap->next) {
673 if (addr_eq(packet->client_addr, ap->addr)) {
674 note("BOOTREPLY from %s rejected.", piaddr(ap->addr));
675 return;
676 }
677 }
678 dhcpoffer(packet);
679 }
680
681 void
682 dhcp(struct packet *packet)
683 {
684 struct iaddrlist *ap;
685 void (*handler)(struct packet *);
686 char *type;
687
688 switch (packet->packet_type) {
689 case DHCPOFFER:
690 handler = dhcpoffer;
691 type = "DHCPOFFER";
692 break;
693 case DHCPNAK:
694 handler = dhcpnak;
695 type = "DHCPNACK";
696 break;
697 case DHCPACK:
698 handler = dhcpack;
699 type = "DHCPACK";
700 break;
701 default:
702 return;
703 }
704
705 /* If there's a reject list, make sure this packet's sender isn't
706 on it. */
707 for (ap = packet->interface->client->config->reject_list;
708 ap; ap = ap->next) {
709 if (addr_eq(packet->client_addr, ap->addr)) {
710 note("%s from %s rejected.", type, piaddr(ap->addr));
711 return;
712 }
713 }
714 (*handler)(packet);
715 }
716
717 void
718 dhcpoffer(struct packet *packet)
719 {
720 struct interface_info *ip = packet->interface;
721 struct client_lease *lease, *lp;
722 int i;
723 int arp_timeout_needed = 0, stop_selecting;
724 char *name = packet->options[DHO_DHCP_MESSAGE_TYPE].len ?
725 "DHCPOFFER" : "BOOTREPLY";
726 time_t cur_time;
727
728 time(&cur_time);
729
730 /* If we're not receptive to an offer right now, or if the offer
731 has an unrecognizable transaction id, then just drop it. */
732 if (ip->client->state != S_SELECTING ||
733 packet->interface->client->xid != packet->raw->xid ||
734 (packet->interface->hw_address.hlen != packet->raw->hlen) ||
735 (memcmp(packet->interface->hw_address.haddr,
736 packet->raw->chaddr, packet->raw->hlen)))
737 return;
738
739 note("%s from %s", name, piaddr(packet->client_addr));
740
741
742 /* If this lease doesn't supply the minimum required parameters,
743 blow it off. */
744 for (i = 0; ip->client->config->required_options[i]; i++) {
745 if (!packet->options[ip->client->config->
746 required_options[i]].len) {
747 note("%s isn't satisfactory.", name);
748 return;
749 }
750 }
751
752 /* If we've already seen this lease, don't record it again. */
753 for (lease = ip->client->offered_leases;
754 lease; lease = lease->next) {
755 if (lease->address.len == sizeof(packet->raw->yiaddr) &&
756 !memcmp(lease->address.iabuf,
757 &packet->raw->yiaddr, lease->address.len)) {
758 debug("%s already seen.", name);
759 return;
760 }
761 }
762
763 lease = packet_to_lease(packet);
764 if (!lease) {
765 note("packet_to_lease failed.");
766 return;
767 }
768
769 /* If this lease was acquired through a BOOTREPLY, record that
770 fact. */
771 if (!packet->options[DHO_DHCP_MESSAGE_TYPE].len)
772 lease->is_bootp = 1;
773
774 /* Record the medium under which this lease was offered. */
775 lease->medium = ip->client->medium;
776
777 /* Send out an ARP Request for the offered IP address. */
778 if( !check_arp( ip, lease ) ) {
779 note("Arp check failed\n");
780 return;
781 }
782
783 /* Figure out when we're supposed to stop selecting. */
784 stop_selecting =
785 ip->client->first_sending + ip->client->config->select_interval;
786
787 /* If this is the lease we asked for, put it at the head of the
788 list, and don't mess with the arp request timeout. */
789 if (lease->address.len == ip->client->requested_address.len &&
790 !memcmp(lease->address.iabuf,
791 ip->client->requested_address.iabuf,
792 ip->client->requested_address.len)) {
793 lease->next = ip->client->offered_leases;
794 ip->client->offered_leases = lease;
795 } else {
796 /* If we already have an offer, and arping for this
797 offer would take us past the selection timeout,
798 then don't extend the timeout - just hope for the
799 best. */
800 if (ip->client->offered_leases &&
801 (cur_time + arp_timeout_needed) > stop_selecting)
802 arp_timeout_needed = 0;
803
804 /* Put the lease at the end of the list. */
805 lease->next = NULL;
806 if (!ip->client->offered_leases)
807 ip->client->offered_leases = lease;
808 else {
809 for (lp = ip->client->offered_leases; lp->next;
810 lp = lp->next)
811 ; /* nothing */
812 lp->next = lease;
813 }
814 }
815
816 /* If we're supposed to stop selecting before we've had time
817 to wait for the ARPREPLY, add some delay to wait for
818 the ARPREPLY. */
819 if (stop_selecting - cur_time < arp_timeout_needed)
820 stop_selecting = cur_time + arp_timeout_needed;
821
822 /* If the selecting interval has expired, go immediately to
823 state_selecting(). Otherwise, time out into
824 state_selecting at the select interval. */
825 if (stop_selecting <= 0)
826 state_selecting(ip);
827 else {
828 add_timeout(stop_selecting, state_selecting, ip);
829 cancel_timeout(send_discover, ip);
830 }
831 }
832
833 /* Allocate a client_lease structure and initialize it from the parameters
834 in the specified packet. */
835
836 struct client_lease *
837 packet_to_lease(struct packet *packet)
838 {
839 struct client_lease *lease;
840 int i;
841
842 lease = malloc(sizeof(struct client_lease));
843
844 if (!lease) {
845 warning("dhcpoffer: no memory to record lease.");
846 return (NULL);
847 }
848
849 memset(lease, 0, sizeof(*lease));
850
851 /* Copy the lease options. */
852 for (i = 0; i < 256; i++) {
853 if (packet->options[i].len) {
854 lease->options[i].data =
855 malloc(packet->options[i].len + 1);
856 if (!lease->options[i].data) {
857 warning("dhcpoffer: no memory for option %d", i);
858 free_client_lease(lease);
859 return (NULL);
860 } else {
861 memcpy(lease->options[i].data,
862 packet->options[i].data,
863 packet->options[i].len);
864 lease->options[i].len =
865 packet->options[i].len;
866 lease->options[i].data[lease->options[i].len] =
867 0;
868 }
869 if (!check_option(lease,i)) {
870 /* ignore a bogus lease offer */
871 warning("Invalid lease option - ignoring offer");
872 free_client_lease(lease);
873 return (NULL);
874 }
875 }
876 }
877
878 lease->address.len = sizeof(packet->raw->yiaddr);
879 memcpy(lease->address.iabuf, &packet->raw->yiaddr, lease->address.len);
880 #ifdef __REACTOS__
881 lease->serveraddress.len = sizeof(packet->raw->siaddr);
882 memcpy(lease->serveraddress.iabuf, &packet->raw->siaddr, lease->address.len);
883 #endif
884
885 /* If the server name was filled out, copy it. */
886 if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len ||
887 !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 2)) &&
888 packet->raw->sname[0]) {
889 lease->server_name = malloc(DHCP_SNAME_LEN + 1);
890 if (!lease->server_name) {
891 warning("dhcpoffer: no memory for server name.");
892 free_client_lease(lease);
893 return (NULL);
894 }
895 memcpy(lease->server_name, packet->raw->sname, DHCP_SNAME_LEN);
896 lease->server_name[DHCP_SNAME_LEN]='\0';
897 if (!res_hnok(lease->server_name) ) {
898 warning("Bogus server name %s", lease->server_name );
899 free_client_lease(lease);
900 return (NULL);
901 }
902
903 }
904
905 /* Ditto for the filename. */
906 if ((!packet->options[DHO_DHCP_OPTION_OVERLOAD].len ||
907 !(packet->options[DHO_DHCP_OPTION_OVERLOAD].data[0] & 1)) &&
908 packet->raw->file[0]) {
909 /* Don't count on the NUL terminator. */
910 lease->filename = malloc(DHCP_FILE_LEN + 1);
911 if (!lease->filename) {
912 warning("dhcpoffer: no memory for filename.");
913 free_client_lease(lease);
914 return (NULL);
915 }
916 memcpy(lease->filename, packet->raw->file, DHCP_FILE_LEN);
917 lease->filename[DHCP_FILE_LEN]='\0';
918 }
919 return lease;
920 }
921
922 void
923 dhcpnak(struct packet *packet)
924 {
925 struct interface_info *ip = packet->interface;
926
927 /* If we're not receptive to an offer right now, or if the offer
928 has an unrecognizable transaction id, then just drop it. */
929 if (packet->interface->client->xid != packet->raw->xid ||
930 (packet->interface->hw_address.hlen != packet->raw->hlen) ||
931 (memcmp(packet->interface->hw_address.haddr,
932 packet->raw->chaddr, packet->raw->hlen)))
933 return;
934
935 if (ip->client->state != S_REBOOTING &&
936 ip->client->state != S_REQUESTING &&
937 ip->client->state != S_RENEWING &&
938 ip->client->state != S_REBINDING)
939 return;
940
941 note("DHCPNAK from %s", piaddr(packet->client_addr));
942
943 if (!ip->client->active) {
944 note("DHCPNAK with no active lease.\n");
945 return;
946 }
947
948 free_client_lease(ip->client->active);
949 ip->client->active = NULL;
950
951 /* Stop sending DHCPREQUEST packets... */
952 cancel_timeout(send_request, ip);
953
954 ip->client->state = S_INIT;
955 state_init(ip);
956 }
957
958 /* Send out a DHCPDISCOVER packet, and set a timeout to send out another
959 one after the right interval has expired. If we don't get an offer by
960 the time we reach the panic interval, call the panic function. */
961
962 void
963 send_discover(void *ipp)
964 {
965 struct interface_info *ip = ipp;
966 int interval, increase = 1;
967 time_t cur_time;
968
969 DH_DbgPrint(MID_TRACE,("Doing discover on interface %p\n",ip));
970
971 time(&cur_time);
972
973 /* Figure out how long it's been since we started transmitting. */
974 interval = cur_time - ip->client->first_sending;
975
976 /* If we're past the panic timeout, call the script and tell it
977 we haven't found anything for this interface yet. */
978 if (interval > ip->client->config->timeout) {
979 state_panic(ip);
980 ip->client->first_sending = cur_time;
981 }
982
983 /* If we're selecting media, try the whole list before doing
984 the exponential backoff, but if we've already received an
985 offer, stop looping, because we obviously have it right. */
986 if (!ip->client->offered_leases &&
987 ip->client->config->media) {
988 int fail = 0;
989
990 if (ip->client->medium) {
991 ip->client->medium = ip->client->medium->next;
992 increase = 0;
993 }
994 if (!ip->client->medium) {
995 if (fail)
996 error("No valid media types for %s!", ip->name);
997 ip->client->medium = ip->client->config->media;
998 increase = 1;
999 }
1000
1001 note("Trying medium \"%s\" %d", ip->client->medium->string,
1002 increase);
1003 /* XXX Support other media types eventually */
1004 }
1005
1006 /*
1007 * If we're supposed to increase the interval, do so. If it's
1008 * currently zero (i.e., we haven't sent any packets yet), set
1009 * it to one; otherwise, add to it a random number between zero
1010 * and two times itself. On average, this means that it will
1011 * double with every transmission.
1012 */
1013 if (increase) {
1014 if (!ip->client->interval)
1015 ip->client->interval =
1016 ip->client->config->initial_interval;
1017 else {
1018 ip->client->interval += (rand() >> 2) %
1019 (2 * ip->client->interval);
1020 }
1021
1022 /* Don't backoff past cutoff. */
1023 if (ip->client->interval >
1024 ip->client->config->backoff_cutoff)
1025 ip->client->interval =
1026 ((ip->client->config->backoff_cutoff / 2)
1027 + ((rand() >> 2) %
1028 ip->client->config->backoff_cutoff));
1029 } else if (!ip->client->interval)
1030 ip->client->interval =
1031 ip->client->config->initial_interval;
1032
1033 /* If the backoff would take us to the panic timeout, just use that
1034 as the interval. */
1035 if (cur_time + ip->client->interval >
1036 ip->client->first_sending + ip->client->config->timeout)
1037 ip->client->interval =
1038 (ip->client->first_sending +
1039 ip->client->config->timeout) - cur_time + 1;
1040
1041 /* Record the number of seconds since we started sending. */
1042 if (interval < 65536)
1043 ip->client->packet.secs = htons(interval);
1044 else
1045 ip->client->packet.secs = htons(65535);
1046 ip->client->secs = ip->client->packet.secs;
1047
1048 note("DHCPDISCOVER on %s to %s port %d interval %ld",
1049 ip->name, inet_ntoa(sockaddr_broadcast.sin_addr),
1050 ntohs(sockaddr_broadcast.sin_port), (long int)ip->client->interval);
1051
1052 /* Send out a packet. */
1053 (void)send_packet(ip, &ip->client->packet, ip->client->packet_length,
1054 inaddr_any, &sockaddr_broadcast, NULL);
1055
1056 DH_DbgPrint(MID_TRACE,("discover timeout: now %x -> then %x\n",
1057 cur_time, cur_time + ip->client->interval));
1058
1059 add_timeout(cur_time + ip->client->interval, send_discover, ip);
1060 }
1061
1062 /*
1063 * state_panic gets called if we haven't received any offers in a preset
1064 * amount of time. When this happens, we try to use existing leases
1065 * that haven't yet expired, and failing that, we call the client script
1066 * and hope it can do something.
1067 */
1068 void
1069 state_panic(void *ipp)
1070 {
1071 struct interface_info *ip = ipp;
1072 PDHCP_ADAPTER Adapter = AdapterFindInfo(ip);
1073
1074 note("No DHCPOFFERS received.");
1075
1076 if (!Adapter->NteContext)
1077 {
1078 /* Generate an automatic private address */
1079 DbgPrint("DHCPCSVC: Failed to receive a response from a DHCP server. An automatic private address will be assigned.\n");
1080
1081 /* FIXME: The address generation code sucks */
1082 AddIPAddress(htonl(0xA9FE0000 | (rand() % 0xFFFF)), //169.254.X.X
1083 htonl(0xFFFF0000), //255.255.0.0
1084 Adapter->IfMib.dwIndex,
1085 &Adapter->NteContext,
1086 &Adapter->NteInstance);
1087 }
1088 }
1089
1090 void
1091 send_request(void *ipp)
1092 {
1093 struct interface_info *ip = ipp;
1094 struct sockaddr_in destination;
1095 struct in_addr from;
1096 int interval;
1097 time_t cur_time;
1098
1099 time(&cur_time);
1100
1101 /* Figure out how long it's been since we started transmitting. */
1102 interval = cur_time - ip->client->first_sending;
1103
1104 /* If we're in the INIT-REBOOT or REQUESTING state and we're
1105 past the reboot timeout, go to INIT and see if we can
1106 DISCOVER an address... */
1107 /* XXX In the INIT-REBOOT state, if we don't get an ACK, it
1108 means either that we're on a network with no DHCP server,
1109 or that our server is down. In the latter case, assuming
1110 that there is a backup DHCP server, DHCPDISCOVER will get
1111 us a new address, but we could also have successfully
1112 reused our old address. In the former case, we're hosed
1113 anyway. This is not a win-prone situation. */
1114 if ((ip->client->state == S_REBOOTING ||
1115 ip->client->state == S_REQUESTING) &&
1116 interval > ip->client->config->reboot_timeout) {
1117 ip->client->state = S_INIT;
1118 cancel_timeout(send_request, ip);
1119 state_init(ip);
1120 return;
1121 }
1122
1123 /* If we're in the reboot state, make sure the media is set up
1124 correctly. */
1125 if (ip->client->state == S_REBOOTING &&
1126 !ip->client->medium &&
1127 ip->client->active->medium ) {
1128 /* If the medium we chose won't fly, go to INIT state. */
1129 /* XXX Nothing for now */
1130
1131 /* Record the medium. */
1132 ip->client->medium = ip->client->active->medium;
1133 }
1134
1135 /* If the lease has expired, relinquish the address and go back
1136 to the INIT state. */
1137 if (ip->client->state != S_REQUESTING &&
1138 cur_time > ip->client->active->expiry) {
1139 PDHCP_ADAPTER Adapter = AdapterFindInfo( ip );
1140 /* Run the client script with the new parameters. */
1141 /* No script actions necessary in the expiry case */
1142 /* Now do a preinit on the interface so that we can
1143 discover a new address. */
1144
1145 if( Adapter )
1146 {
1147 DeleteIPAddress( Adapter->NteContext );
1148 Adapter->NteContext = 0;
1149 }
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(struct interface_info *ifi)
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", 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(ip);
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", path_dhclient_db);
1556 return;
1557 }
1558 }
1559
1560 fprintf(leaseFile, "lease {\n");
1561 if (lease->is_bootp)
1562 fprintf(leaseFile, " bootp;\n");
1563 fprintf(leaseFile, " interface \"%s\";\n", ip->name);
1564 fprintf(leaseFile, " fixed-address %s;\n", piaddr(lease->address));
1565 if (lease->filename)
1566 fprintf(leaseFile, " filename \"%s\";\n", lease->filename);
1567 if (lease->server_name)
1568 fprintf(leaseFile, " server-name \"%s\";\n",
1569 lease->server_name);
1570 if (lease->medium)
1571 fprintf(leaseFile, " medium \"%s\";\n", lease->medium->string);
1572 for (i = 0; i < 256; i++)
1573 if (lease->options[i].len)
1574 fprintf(leaseFile, " option %s %s;\n",
1575 dhcp_options[i].name,
1576 pretty_print_option(i, lease->options[i].data,
1577 lease->options[i].len, 1, 1));
1578
1579 t = gmtime(&lease->renewal);
1580 if (t)
1581 fprintf(leaseFile, " renew %d %d/%d/%d %02d:%02d:%02d;\n",
1582 t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
1583 t->tm_hour, t->tm_min, t->tm_sec);
1584 t = gmtime(&lease->rebind);
1585 if (t)
1586 fprintf(leaseFile, " rebind %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 t = gmtime(&lease->expiry);
1590 if (t)
1591 fprintf(leaseFile, " expire %d %d/%d/%d %02d:%02d:%02d;\n",
1592 t->tm_wday, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
1593 t->tm_hour, t->tm_min, t->tm_sec);
1594 fprintf(leaseFile, "}\n");
1595 fflush(leaseFile);
1596 }
1597
1598 void
1599 priv_script_init(struct interface_info *ip, char *reason, char *medium)
1600 {
1601 if (ip) {
1602 // XXX Do we need to do anything?
1603 }
1604 }
1605
1606 void
1607 priv_script_write_params(struct interface_info *ip, char *prefix, struct client_lease *lease)
1608 {
1609 u_int8_t dbuf[1500];
1610 int i, len = 0;
1611
1612 #if 0
1613 script_set_env(ip->client, prefix, "ip_address",
1614 piaddr(lease->address));
1615 #endif
1616
1617 if (lease->options[DHO_SUBNET_MASK].len &&
1618 (lease->options[DHO_SUBNET_MASK].len <
1619 sizeof(lease->address.iabuf))) {
1620 struct iaddr netmask, subnet, broadcast;
1621
1622 memcpy(netmask.iabuf, lease->options[DHO_SUBNET_MASK].data,
1623 lease->options[DHO_SUBNET_MASK].len);
1624 netmask.len = lease->options[DHO_SUBNET_MASK].len;
1625
1626 subnet = subnet_number(lease->address, netmask);
1627 if (subnet.len) {
1628 #if 0
1629 script_set_env(ip->client, prefix, "network_number",
1630 piaddr(subnet));
1631 #endif
1632 if (!lease->options[DHO_BROADCAST_ADDRESS].len) {
1633 broadcast = broadcast_addr(subnet, netmask);
1634 if (broadcast.len)
1635 #if 0
1636 script_set_env(ip->client, prefix,
1637 "broadcast_address",
1638 piaddr(broadcast));
1639 #else
1640 ;
1641 #endif
1642 }
1643 }
1644 }
1645
1646 #if 0
1647 if (lease->filename)
1648 script_set_env(ip->client, prefix, "filename", lease->filename);
1649 if (lease->server_name)
1650 script_set_env(ip->client, prefix, "server_name",
1651 lease->server_name);
1652 #endif
1653
1654 for (i = 0; i < 256; i++) {
1655 u_int8_t *dp = NULL;
1656
1657 if (ip->client->config->defaults[i].len) {
1658 if (lease->options[i].len) {
1659 switch (
1660 ip->client->config->default_actions[i]) {
1661 case ACTION_DEFAULT:
1662 dp = lease->options[i].data;
1663 len = lease->options[i].len;
1664 break;
1665 case ACTION_SUPERSEDE:
1666 supersede:
1667 dp = ip->client->
1668 config->defaults[i].data;
1669 len = ip->client->
1670 config->defaults[i].len;
1671 break;
1672 case ACTION_PREPEND:
1673 len = ip->client->
1674 config->defaults[i].len +
1675 lease->options[i].len;
1676 if (len >= sizeof(dbuf)) {
1677 warning("no space to %s %s",
1678 "prepend option",
1679 dhcp_options[i].name);
1680 goto supersede;
1681 }
1682 dp = dbuf;
1683 memcpy(dp,
1684 ip->client->
1685 config->defaults[i].data,
1686 ip->client->
1687 config->defaults[i].len);
1688 memcpy(dp + ip->client->
1689 config->defaults[i].len,
1690 lease->options[i].data,
1691 lease->options[i].len);
1692 dp[len] = '\0';
1693 break;
1694 case ACTION_APPEND:
1695 len = ip->client->
1696 config->defaults[i].len +
1697 lease->options[i].len + 1;
1698 if (len > sizeof(dbuf)) {
1699 warning("no space to %s %s",
1700 "append option",
1701 dhcp_options[i].name);
1702 goto supersede;
1703 }
1704 dp = dbuf;
1705 memcpy(dp,
1706 lease->options[i].data,
1707 lease->options[i].len);
1708 memcpy(dp + lease->options[i].len,
1709 ip->client->
1710 config->defaults[i].data,
1711 ip->client->
1712 config->defaults[i].len);
1713 dp[len-1] = '\0';
1714 }
1715 } else {
1716 dp = ip->client->
1717 config->defaults[i].data;
1718 len = ip->client->
1719 config->defaults[i].len;
1720 }
1721 } else if (lease->options[i].len) {
1722 len = lease->options[i].len;
1723 dp = lease->options[i].data;
1724 } else {
1725 len = 0;
1726 }
1727 #if 0
1728 if (len) {
1729 char name[256];
1730
1731 if (dhcp_option_ev_name(name, sizeof(name),
1732 &dhcp_options[i]))
1733 script_set_env(ip->client, prefix, name,
1734 pretty_print_option(i, dp, len, 0, 0));
1735 }
1736 #endif
1737 }
1738 #if 0
1739 snprintf(tbuf, sizeof(tbuf), "%d", (int)lease->expiry);
1740 script_set_env(ip->client, prefix, "expiry", tbuf);
1741 #endif
1742 }
1743
1744 int
1745 dhcp_option_ev_name(char *buf, size_t buflen, struct dhcp_option *option)
1746 {
1747 int i;
1748
1749 for (i = 0; option->name[i]; i++) {
1750 if (i + 1 == buflen)
1751 return 0;
1752 if (option->name[i] == '-')
1753 buf[i] = '_';
1754 else
1755 buf[i] = option->name[i];
1756 }
1757
1758 buf[i] = 0;
1759 return 1;
1760 }
1761
1762 #if 0
1763 void
1764 go_daemon(void)
1765 {
1766 static int state = 0;
1767
1768 if (no_daemon || state)
1769 return;
1770
1771 state = 1;
1772
1773 /* Stop logging to stderr... */
1774 log_perror = 0;
1775
1776 if (daemon(1, 0) == -1)
1777 error("daemon");
1778
1779 /* we are chrooted, daemon(3) fails to open /dev/null */
1780 if (nullfd != -1) {
1781 dup2(nullfd, STDIN_FILENO);
1782 dup2(nullfd, STDOUT_FILENO);
1783 dup2(nullfd, STDERR_FILENO);
1784 close(nullfd);
1785 nullfd = -1;
1786 }
1787 }
1788 #endif
1789
1790 int
1791 check_option(struct client_lease *l, int option)
1792 {
1793 char *opbuf;
1794 char *sbuf;
1795
1796 /* we use this, since this is what gets passed to dhclient-script */
1797
1798 opbuf = pretty_print_option(option, l->options[option].data,
1799 l->options[option].len, 0, 0);
1800
1801 sbuf = option_as_string(option, l->options[option].data,
1802 l->options[option].len);
1803
1804 switch (option) {
1805 case DHO_SUBNET_MASK:
1806 case DHO_TIME_SERVERS:
1807 case DHO_NAME_SERVERS:
1808 case DHO_ROUTERS:
1809 case DHO_DOMAIN_NAME_SERVERS:
1810 case DHO_LOG_SERVERS:
1811 case DHO_COOKIE_SERVERS:
1812 case DHO_LPR_SERVERS:
1813 case DHO_IMPRESS_SERVERS:
1814 case DHO_RESOURCE_LOCATION_SERVERS:
1815 case DHO_SWAP_SERVER:
1816 case DHO_BROADCAST_ADDRESS:
1817 case DHO_NIS_SERVERS:
1818 case DHO_NTP_SERVERS:
1819 case DHO_NETBIOS_NAME_SERVERS:
1820 case DHO_NETBIOS_DD_SERVER:
1821 case DHO_FONT_SERVERS:
1822 case DHO_DHCP_SERVER_IDENTIFIER:
1823 if (!ipv4addrs(opbuf)) {
1824 warning("Invalid IP address in option(%d): %s", option, opbuf);
1825 return (0);
1826 }
1827 return (1) ;
1828 case DHO_HOST_NAME:
1829 case DHO_DOMAIN_NAME:
1830 case DHO_NIS_DOMAIN:
1831 if (!res_hnok(sbuf))
1832 warning("Bogus Host Name option %d: %s (%s)", option,
1833 sbuf, opbuf);
1834 return (1);
1835 case DHO_PAD:
1836 case DHO_TIME_OFFSET:
1837 case DHO_BOOT_SIZE:
1838 case DHO_MERIT_DUMP:
1839 case DHO_ROOT_PATH:
1840 case DHO_EXTENSIONS_PATH:
1841 case DHO_IP_FORWARDING:
1842 case DHO_NON_LOCAL_SOURCE_ROUTING:
1843 case DHO_POLICY_FILTER:
1844 case DHO_MAX_DGRAM_REASSEMBLY:
1845 case DHO_DEFAULT_IP_TTL:
1846 case DHO_PATH_MTU_AGING_TIMEOUT:
1847 case DHO_PATH_MTU_PLATEAU_TABLE:
1848 case DHO_INTERFACE_MTU:
1849 case DHO_ALL_SUBNETS_LOCAL:
1850 case DHO_PERFORM_MASK_DISCOVERY:
1851 case DHO_MASK_SUPPLIER:
1852 case DHO_ROUTER_DISCOVERY:
1853 case DHO_ROUTER_SOLICITATION_ADDRESS:
1854 case DHO_STATIC_ROUTES:
1855 case DHO_TRAILER_ENCAPSULATION:
1856 case DHO_ARP_CACHE_TIMEOUT:
1857 case DHO_IEEE802_3_ENCAPSULATION:
1858 case DHO_DEFAULT_TCP_TTL:
1859 case DHO_TCP_KEEPALIVE_INTERVAL:
1860 case DHO_TCP_KEEPALIVE_GARBAGE:
1861 case DHO_VENDOR_ENCAPSULATED_OPTIONS:
1862 case DHO_NETBIOS_NODE_TYPE:
1863 case DHO_NETBIOS_SCOPE:
1864 case DHO_X_DISPLAY_MANAGER:
1865 case DHO_DHCP_REQUESTED_ADDRESS:
1866 case DHO_DHCP_LEASE_TIME:
1867 case DHO_DHCP_OPTION_OVERLOAD:
1868 case DHO_DHCP_MESSAGE_TYPE:
1869 case DHO_DHCP_PARAMETER_REQUEST_LIST:
1870 case DHO_DHCP_MESSAGE:
1871 case DHO_DHCP_MAX_MESSAGE_SIZE:
1872 case DHO_DHCP_RENEWAL_TIME:
1873 case DHO_DHCP_REBINDING_TIME:
1874 case DHO_DHCP_CLASS_IDENTIFIER:
1875 case DHO_DHCP_CLIENT_IDENTIFIER:
1876 case DHO_DHCP_USER_CLASS_ID:
1877 case DHO_END:
1878 return (1);
1879 default:
1880 warning("unknown dhcp option value 0x%x", option);
1881 return (unknown_ok);
1882 }
1883 }
1884
1885 int
1886 res_hnok(const char *dn)
1887 {
1888 int pch = PERIOD, ch = *dn++;
1889
1890 while (ch != '\0') {
1891 int nch = *dn++;
1892
1893 if (periodchar(ch)) {
1894 ;
1895 } else if (periodchar(pch)) {
1896 if (!borderchar(ch))
1897 return (0);
1898 } else if (periodchar(nch) || nch == '\0') {
1899 if (!borderchar(ch))
1900 return (0);
1901 } else {
1902 if (!middlechar(ch))
1903 return (0);
1904 }
1905 pch = ch, ch = nch;
1906 }
1907 return (1);
1908 }
1909
1910 /* Does buf consist only of dotted decimal ipv4 addrs?
1911 * return how many if so,
1912 * otherwise, return 0
1913 */
1914 int
1915 ipv4addrs(char * buf)
1916 {
1917 char *tmp;
1918 struct in_addr jnk;
1919 int i = 0;
1920
1921 note("Input: %s", buf);
1922
1923 do {
1924 tmp = strtok(buf, " ");
1925 note("got %s", tmp);
1926 if( tmp && inet_aton(tmp, &jnk) ) i++;
1927 buf = NULL;
1928 } while( tmp );
1929
1930 return (i);
1931 }
1932
1933
1934 char *
1935 option_as_string(unsigned int code, unsigned char *data, int len)
1936 {
1937 static char optbuf[32768]; /* XXX */
1938 char *op = optbuf;
1939 int opleft = sizeof(optbuf);
1940 unsigned char *dp = data;
1941
1942 if (code > 255)
1943 error("option_as_string: bad code %d", code);
1944
1945 for (; dp < data + len; dp++) {
1946 if (!isascii(*dp) || !isprint(*dp)) {
1947 if (dp + 1 != data + len || *dp != 0) {
1948 _snprintf(op, opleft, "\\%03o", *dp);
1949 op += 4;
1950 opleft -= 4;
1951 }
1952 } else if (*dp == '"' || *dp == '\'' || *dp == '$' ||
1953 *dp == '`' || *dp == '\\') {
1954 *op++ = '\\';
1955 *op++ = *dp;
1956 opleft -= 2;
1957 } else {
1958 *op++ = *dp;
1959 opleft--;
1960 }
1961 }
1962 if (opleft < 1)
1963 goto toobig;
1964 *op = 0;
1965 return optbuf;
1966 toobig:
1967 warning("dhcp option too large");
1968 return "<error>";
1969 }
1970