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