Created
September 1, 2016 14:25
-
-
Save sillywilly42/7c1e3460281005dc07c613f5f84a4a9a to your computer and use it in GitHub Desktop.
Queries the local NetBoot service and displays response.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // BSDPQuery.m | |
| // bsdpobjc | |
| // | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <errno.h> | |
| #include <sys/socket.h> | |
| #include <netinet/in.h> | |
| #include <arpa/inet.h> | |
| #include <unistd.h> | |
| #import "BSDPQuery.h" | |
| struct DHCPHeader { | |
| uint8_t op; | |
| uint8_t htype; | |
| uint8_t hlen; | |
| uint8_t hops; | |
| uint32_t xid; | |
| uint16_t secs; | |
| uint16_t flags; | |
| uint8_t ciaddr[4]; | |
| uint8_t yiaddr[4]; | |
| uint8_t siaddr[4]; | |
| uint8_t giaddr[4]; | |
| uint8_t chaddr[16]; | |
| uint8_t sname[64]; | |
| uint8_t file[128]; | |
| uint8_t cookie[4]; | |
| uint8_t options[312]; | |
| }; | |
| typedef struct DHCPHeader DHCPHeader; | |
| union { | |
| uint8_t array[2]; | |
| uint16 bytes; | |
| } converter; | |
| @interface BSDPQuery () | |
| @property GCDAsyncUdpSocket *socket; | |
| @property int xid; | |
| @end | |
| @implementation BSDPQuery | |
| - (instancetype)init { | |
| self = [super init]; | |
| if (self) { | |
| srand((int)time(NULL)); | |
| _xid = rand(); | |
| _socket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self | |
| delegateQueue:dispatch_get_main_queue()]; | |
| NSError *error = nil; | |
| if (![_socket bindToPort:993 error:&error]) { | |
| NSLog(@"Error binding: %@", error); | |
| return self; | |
| } | |
| if (![_socket beginReceiving:&error]) { | |
| NSLog(@"Error receiving: %@", error); | |
| return self; | |
| } | |
| [self sendBSDPPacket]; | |
| [NSTimer scheduledTimerWithTimeInterval:5.0 | |
| target:self | |
| selector:@selector(exit) | |
| userInfo:nil | |
| repeats:NO]; | |
| } | |
| return self; | |
| } | |
| - (unsigned char *)getIP { | |
| const char *google_dns_server = "8.8.8.8"; | |
| int dns_port = 53; | |
| struct sockaddr_in serv; | |
| int sock = socket (AF_INET, SOCK_DGRAM, 0); | |
| // Socket could not be created | |
| if(sock < 0) { | |
| perror("Socket error"); | |
| } | |
| memset(&serv, 0, sizeof(serv)); | |
| serv.sin_family = AF_INET; | |
| serv.sin_addr.s_addr = inet_addr(google_dns_server); | |
| serv.sin_port = htons(dns_port); | |
| int err = connect(sock, (const struct sockaddr *) &serv, sizeof(serv)); | |
| struct sockaddr_in name; | |
| socklen_t namelen = sizeof(name); | |
| err = getsockname(sock, (struct sockaddr *)&name, &namelen); | |
| unsigned char *ip = (unsigned char *)&name.sin_addr.s_addr; | |
| close(sock); | |
| return ip; | |
| } | |
| - (void)sendBSDPPacket { | |
| NSMutableData *packet; | |
| DHCPHeader *dhcpPtr; | |
| packet = [NSMutableData dataWithLength:sizeof(*dhcpPtr)]; | |
| assert(packet != nil); | |
| dhcpPtr = packet.mutableBytes; | |
| dhcpPtr->op = 1; // Message type: Boot Request (1) | |
| dhcpPtr->htype = 1; // Hardware type: Ethernet | |
| dhcpPtr->hlen = 6; // Hardware address length: 6 | |
| dhcpPtr->xid = self.xid; | |
| unsigned char *ip = [self getIP]; | |
| dhcpPtr->ciaddr[0] = ip[0]; | |
| dhcpPtr->ciaddr[1] = ip[1]; | |
| dhcpPtr->ciaddr[2] = ip[2]; | |
| dhcpPtr->ciaddr[3] = ip[3]; | |
| dhcpPtr->chaddr[0] = 0xa9; | |
| dhcpPtr->chaddr[1] = 0x21; | |
| dhcpPtr->chaddr[2] = 0x67; | |
| dhcpPtr->chaddr[3] = 0x02; | |
| dhcpPtr->chaddr[4] = 0x5c; | |
| dhcpPtr->chaddr[5] = 0xa9; | |
| static const uint8_t cookie[4] = {0x63, 0x82, 0x53, 0x63}; | |
| memcpy(dhcpPtr->cookie, cookie, sizeof(cookie)); | |
| // Op: 53, Len: 1, DHCP Message Type = DHCP INFORM[LIST] | |
| static const uint8_t option53[3] = {0x35, 0x01, 0x08}; | |
| memcpy(dhcpPtr->options, option53, sizeof(option53)); | |
| // Op: 55, Len: 2, Parameter Request List - Option 60 (3c) and 43 (2b) | |
| static const uint8_t option55[4] = {0x37, 0x02, 0x3c, 0x2b}; | |
| memcpy(dhcpPtr->options + 3, option55, sizeof(option55)); | |
| // Op: 57, Len: 2, Max DHCP message size: 1500 | |
| static const uint8_t option57[4] = {0x39, 0x02, 0x05, 0xdc}; | |
| memcpy(dhcpPtr->options + 7, option57, sizeof(option57)); | |
| // Op: 60, Len: 23, Client identifier APLBSDPC/i386/iMac14,2 | |
| static const uint8_t option60[25] = | |
| {0x3c, 0x17, 0x41, 0x41, 0x50, 0x4c, 0x42, 0x53, 0x44, 0x50, 0x43, 0x2f, 0x69, 0x33, 0x38, | |
| 0x36, 0x2f, 0x69, 0x4d, 0x61, 0x63, 0x31, 0x34, 0x2c, 0x32}; | |
| memcpy(dhcpPtr->options + 11, option60, sizeof(option60)); | |
| static const uint8_t option43[17] = | |
| {0x2b, 0x0f, // DHCP Op: 43, Len: 15 Vendor Specific Information | |
| 0x01, 0x01, 0x01, // BDSP Op: 1, Len: 1 BSDP Message Type (1 = LIST) | |
| 0x02, 0x02, 0x01, 0x01, // BDSP Op: 2, Len: 2 BSDP Version (1.1) | |
| 0x05, 0x02, 0x03, 0xe1, // BDSP Op: 5, Len: 2 BSDP Reply Port (Set to 993) | |
| 0x0c, 0x02, 0x20, 0x00}; // BDSP Op: 12, Len: 2 BSDP Maximum Message Size | |
| memcpy(dhcpPtr->options + 36, option43, sizeof(option43)); | |
| // End of options. | |
| dhcpPtr->options[53] = 0xff; | |
| //[self parsePacket:packet]; | |
| [self.socket enableBroadcast:YES error:nil]; | |
| [self.socket sendData:packet toHost:@"255.255.255.255" port:67 withTimeout:-1 tag:0]; | |
| } | |
| - (NSDictionary *)parsePacket:(NSData *)packet { | |
| const struct DHCPHeader *dhcpHeader; | |
| dhcpHeader = (const DHCPHeader *)packet.bytes; | |
| NSDictionary *data = @{ | |
| @"DHCP OP": @(dhcpHeader->op), | |
| @"DHCP HTYPE": @(dhcpHeader->htype), | |
| @"DHCP HLEN": @(dhcpHeader->hlen), | |
| @"DHCP HOPS": @(dhcpHeader->hops), | |
| @"DHCP XID": @(dhcpHeader->xid), | |
| @"DHCP SECS": @(dhcpHeader->secs), | |
| @"DHCP FLAGS": @(dhcpHeader->flags), | |
| @"DHCP CIADDR": [NSString stringWithFormat:@"%hhu.%hhu.%hhu.%hhu", | |
| dhcpHeader->ciaddr[0], dhcpHeader->ciaddr[1], | |
| dhcpHeader->ciaddr[2], dhcpHeader->ciaddr[3]], | |
| @"DHCP YIADDR": [NSString stringWithFormat:@"%hhu.%hhu.%hhu.%hhu", | |
| dhcpHeader->yiaddr[0], dhcpHeader->yiaddr[1], | |
| dhcpHeader->yiaddr[2], dhcpHeader->yiaddr[3]], | |
| @"DHCP SIADDR": [NSString stringWithFormat:@"%hhu.%hhu.%hhu.%hhu", | |
| dhcpHeader->siaddr[0], dhcpHeader->siaddr[1], | |
| dhcpHeader->siaddr[2], dhcpHeader->siaddr[3]], | |
| @"DHCP GIADDR": [NSString stringWithFormat:@"%hhu.%hhu.%hhu.%hhu", | |
| dhcpHeader->giaddr[0], dhcpHeader->giaddr[1], | |
| dhcpHeader->giaddr[2], dhcpHeader->giaddr[3]], | |
| @"DHCP CHADDR": [NSString stringWithFormat:@"%X:%X:%X:%X:%X:%X", | |
| dhcpHeader->chaddr[0], dhcpHeader->chaddr[1], dhcpHeader->chaddr[2], | |
| dhcpHeader->chaddr[3], dhcpHeader->chaddr[4], dhcpHeader->chaddr[5]], | |
| @"DHCP SNAME": [NSString stringWithCString:(const char *)dhcpHeader->sname | |
| encoding:NSASCIIStringEncoding], | |
| @"DHCP FILE": [NSString stringWithCString:(const char *)dhcpHeader->file | |
| encoding:NSASCIIStringEncoding], | |
| @"DHCP MAGIC COOKIE": [NSString stringWithFormat:@"%X-%X-%X-%X", | |
| dhcpHeader->cookie[0], dhcpHeader->cookie[1], | |
| dhcpHeader->cookie[2], dhcpHeader->cookie[3]], | |
| @"DHCP OPTIONS": [self parseOptions:dhcpHeader->options], | |
| }; | |
| return data; | |
| } | |
| - (NSDictionary *)parseOptions:(uint8_t const[312])data { | |
| int position = 0; | |
| NSMutableDictionary *options = [[NSMutableDictionary alloc] initWithCapacity:4]; | |
| while (position < 312) { | |
| uint8_t option = data[position]; | |
| position++; | |
| if (option == 255) { | |
| break; | |
| } else if (option == 0) { | |
| continue; | |
| } | |
| uint8_t length = data[position]; | |
| position++; | |
| uint8_t value[length]; | |
| memcpy(value, data + position, length); | |
| if (option == 60) { | |
| options[@"Class-identifier"] = [NSString stringWithCString:(const char *)value | |
| encoding:NSASCIIStringEncoding]; | |
| } else if (option == 54) { | |
| NSString *ipAddress = [NSString stringWithFormat:@"%d.%d.%d.%d", | |
| value[0], value[1], value[2], value[3]]; | |
| options[@"Server Identifier"] = ipAddress; | |
| } else if (option == 53) { | |
| NSString *dhcpMessageType; | |
| switch (value[0]) { | |
| case 1: | |
| dhcpMessageType = @"DHCPDISCOVER"; | |
| break; | |
| case 2: | |
| dhcpMessageType = @"DHCPOFFER"; | |
| break; | |
| case 3: | |
| dhcpMessageType = @"DHCPREQUEST"; | |
| break; | |
| case 4: | |
| dhcpMessageType = @"DHCPDECLINE"; | |
| break; | |
| case 5: | |
| dhcpMessageType = @"DHCPACK"; | |
| break; | |
| case 6: | |
| dhcpMessageType = @"DHCPNAK"; | |
| break; | |
| case 7: | |
| dhcpMessageType = @"DHCPRELEASE"; | |
| break; | |
| case 8: | |
| dhcpMessageType = @"DHCPINFORM"; | |
| break; | |
| default: | |
| dhcpMessageType = @"UNKNOWN"; | |
| break; | |
| } | |
| options[@"DHCP Message Type"] = dhcpMessageType; | |
| } else if (option == 43) { | |
| options[@"Vendor Specific Information"] = [self parseVendorInfo:value length:length]; | |
| } | |
| position += length; | |
| //NSLog(@"DHCP Option: %d Length: %d", option, length); | |
| } | |
| return options; | |
| } | |
| - (NSDictionary *)parseVendorInfo:(uint8_t *)data length:(int)length { | |
| NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithCapacity:5]; | |
| int position = 0; | |
| while (position < length) { | |
| uint8_t code = data[position]; | |
| position++; | |
| uint8_t length = data[position]; | |
| position++; | |
| uint8_t value[length]; | |
| memcpy(value, data + position, length); | |
| if (code == 1) { | |
| NSString *bsdpMessageType; | |
| switch (value[0]) { | |
| case 1: | |
| bsdpMessageType = @"LIST"; | |
| break; | |
| case 2: | |
| bsdpMessageType = @"SELECT"; | |
| break; | |
| case 3: | |
| bsdpMessageType = @"FAILED"; | |
| break; | |
| default: | |
| bsdpMessageType = @"UNKNOWN"; | |
| break; | |
| } | |
| result[@"BSDP Message Type"] = bsdpMessageType; | |
| } else if (code == 4) { | |
| converter.array[0] = value[1]; | |
| converter.array[1] = value[0]; | |
| result[@"BSDP Server Priority"] = @(converter.bytes); | |
| } else if (code == 7) { | |
| result[@"BSDP Default Boot Image ID"] = [self parseBootImageID:value]; | |
| } else if (code == 9) { | |
| result[@"BSDP Boot Image List"] = [self parseImageList:value length:length]; | |
| } | |
| position += length; | |
| //NSLog(@"Code: %d Length: %d", code, length); | |
| } | |
| return result; | |
| } | |
| - (NSArray *)parseImageList:(uint8_t *)data length:(int)length{ | |
| NSMutableArray *images = [[NSMutableArray alloc] initWithCapacity:5]; | |
| int position = 0; | |
| while (position < length) { | |
| NSMutableDictionary *image = [[NSMutableDictionary alloc] initWithCapacity:2]; | |
| uint8_t id_number[4]; | |
| memcpy(id_number, data + position, 4); | |
| image[@"id"] = [self parseBootImageID:id_number]; | |
| position += 4; | |
| uint8_t nameLength = data[position]; | |
| position++; | |
| uint8_t name[nameLength]; | |
| memcpy(name, data + position, nameLength); | |
| position += nameLength; | |
| image[@"name"] = [[NSString alloc] initWithBytes:name length:nameLength encoding:NSUTF8StringEncoding]; | |
| [images addObject:image]; | |
| } | |
| return images; | |
| } | |
| - (NSDictionary *)parseBootImageID:(uint8_t *)value { | |
| NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithCapacity:2]; | |
| result[@"Installable Image"] = @((value[0] >> 7 & 1) == 1); | |
| NSString *attributes; | |
| switch (value[0] & ~128) { | |
| case 0: | |
| attributes = @"Mac OS 9"; | |
| break; | |
| case 1: | |
| attributes = @"Mac OS X"; | |
| break; | |
| case 2: | |
| attributes = @"Mac OS X Server"; | |
| break; | |
| case 3: | |
| attributes = @"Hardware Diagnostics"; | |
| break; | |
| default: | |
| attributes = @"UNKNOWN"; | |
| break; | |
| } | |
| result[@"Type"] = attributes; | |
| converter.array[0] = value[3]; | |
| converter.array[1] = value[2]; | |
| result[@"Index"] = @(converter.bytes); | |
| return result; | |
| } | |
| - (void)udpSocket:(GCDAsyncUdpSocket *)sock | |
| didReceiveData:(NSData *)data | |
| fromAddress:(NSData *)address | |
| withFilterContext:(id)filterContext { | |
| NSDictionary *packet = [self parsePacket:data]; | |
| if ([packet[@"DHCP XID"] isEqual: @(self.xid)]) { | |
| NSData *plist = [NSPropertyListSerialization dataWithPropertyList:(id)packet | |
| format:NSPropertyListXMLFormat_v1_0 | |
| options:0 | |
| error:nil]; | |
| fprintf(stdout, "%s\n", [[[NSString alloc] initWithData:plist | |
| encoding:NSUTF8StringEncoding] UTF8String]); | |
| exit(0); | |
| } | |
| } | |
| - (void)exit { | |
| exit(1); | |
| } | |
| @end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment