PageRenderTime 93ms CodeModel.GetById 16ms app.highlight 73ms RepoModel.GetById 2ms app.codeStats 0ms

/core/externals/update-engine/Common/KSEthernetAddress.m

http://macfuse.googlecode.com/
Objective C | 243 lines | 112 code | 48 blank | 83 comment | 21 complexity | 1dbe0b1c3c6cf0d67b08e049778f3d70 MD5 | raw file
  1// Copyright 2008 Google Inc.
  2//
  3// Licensed under the Apache License, Version 2.0 (the "License");
  4// you may not use this file except in compliance with the License.
  5// You may obtain a copy of the License at
  6//
  7//     http://www.apache.org/licenses/LICENSE-2.0
  8//
  9// Unless required by applicable law or agreed to in writing, software
 10// distributed under the License is distributed on an "AS IS" BASIS,
 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12// See the License for the specific language governing permissions and
 13// limitations under the License.
 14
 15#import "KSEthernetAddress.h"
 16#import <openssl/md5.h>
 17
 18#import <CoreFoundation/CoreFoundation.h>
 19
 20#import <IOKit/IOKitLib.h>
 21#import <IOKit/network/IOEthernetController.h>
 22#import <IOKit/network/IOEthernetInterface.h>
 23#import <IOKit/network/IONetworkInterface.h>
 24
 25#import "GTMLogger.h"
 26
 27// Helper functions, at end of file.
 28static kern_return_t FindEthernetInterfaces(io_iterator_t *matchingServices);
 29static kern_return_t GetMACAddress(io_iterator_t intfIterator,
 30                                   UInt8 *MACAddress, UInt8 bufferSize);
 31
 32@implementation KSEthernetAddress
 33
 34// Return the MAC address of this host.
 35// The result may be used as an ID which is unique to this host.
 36+ (NSString *)ethernetAddress {
 37  NSString *result = nil;
 38
 39  kern_return_t kernResult = KERN_SUCCESS;
 40
 41  io_iterator_t intfIterator;
 42  UInt8 ethernetAddress[kIOEthernetAddressSize];
 43
 44  kernResult = FindEthernetInterfaces(&intfIterator);
 45
 46  if (kernResult != KERN_SUCCESS) {
 47    return nil;  // COV_NF_LINE
 48  } else {
 49    kernResult = GetMACAddress(intfIterator, ethernetAddress, 
 50                               sizeof(ethernetAddress));
 51
 52    if (kernResult != KERN_SUCCESS) {
 53      return nil;  // COV_NF_LINE
 54    } else {
 55      result = [NSString stringWithFormat:@"%02x:%02x:%02x:%02x:%02x:%02x",
 56                         ethernetAddress[0], ethernetAddress[1], 
 57                         ethernetAddress[2], ethernetAddress[3],
 58                         ethernetAddress[4], ethernetAddress[5]];
 59    }
 60  }
 61
 62  IOObjectRelease(intfIterator);  // Release the iterator.
 63
 64  return result;
 65}
 66
 67
 68// Return the MAC address of this host, obfuscated for privacy.
 69// The result may be used as an ID which is unique to this host.
 70+ (NSString *)obfuscatedEthernetAddress {
 71  NSString *address = [self ethernetAddress];
 72
 73  if (!address) return nil;
 74
 75  const char *s = [address UTF8String];
 76
 77  MD5_CTX c;
 78  MD5_Init(&c);
 79  MD5_Update(&c, s, strlen(s));
 80
 81  unsigned char hash[16];
 82  MD5_Final(hash, &c);
 83
 84  UInt32 *hash32 = (UInt32*)hash;
 85
 86  NSString *result = [NSString stringWithFormat:@"%04x%04x%04x%04x",
 87                               hash32[0], hash32[1], hash32[2], hash32[3] ];
 88  return result;
 89}
 90
 91@end  // KSEthernetAddress
 92
 93
 94// code adapted from Apple sample code GetPrimaryMACAddress.c
 95// http://developer.apple.com/samplecode/GetPrimaryMACAddress/listing1.html
 96//
 97
 98// Returns an iterator containing the primary (built-in) Ethernet interface.
 99// The caller is responsible for
