1/* 2 * Copyright (c) 2002-2012 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* 25 * BSDPClient.c 26 * - BSDP client library functions 27 */ 28 29/* 30 * Modification History 31 * 32 * February 25, 2002 Dieter Siegmund (dieter@apple.com) 33 * - initial revision 34 */ 35 36#include <stdlib.h> 37#include <unistd.h> 38#include <string.h> 39#include <stdio.h> 40#include <stdarg.h> 41#include <sys/types.h> 42#include <sys/errno.h> 43#include <sys/socket.h> 44#include <sys/ioctl.h> 45#include <sys/fcntl.h> 46#include <ctype.h> 47#include <net/if.h> 48#include <net/ethernet.h> 49#include <netinet/in.h> 50#include <netinet/udp.h> 51#include <netinet/in_systm.h> 52#include <netinet/ip.h> 53#include <netinet/bootp.h> 54#include <arpa/inet.h> 55 56#include <CoreFoundation/CFDictionary.h> 57#include <CoreFoundation/CFString.h> 58#include <CoreFoundation/CFNumber.h> 59#include <CoreFoundation/CFRunLoop.h> 60#include <CoreFoundation/CFSocket.h> 61#include <SystemConfiguration/SystemConfiguration.h> 62#include <SystemConfiguration/SCValidation.h> 63#include <SystemConfiguration/SCPrivate.h> 64 65#include "ioregpath.h" 66#include "BSDPClient.h" 67#include "BSDPClientPrivate.h" 68#include "rfc_options.h" 69#include "dhcp_options.h" 70#include "bsdp.h" 71#include "util.h" 72#include "cfutil.h" 73#include "dhcplib.h" 74#include "interfaces.h" 75#include "bootp_transmit.h" 76 77#define BSDPCLIENT_MAX_WAIT_SECS 16 78#define BSDPCLIENT_LIST_MAX_TRIES 7 79#define BSDPCLIENT_SELECT_MAX_TRIES 2 80#define BSDPCLIENT_INITIAL_TIMEOUT_SECS 2 81 82#ifdef TEST_BAD_SYSID 83const uint8_t bad_sysid1[] = { 84 'A', 'A', 'P', 'L', 'B', 'S', 'D', 'P', 'C', 85 '/', 'p', 'p', 'c', 86 '/', '\n', 'b', 'a', 'd', '1' 87}; 88const uint8_t bad_sysid2[] = { 89 'A', 'A', 'P', 'L', 'B', 'S', 'D', 'P', 'C', 90 '/', 'p', 'p', 'c', 'n', 91 '/', '\0', 'b', 'a', 'd', '2' 92}; 93const uint8_t bad_sysid3[] = { 94 'A', 'A', 'P', 'L', 'B', 'S', 'D', 'P', 'C', 95 '/', 'p', '\0', 'c', 'a', 'b', 96 '/', 'b', 'a', 'd', '3' 97}; 98const uint8_t bad_sysid4[] = { 99 'A', 'A', 'P', 'L', 'B', 'S', 'D', 'P', 'C', 100 '/', 'p', '\n', 'c', 101 '/', 'b', 'a', 'd', 's', 'y', 's', '4' 102}; 103 104#define BAD_SYSID_COUNT 4 105struct { 106 const uint8_t * sysid; 107 int size; 108} bad_sysids[BAD_SYSID_COUNT] = { 109 { bad_sysid1, sizeof(bad_sysid1) }, 110 { bad_sysid2, sizeof(bad_sysid2) }, 111 { bad_sysid3, sizeof(bad_sysid3) }, 112 { bad_sysid4, sizeof(bad_sysid4) }, 113}; 114#endif /* TEST_BAD_SYSID */ 115static const unsigned char rfc_magic[4] = RFC_OPTIONS_MAGIC; 116 117static const u_char dhcp_params[] = { 118 dhcptag_vendor_class_identifier_e, 119 dhcptag_vendor_specific_e, 120}; 121#define N_DHCP_PARAMS (sizeof(dhcp_params) / sizeof(dhcp_params[0])) 122 123#define NetBoot2InfoVersion 0x33000 124typedef enum { 125 kNetBootVersionNone = 0, 126 kNetBootVersion1 = 1, 127 kNetBootVersion2 = 2 128} NetBootVersion; 129 130typedef enum { 131 kBSDPClientStateInit = 0, 132 kBSDPClientStateList = 1, 133 kBSDPClientStateSelect = 2, 134} BSDPClientState; 135 136typedef void (*BSDPClientTimerCallBack)(BSDPClientRef client); 137 138typedef union { 139 BSDPClientListCallBack list; 140 BSDPClientSelectCallBack select; 141} BSDPClientCallBackUnion; 142 143#define MAX_ATTRS 10 144 145struct BSDPClient_s { 146 char * system_id; 147 bsdp_version_t client_version; /* network order */ 148 boolean_t old_firmware; 149 int fd; 150 u_short client_port; 151 CFSocketRef socket; 152 CFRunLoopSourceRef rls; 153 interface_t * if_p; 154 u_int32_t xid; 155 /* 156 * send_buf is cast to some struct types containing short fields; 157 * force it to be aligned as much as an int 158 */ 159 int send_buf[512]; 160 BSDPClientState state; 161 CFRunLoopTimerRef timer; 162 BSDPClientTimerCallBack timer_callback; 163 int try; 164 int wait_secs; 165 u_int16_t attrs[MAX_ATTRS]; 166 int n_attrs; 167 struct in_addr our_ip; 168 boolean_t got_responses; 169 170 /* values provided by caller */ 171 struct { 172 BSDPClientCallBackUnion func; 173 void * arg; 174 struct in_addr server_ip; 175 bsdp_image_id_t image_identifier; 176 } callback; 177 178}; 179 180/* 181 * Function: mySCNetworkServicePathCopyServiceID 182 * Purpose: 183 * Take a path of the form: 184 * "<domain>:/Network/Service/<serviceID>[/<entity>]"; 185 * and return the <serviceID> portion of the string. 186 */ 187static CFStringRef 188mySCNetworkServicePathCopyServiceID(CFStringRef path) 189{ 190 CFArrayRef arr; 191 CFStringRef serviceID = NULL; 192 193 arr = CFStringCreateArrayBySeparatingStrings(NULL, path, CFSTR("/")); 194 if (arr == NULL) { 195 goto done; 196 } 197 /* 198 * arr = {"<domain:>","Network","Service","<serviceID>"[,"<entity>"]} 199 * and we want the 4th component (arr[3]). 200 */ 201 if (CFArrayGetCount(arr) < 4) { 202 goto done; 203 } 204 serviceID = CFRetain(CFArrayGetValueAtIndex(arr, 3)); 205 done: 206 if (arr != NULL) { 207 CFRelease(arr); 208 } 209 return (serviceID); 210 211} 212 213static Boolean 214get_dhcp_address(const char * ifname, struct in_addr * ret_ip) 215{ 216 CFStringRef ifname_cf = NULL; 217 int count; 218 CFDictionaryRef info_dict = NULL; 219 int i; 220 const void * * keys = NULL; 221 CFStringRef pattern; 222 CFMutableArrayRef patterns; 223 Boolean ret = FALSE; 224 SCDynamicStoreRef store; 225 const void * * values = NULL; 226 227 store = SCDynamicStoreCreate(NULL, CFSTR("get_dhcp_address"), 228 NULL, NULL); 229 if (store == NULL) { 230 goto done; 231 } 232 ifname_cf = CFStringCreateWithCString(NULL, ifname, kCFStringEncodingASCII); 233 234 /* pattern State:/Network/Service/[^/]+/DHCP */ 235 pattern 236 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 237 kSCDynamicStoreDomainState, 238 kSCCompAnyRegex, 239 kSCEntNetDHCP); 240 patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 241 CFArrayAppendValue(patterns, pattern); 242 CFRelease(pattern); 243 pattern 244 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 245 kSCDynamicStoreDomainState, 246 kSCCompAnyRegex, 247 kSCEntNetIPv4); 248 CFArrayAppendValue(patterns, pattern); 249 CFRelease(pattern); 250 251 info_dict = SCDynamicStoreCopyMultiple(store, NULL, patterns); 252 CFRelease(patterns); 253 if (isA_CFDictionary(info_dict) == NULL) { 254 goto done; 255 } 256 count = CFDictionaryGetCount(info_dict); 257 values = malloc(sizeof(void *) * count); 258 keys = malloc(sizeof(void *) * count); 259 CFDictionaryGetKeysAndValues(info_dict, keys, values); 260 for (i = 0; i < count; i++) { 261 CFArrayRef addrs; 262 CFDictionaryRef ipv4_dict; 263 Boolean got_match; 264 CFStringRef key; 265 CFStringRef name; 266 CFStringRef serviceID; 267 268 if (CFStringHasSuffix(keys[i], kSCEntNetIPv4) == FALSE) { 269 continue; 270 } 271 ipv4_dict = isA_CFDictionary(values[i]); 272 if (ipv4_dict == NULL) { 273 continue; 274 } 275 name = CFDictionaryGetValue(ipv4_dict, kSCPropInterfaceName); 276 if (name == NULL) { 277 continue; 278 } 279 if (CFEqual(name, ifname_cf) == FALSE) { 280 continue; 281 } 282 /* look for the DHCP entity for this service ID */ 283 serviceID = mySCNetworkServicePathCopyServiceID(keys[i]); 284 if (serviceID == NULL) { 285 continue; 286 } 287 key 288 = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, 289 kSCDynamicStoreDomainState, 290 serviceID, 291 kSCEntNetDHCP); 292 CFRelease(serviceID); 293 got_match = CFDictionaryContainsKey(info_dict, key); 294 CFRelease(key); 295 if (got_match == FALSE) { 296 continue; 297 } 298 /* grab the IP address for this IPv4 dict */ 299 addrs = CFDictionaryGetValue(ipv4_dict, kSCPropNetIPv4Addresses); 300 if (isA_CFArray(addrs) != NULL && CFArrayGetCount(addrs) > 0 301 && my_CFStringToIPAddress(CFArrayGetValueAtIndex(addrs, 0), 302 ret_ip)) { 303 ret = TRUE; 304 } 305 break; 306 } 307 308 done: 309 if (ifname_cf != NULL) { 310 CFRelease(ifname_cf); 311 } 312 if (values != NULL) { 313 free(values); 314 } 315 if (keys != NULL) { 316 free(keys); 317 } 318 if (info_dict != NULL) { 319 CFRelease(info_dict); 320 } 321 if (store != NULL) { 322 CFRelease(store); 323 } 324 return (ret); 325} 326 327static void 328BSDPClientProcessList(BSDPClientRef client, struct in_addr server_ip, 329 struct dhcp * reply, int reply_len, 330 dhcpol_t * options_p, dhcpol_t * bsdp_options_p); 331static void 332BSDPClientProcessSelect(BSDPClientRef client, bsdp_msgtype_t bsdp_msg); 333 334void 335my_log(int priority, const char *message, ...) 336{ 337 va_list ap; 338 339 va_start(ap, message); 340 vfprintf(stderr, message, ap); 341 return; 342} 343 344static char * SystemIdentifierCopy(void); 345 346#if defined(__ppc__) || defined(__ppc64__) 347#define BSDP_ARCHITECTURE "ppc" 348static NetBootVersion 349NetBootVersionGet() 350{ 351 CFDictionaryRef properties = NULL; 352 CFDataRef info = NULL; 353 u_int32_t version; 354 NetBootVersion support = kNetBootVersionNone; 355 356 properties = myIORegistryEntryCopyValue("IODeviceTree:/rom/boot-rom"); 357 if (properties != NULL) { 358 info = CFDictionaryGetValue(properties, CFSTR("info")); 359 } 360 if (info == NULL) { 361 goto done; 362 } 363 CFDataGetBytes(info, CFRangeMake(8, sizeof(version)), (void *)&version); 364 if (ntohl(version) < NetBoot2InfoVersion) { 365 support = kNetBootVersion1; 366 } 367 else { 368 support = kNetBootVersion2; 369 } 370 done: 371 my_CFRelease(&properties); 372 return (support); 373} 374 375static BSDPClientStatus 376CopyNetBootVersionAndSystemIdentifier(NetBootVersion * version_p, 377 char * * system_id_p) 378{ 379 *system_id_p = NULL; 380 *version_p = NetBootVersionGet(); 381 if (*version_p == kNetBootVersionNone) { 382 return (kBSDPClientStatusUnsupportedFirmware); 383 } 384 *system_id_p = SystemIdentifierCopy(); 385 if (*system_id_p == NULL) { 386 return (kBSDPClientStatusAllocationError); 387 } 388 return (kBSDPClientStatusOK); 389} 390 391#elif defined(__i386__) || defined(__x86_64__) 392#define BSDP_ARCHITECTURE "i386" 393 394static BSDPClientStatus 395CopyNetBootVersionAndSystemIdentifier(NetBootVersion * version_p, 396 char * * system_id_p) 397{ 398 *system_id_p = SystemIdentifierCopy(); 399 if (*system_id_p == NULL) { 400 return (kBSDPClientStatusAllocationError); 401 } 402 *version_p = kNetBootVersion2; 403 return (kBSDPClientStatusOK); 404} 405 406#else 407 408static BSDPClientStatus 409CopyNetBootVersionAndSystemIdentifier(NetBootVersion * version_p, 410 char * * system_id_p) 411{ 412 return (kBSDPClientStatusUnsupportedFirmware); 413} 414 415#endif 416 417static char * 418SystemIdentifierCopy() 419{ 420 CFDictionaryRef properties = NULL; 421 char * system_id = NULL; 422 CFDataRef system_id_data = NULL; 423 int system_id_len = 0; 424 425 properties = myIORegistryEntryCopyValue("IODeviceTree:/"); 426 if (properties != NULL) { 427 system_id_data = CFDictionaryGetValue(properties, CFSTR("model")); 428 if (system_id_data) { 429 system_id_len = CFDataGetLength(system_id_data); 430 } 431 } 432 if (system_id_len == 0) { 433 goto done; 434 } 435 system_id = (char *)malloc(system_id_len + 1); 436 if (system_id == NULL) { 437 goto done; 438 } 439 CFDataGetBytes(system_id_data, CFRangeMake(0, system_id_len), 440 (UInt8 *)system_id); 441 system_id[system_id_len] = '\0'; 442 my_CFRelease(&properties); 443 return (system_id); 444 445 done: 446 my_CFRelease(&properties); 447 return (NULL); 448} 449 450#define RX_BUF_SIZE (8 * 1024) 451 452static struct dhcp * 453make_bsdp_request(char * system_id, struct dhcp * request, int pkt_size, 454 dhcp_msgtype_t msg, u_char * hwaddr, u_char hwtype, 455 u_char hwlen, dhcpoa_t * options_p) 456{ 457 char vendor_class_id[DHCP_OPTION_SIZE_MAX]; 458 uint16_t max_dhcp_message_size = htons(ETHERMTU); 459 460 bzero(request, pkt_size); 461 request->dp_op = BOOTREQUEST; 462 request->dp_htype = hwtype; 463 request->dp_hlen = hwlen; 464 bcopy(hwaddr, request->dp_chaddr, hwlen); 465 bcopy(rfc_magic, request->dp_options, sizeof(rfc_magic)); 466 dhcpoa_init(options_p, request->dp_options + sizeof(rfc_magic), 467 pkt_size - sizeof(struct dhcp) - sizeof(rfc_magic)); 468 469 /* make the request a dhcp message */ 470 if (dhcpoa_add_dhcpmsg(options_p, msg) != dhcpoa_success_e) { 471 fprintf(stderr, 472 "make_bsdp_request: couldn't add dhcp message tag %d, %s", msg, 473 dhcpoa_err(options_p)); 474 goto err; 475 } 476 477 /* add the list of required parameters */ 478 if (dhcpoa_add(options_p, dhcptag_parameter_request_list_e, 479 N_DHCP_PARAMS, dhcp_params) 480 != dhcpoa_success_e) { 481 fprintf(stderr, "make_bsdp_request: " 482 "couldn't add parameter request list, %s", 483 dhcpoa_err(options_p)); 484 goto err; 485 } 486 /* add the max message size */ 487 if (dhcpoa_add(options_p, dhcptag_max_dhcp_message_size_e, 488 sizeof(max_dhcp_message_size), &max_dhcp_message_size) 489 != dhcpoa_success_e) { 490 fprintf(stderr, "make_bsdp_request: " 491 "couldn't add max message size, %s", 492 dhcpoa_err(options_p)); 493 goto err; 494 } 495#ifndef TEST_BAD_SYSID 496 /* add our vendor class identifier */ 497 snprintf(vendor_class_id, sizeof(vendor_class_id), 498 BSDP_VENDOR_CLASS_ID "/" BSDP_ARCHITECTURE "/%s", system_id); 499 if (dhcpoa_add(options_p, 500 dhcptag_vendor_class_identifier_e, 501 strlen(vendor_class_id), vendor_class_id) 502 != dhcpoa_success_e) { 503 fprintf(stderr, "make_bsdp_request: add class id failed, %s", 504 dhcpoa_err(options_p)); 505 goto err; 506 } 507#else /* TEST_BAD_SYSID */ 508 { 509 static int bad_sysid_index; 510 511 if (dhcpoa_add(options_p, 512 dhcptag_vendor_class_identifier_e, 513 bad_sysids[bad_sysid_index].size, 514 bad_sysids[bad_sysid_index].sysid) 515 != dhcpoa_success_e) { 516 fprintf(stderr, "make_bsdp_request: add class id failed, %s", 517 dhcpoa_err(options_p)); 518 goto err; 519 } 520 bad_sysid_index++; 521 if (bad_sysid_index == BAD_SYSID_COUNT) { 522 bad_sysid_index = 0; 523 } 524 } 525#endif /* TEST_BAD_SYSID */ 526 return (request); 527 528 err: 529 return (NULL); 530} 531 532static int 533S_open_socket(u_short * ret_port) 534{ 535 u_short client_port; 536 struct sockaddr_in me; 537 socklen_t me_len; 538 int opt; 539 int sockfd; 540 int status; 541 542 sockfd = socket(AF_INET, SOCK_DGRAM, 0); 543 if (sockfd < 0) { 544 perror("socket"); 545 goto failed; 546 } 547 548 bzero((char *)&me, sizeof(me)); 549 me.sin_family = AF_INET; 550 551 /* get a privileged port */ 552 opt = IP_PORTRANGE_LOW; 553 status = setsockopt(sockfd, IPPROTO_IP, IP_PORTRANGE, &opt, 554 sizeof(opt)); 555 556 if (status < 0) { 557 perror("setsockopt IPPROTO_IP IP_PORTRANGE"); 558 goto failed; 559 } 560 561#ifdef SO_TC_CTL 562 opt = SO_TC_CTL; 563 /* set traffic class, we don't care if it failed. */ 564 (void)setsockopt(sockfd, SOL_SOCKET, SO_TRAFFIC_CLASS, &opt, 565 sizeof(opt)); 566#endif /* SO_TC_CTL */ 567 568 status = bind(sockfd, (struct sockaddr *)&me, sizeof(me)); 569 if (status != 0) { 570 perror("bind"); 571 goto failed; 572 } 573 me_len = sizeof(me); 574 if (getsockname(sockfd, (struct sockaddr *)&me, &me_len) < 0) { 575 perror("getsockname"); 576 goto failed; 577 } 578 client_port = ntohs(me.sin_port); 579 opt = 1; 580 status = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &opt, 581 sizeof(opt)); 582 if (status < 0) { 583 perror("setsockopt SO_BROADCAST"); 584 goto failed; 585 } 586 opt = 1; 587 status = ioctl(sockfd, FIONBIO, &opt); 588 if (status < 0) { 589 perror("FIONBIO"); 590 goto failed; 591 } 592 *ret_port = client_port; 593 return sockfd; 594 595 failed: 596 if (sockfd >= 0) { 597 close(sockfd); 598 } 599 return (-1); 600} 601 602/* deprecated: BSDPImageDescriptionIndexIsServerLocal */ 603Boolean 604BSDPImageDescriptionIndexIsServerLocal(CFNumberRef index) 605{ 606 u_int16_t index_val = 1; 607 608 (void)CFNumberGetValue(index, kCFNumberShortType, &index_val); 609 return (bsdp_image_index_is_server_local(index_val)); 610} 611 612Boolean 613BSDPImageDescriptionIdentifierIsServerLocal(CFNumberRef identifier) 614{ 615 u_int32_t identifier_val = 1; 616 617 (void)CFNumberGetValue(identifier, kCFNumberSInt32Type, &identifier_val); 618 return (bsdp_image_identifier_is_server_local(identifier_val)); 619} 620 621Boolean 622BSDPImageDescriptionIdentifierIsInstall(CFNumberRef identifier) 623{ 624 u_int32_t identifier_val = 1; 625 626 (void)CFNumberGetValue(identifier, kCFNumberSInt32Type, &identifier_val); 627 return (bsdp_image_identifier_is_install(identifier_val)); 628} 629 630BSDPImageKind 631BSDPImageDescriptionIdentifierImageKind(CFNumberRef identifier) 632{ 633 u_int32_t identifier_val = 1; 634 635 (void)CFNumberGetValue(identifier, kCFNumberSInt32Type, &identifier_val); 636 return (bsdp_image_kind_from_attributes(bsdp_image_attributes(identifier_val))); 637} 638 639/** 640 ** BSDPClient timer functions 641 **/ 642 643static void 644BSDPClientProcessTimer(CFRunLoopTimerRef timer, void * info) 645{ 646 BSDPClientRef client; 647 648 client = (BSDPClientRef)info; 649 (*client->timer_callback)(client); 650 return; 651} 652 653static void 654BSDPClientCancelTimer(BSDPClientRef client) 655{ 656 if (client->timer) { 657 CFRunLoopTimerInvalidate(client->timer); 658 my_CFRelease(&client->timer); 659 } 660 client->timer_callback = NULL; 661 return; 662} 663 664static void 665BSDPClientSetTimer(BSDPClientRef client, struct timeval rel_time, 666 BSDPClientTimerCallBack callback) 667{ 668 CFRunLoopTimerContext context = { 0, NULL, NULL, NULL, NULL }; 669 CFAbsoluteTime wakeup_time; 670 671 BSDPClientCancelTimer(client); 672 client->timer_callback = callback; 673 wakeup_time = CFAbsoluteTimeGetCurrent() + rel_time.tv_sec 674 + ((double)rel_time.tv_usec / USECS_PER_SEC); 675 context.info = client; 676 client->timer 677 = CFRunLoopTimerCreate(NULL, wakeup_time, 678 0.0, 0, 0, 679 BSDPClientProcessTimer, 680 &context); 681 CFRunLoopAddTimer(CFRunLoopGetCurrent(), client->timer, 682 kCFRunLoopDefaultMode); 683 return; 684} 685 686/* 687 * Function: BSDPClientProcess 688 * Purpose: 689 * Process a packet received on our open socket. 690 * Ensure that the packet is a BSDP packet[DHCP ACK] that 691 * matches our currently outstanding request. 692 * 693 * Dispatch to the appropriate handler (list or select) 694 * depending on our current running state. 695 */ 696static void 697BSDPClientProcess(CFSocketRef s, CFSocketCallBackType type, 698 CFDataRef address, const void *data, void *info) 699{ 700 dhcpol_t bsdp_options; 701 bsdp_msgtype_t bsdp_msg; 702 BSDPClientRef client = (BSDPClientRef)info; 703 dhcpo_err_str_t err; 704 struct sockaddr_in from; 705 socklen_t fromlen; 706 int n; 707 void * opt; 708 int opt_len; 709 dhcpol_t options; 710 uint32_t receive_buf[RX_BUF_SIZE / sizeof(uint32_t)]; 711 struct dhcp * reply; 712 struct in_addr server_ip; 713 714 n = recvfrom(client->fd, receive_buf, 715 sizeof(receive_buf), 0, 716 (struct sockaddr *)&from, &fromlen); 717 if (n < 0) { 718 if (errno != EAGAIN) { 719 fprintf(stderr, "BSDPClientProcess: recvfrom %s", 720 strerror(errno)); 721 } 722 return; 723 } 724 if (n < sizeof(struct dhcp)) { 725 /* packet is too short */ 726 return; 727 } 728 729 switch (client->state) { 730 case kBSDPClientStateInit: 731 default: 732 /* throw it away */ 733 return; 734 case kBSDPClientStateList: 735 case kBSDPClientStateSelect: 736 break; 737 } 738 739 reply = (struct dhcp *)receive_buf; 740 if (dhcp_packet_match((struct bootp *)receive_buf, client->xid, 741 (u_char) if_link_arptype(client->if_p), 742 if_link_address(client->if_p), 743 if_link_length(client->if_p)) == FALSE 744 || reply->dp_ciaddr.s_addr != client->our_ip.s_addr) { 745 /* wasn't us */ 746 return; 747 } 748 749 dhcpol_init(&options); 750 dhcpol_init(&bsdp_options); 751 752 if (dhcpol_parse_packet(&options, reply, n, &err) == FALSE) { 753 fprintf(stderr, 754 "BSDPClientProcess: dhcpol_parse_packet failed, %s\n", 755 err.str); 756 goto done; 757 } 758 759 /* get the DHCP message type */ 760 opt = dhcpol_find(&options, dhcptag_dhcp_message_type_e, NULL, NULL); 761 if (opt == NULL || *((unsigned char *)opt) != dhcp_msgtype_ack_e) { 762 goto done; /* response must be a DHCP ack */ 763 } 764 765 /* get the vendor class identifier */ 766 opt = dhcpol_find(&options, dhcptag_vendor_class_identifier_e, 767 &opt_len, NULL); 768 if (opt == NULL 769 || opt_len != strlen(BSDP_VENDOR_CLASS_ID) 770 || bcmp(opt, BSDP_VENDOR_CLASS_ID, opt_len)) { 771 goto done; /* not BSDP */ 772 } 773 /* get the server identifier */ 774 opt = dhcpol_find(&options, dhcptag_server_identifier_e, 775 &opt_len, NULL); 776 if (opt == NULL || opt_len != sizeof(server_ip)) { 777 goto done; 778 } 779 server_ip = *((struct in_addr *)opt); 780 781 /* decode the BSDP options */ 782 if (dhcpol_parse_vendor(&bsdp_options, &options, &err) == FALSE) { 783 fprintf(stderr, 784 "BSDPClientProcess: dhcpol_parse_vendor failed, %s", err.str); 785 goto done; 786 } 787 /* get the BSDP message type */ 788 opt = dhcpol_find(&bsdp_options, bsdptag_message_type_e, 789 &opt_len, NULL); 790 if (opt == NULL || opt_len != 1) { 791 goto done; /* no message id */ 792 } 793 bsdp_msg = *((unsigned char *)opt); 794 switch (client->state) { 795 case kBSDPClientStateInit: 796 default: 797 break; 798 case kBSDPClientStateList: 799 /* ACK[LIST] */ 800 if (bsdp_msg == bsdp_msgtype_list_e) { 801 BSDPClientProcessList(client, server_ip, 802 (struct dhcp *)receive_buf, n, 803 &options, 804 &bsdp_options); 805 } 806 break; 807 case kBSDPClientStateSelect: 808 /* ACK[SELECT] or ACK[FAILED] */ 809 if (bsdp_msg == bsdp_msgtype_select_e 810 || bsdp_msg == bsdp_msgtype_failed_e) { 811 BSDPClientProcessSelect(client, bsdp_msg); 812 } 813 break; 814 } 815 done: 816 dhcpol_free(&options); 817 dhcpol_free(&bsdp_options); 818 return; 819} 820 821 822/* 823 * Function: BSDPClientCreateWithInterfaceAndAttributes 824 * Purpose: 825 * Instantiate a BSDPClientRef, checking to ensure that the machine 826 * is NetBoot-compatible. 827 */ 828BSDPClientRef 829BSDPClientCreateWithInterfaceAndAttributes(BSDPClientStatus * status_p, 830 const char * ifname, 831 const u_int16_t * attrs, int n_attrs) 832{ 833 BSDPClientRef client = NULL; 834 u_short client_port; 835 bsdp_version_t client_version = htons(BSDP_VERSION_1_1); 836 CFSocketContext context = { 0, NULL, NULL, NULL, NULL }; 837 interface_t * if_p = NULL; 838 interface_list_t * ifl = NULL; 839 int fd = -1; 840 boolean_t old_firmware = FALSE; 841 CFRunLoopSourceRef rls = NULL; 842 CFSocketRef socket = NULL; 843 BSDPClientStatus status = kBSDPClientStatusAllocationError; 844 char * system_id = NULL; 845 BSDPClientStatus this_status; 846 NetBootVersion version; 847 848 this_status = CopyNetBootVersionAndSystemIdentifier(&version, &system_id); 849 if (this_status != kBSDPClientStatusOK) { 850 status = this_status; 851 goto cleanup; 852 } 853 if (version == kNetBootVersion1) { 854 old_firmware = TRUE; 855 } 856 ifl = ifl_init(); 857 if (ifl == NULL) { 858 goto cleanup; 859 } 860 861 if_p = ifl_find_name(ifl, ifname); 862 if (if_p == NULL) { 863 status = kBSDPClientStatusNoSuchInterface; 864 goto cleanup; 865 } 866 if (if_ift_type(if_p) != IFT_ETHER) { 867 status = kBSDPClientStatusInvalidArgument; 868 goto cleanup; 869 } 870 /* make a persistent copy */ 871 if_p = if_dup(if_p); 872 if (if_p == NULL) { 873 goto cleanup; 874 } 875 if (if_inet_addr(if_p).s_addr == 0) { 876 status = kBSDPClientStatusInterfaceNotConfigured; 877 goto cleanup; 878 } 879 client = malloc(sizeof(*client)); 880 if (client == NULL) { 881 goto cleanup; 882 } 883 bzero(client, sizeof(*client)); 884 885 /* use the DHCP-supplied address, if it is available */ 886 if (get_dhcp_address(ifname, &client->our_ip) == FALSE) { 887 client->our_ip = if_inet_addr(if_p); 888 } 889 if (n_attrs > 0) { 890 int i; 891 892 if (n_attrs > MAX_ATTRS) { 893 n_attrs = MAX_ATTRS; 894 } 895 for (i = 0; i < n_attrs; i++) { 896 client->attrs[i] = htons(attrs[i]); 897 } 898 client->n_attrs = n_attrs; 899 } 900 fd = S_open_socket(&client_port); 901 if (fd < 0) { 902 if (errno == EPERM || errno == EACCES) { 903 perror("socket"); 904 status = kBSDPClientStatusPermissionDenied; 905 } 906 goto cleanup; 907 } 908 context.info = client; 909 socket = CFSocketCreateWithNative(NULL, fd, kCFSocketReadCallBack, 910 BSDPClientProcess, &context); 911 if (socket == NULL) { 912 goto cleanup; 913 } 914 rls = CFSocketCreateRunLoopSource(NULL, socket, 0); 915 if (rls == NULL) { 916 goto cleanup; 917 } 918 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, 919 kCFRunLoopDefaultMode); 920 client->system_id = system_id; 921 client->client_version = client_version; 922 client->old_firmware = old_firmware; 923 client->fd = fd; 924 client->rls = rls; 925 client->client_port = client_port; 926 client->socket = socket; 927 client->xid = arc4random(); 928 client->if_p = if_p; 929 client->state = kBSDPClientStateInit; 930 ifl_free(&ifl); 931 *status_p = kBSDPClientStatusOK; 932 return (client); 933 934 cleanup: 935 if (rls != NULL) { 936 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), rls, 937 kCFRunLoopDefaultMode); 938 /* release the run loop source */ 939 CFRelease(rls); 940 } 941 if (socket != NULL) { 942 /* remove one socket reference, close the file descriptor */ 943 CFSocketInvalidate(socket); 944 945 /* release the socket */ 946 CFRelease(socket); 947 fd = -1; 948 } 949 if (fd >= 0) { 950 close(fd); 951 } 952 if (client != NULL) { 953 free(client); 954 } 955 if (ifl != NULL) { 956 ifl_free(&ifl); 957 } 958 if (if_p != NULL) { 959 if_free(&if_p); 960 } 961 if (system_id != NULL) { 962 free(system_id); 963 } 964 *status_p = status; 965 return (NULL); 966} 967 968/* 969 * Function: BSDPClientCreateWithInterface 970 * Purpose: 971 * Allocate a new session. 972 */ 973BSDPClientRef 974BSDPClientCreateWithInterface(BSDPClientStatus * status_p, 975 const char * ifname) 976{ 977 return (BSDPClientCreateWithInterfaceAndAttributes(status_p, ifname, 978 NULL, 0)); 979} 980 981/* 982 * Function: BSDPClientCreate 983 * Purpose: 984 * Published entry point to instantiate a BSDPClientRef over "en0". 985 * XXX we should ask IOKit which interface is the primary. 986 */ 987BSDPClientRef 988BSDPClientCreate(BSDPClientStatus * status_p) 989{ 990 return (BSDPClientCreateWithInterface(status_p, "en0")); 991} 992 993void 994BSDPClientFree(BSDPClientRef * client_p) 995{ 996 BSDPClientRef client; 997 998 if (client_p == NULL) { 999 return; 1000 } 1001 client = *client_p; 1002 if (client == NULL) { 1003 return; 1004 } 1005 BSDPClientCancelTimer(client); 1006 if (client->socket != NULL) { 1007 /* remove one socket reference, close the file descriptor */ 1008 CFSocketInvalidate(client->socket); 1009 1010 /* release the socket */ 1011 CFRelease(client->socket); 1012 } 1013 if (client->rls != NULL) { 1014 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), client->rls, 1015 kCFRunLoopDefaultMode); 1016 /* release the run loop source */ 1017 CFRelease(client->rls); 1018 } 1019 if (client->if_p != NULL) { 1020 if_free(&client->if_p); 1021 } 1022 if (client->system_id != NULL) { 1023 free(client->system_id); 1024 } 1025 free(client); 1026 *client_p = NULL; 1027 return; 1028} 1029 1030/** 1031 ** BSDP List Routines 1032 **/ 1033static boolean_t 1034attributes_match(u_int16_t attrs, 1035 const u_int16_t * attrs_list, int n_attrs_list) 1036{ 1037 int i; 1038 1039 if (attrs_list == NULL || n_attrs_list == 0) { 1040 return (TRUE); 1041 } 1042 for (i = 0; i < n_attrs_list; i++) { 1043 if (attrs_list[i] == attrs) { 1044 return (TRUE); 1045 } 1046 } 1047 return (FALSE); 1048} 1049 1050static CFArrayRef 1051BSDPClientCreateImageList(BSDPClientRef client, 1052 bsdp_image_id_t default_image_id, 1053 bsdp_image_id_t selected_image_id, 1054 void * image_list, int image_list_len) 1055{ 1056 bsdp_image_description_t * descr; 1057 CFMutableArrayRef images = NULL; 1058 int length; 1059 1060 images = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 1061 descr = image_list; 1062 for (length = image_list_len; length > sizeof(*descr); ) { 1063 u_int16_t attributes; 1064 bsdp_image_id_t boot_image_id; 1065 CFMutableDictionaryRef this_dict = NULL; 1066 int this_len; 1067 CFStringRef cf_image_name = NULL; 1068 CFNumberRef cf_image_id = NULL; 1069 CFNumberRef cf_image_index = NULL; 1070 1071 this_len = sizeof(*descr) + descr->name_length; 1072 if (length < this_len) { 1073 fprintf(stderr, "short image list at offset %d\n", 1074 (int)((void *)descr - image_list)); 1075 goto failed; 1076 } 1077 boot_image_id = ntohl(*((bsdp_image_id_t *)descr->boot_image_id)); 1078 attributes = bsdp_image_attributes(boot_image_id); 1079 if (boot_image_id != BOOT_IMAGE_ID_NULL 1080 && attributes_match(htons(attributes), 1081 client->attrs, client->n_attrs)) { 1082 uint32_t index; 1083 1084 this_dict 1085 = CFDictionaryCreateMutable(NULL, 0, 1086 &kCFTypeDictionaryKeyCallBacks, 1087 &kCFTypeDictionaryValueCallBacks); 1088 cf_image_id = CFNumberCreate(NULL, kCFNumberSInt32Type, 1089 &boot_image_id); 1090 index = bsdp_image_index(boot_image_id); 1091 cf_image_index = CFNumberCreate(NULL, kCFNumberSInt32Type, 1092 &index); 1093 cf_image_name = CFStringCreateWithBytes(NULL, 1094 descr->name, 1095 descr->name_length, 1096 kCFStringEncodingUTF8, 1097 TRUE); 1098 if (this_dict != NULL && cf_image_id != NULL 1099 && cf_image_index != NULL && cf_image_name != NULL) { 1100 CFDictionarySetValue(this_dict, 1101 kBSDPImageDescriptionName, cf_image_name); 1102 CFDictionarySetValue(this_dict, 1103 kBSDPImageDescriptionIdentifier, 1104 cf_image_id); 1105 CFDictionarySetValue(this_dict, kBSDPImageDescriptionIndex, 1106 cf_image_index); 1107 if (attributes & BSDP_IMAGE_ATTRIBUTES_INSTALL) { 1108 CFDictionarySetValue(this_dict, 1109 kBSDPImageDescriptionIsInstall, 1110 kCFBooleanTrue); 1111 } 1112 if (boot_image_id == default_image_id) { 1113 CFDictionarySetValue(this_dict, 1114 kBSDPImageDescriptionIsDefault, 1115 kCFBooleanTrue); 1116 } 1117 if (boot_image_id == selected_image_id) { 1118 CFDictionarySetValue(this_dict, 1119 kBSDPImageDescriptionIsSelected, 1120 kCFBooleanTrue); 1121 } 1122 CFArrayAppendValue(images, this_dict); 1123 } 1124 my_CFRelease(&cf_image_index); 1125 my_CFRelease(&cf_image_id); 1126 my_CFRelease(&cf_image_name); 1127 my_CFRelease(&this_dict); 1128 } 1129 descr = ((void *)descr) + this_len; 1130 length -= this_len; 1131 } 1132 if (CFArrayGetCount(images) == 0) { 1133 goto failed; 1134 } 1135 return ((CFArrayRef)images); 1136 1137 failed: 1138 my_CFRelease(&images); 1139 return (NULL); 1140} 1141 1142static void 1143BSDPClientProcessList(BSDPClientRef client, struct in_addr server_ip, 1144 struct dhcp * reply, int reply_len, 1145 dhcpol_t * options_p, dhcpol_t * bsdp_options_p) 1146{ 1147 CFNumberRef cf_priority = NULL; 1148 CFStringRef cf_server_ip = NULL; 1149 bsdp_image_id_t default_image_id = BOOT_IMAGE_ID_NULL; 1150 void * image_list = NULL; 1151 int image_list_len = 0; 1152 void * opt; 1153 int opt_len; 1154 CFArrayRef images = NULL; 1155 uint32_t priority = 0; 1156 bsdp_image_id_t selected_image_id = BOOT_IMAGE_ID_NULL; 1157 1158 /* get the server priority */ 1159 opt = dhcpol_find(bsdp_options_p, bsdptag_server_priority_e, &opt_len, 1160 NULL); 1161 if (opt != NULL && opt_len == sizeof(bsdp_priority_t)) { 1162 priority = (uint32_t)ntohs(*((bsdp_priority_t *)opt)); 1163 } 1164 /* get the default boot image */ 1165 opt = dhcpol_find(bsdp_options_p, bsdptag_default_boot_image_e, &opt_len, 1166 NULL); 1167 if (opt != NULL && opt_len == sizeof(default_image_id)) { 1168 default_image_id = ntohl(*((bsdp_image_id_t *)opt)); 1169 } 1170 /* get the selected boot image */ 1171 opt = dhcpol_find(bsdp_options_p, bsdptag_selected_boot_image_e, &opt_len, 1172 NULL); 1173 if (opt && opt_len == sizeof(selected_image_id)) { 1174 selected_image_id = ntohl(*((bsdp_image_id_t *)opt)); 1175 } 1176 /* get the list of images */ 1177 image_list = dhcpol_option_copy(bsdp_options_p, bsdptag_boot_image_list_e, 1178 &image_list_len); 1179 if (image_list == NULL) { 1180 goto done; 1181 } 1182 cf_priority = CFNumberCreate(NULL, kCFNumberSInt32Type, &priority); 1183 cf_server_ip = CFStringCreateWithCString(NULL, inet_ntoa(server_ip), 1184 kCFStringEncodingASCII); 1185 images = BSDPClientCreateImageList(client, default_image_id, 1186 selected_image_id, 1187 image_list, image_list_len); 1188 if (images != NULL && cf_priority != NULL && cf_server_ip != NULL) { 1189 client->got_responses = TRUE; 1190 (*client->callback.func.list)(client, 1191 kBSDPClientStatusOK, 1192 cf_server_ip, 1193 cf_priority, 1194 images, 1195 client->callback.arg); 1196 } 1197 1198 done: 1199 my_CFRelease(&images); 1200 my_CFRelease(&cf_priority); 1201 my_CFRelease(&cf_server_ip); 1202 if (image_list != NULL) { 1203 free(image_list); 1204 } 1205 my_CFRelease(&images); 1206 return; 1207} 1208 1209static BSDPClientStatus 1210BSDPClientSendListRequest(BSDPClientRef client) 1211{ 1212 char bsdp_buf[DHCP_OPTION_SIZE_MAX]; 1213 dhcpoa_t bsdp_options; 1214 char buf[DHCP_PACKET_MIN]; 1215 struct in_addr ip_broadcast; 1216 dhcpoa_t options; 1217 uint16_t max_message_size = htons(RX_BUF_SIZE); /* max receive size */ 1218 unsigned char msgtype; 1219 u_int16_t port = htons(client->client_port); 1220 struct dhcp * request; 1221 int request_size = 0; 1222 BSDPClientStatus status = kBSDPClientStatusAllocationError; 1223 1224 ip_broadcast.s_addr = htonl(INADDR_BROADCAST); 1225 request = make_bsdp_request(client->system_id, 1226 (struct dhcp *)buf, sizeof(buf), 1227 dhcp_msgtype_inform_e, 1228 if_link_address(client->if_p), 1229 if_link_arptype(client->if_p), 1230 if_link_length(client->if_p), 1231 &options); 1232 if (request == NULL) { 1233 goto failed; 1234 } 1235 request->dp_xid = htonl(client->xid); 1236 request->dp_ciaddr = client->our_ip; 1237 dhcpoa_init_no_end(&bsdp_options, bsdp_buf, sizeof(bsdp_buf)); 1238 msgtype = bsdp_msgtype_list_e; 1239 if (dhcpoa_add(&bsdp_options, bsdptag_message_type_e, 1240 sizeof(msgtype), &msgtype) 1241 != dhcpoa_success_e) { 1242 fprintf(stderr, "BSDPClientSendListRequest add message type failed, %s", 1243 dhcpoa_err(&bsdp_options)); 1244 goto failed; 1245 } 1246 if (dhcpoa_add(&bsdp_options, bsdptag_version_e, 1247 sizeof(client->client_version), 1248 &client->client_version) != dhcpoa_success_e) { 1249 fprintf(stderr, "BSDPClientSendListRequest add version failed, %s", 1250 dhcpoa_err(&bsdp_options)); 1251 goto failed; 1252 } 1253 if (client->old_firmware == TRUE) { 1254 if (dhcpoa_add(&bsdp_options, bsdptag_netboot_1_0_firmware_e, 1255 0, NULL) != dhcpoa_success_e) { 1256 fprintf(stderr, "BSDPClientSendListRequest old_firmware failed, %s", 1257 dhcpoa_err(&bsdp_options)); 1258 goto failed; 1259 } 1260 } 1261 if (dhcpoa_add(&bsdp_options, bsdptag_reply_port_e, sizeof(port), 1262 &port) != dhcpoa_success_e) { 1263 fprintf(stderr, "BSDPClientSendListRequest add reply port failed, %s", 1264 dhcpoa_err(&bsdp_options)); 1265 goto failed; 1266 } 1267 if (dhcpoa_add(&bsdp_options, bsdptag_max_message_size_e, 1268 sizeof(max_message_size), &max_message_size) 1269 != dhcpoa_success_e) { 1270 fprintf(stderr, 1271 "BSDPClientSendListRequest add max message size failed, %s", 1272 dhcpoa_err(&bsdp_options)); 1273 goto failed; 1274 } 1275 if (client->n_attrs > 0) { 1276 if (dhcpoa_add(&bsdp_options,bsdptag_image_attributes_filter_list_e, 1277 client->n_attrs * sizeof(client->attrs[0]), 1278 client->attrs) != dhcpoa_success_e) { 1279 fprintf(stderr, 1280 "BSDPClientSendListRequest add image attributes failed, %s", 1281 dhcpoa_err(&bsdp_options)); 1282 goto failed; 1283 } 1284 } 1285 if (dhcpoa_add(&options, dhcptag_vendor_specific_e, 1286 dhcpoa_used(&bsdp_options), &bsdp_buf) 1287 != dhcpoa_success_e) { 1288 fprintf(stderr, 1289 "BSDPClientSendListRequest add vendor specific failed, %s", 1290 dhcpoa_err(&options)); 1291 goto failed; 1292 } 1293 if (dhcpoa_add(&options, dhcptag_end_e, 0, NULL) 1294 != dhcpoa_success_e) { 1295 fprintf(stderr, 1296 "BSDPClientSendListRequest add dhcp options end failed, %s", 1297 dhcpoa_err(&bsdp_options)); 1298 goto failed; 1299 } 1300 request_size = sizeof(*request) + sizeof(rfc_magic) 1301 + dhcpoa_used(&options); 1302 if (request_size < sizeof(struct bootp)) { 1303 /* pad out to BOOTP-sized packet */ 1304 request_size = sizeof(struct bootp); 1305 } 1306 if (bootp_transmit(client->fd, (char *)client->send_buf, 1307 if_name(client->if_p), ARPHRD_ETHER, NULL, 0, 1308 ip_broadcast, client->our_ip, 1309 IPPORT_BOOTPS, client->client_port, 1310 request, request_size) < 0) { 1311 fprintf(stderr, 1312 "BSDPClientSendListRequest: bootp_transmit failed %s\n", 1313 strerror(errno)); 1314 status = kBSDPClientStatusTransmitFailed; 1315 goto failed; 1316 } 1317 status = kBSDPClientStatusOK; 1318 1319 failed: 1320 return (status); 1321} 1322 1323static void 1324BSDPClientListTimeout(BSDPClientRef client) 1325{ 1326 BSDPClientStatus status = kBSDPClientStatusOK; 1327 struct timeval t; 1328 1329 if (client->try == BSDPCLIENT_LIST_MAX_TRIES) { 1330 if (client->got_responses == FALSE) { 1331 status = kBSDPClientStatusOperationTimedOut; 1332 goto report_error; 1333 } 1334 return; 1335 } 1336 client->try++; 1337 client->wait_secs *= 2; 1338 if (client->wait_secs > BSDPCLIENT_MAX_WAIT_SECS) { 1339 client->wait_secs = BSDPCLIENT_MAX_WAIT_SECS; 1340 } 1341 status = BSDPClientSendListRequest(client); 1342 if (status != kBSDPClientStatusOK) { 1343 goto report_error; 1344 } 1345 t.tv_sec = client->wait_secs; 1346 t.tv_usec = random_range(0, USECS_PER_SEC - 1); 1347 BSDPClientSetTimer(client, t, BSDPClientListTimeout); 1348 return; 1349 1350 report_error: 1351 (*client->callback.func.list)(client, status, NULL, NULL, 1352 NULL, client->callback.arg); 1353 return; 1354} 1355 1356BSDPClientStatus 1357BSDPClientList(BSDPClientRef client, BSDPClientListCallBack callback, 1358 void * info) 1359{ 1360 struct timeval t; 1361 BSDPClientStatus status = kBSDPClientStatusAllocationError; 1362 1363 client->state = kBSDPClientStateInit; 1364 BSDPClientCancelTimer(client); 1365 if (callback == NULL) { 1366 status = kBSDPClientStatusInvalidArgument; 1367 goto failed; 1368 } 1369 client->xid++; 1370 status = BSDPClientSendListRequest(client); 1371 if (status != kBSDPClientStatusOK) { 1372 goto failed; 1373 } 1374 client->state = kBSDPClientStateList; 1375 client->try = 1; 1376 client->got_responses = FALSE; 1377 client->callback.func.list = callback; 1378 client->callback.arg = info; 1379 client->wait_secs = BSDPCLIENT_INITIAL_TIMEOUT_SECS; 1380 t.tv_sec = client->wait_secs; 1381 t.tv_usec = random_range(0, USECS_PER_SEC - 1); 1382 BSDPClientSetTimer(client, t, BSDPClientListTimeout); 1383 1384 failed: 1385 return (status); 1386} 1387 1388/** 1389 ** BSDP Select Routines 1390 **/ 1391 1392static void 1393BSDPClientProcessSelect(BSDPClientRef client, bsdp_msgtype_t bsdp_msg) 1394{ 1395 BSDPClientStatus status; 1396 1397 BSDPClientCancelTimer(client); 1398 if (bsdp_msg == bsdp_msgtype_select_e) { 1399 status = kBSDPClientStatusOK; 1400 } 1401 else { 1402 status = kBSDPClientStatusServerSentFailure; 1403 } 1404 (*client->callback.func.select)(client, status, client->callback.arg); 1405 return; 1406} 1407 1408static BSDPClientStatus 1409BSDPClientSendSelectRequest(BSDPClientRef client) 1410{ 1411 char bsdp_buf[DHCP_OPTION_SIZE_MAX]; 1412 dhcpoa_t bsdp_options; 1413 char buf[DHCP_PACKET_MIN]; 1414 bsdp_image_id_t image_id = htonl(client->callback.image_identifier); 1415 struct in_addr ip_broadcast; 1416 dhcpoa_t options; 1417 unsigned char msgtype; 1418 u_int16_t port = htons(client->client_port); 1419 struct dhcp * request; 1420 int request_size = 0; 1421 BSDPClientStatus status = kBSDPClientStatusAllocationError; 1422 1423 ip_broadcast.s_addr = htonl(INADDR_BROADCAST); 1424 request = make_bsdp_request(client->system_id, 1425 (struct dhcp *)buf, sizeof(buf), 1426 dhcp_msgtype_inform_e, 1427 if_link_address(client->if_p), 1428 if_link_arptype(client->if_p), 1429 if_link_length(client->if_p), 1430 &options); 1431 if (request == NULL) { 1432 goto failed; 1433 } 1434 request->dp_xid = htonl(client->xid); 1435 request->dp_ciaddr = client->our_ip; 1436 dhcpoa_init_no_end(&bsdp_options, bsdp_buf, sizeof(bsdp_buf)); 1437 msgtype = bsdp_msgtype_select_e; 1438 if (dhcpoa_add(&bsdp_options, bsdptag_message_type_e, 1439 sizeof(msgtype), &msgtype) 1440 != dhcpoa_success_e) { 1441 fprintf(stderr, 1442 "BSDPClientSendSelectRequest add message type failed, %s", 1443 dhcpoa_err(&bsdp_options)); 1444 goto failed; 1445 } 1446 if (dhcpoa_add(&bsdp_options, bsdptag_version_e, 1447 sizeof(client->client_version), 1448 &client->client_version) != dhcpoa_success_e) { 1449 fprintf(stderr, "BSDPClientSendSelectRequest add version failed, %s", 1450 dhcpoa_err(&bsdp_options)); 1451 goto failed; 1452 } 1453 if (client->old_firmware == TRUE) { 1454 if (dhcpoa_add(&bsdp_options, bsdptag_netboot_1_0_firmware_e, 1455 0, NULL) != dhcpoa_success_e) { 1456 fprintf(stderr, "BSDPClientSendListRequest old_firmware failed, %s", 1457 dhcpoa_err(&bsdp_options)); 1458 goto failed; 1459 } 1460 } 1461 if (dhcpoa_add(&bsdp_options, bsdptag_reply_port_e, sizeof(port), 1462 &port) != dhcpoa_success_e) { 1463 fprintf(stderr, "BSDPClientSendSelectRequest add reply port failed, %s", 1464 dhcpoa_err(&bsdp_options)); 1465 goto failed; 1466 } 1467 if (dhcpoa_add(&bsdp_options, bsdptag_server_identifier_e, 1468 sizeof(struct in_addr), 1469 &client->callback.server_ip) != dhcpoa_success_e) { 1470 fprintf(stderr, 1471 "BSDPClientSendSelectRequest: add server identifier failed, %s", 1472 dhcpoa_err(&bsdp_options)); 1473 goto failed; 1474 } 1475 if (dhcpoa_add(&bsdp_options, bsdptag_selected_boot_image_e, 1476 sizeof(image_id), &image_id) != dhcpoa_success_e) { 1477 fprintf(stderr, 1478 "BSDPClientSendSelectRequest: add selected image failed, %s", 1479 dhcpoa_err(&bsdp_options)); 1480 goto failed; 1481 } 1482 if (dhcpoa_add(&options, dhcptag_vendor_specific_e, 1483 dhcpoa_used(&bsdp_options), &bsdp_buf) 1484 != dhcpoa_success_e) { 1485 fprintf(stderr, 1486 "BSDPClientSendSelectRequest add vendor specific failed, %s", 1487 dhcpoa_err(&options)); 1488 goto failed; 1489 } 1490 if (dhcpoa_add(&options, dhcptag_end_e, 0, NULL) 1491 != dhcpoa_success_e) { 1492 fprintf(stderr, 1493 "BSDPClientSendSelectRequest add dhcp options end failed, %s", 1494 dhcpoa_err(&bsdp_options)); 1495 goto failed; 1496 } 1497 request_size = sizeof(*request) + sizeof(rfc_magic) 1498 + dhcpoa_used(&options); 1499 if (request_size < sizeof(struct bootp)) { 1500 /* pad out to BOOTP-sized packet */ 1501 request_size = sizeof(struct bootp); 1502 } 1503 /* send the packet */ 1504 if (bootp_transmit(client->fd, (char *)client->send_buf, 1505 if_name(client->if_p), ARPHRD_ETHER, NULL, 0, 1506 ip_broadcast, client->our_ip, 1507 IPPORT_BOOTPS, client->client_port, 1508 request, request_size) < 0) { 1509 fprintf(stderr, 1510 "BSDPClientSendSelectRequest: bootp_transmit failed %s\n", 1511 strerror(errno)); 1512 status = kBSDPClientStatusTransmitFailed; 1513 goto failed; 1514 } 1515 status = kBSDPClientStatusOK; 1516 1517 failed: 1518 return (status); 1519} 1520 1521static void 1522BSDPClientSelectTimeout(BSDPClientRef client) 1523{ 1524 BSDPClientStatus status = kBSDPClientStatusOK; 1525 struct timeval t; 1526 1527 if (client->try == BSDPCLIENT_SELECT_MAX_TRIES) { 1528 status = kBSDPClientStatusOperationTimedOut; 1529 goto report_error; 1530 } 1531 client->try++; 1532 client->wait_secs *= 2; 1533 status = BSDPClientSendSelectRequest(client); 1534 if (status != kBSDPClientStatusOK) { 1535 goto report_error; 1536 } 1537 t.tv_sec = client->wait_secs; 1538 t.tv_usec = 0; 1539 BSDPClientSetTimer(client, t, BSDPClientSelectTimeout); 1540 return; 1541 1542 report_error: 1543 (*client->callback.func.select)(client, status, client->callback.arg); 1544 return; 1545} 1546 1547BSDPClientStatus 1548BSDPClientSelect(BSDPClientRef client, 1549 CFStringRef ServerAddress, 1550 CFNumberRef Identifier, 1551 BSDPClientSelectCallBack callback, void * info) 1552{ 1553 unsigned long image_identifier; 1554 struct timeval t; 1555 BSDPClientStatus status = kBSDPClientStatusAllocationError; 1556 1557 (void)my_CFStringToIPAddress(ServerAddress, &client->callback.server_ip); 1558 client->state = kBSDPClientStateInit; 1559 BSDPClientCancelTimer(client); 1560 if (callback == NULL 1561 || CFNumberGetValue(Identifier, kCFNumberLongType, 1562 &image_identifier) == FALSE) { 1563 status = kBSDPClientStatusInvalidArgument; 1564 goto failed; 1565 } 1566 client->xid++; 1567 client->callback.image_identifier = image_identifier; 1568 status = BSDPClientSendSelectRequest(client); 1569 if (status != kBSDPClientStatusOK) { 1570 goto failed; 1571 } 1572 client->state = kBSDPClientStateSelect; 1573 client->try = 1; 1574 client->callback.func.select = callback; 1575 client->callback.arg = info; 1576 client->wait_secs = BSDPCLIENT_INITIAL_TIMEOUT_SECS; 1577 t.tv_sec = client->wait_secs; 1578 t.tv_usec = 0; 1579 BSDPClientSetTimer(client, t, BSDPClientSelectTimeout); 1580 1581 failed: 1582 return (status); 1583} 1584 1585BSDPClientStatus 1586BSPPClientSelect(BSDPClientRef client, 1587 CFStringRef ServerAddress, 1588 CFNumberRef Identifier, 1589 BSDPClientSelectCallBack callback, void * info) 1590{ 1591 return (BSDPClientSelect(client, ServerAddress, Identifier, 1592 callback, info)); 1593} 1594 1595#ifdef TEST_BAD_SYSID 1596static void bad_sysid_callback(BSDPClientRef client, 1597 BSDPClientStatus status, 1598 CFStringRef ServerAddress, 1599 CFNumberRef ServerPriority, 1600 CFArrayRef list, 1601 void *info) 1602{ 1603 return; 1604} 1605 1606 1607int 1608main(int argc, char * argv[]) 1609{ 1610 BSDPClientStatus status; 1611 char * if_name = "en0"; 1612 BSDPClientRef client; 1613 1614 if (argc > 1) { 1615 if_name = argv[1]; 1616 } 1617 1618 client = BSDPClientCreateWithInterface(&status, if_name); 1619 if (client == NULL) { 1620 fprintf(stderr, "BSDPClientCreateWithInterface(%s) failed: %s\n", 1621 if_name, BSDPClientStatusString(status)); 1622 exit(2); 1623 } 1624 status = BSDPClientList(client, bad_sysid_callback, NULL); 1625 CFRunLoopRun(); 1626 exit (0); 1627 return (0); 1628} 1629#endif /* TEST_BAD_SYSID */ 1630