Skip to content

Instantly share code, notes, and snippets.

@sillywilly42
Created September 1, 2016 14:25
Show Gist options
  • Select an option

  • Save sillywilly42/7c1e3460281005dc07c613f5f84a4a9a to your computer and use it in GitHub Desktop.

Select an option

Save sillywilly42/7c1e3460281005dc07c613f5f84a4a9a to your computer and use it in GitHub Desktop.
Queries the local NetBoot service and displays response.
//
// 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