100// releasing the iterator after the caller is done with it.
101static kern_return_t FindEthernetInterfaces(io_iterator_t *matchingServices) {
102  kern_return_t kernResult;
103  CFMutableDictionaryRef matchingDict;
104  CFMutableDictionaryRef propertyMatchDict;
105
106  // Ethernet interfaces are instances of class kIOEthernetInterfaceClass.
107  // IOServiceMatching is a convenience function to create a dictionary with
108  // the key kIOProviderClassKey and the specified value.
109  matchingDict = IOServiceMatching(kIOEthernetInterfaceClass);
110
111  // Note that another option here would be:
112  // matchingDict = IOBSDMatching("en0");
113
114  if (matchingDict == NULL) {
115    GTMLoggerError(@"IOServiceMatching returned a NULL dictionary.\n");  // COV_NF_LINE
116  } else {
117    // Each IONetworkInterface object has a Boolean property with the key
118    // kIOPrimaryInterface.
119    // Only the primary (built-in) interface has this property set to TRUE.
120
121    // IOServiceGetMatchingServices uses the default matching criteria
122    // defined by IOService. This considers only the following properties
123    // plus any family-specific matching in this order of precedence
124    // (see IOService::passiveMatch):
125    //
126    // kIOProviderClassKey (IOServiceMatching)
127    // kIONameMatchKey (IOServiceNameMatching)
128    // kIOPropertyMatchKey
129    // kIOPathMatchKey
130    // kIOMatchedServiceCountKey
131    // family-specific matching
132    // kIOBSDNameKey (IOBSDNameMatching)
133    // kIOLocationMatchKey
134
135    // The IONetworkingFamily does not define any family-specific matching.
136    // This means that in order to have IOServiceGetMatchingServices consider
137    // the kIOPrimaryInterface property, we must add that property
138    // to a separate dictionary and then add that to our matching dictionary
139    // specifying kIOPropertyMatchKey.
140
141    propertyMatchDict =
142      CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
143                                &kCFTypeDictionaryKeyCallBacks,
144                                &kCFTypeDictionaryValueCallBacks);
145    
146    if (propertyMatchDict == NULL) {
147      GTMLoggerError(@"CFDictionaryCreateMutable returned a NULL dictionary.\n");  // COV_NF_LINE
148    } else {
149      // Set the value in the dictionary of the property with the
150      // given key, or add the key to the dictionary if it doesn't exist.
151      // This call retains the value object passed in.
152      CFDictionarySetValue(propertyMatchDict, CFSTR(kIOPrimaryInterface),
153                           kCFBooleanTrue);
154
155      // Now add the dictionary containing the matching value for
156      // kIOPrimaryInterface to our main matching dictionary.
157      // This call will retain propertyMatchDict, so we can release our
158      // reference on propertyMatchDict after adding it to matchingDict.
159      CFDictionarySetValue(matchingDict, CFSTR(kIOPropertyMatchKey),
160                           propertyMatchDict);
161      CFRelease(propertyMatchDict);
162    }
163  }
164
165  // IOServiceGetMatchingServices retains the returned iterator, so release
166  // the iterator when we're done with it.
167  // IOServiceGetMatchingServices also consumes a reference on the matching
168  // dictionary so we don't need to release the dictionary explicitly.
169  kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict,
170                                            matchingServices);
171  if (kernResult != KERN_SUCCESS) {
172    GTMLoggerError(@"IOServiceGetMatchingServices returned 0x%08x\n",
173                   kernResult);  // COV_NF_LINE
174  }
175
176  return kernResult;
177}
178
179// Given an iterator across a set of Ethernet interfaces, return the MAC address
180// of the last one.
181// If no interfaces are found the MAC address is set to an empty string.
182// In this sample the iterator should contain just the primary interface.
183static kern_return_t GetMACAddress(io_iterator_t intfIterator,
184                                   UInt8 *ethernetAddress,
185                                   UInt8 bufferSize) {
186  io_object_t intfService;
187  io_object_t controllerService;
188  kern_return_t kernResult = KERN_FAILURE;
189
190  // Make sure the caller provided enough buffer space. Protect against buffer
191  // overflow problems.
192  if (bufferSize < kIOEthernetAddressSize) {
193    return kernResult;  // COV_NF_LINE
194  }
195
196  // Initialize the returned address
197  bzero(ethernetAddress, bufferSize);
198
199  // IOIteratorNext retains the returned object,
200  // so release it when we're done with it.
201  while ((intfService = IOIteratorNext(intfIterator))) {
202    CFTypeRef ethernetAddressAsCFData;
203
204    // IONetworkControllers can't be found directly by the
205    // IOServiceGetMatchingServices call, since they are hardware nubs
206    // and do not participate in driver matching. In other words,
207    // registerService() is never called on them. So we've found the
208    // IONetworkInterface and will get its parent controller
209    // by asking for it specifically.
210
211    // IORegistryEntryGetParentEntry retains the returned object,
212    // so release it when we're done with it.
213    kernResult = IORegistryEntryGetParentEntry(intfService,
214                                               kIOServicePlane,
215                                               &controllerService);
216
217    if (kernResult != KERN_SUCCESS) {
218      GTMLoggerError(@"IORegistryEntryGetParentEntry returned 0x%08x\n",
219                     kernResult);  // COV_NF_LINE
220    } else {
221      // Retrieve the MAC address property from the I/O Registry in
222      // the form of a CFData
223      ethernetAddressAsCFData = IORegistryEntryCreateCFProperty(
224        controllerService, CFSTR(kIOMACAddress), kCFAllocatorDefault, 0);
225      
226      if (ethernetAddressAsCFData) {
227        // Get the raw bytes of the MAC address from the CFData
228        CFDataGetBytes(ethernetAddressAsCFData,
229                       CFRangeMake(0, kIOEthernetAddressSize), ethernetAddress);
230        CFRelease(ethernetAddressAsCFData);
231      }
232
233      // Done with the parent Ethernet controller object so we release it.
234      IOObjectRelease(controllerService);
235    }
236
237    // Done with the Ethernet interface object so we release it.
238    IOObjectRelease(intfService);
239  }
240
241  return kernResult;
242}
243