1
// Copyright 2007, Google Inc.
3
// Redistribution and use in source and binary forms, with or without
4
// modification, are permitted provided that the following conditions are met:
6
// 1. Redistributions of source code must retain the above copyright notice,
7
// this list of conditions and the following disclaimer.
8
// 2. Redistributions in binary form must reproduce the above copyright notice,
9
// this list of conditions and the following disclaimer in the documentation
10
// and/or other materials provided with the distribution.
11
// 3. Neither the name of Google Inc. nor the names of its contributors may be
12
// used to endorse or promote products derived from this software without
13
// specific prior written permission.
15
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
#import "gears/base/common/exception_handler_osx/nshost_macaddress.h"
28
#import <openssl/md5.h>
31
#import <CoreFoundation/CoreFoundation.h>
33
#import <IOKit/IOKitLib.h>
34
#import <IOKit/network/IOEthernetController.h>
35
#import <IOKit/network/IOEthernetInterface.h>
36
#import <IOKit/network/IONetworkInterface.h>
38
void GMLog(id x, ...) {}
39
#define GMDebuggerAssertAlways(x,y,z)
42
static kern_return_t FindEthernetInterfaces(io_iterator_t *matchingServices);
43
static kern_return_t GetMACAddress(io_iterator_t intfIterator,
44
UInt8 *MACAddress, UInt8 bufferSize);
46
@implementation NSHost (MACAddress)
48
// Return the MAC address of this host.
49
// This must be called from [NSHost currentHost];
50
// The result may be used as an ID which is unique to this host.
51
- (NSString *)MACAddress {
52
NSString *result = nil;
54
if (![self isEqualToHost:[[self class] currentHost]]) {
55
GMDebuggerAssertAlways(@"%@ isn't current host %@", self,
56
[[self class] currentHost]);
60
kern_return_t kernResult = KERN_SUCCESS;
62
io_iterator_t intfIterator;
63
UInt8 MACAddress[kIOEthernetAddressSize];
65
kernResult = FindEthernetInterfaces(&intfIterator);
67
if (KERN_SUCCESS != kernResult) {
71
kernResult = GetMACAddress(intfIterator, MACAddress, sizeof(MACAddress));
73
if (KERN_SUCCESS != kernResult) {
77
result = [NSString stringWithFormat:@"%02x:%02x:%02x:%02x:%02x:%02x",
78
MACAddress[0], MACAddress[1], MACAddress[2], MACAddress[3],
79
MACAddress[4], MACAddress[5] ];
83
IOObjectRelease(intfIterator); // Release the iterator.
88
// Return the MAC address of this host, obfuscated for privacy.
89
// This must be called from [NSHost currentHost];
90
// The result may be used as an ID which is unique to this host.
91
- (NSString *)obfuscatedMACAddress {
92
NSString *address = [self MACAddress];
94
if (!address) return nil;
96
const char *s = [address UTF8String];
100
MD5_Update(&c, s, strlen(s) );
102
unsigned char hash[16];
105
UInt32 *hash32 = (UInt32*)hash;
107
NSString *result = [NSString stringWithFormat:@"%04x%04x%04x%04x",
108
hash32[0], hash32[1], hash32[2], hash32[3] ];
115
// code adapted from Apple sample code GetPrimaryMACAddress.c
116
// http://developer.apple.com/samplecode/GetPrimaryMACAddress/listing1.html
119
// Returns an iterator containing the primary (built-in) Ethernet interface.
120
// The caller is responsible for
121
// releasing the iterator after the caller is done with it.
122
static kern_return_t FindEthernetInterfaces(io_iterator_t *matchingServices) {
123
kern_return_t kernResult;
124
CFMutableDictionaryRef matchingDict;
125
CFMutableDictionaryRef propertyMatchDict;
127
// Ethernet interfaces are instances of class kIOEthernetInterfaceClass.
128
// IOServiceMatching is a convenience function to create a dictionary with
129
// the key kIOProviderClassKey and the specified value.
130
matchingDict = IOServiceMatching(kIOEthernetInterfaceClass);
132
// Note that another option here would be:
133
// matchingDict = IOBSDMatching("en0");
135
if (NULL == matchingDict) {
136
GMLog(@"IOServiceMatching returned a NULL dictionary.\n");
139
// Each IONetworkInterface object has a Boolean property with the key
140
// kIOPrimaryInterface.
141
// Only the primary (built-in) interface has this property set to TRUE.
143
// IOServiceGetMatchingServices uses the default matching criteria
144
// defined by IOService. This considers only the following properties
145
// plus any family-specific matching in this order of precedence
146
// (see IOService::passiveMatch):
148
// kIOProviderClassKey (IOServiceMatching)
149
// kIONameMatchKey (IOServiceNameMatching)
150
// kIOPropertyMatchKey
152
// kIOMatchedServiceCountKey
153
// family-specific matching
154
// kIOBSDNameKey (IOBSDNameMatching)
155
// kIOLocationMatchKey
157
// The IONetworkingFamily does not define any family-specific matching.
158
// This means that in order to have IOServiceGetMatchingServices consider
159
// the kIOPrimaryInterface property, we must add that property
160
// to a separate dictionary and then add that to our matching dictionary
161
// specifying kIOPropertyMatchKey.
163
propertyMatchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
164
&kCFTypeDictionaryKeyCallBacks,
165
&kCFTypeDictionaryValueCallBacks);
167
if (NULL == propertyMatchDict) {
168
GMLog(@"CFDictionaryCreateMutable returned a NULL dictionary.\n");
171
// Set the value in the dictionary of the property with the
172
// given key, or add the key to the dictionary if it doesn't exist.
173
// This call retains the value object passed in.
174
CFDictionarySetValue(propertyMatchDict, CFSTR(kIOPrimaryInterface),
177
// Now add the dictionary containing the matching value for
178
// kIOPrimaryInterface to our main matching dictionary.
179
// This call will retain propertyMatchDict, so we can release our
180
// reference on propertyMatchDict after adding it to matchingDict.
181
CFDictionarySetValue(matchingDict, CFSTR(kIOPropertyMatchKey),
183
CFRelease(propertyMatchDict);
187
// IOServiceGetMatchingServices retains the returned iterator, so release
188
// the iterator when we're done with it.
189
// IOServiceGetMatchingServices also consumes a reference on the matching
190
// dictionary so we don't need to release the dictionary explicitly.
191
kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict,
193
if (KERN_SUCCESS != kernResult) {
194
GMLog(@"IOServiceGetMatchingServices returned 0x%08x\n", kernResult);
200
// Given an iterator across a set of Ethernet interfaces, return the MAC address
202
// If no interfaces are found the MAC address is set to an empty string.
203
// In this sample the iterator should contain just the primary interface.
204
static kern_return_t GetMACAddress( io_iterator_t intfIterator,
207
io_object_t intfService;
208
io_object_t controllerService;
209
kern_return_t kernResult = KERN_FAILURE;
211
// Make sure the caller provided enough buffer space. Protect against buffer
212
// overflow problems.
213
if (bufferSize < kIOEthernetAddressSize) {
217
// Initialize the returned address
218
bzero(MACAddress, bufferSize);
220
// IOIteratorNext retains the returned object,
221
// so release it when we're done with it.
222
while ((intfService = IOIteratorNext(intfIterator))) {
223
CFTypeRef MACAddressAsCFData;
225
// IONetworkControllers can't be found directly by the
226
// IOServiceGetMatchingServices call, since they are hardware nubs
227
// and do not participate in driver matching. In other words,
228
// registerService() is never called on them. So we've found the
229
// IONetworkInterface and will get its parent controller
230
// by asking for it specifically.
232
// IORegistryEntryGetParentEntry retains the returned object,
233
// so release it when we're done with it.
234
kernResult = IORegistryEntryGetParentEntry(intfService,
238
if (KERN_SUCCESS != kernResult) {
239
printf("IORegistryEntryGetParentEntry returned 0x%08x\n",
243
// Retrieve the MAC address property from the I/O Registry in
244
// the form of a CFData
245
MACAddressAsCFData = IORegistryEntryCreateCFProperty(
247
CFSTR(kIOMACAddress),
251
if (MACAddressAsCFData) {
252
// Get the raw bytes of the MAC address from the CFData
253
CFDataGetBytes(MACAddressAsCFData,
254
CFRangeMake(0, kIOEthernetAddressSize), MACAddress);
255
CFRelease(MACAddressAsCFData);
258
// Done with the parent Ethernet controller object so we release it.
259
IOObjectRelease(controllerService);
262
// Done with the Ethernet interface object so we release it.
263
IOObjectRelease(intfService);