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