1/* 2 * Copyright (c) 2002-2014 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 if (status < 0) { 556 perror("setsockopt IPPROTO_IP IP_PORTRANGE"); 557 goto failed; 558 } 559 560 /* bind the socket */ 561 status = bind(sockfd, (struct sockaddr *)&me, sizeof(me)); 562 if (status != 0) { 563 perror("bind"); 564 goto failed; 565 } 566 me_len = sizeof(me); 567 if (getsockname(sockfd, (struct sockaddr *)&me, &me_len) < 0) { 568 perror("getsockname"); 569 goto failed; 570 } 571 client_port = ntohs(me.sin_port); 572 573 opt = 1; 574 575#if defined(SO_RECV_ANYIF) 576 /* receive over any interface */ 577 if (setsockopt(sockfd, SOL_SOCKET, SO_RECV_ANYIF, (caddr_t)&opt, 578 sizeof(opt)) < 0) { 579 perror("setsockopt SO_RECV_ANYIF"); 580 } 581#endif /* SO_RECV_ANYIF */ 582 583 /* broadcast */ 584 status = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &opt, 585 sizeof(opt)); 586 if (status < 0) { 587 perror("setsockopt SO_BROADCAST"); 588 goto failed; 589 } 590 /* non-blocking I/O */ 591 status = ioctl(sockfd, FIONBIO, &opt); 592 if (status < 0) { 593 perror("ioctl FIONBIO"); 594 goto failed; 595 } 596 597#if defined(SO_TRAFFIC_CLASS) 598 opt = SO_TC_CTL; 599 /* set control traffic class */ 600 status = setsockopt(sockfd, SOL_SOCKET, SO_TRAFFIC_CLASS, &opt, 601 sizeof(opt)); 602 if (status < 0) { 603 perror("setsockopt SO_TRAFFIC_CLASS"); 604 } 605#endif /* SO_TRAFFIC_CLASS */ 606 607 *ret_port = client_port; 608 return sockfd; 609 610 failed: 611 if (sockfd >= 0) { 612 close(sockfd); 613 } 614 return (-1); 615} 616 617/* deprecated: BSDPImageDescriptionIndexIsServerLocal */ 618Boolean 619BSDPImageDescriptionIndexIsServerLocal(CFNumberRef index) 620{ 621 u_int16_t index_val = 1; 622 623 (void)CFNumberGetValue(index, kCFNumberShortType, &index_val); 624 return (bsdp_image_index_is_server_local(index_val)); 625} 626 627Boolean 628BSDPImageDescriptionIdentifierIsServerLocal(CFNumberRef identifier) 629{ 630 u_int32_t identifier_val = 1; 631 632 (void)CFNumberGetValue(identifier, kCFNumberSInt32Type, &identifier_val); 633 return (bsdp_image_identifier_is_server_local(identifier_val)); 634} 635 636Boolean 637BSDPImageDescriptionIdentifierIsInstall(CFNumberRef identifier) 638{ 639 u_int32_t identifier_val = 1; 640 641 (void)CFNumberGetValue(identifier, kCFNumberSInt32Type, &identifier_val); 642 return (bsdp_image_identifier_is_install(identifier_val)); 643} 644 645BSDPImageKind 646BSDPImageDescriptionIdentifierImageKind(CFNumberRef identifier) 647{ 648 u_int32_t identifier_val = 1; 649 650 (void)CFNumberGetValue(identifier, kCFNumberSInt32Type, &identifier_val); 651 return (bsdp_image_kind_from_attributes(bsdp_image_attributes(identifier_val))); 652} 653 654/** 655 ** BSDPClient timer functions 656 **/ 657 658static void 659BSDPClientProcessTimer(CFRunLoopTimerRef timer, void * info) 660{ 661 BSDPClientRef client; 662 663 client = (BSDPClientRef)info; 664 (*client->timer_callback)(client); 665 return; 666} 667 668static void 669BSDPClientCancelTimer(BSDPClientRef client) 670{ 671 if (client->timer) { 672 CFRunLoopTimerInvalidate(client->timer); 673 my_CFRelease(&client->timer); 674 } 675 client->timer_callback = NULL; 676 return; 677} 678 679static void 680BSDPClientSetTimer(BSDPClientRef client, struct timeval rel_time, 681 BSDPClientTimerCallBack callback) 682{ 683 CFRunLoopTimerContext context = { 0, NULL, NULL, NULL, NULL }; 684 CFAbsoluteTime wakeup_time; 685 686 BSDPClientCancelTimer(client); 687 client->timer_callback = callback; 688 wakeup_time = CFAbsoluteTimeGetCurrent() + rel_time.tv_sec 689 + ((double)rel_time.tv_usec / USECS_PER_SEC); 690 context.info = client; 691 client->timer 692 = CFRunLoopTimerCreate(NULL, wakeup_time, 693 0.0, 0, 0, 694 BSDPClientProcessTimer, 695 &context); 696 CFRunLoopAddTimer(CFRunLoopGetCurrent(), client->timer, 697 kCFRunLoopDefaultMode); 698 return; 699} 700 701/* 702 * Function: BSDPClientProcess 703 * Purpose: 704 * Process a packet received on our open socket. 705 * Ensure that the packet is a BSDP packet[DHCP ACK] that 706 * matches our currently outstanding request. 707 * 708 * Dispatch to the appropriate handler (list or select) 709 * depending on our current running state. 710 */ 711static void 712BSDPClientProcess(CFSocketRef s, CFSocketCallBackType type, 713 CFDataRef address, const void *data, void *info) 714{ 715 dhcpol_t bsdp_options; 716 bsdp_msgtype_t bsdp_msg; 717 BSDPClientRef client = (BSDPClientRef)info; 718 dhcpo_err_str_t err; 719 struct sockaddr_in from; 720 socklen_t fromlen; 721 int n; 722 void * opt; 723 int opt_len; 724 dhcpol_t options; 725 uint32_t receive_buf[RX_BUF_SIZE / sizeof(uint32_t)]; 726 struct dhcp * reply; 727 struct in_addr server_ip; 728 729 n = recvfrom(client->fd, receive_buf, 730 sizeof(receive_buf), 0, 731 (struct sockaddr *)&from, &fromlen); 732 if (n < 0) { 733 if (errno != EAGAIN) { 734 fprintf(stderr, "BSDPClientProcess: recvfrom %s", 735 strerror(errno)); 736 } 737 return; 738 } 739 if (n < sizeof(struct dhcp)) { 740 /* packet is too short */ 741 return; 742 } 743 744 switch (client->state) { 745 case kBSDPClientStateInit: 746 default: 747 /* throw it away */ 748 return; 749 case kBSDPClientStateList: 750 case kBSDPClientStateSelect: 751 break; 752 } 753 754 reply = (struct dhcp *)receive_buf; 755 if (dhcp_packet_match((struct bootp *)receive_buf, client->xid, 756 (u_char) if_link_arptype(client->if_p), 757 if_link_address(client->if_p), 758 if_link_length(client->if_p)) == FALSE 759 || reply->dp_ciaddr.s_addr != client->our_ip.s_addr) { 760 /* wasn't us */ 761 return; 762 } 763 764 dhcpol_init(&options); 765 dhcpol_init(&bsdp_options); 766 767 if (dhcpol_parse_packet(&options, reply, n, &err) == FALSE) { 768 fprintf(stderr, 769 "BSDPClientProcess: dhcpol_parse_packet failed, %s\n", 770 err.str); 771 goto done; 772 } 773 774 /* get the DHCP message type */ 775 opt = dhcpol_find(&options, dhcptag_dhcp_message_type_e, NULL, NULL); 776 if (opt == NULL || *((unsigned char *)opt) != dhcp_msgtype_ack_e) { 777 goto done; /* response must be a DHCP ack */ 778 } 779 780 /* get the vendor class identifier */ 781 opt = dhcpol_find(&options, dhcptag_vendor_class_identifier_e, 782 &opt_len, NULL); 783 if (opt == NULL 784 || opt_len != strlen(BSDP_VENDOR_CLASS_ID) 785 || bcmp(opt, BSDP_VENDOR_CLASS_ID, opt_len)) { 786 goto done; /* not BSDP */ 787 } 788 /* get the server identifier */ 789 opt = dhcpol_find(&options, dhcptag_server_identifier_e, 790 &opt_len, NULL); 791 if (opt == NULL || opt_len != sizeof(server_ip)) { 792 goto done; 793 } 794 server_ip = *((struct in_addr *)opt); 795 796 /* decode the BSDP options */ 797 if (dhcpol_parse_vendor(&bsdp_options, &options, &err) == FALSE) { 798 fprintf(stderr, 799 "BSDPClientProcess: dhcpol_parse_vendor failed, %s", err.str); 800 goto done; 801 } 802 /* get the BSDP message type */ 803 opt = dhcpol_find(&bsdp_options, bsdptag_message_type_e, 804 &opt_len, NULL); 805 if (opt == NULL || opt_len != 1) { 806 goto done; /* no message id */ 807 } 808 bsdp_msg = *((unsigned char *)opt); 809 switch (client->state) { 810 case kBSDPClientStateInit: 811 default: 812 break; 813 case kBSDPClientStateList: 814 /* ACK[LIST] */ 815 if (bsdp_msg == bsdp_msgtype_list_e) { 816 BSDPClientProcessList(client, server_ip, 817 (struct dhcp *)receive_buf, n, 818 &options, 819 &bsdp_options); 820 } 821 break; 822 case kBSDPClientStateSelect: 823 /* ACK[SELECT] or ACK[FAILED] */ 824 if (bsdp_msg == bsdp_msgtype_select_e 825 || bsdp_msg == bsdp_msgtype_failed_e) { 826 BSDPClientProcessSelect(client, bsdp_msg); 827 } 828 break; 829 } 830 done: 831 dhcpol_free(&options); 832 dhcpol_free(&bsdp_options); 833 return; 834} 835 836 837/* 838 * Function: BSDPClientCreateWithInterfaceAndAttributes 839 * Purpose: 840 * Instantiate a BSDPClientRef, checking to ensure that the machine 841 * is NetBoot-compatible. 842 */ 843BSDPClientRef 844BSDPClientCreateWithInterfaceAndAttributes(BSDPClientStatus * status_p, 845 const char * ifname, 846 const u_int16_t * attrs, int n_attrs) 847{ 848 BSDPClientRef client = NULL; 849 u_short client_port; 850 bsdp_version_t client_version = htons(BSDP_VERSION_1_1); 851 CFSocketContext context = { 0, NULL, NULL, NULL, NULL }; 852 interface_t * if_p = NULL; 853 interface_list_t * ifl = NULL; 854 int fd = -1; 855 boolean_t old_firmware = FALSE; 856 CFRunLoopSourceRef rls = NULL; 857 CFSocketRef socket = NULL; 858 BSDPClientStatus status = kBSDPClientStatusAllocationError; 859 char * system_id = NULL; 860 BSDPClientStatus this_status; 861 NetBootVersion version; 862 863 this_status = CopyNetBootVersionAndSystemIdentifier(&version, &system_id); 864 if (this_status != kBSDPClientStatusOK) { 865 status = this_status; 866 goto cleanup; 867 } 868 if (version == kNetBootVersion1) { 869 old_firmware = TRUE; 870 } 871 ifl = ifl_init(); 872 if (ifl == NULL) { 873 goto cleanup; 874 } 875 876 if_p = ifl_find_name(ifl, ifname); 877 if (if_p == NULL) { 878 status = kBSDPClientStatusNoSuchInterface; 879 goto cleanup; 880 } 881 if (if_ift_type(if_p) != IFT_ETHER) { 882 status = kBSDPClientStatusInvalidArgument; 883 goto cleanup; 884 } 885 /* make a persistent copy */ 886 if_p = if_dup(if_p); 887 if (if_p == NULL) { 888 goto cleanup; 889 } 890 if (if_inet_addr(if_p).s_addr == 0) { 891 status = kBSDPClientStatusInterfaceNotConfigured; 892 goto cleanup; 893 } 894 client = malloc(sizeof(*client)); 895 if (client == NULL) { 896 goto cleanup; 897 } 898 bzero(client, sizeof(*client)); 899 900 /* use the DHCP-supplied address, if it is available */ 901 if (get_dhcp_address(ifname, &client->our_ip) == FALSE) { 902 client->our_ip = if_inet_addr(if_p); 903 } 904 if (n_attrs > 0) { 905 int i; 906 907 if (n_attrs > MAX_ATTRS) { 908 n_attrs = MAX_ATTRS; 909 } 910 for (i = 0; i < n_attrs; i++) { 911 client->attrs[i] = htons(attrs[i]); 912 } 913 client->n_attrs = n_attrs; 914 } 915 fd = S_open_socket(&client_port); 916 if (fd < 0) { 917 if (errno == EPERM || errno == EACCES) { 918 perror("socket"); 919 status = kBSDPClientStatusPermissionDenied; 920 } 921 goto cleanup; 922 } 923 context.info = client; 924 socket = CFSocketCreateWithNative(NULL, fd, kCFSocketReadCallBack, 925 BSDPClientProcess, &context); 926 if (socket == NULL) { 927 goto cleanup; 928 } 929 rls = CFSocketCreateRunLoopSource(NULL, socket, 0); 930 if (rls == NULL) { 931 goto cleanup; 932 } 933 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, 934 kCFRunLoopDefaultMode); 935 client->system_id = system_id; 936 client->client_version = client_version; 937 client->old_firmware = old_firmware; 938 client->fd = fd; 939 client->rls = rls; 940 client->client_port = client_port; 941 client->socket = socket; 942 client->xid = arc4random(); 943 client->if_p = if_p; 944 client->state = kBSDPClientStateInit; 945 ifl_free(&ifl); 946 *status_p = kBSDPClientStatusOK; 947 return (client); 948 949 cleanup: 950 if (rls != NULL) { 951 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), rls, 952 kCFRunLoopDefaultMode); 953 /* release the run loop source */ 954 CFRelease(rls); 955 } 956 if (socket != NULL) { 957 /* remove one socket reference, close the file descriptor */ 958 CFSocketInvalidate(socket); 959 960 /* release the socket */ 961 CFRelease(socket); 962 fd = -1; 963 } 964 if (fd >= 0) { 965 close(fd); 966 } 967 if (client != NULL) { 968 free(client); 969 } 970 if (ifl != NULL) { 971 ifl_free(&ifl); 972 } 973 if (if_p != NULL) { 974 if_free(&if_p); 975 } 976 if (system_id != NULL) { 977 free(system_id); 978 } 979 *status_p = status; 980 return (NULL); 981} 982 983/* 984 * Function: BSDPClientCreateWithInterface 985 * Purpose: 986 * Allocate a new session. 987 */ 988BSDPClientRef 989BSDPClientCreateWithInterface(BSDPClientStatus * status_p, 990 const char * ifname) 991{ 992 return (BSDPClientCreateWithInterfaceAndAttributes(status_p, ifname, 993 NULL, 0)); 994} 995 996/* 997 * Function: BSDPClientCreate 998 * Purpose: 999 * Published entry point to instantiate a BSDPClientRef over "en0". 1000 * XXX we should ask IOKit which interface is the primary. 1001 */ 1002BSDPClientRef 1003BSDPClientCreate(BSDPClientStatus * status_p) 1004{ 1005 return (BSDPClientCreateWithInterface(status_p, "en0")); 1006} 1007 1008void 1009BSDPClientFree(BSDPClientRef * client_p) 1010{ 1011 BSDPClientRef client; 1012 1013 if (client_p == NULL) { 1014 return; 1015 } 1016 client = *client_p; 1017 if (client == NULL) { 1018 return; 1019 } 1020 BSDPClientCancelTimer(client); 1021 if (client->socket != NULL) { 1022 /* remove one socket reference, close the file descriptor */ 1023 CFSocketInvalidate(client->socket); 1024 1025 /* release the socket */ 1026 CFRelease(client->socket); 1027 } 1028 if (client->rls != NULL) { 1029 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), client->rls, 1030 kCFRunLoopDefaultMode); 1031 /* release the run loop source */ 1032 CFRelease(client->rls); 1033 } 1034 if (client->if_p != NULL) { 1035 if_free(&client->if_p); 1036 } 1037 if (client->system_id != NULL) { 1038 free(client->system_id); 1039 } 1040 free(client); 1041 *client_p = NULL; 1042 return; 1043} 1044 1045/** 1046 ** BSDP List Routines 1047 **/ 1048static boolean_t 1049attributes_match(u_int16_t attrs, 1050 const u_int16_t * attrs_list, int n_attrs_list) 1051{ 1052 int i; 1053 1054 if (attrs_list == NULL || n_attrs_list == 0) { 1055 return (TRUE); 1056 } 1057 for (i = 0; i < n_attrs_list; i++) { 1058 if (attrs_list[i] == attrs) { 1059 return (TRUE); 1060 } 1061 } 1062 return (FALSE); 1063} 1064 1065static CFArrayRef 1066BSDPClientCreateImageList(BSDPClientRef client, 1067 bsdp_image_id_t default_image_id, 1068 bsdp_image_id_t selected_image_id, 1069 void * image_list, int image_list_len) 1070{ 1071 bsdp_image_description_t * descr; 1072 CFMutableArrayRef images = NULL; 1073 int length; 1074 1075 images = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 1076 descr = image_list; 1077 for (length = image_list_len; length > sizeof(*descr); ) { 1078 u_int16_t attributes; 1079 bsdp_image_id_t boot_image_id; 1080 CFMutableDictionaryRef this_dict = NULL; 1081 int this_len; 1082 CFStringRef cf_image_name = NULL; 1083 CFNumberRef cf_image_id = NULL; 1084 CFNumberRef cf_image_index = NULL; 1085 1086 this_len = sizeof(*descr) + descr->name_length; 1087 if (length < this_len) { 1088 fprintf(stderr, "short image list at offset %d\n", 1089 (int)((void *)descr - image_list)); 1090 goto failed; 1091 } 1092 boot_image_id = ntohl(*((bsdp_image_id_t *)descr->boot_image_id)); 1093 attributes = bsdp_image_attributes(boot_image_id); 1094 if (boot_image_id != BOOT_IMAGE_ID_NULL 1095 && attributes_match(htons(attributes), 1096 client->attrs, client->n_attrs)) { 1097 uint32_t index; 1098 1099 this_dict 1100 = CFDictionaryCreateMutable(NULL, 0, 1101 &kCFTypeDictionaryKeyCallBacks, 1102 &kCFTypeDictionaryValueCallBacks); 1103 cf_image_id = CFNumberCreate(NULL, kCFNumberSInt32Type, 1104 &boot_image_id); 1105 index = bsdp_image_index(boot_image_id); 1106 cf_image_index = CFNumberCreate(NULL, kCFNumberSInt32Type, 1107 &index); 1108 cf_image_name = CFStringCreateWithBytes(NULL, 1109 descr->name, 1110 descr->name_length, 1111 kCFStringEncodingUTF8, 1112 TRUE); 1113 if (this_dict != NULL && cf_image_id != NULL 1114 && cf_image_index != NULL && cf_image_name != NULL) { 1115 CFDictionarySetValue(this_dict, 1116 kBSDPImageDescriptionName, cf_image_name); 1117 CFDictionarySetValue(this_dict, 1118 kBSDPImageDescriptionIdentifier, 1119 cf_image_id); 1120 CFDictionarySetValue(this_dict, kBSDPImageDescriptionIndex, 1121 cf_image_index); 1122 if (attributes & BSDP_IMAGE_ATTRIBUTES_INSTALL) { 1123 CFDictionarySetValue(this_dict, 1124 kBSDPImageDescriptionIsInstall, 1125 kCFBooleanTrue); 1126 } 1127 if (boot_image_id == default_image_id) { 1128 CFDictionarySetValue(this_dict, 1129 kBSDPImageDescriptionIsDefault, 1130 kCFBooleanTrue); 1131 } 1132 if (boot_image_id == selected_image_id) { 1133 CFDictionarySetValue(this_dict, 1134 kBSDPImageDescriptionIsSelected, 1135 kCFBooleanTrue); 1136 } 1137 CFArrayAppendValue(images, this_dict); 1138 } 1139 my_CFRelease(&cf_image_index); 1140 my_CFRelease(&cf_image_id); 1141 my_CFRelease(&cf_image_name); 1142 my_CFRelease(&this_dict); 1143 } 1144 descr = ((void *)descr) + this_len; 1145 length -= this_len; 1146 } 1147 if (CFArrayGetCount(images) == 0) { 1148 goto failed; 1149 } 1150 return ((CFArrayRef)images); 1151 1152 failed: 1153 my_CFRelease(&images); 1154 return (NULL); 1155} 1156 1157static void 1158BSDPClientProcessList(BSDPClientRef client, struct in_addr server_ip, 1159 struct dhcp * reply, int reply_len, 1160 dhcpol_t * options_p, dhcpol_t * bsdp_options_p) 1161{ 1162 CFNumberRef cf_priority = NULL; 1163 CFStringRef cf_server_ip = NULL; 1164 bsdp_image_id_t default_image_id = BOOT_IMAGE_ID_NULL; 1165 void * image_list = NULL; 1166 int image_list_len = 0; 1167 void * opt; 1168 int opt_len; 1169 CFArrayRef images = NULL; 1170 uint32_t priority = 0; 1171 bsdp_image_id_t selected_image_id = BOOT_IMAGE_ID_NULL; 1172 1173 /* get the server priority */ 1174 opt = dhcpol_find(bsdp_options_p, bsdptag_server_priority_e, &opt_len, 1175 NULL); 1176 if (opt != NULL && opt_len == sizeof(bsdp_priority_t)) { 1177 priority = (uint32_t)ntohs(*((bsdp_priority_t *)opt)); 1178 } 1179 /* get the default boot image */ 1180 opt = dhcpol_find(bsdp_options_p, bsdptag_default_boot_image_e, &opt_len, 1181 NULL); 1182 if (opt != NULL && opt_len == sizeof(default_image_id)) { 1183 default_image_id = ntohl(*((bsdp_image_id_t *)opt)); 1184 } 1185 /* get the selected boot image */ 1186 opt = dhcpol_find(bsdp_options_p, bsdptag_selected_boot_image_e, &opt_len, 1187 NULL); 1188 if (opt && opt_len == sizeof(selected_image_id)) { 1189 selected_image_id = ntohl(*((bsdp_image_id_t *)opt)); 1190 } 1191 /* get the list of images */ 1192 image_list = dhcpol_option_copy(bsdp_options_p, bsdptag_boot_image_list_e, 1193 &image_list_len); 1194 if (image_list == NULL) { 1195 goto done; 1196 } 1197 cf_priority = CFNumberCreate(NULL, kCFNumberSInt32Type, &priority); 1198 cf_server_ip = CFStringCreateWithCString(NULL, inet_ntoa(server_ip), 1199 kCFStringEncodingASCII); 1200 images = BSDPClientCreateImageList(client, default_image_id, 1201 selected_image_id, 1202 image_list, image_list_len); 1203 if (images != NULL && cf_priority != NULL && cf_server_ip != NULL) { 1204 client->got_responses = TRUE; 1205 (*client->callback.func.list)(client, 1206 kBSDPClientStatusOK, 1207 cf_server_ip, 1208 cf_priority, 1209 images, 1210 client->callback.arg); 1211 } 1212 1213 done: 1214 my_CFRelease(&images); 1215 my_CFRelease(&cf_priority); 1216 my_CFRelease(&cf_server_ip); 1217 if (image_list != NULL) { 1218 free(image_list); 1219 } 1220 my_CFRelease(&images); 1221 return; 1222} 1223 1224static BSDPClientStatus 1225BSDPClientSendListRequest(BSDPClientRef client) 1226{ 1227 char bsdp_buf[DHCP_OPTION_SIZE_MAX]; 1228 dhcpoa_t bsdp_options; 1229 char buf[DHCP_PACKET_MIN]; 1230 struct in_addr ip_broadcast; 1231 dhcpoa_t options; 1232 uint16_t max_message_size = htons(RX_BUF_SIZE); /* max receive size */ 1233 unsigned char msgtype; 1234 u_int16_t port = htons(client->client_port); 1235 struct dhcp * request; 1236 int request_size = 0; 1237 BSDPClientStatus status = kBSDPClientStatusAllocationError; 1238 1239 ip_broadcast.s_addr = htonl(INADDR_BROADCAST); 1240 request = make_bsdp_request(client->system_id, 1241 (struct dhcp *)buf, sizeof(buf), 1242 dhcp_msgtype_inform_e, 1243 if_link_address(client->if_p), 1244 if_link_arptype(client->if_p), 1245 if_link_length(client->if_p), 1246 &options); 1247 if (request == NULL) { 1248 goto failed; 1249 } 1250 request->dp_xid = htonl(client->xid); 1251 request->dp_ciaddr = client->our_ip; 1252 dhcpoa_init_no_end(&bsdp_options, bsdp_buf, sizeof(bsdp_buf)); 1253 msgtype = bsdp_msgtype_list_e; 1254 if (dhcpoa_add(&bsdp_options, bsdptag_message_type_e, 1255 sizeof(msgtype), &msgtype) 1256 != dhcpoa_success_e) { 1257 fprintf(stderr, "BSDPClientSendListRequest add message type failed, %s", 1258 dhcpoa_err(&bsdp_options)); 1259 goto failed; 1260 } 1261 if (dhcpoa_add(&bsdp_options, bsdptag_version_e, 1262 sizeof(client->client_version), 1263 &client->client_version) != dhcpoa_success_e) { 1264 fprintf(stderr, "BSDPClientSendListRequest add version failed, %s", 1265 dhcpoa_err(&bsdp_options)); 1266 goto failed; 1267 } 1268 if (client->old_firmware == TRUE) { 1269 if (dhcpoa_add(&bsdp_options, bsdptag_netboot_1_0_firmware_e, 1270 0, NULL) != dhcpoa_success_e) { 1271 fprintf(stderr, "BSDPClientSendListRequest old_firmware failed, %s", 1272 dhcpoa_err(&bsdp_options)); 1273 goto failed; 1274 } 1275 } 1276 if (dhcpoa_add(&bsdp_options, bsdptag_reply_port_e, sizeof(port), 1277 &port) != dhcpoa_success_e) { 1278 fprintf(stderr, "BSDPClientSendListRequest add reply port failed, %s", 1279 dhcpoa_err(&bsdp_options)); 1280 goto failed; 1281 } 1282 if (dhcpoa_add(&bsdp_options, bsdptag_max_message_size_e, 1283 sizeof(max_message_size), &max_message_size) 1284 != dhcpoa_success_e) { 1285 fprintf(stderr, 1286 "BSDPClientSendListRequest add max message size failed, %s", 1287 dhcpoa_err(&bsdp_options)); 1288 goto failed; 1289 } 1290 if (client->n_attrs > 0) { 1291 if (dhcpoa_add(&bsdp_options,bsdptag_image_attributes_filter_list_e, 1292 client->n_attrs * sizeof(client->attrs[0]), 1293 client->attrs) != dhcpoa_success_e) { 1294 fprintf(stderr, 1295 "BSDPClientSendListRequest add image attributes failed, %s", 1296 dhcpoa_err(&bsdp_options)); 1297 goto failed; 1298 } 1299 } 1300 if (dhcpoa_add(&options, dhcptag_vendor_specific_e, 1301 dhcpoa_used(&bsdp_options), &bsdp_buf) 1302 != dhcpoa_success_e) { 1303 fprintf(stderr, 1304 "BSDPClientSendListRequest add vendor specific failed, %s", 1305 dhcpoa_err(&options)); 1306 goto failed; 1307 } 1308 if (dhcpoa_add(&options, dhcptag_end_e, 0, NULL) 1309 != dhcpoa_success_e) { 1310 fprintf(stderr, 1311 "BSDPClientSendListRequest add dhcp options end failed, %s", 1312 dhcpoa_err(&bsdp_options)); 1313 goto failed; 1314 } 1315 request_size = sizeof(*request) + sizeof(rfc_magic) 1316 + dhcpoa_used(&options); 1317 if (request_size < sizeof(struct bootp)) { 1318 /* pad out to BOOTP-sized packet */ 1319 request_size = sizeof(struct bootp); 1320 } 1321 if (bootp_transmit(client->fd, (char *)client->send_buf, 1322 if_name(client->if_p), ARPHRD_ETHER, NULL, 0, 1323 ip_broadcast, client->our_ip, 1324 IPPORT_BOOTPS, client->client_port, 1325 request, request_size) < 0) { 1326 fprintf(stderr, 1327 "BSDPClientSendListRequest: bootp_transmit failed %s\n", 1328 strerror(errno)); 1329 status = kBSDPClientStatusTransmitFailed; 1330 goto failed; 1331 } 1332 status = kBSDPClientStatusOK; 1333 1334 failed: 1335 return (status); 1336} 1337 1338static void 1339BSDPClientListTimeout(BSDPClientRef client) 1340{ 1341 BSDPClientStatus status = kBSDPClientStatusOK; 1342 struct timeval t; 1343 1344 if (client->try == BSDPCLIENT_LIST_MAX_TRIES) { 1345 if (client->got_responses == FALSE) { 1346 status = kBSDPClientStatusOperationTimedOut; 1347 goto report_error; 1348 } 1349 return; 1350 } 1351 client->try++; 1352 client->wait_secs *= 2; 1353 if (client->wait_secs > BSDPCLIENT_MAX_WAIT_SECS) { 1354 client->wait_secs = BSDPCLIENT_MAX_WAIT_SECS; 1355 } 1356 status = BSDPClientSendListRequest(client); 1357 if (status != kBSDPClientStatusOK) { 1358 goto report_error; 1359 } 1360 t.tv_sec = client->wait_secs; 1361 t.tv_usec = random_range(0, USECS_PER_SEC - 1); 1362 BSDPClientSetTimer(client, t, BSDPClientListTimeout); 1363 return; 1364 1365 report_error: 1366 (*client->callback.func.list)(client, status, NULL, NULL, 1367 NULL, client->callback.arg); 1368 return; 1369} 1370 1371BSDPClientStatus 1372BSDPClientList(BSDPClientRef client, BSDPClientListCallBack callback, 1373 void * info) 1374{ 1375 struct timeval t; 1376 BSDPClientStatus status = kBSDPClientStatusAllocationError; 1377 1378 client->state = kBSDPClientStateInit; 1379 BSDPClientCancelTimer(client); 1380 if (callback == NULL) { 1381 status = kBSDPClientStatusInvalidArgument; 1382 goto failed; 1383 } 1384 client->xid++; 1385 status = BSDPClientSendListRequest(client); 1386 if (status != kBSDPClientStatusOK) { 1387 goto failed; 1388 } 1389 client->state = kBSDPClientStateList; 1390 client->try = 1; 1391 client->got_responses = FALSE; 1392 client->callback.func.list = callback; 1393 client->callback.arg = info; 1394 client->wait_secs = BSDPCLIENT_INITIAL_TIMEOUT_SECS; 1395 t.tv_sec = client->wait_secs; 1396 t.tv_usec = random_range(0, USECS_PER_SEC - 1); 1397 BSDPClientSetTimer(client, t, BSDPClientListTimeout); 1398 1399 failed: 1400 return (status); 1401} 1402 1403/** 1404 ** BSDP Select Routines 1405 **/ 1406 1407static void 1408BSDPClientProcessSelect(BSDPClientRef client, bsdp_msgtype_t bsdp_msg) 1409{ 1410 BSDPClientStatus status; 1411 1412 BSDPClientCancelTimer(client); 1413 if (bsdp_msg == bsdp_msgtype_select_e) { 1414 status = kBSDPClientStatusOK; 1415 } 1416 else { 1417 status = kBSDPClientStatusServerSentFailure; 1418 } 1419 (*client->callback.func.select)(client, status, client->callback.arg); 1420 return; 1421} 1422 1423static BSDPClientStatus 1424BSDPClientSendSelectRequest(BSDPClientRef client) 1425{ 1426 char bsdp_buf[DHCP_OPTION_SIZE_MAX]; 1427 dhcpoa_t bsdp_options; 1428 char buf[DHCP_PACKET_MIN]; 1429 bsdp_image_id_t image_id = htonl(client->callback.image_identifier); 1430 struct in_addr ip_broadcast; 1431 dhcpoa_t options; 1432 unsigned char msgtype; 1433 u_int16_t port = htons(client->client_port); 1434 struct dhcp * request; 1435 int request_size = 0; 1436 BSDPClientStatus status = kBSDPClientStatusAllocationError; 1437 1438 ip_broadcast.s_addr = htonl(INADDR_BROADCAST); 1439 request = make_bsdp_request(client->system_id, 1440 (struct dhcp *)buf, sizeof(buf), 1441 dhcp_msgtype_inform_e, 1442 if_link_address(client->if_p), 1443 if_link_arptype(client->if_p), 1444 if_link_length(client->if_p), 1445 &options); 1446 if (request == NULL) { 1447 goto failed; 1448 } 1449 request->dp_xid = htonl(client->xid); 1450 request->dp_ciaddr = client->our_ip; 1451 dhcpoa_init_no_end(&bsdp_options, bsdp_buf, sizeof(bsdp_buf)); 1452 msgtype = bsdp_msgtype_select_e; 1453 if (dhcpoa_add(&bsdp_options, bsdptag_message_type_e, 1454 sizeof(msgtype), &msgtype) 1455 != dhcpoa_success_e) { 1456 fprintf(stderr, 1457 "BSDPClientSendSelectRequest add message type failed, %s", 1458 dhcpoa_err(&bsdp_options)); 1459 goto failed; 1460 } 1461 if (dhcpoa_add(&bsdp_options, bsdptag_version_e, 1462 sizeof(client->client_version), 1463 &client->client_version) != dhcpoa_success_e) { 1464 fprintf(stderr, "BSDPClientSendSelectRequest add version failed, %s", 1465 dhcpoa_err(&bsdp_options)); 1466 goto failed; 1467 } 1468 if (client->old_firmware == TRUE) { 1469 if (dhcpoa_add(&bsdp_options, bsdptag_netboot_1_0_firmware_e, 1470 0, NULL) != dhcpoa_success_e) { 1471 fprintf(stderr, "BSDPClientSendListRequest old_firmware failed, %s", 1472 dhcpoa_err(&bsdp_options)); 1473 goto failed; 1474 } 1475 } 1476 if (dhcpoa_add(&bsdp_options, bsdptag_reply_port_e, sizeof(port), 1477 &port) != dhcpoa_success_e) { 1478 fprintf(stderr, "BSDPClientSendSelectRequest add reply port failed, %s", 1479 dhcpoa_err(&bsdp_options)); 1480 goto failed; 1481 } 1482 if (dhcpoa_add(&bsdp_options, bsdptag_server_identifier_e, 1483 sizeof(struct in_addr), 1484 &client->callback.server_ip) != dhcpoa_success_e) { 1485 fprintf(stderr, 1486 "BSDPClientSendSelectRequest: add server identifier failed, %s", 1487 dhcpoa_err(&bsdp_options)); 1488 goto failed; 1489 } 1490 if (dhcpoa_add(&bsdp_options, bsdptag_selected_boot_image_e, 1491 sizeof(image_id), &image_id) != dhcpoa_success_e) { 1492 fprintf(stderr, 1493 "BSDPClientSendSelectRequest: add selected image failed, %s", 1494 dhcpoa_err(&bsdp_options)); 1495 goto failed; 1496 } 1497 if (dhcpoa_add(&options, dhcptag_vendor_specific_e, 1498 dhcpoa_used(&bsdp_options), &bsdp_buf) 1499 != dhcpoa_success_e) { 1500 fprintf(stderr, 1501 "BSDPClientSendSelectRequest add vendor specific failed, %s", 1502 dhcpoa_err(&options)); 1503 goto failed; 1504 } 1505 if (dhcpoa_add(&options, dhcptag_end_e, 0, NULL) 1506 != dhcpoa_success_e) { 1507 fprintf(stderr, 1508 "BSDPClientSendSelectRequest add dhcp options end failed, %s", 1509 dhcpoa_err(&bsdp_options)); 1510 goto failed; 1511 } 1512 request_size = sizeof(*request) + sizeof(rfc_magic) 1513 + dhcpoa_used(&options); 1514 if (request_size < sizeof(struct bootp)) { 1515 /* pad out to BOOTP-sized packet */ 1516 request_size = sizeof(struct bootp); 1517 } 1518 /* send the packet */ 1519 if (bootp_transmit(client->fd, (char *)client->send_buf, 1520 if_name(client->if_p), ARPHRD_ETHER, NULL, 0, 1521 ip_broadcast, client->our_ip, 1522 IPPORT_BOOTPS, client->client_port, 1523 request, request_size) < 0) { 1524 fprintf(stderr, 1525 "BSDPClientSendSelectRequest: bootp_transmit failed %s\n", 1526 strerror(errno)); 1527 status = kBSDPClientStatusTransmitFailed; 1528 goto failed; 1529 } 1530 status = kBSDPClientStatusOK; 1531 1532 failed: 1533 return (status); 1534} 1535 1536static void 1537BSDPClientSelectTimeout(BSDPClientRef client) 1538{ 1539 BSDPClientStatus status = kBSDPClientStatusOK; 1540 struct timeval t; 1541 1542 if (client->try == BSDPCLIENT_SELECT_MAX_TRIES) { 1543 status = kBSDPClientStatusOperationTimedOut; 1544 goto report_error; 1545 } 1546 client->try++; 1547 client->wait_secs *= 2; 1548 status = BSDPClientSendSelectRequest(client); 1549 if (status != kBSDPClientStatusOK) { 1550 goto report_error; 1551 } 1552 t.tv_sec = client->wait_secs; 1553 t.tv_usec = 0; 1554 BSDPClientSetTimer(client, t, BSDPClientSelectTimeout); 1555 return; 1556 1557 report_error: 1558 (*client->callback.func.select)(client, status, client->callback.arg); 1559 return; 1560} 1561 1562BSDPClientStatus 1563BSDPClientSelect(BSDPClientRef client, 1564 CFStringRef ServerAddress, 1565 CFNumberRef Identifier, 1566 BSDPClientSelectCallBack callback, void * info) 1567{ 1568 unsigned long image_identifier; 1569 struct timeval t; 1570 BSDPClientStatus status = kBSDPClientStatusAllocationError; 1571 1572 (void)my_CFStringToIPAddress(ServerAddress, &client->callback.server_ip); 1573 client->state = kBSDPClientStateInit; 1574 BSDPClientCancelTimer(client); 1575 if (callback == NULL 1576 || CFNumberGetValue(Identifier, kCFNumberLongType, 1577 &image_identifier) == FALSE) { 1578 status = kBSDPClientStatusInvalidArgument; 1579 goto failed; 1580 } 1581 client->xid++; 1582 client->callback.image_identifier = image_identifier; 1583 status = BSDPClientSendSelectRequest(client); 1584 if (status != kBSDPClientStatusOK) { 1585 goto failed; 1586 } 1587 client->state = kBSDPClientStateSelect; 1588 client->try = 1; 1589 client->callback.func.select = callback; 1590 client->callback.arg = info; 1591 client->wait_secs = BSDPCLIENT_INITIAL_TIMEOUT_SECS; 1592 t.tv_sec = client->wait_secs; 1593 t.tv_usec = 0; 1594 BSDPClientSetTimer(client, t, BSDPClientSelectTimeout); 1595 1596 failed: 1597 return (status); 1598} 1599 1600BSDPClientStatus 1601BSPPClientSelect(BSDPClientRef client, 1602 CFStringRef ServerAddress, 1603 CFNumberRef Identifier, 1604 BSDPClientSelectCallBack callback, void * info) 1605{ 1606 return (BSDPClientSelect(client, ServerAddress, Identifier, 1607 callback, info)); 1608} 1609 1610#ifdef TEST_BAD_SYSID 1611static void bad_sysid_callback(BSDPClientRef client, 1612 BSDPClientStatus status, 1613 CFStringRef ServerAddress, 1614 CFNumberRef ServerPriority, 1615 CFArrayRef list, 1616 void *info) 1617{ 1618 return; 1619} 1620 1621 1622int 1623main(int argc, char * argv[]) 1624{ 1625 BSDPClientStatus status; 1626 char * if_name = "en0"; 1627 BSDPClientRef client; 1628 1629 if (argc > 1) { 1630 if_name = argv[1]; 1631 } 1632 1633 client = BSDPClientCreateWithInterface(&status, if_name); 1634 if (client == NULL) { 1635 fprintf(stderr, "BSDPClientCreateWithInterface(%s) failed: %s\n", 1636 if_name, BSDPClientStatusString(status)); 1637 exit(2); 1638 } 1639 status = BSDPClientList(client, bad_sysid_callback, NULL); 1640 CFRunLoopRun(); 1641 exit (0); 1642 return (0); 1643} 1644#endif /* TEST_BAD_SYSID */ 1645