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); 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); 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); 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 1971