In one of my current projects I had to implement client-server communication and protection by MAC address when client machine can’t connect to server if its MAC address is not allowed, regardless of network or broadband connection. But what was a surprise that wxWidgets does not have API which allows obtaining MAC address in cross-platform way. So, I decided to write a small class which allows obtainig MAC address for Windows, Linux, Mac OS and Windows Mobile. Here it is:
MACAddressUtility.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #ifndef _MACADDRESS_UTILITY_H #define _MACADDRESS_UTILITY_H class MACAddressUtility { public : static long GetMACAddress(unsigned char * result); private : #if defined(WIN32) || defined(UNDER_CE) static long GetMACAddressMSW(unsigned char * result); #elif defined(__APPLE__) static long GetMACAddressMAC(unsigned char * result); #elif defined(LINUX) || defined(linux) static long GetMACAddressLinux(unsigned char * result); #endif }; #endif |
MACAddressUtility.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | #include "MACAddressUtility.h" #include <stdio.h> #if defined(WIN32) || defined(UNDER_CE) # include <windows.h> # if defined(UNDER_CE) # include <Iphlpapi.h> # endif #elif defined(__APPLE__) # include <CoreFoundation/CoreFoundation.h> # include <IOKit/IOKitLib.h> # include <IOKit/network/IOEthernetInterface.h> # include <IOKit/network/IONetworkInterface.h> # include <IOKit/network/IOEthernetController.h> #elif defined(LINUX) || defined(linux) # include <string.h> # include <net/if.h> # include <sys/ioctl.h> # include <sys/socket.h> # include <arpa/inet.h> #endif long MACAddressUtility::GetMACAddress(unsigned char * result) { // Fill result with zeroes memset (result, 0, 6); // Call appropriate function for each platform #if defined(WIN32) || defined(UNDER_CE) return GetMACAddressMSW(result); #elif defined(__APPLE__) return GetMACAddressMAC(result); #elif defined(LINUX) || defined(linux) return GetMACAddressLinux(result); #endif // If platform is not supported then return error code return -1; } #if defined(WIN32) || defined(UNDER_CE) inline long MACAddressUtility::GetMACAddressMSW(unsigned char * result) { #if defined(UNDER_CE) IP_ADAPTER_INFO AdapterInfo[16]; // Allocate information DWORD dwBufLen = sizeof (AdapterInfo); // Save memory size of buffer if (GetAdaptersInfo(AdapterInfo, &dwBufLen) == ERROR_SUCCESS) { memcpy (result, AdapterInfo->Address, 6); } else return -1; #else UUID uuid ; if (UuidCreateSequential(& uuid ) == RPC_S_UUID_NO_ADDRESS) return -1; memcpy (result, ( char *)( uuid .Data4+2), 6); #endif return 0; } #elif defined(__APPLE__) static kern_return_t FindEthernetInterfaces(io_iterator_t *matchingServices) { kern_return_t kernResult; CFMutableDictionaryRef matchingDict; CFMutableDictionaryRef propertyMatchDict; matchingDict = IOServiceMatching(kIOEthernetInterfaceClass); if (NULL != matchingDict) { propertyMatchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (NULL != propertyMatchDict) { CFDictionarySetValue(propertyMatchDict, CFSTR(kIOPrimaryInterface), kCFBooleanTrue); CFDictionarySetValue(matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict); CFRelease(propertyMatchDict); } } kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, matchingServices); return kernResult; } static kern_return_t GetMACAddress(io_iterator_t intfIterator, UInt8 *MACAddress, UInt8 bufferSize) { io_object_t intfService; io_object_t controllerService; kern_return_t kernResult = KERN_FAILURE; if (bufferSize < kIOEthernetAddressSize) { return kernResult; } bzero(MACAddress, bufferSize); while (intfService = IOIteratorNext(intfIterator)) { CFTypeRef MACAddressAsCFData; // IONetworkControllers can't be found directly by the IOServiceGetMatchingServices call, // since they are hardware nubs and do not participate in driver matching. In other words, // registerService() is never called on them. So we've found the IONetworkInterface and will // get its parent controller by asking for it specifically. // IORegistryEntryGetParentEntry retains the returned object, // so release it when we're done with it. kernResult = IORegistryEntryGetParentEntry(intfService, kIOServicePlane, &controllerService); if (KERN_SUCCESS != kernResult) { printf ( "IORegistryEntryGetParentEntry returned 0x%08x\n" , kernResult); } else { // Retrieve the MAC address property from the I/O Registry in the form of a CFData MACAddressAsCFData = IORegistryEntryCreateCFProperty(controllerService, CFSTR(kIOMACAddress), kCFAllocatorDefault, 0); if (MACAddressAsCFData) { CFShow(MACAddressAsCFData); // for display purposes only; output goes to stderr // Get the raw bytes of the MAC address from the CFData CFDataGetBytes((CFDataRef)MACAddressAsCFData, CFRangeMake(0, kIOEthernetAddressSize), MACAddress); CFRelease(MACAddressAsCFData); } // Done with the parent Ethernet controller object so we release it. ( void ) IOObjectRelease(controllerService); } // Done with the Ethernet interface object so we release it. ( void ) IOObjectRelease(intfService); } return kernResult; } long MACAddressUtility::GetMACAddressMAC(unsigned char * result) { io_iterator_t intfIterator; kern_return_t kernResult = KERN_FAILURE; do { kernResult = ::FindEthernetInterfaces(&intfIterator); if (KERN_SUCCESS != kernResult) break ; kernResult = ::GetMACAddress(intfIterator, (UInt8*)result, 6); } while ( false ); ( void ) IOObjectRelease(intfIterator); } #elif defined(LINUX) || defined(linux) long MACAddressUtility::GetMACAddressLinux(unsigned char * result) { struct ifreq ifr; struct ifreq *IFR; struct ifconf ifc; char buf[1024]; int s, i; int ok = 0; s = socket(AF_INET, SOCK_DGRAM, 0); if (s == -1) { return -1; } ifc.ifc_len = sizeof (buf); ifc.ifc_buf = buf; ioctl(s, SIOCGIFCONF, &ifc); IFR = ifc.ifc_req; for (i = ifc.ifc_len / sizeof ( struct ifreq); --i >= 0; IFR++) { strcpy (ifr.ifr_name, IFR->ifr_name); if (ioctl(s, SIOCGIFFLAGS, &ifr) == 0) { if (! (ifr.ifr_flags & IFF_LOOPBACK)) { if (ioctl(s, SIOCGIFHWADDR, &ifr) == 0) { ok = 1; break ; } } } } shutdown(s, SHUT_RDWR); if (ok) { bcopy( ifr.ifr_hwaddr.sa_data, result, 6); } else { return -1; } return 0; } #endif |
wxMACAddressUtility.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #ifndef _WX_MACADDRESS_UTILITY_H #define _WX_MACADDRESS_UTILITY_H #include "MACAddressUtility.h" #include <wx/wx.h> class wxMACAddressUtility { public : static wxString GetMACAddress() { unsigned char result[6]; if (MACAddressUtility::GetMACAddress(result) == 0) { return wxString::Format(wxT( "%02X:%02X:%02X:%02X:%02X:%02X" ), (unsigned int )result[0], (unsigned int )result[1], (unsigned int )result[2], (unsigned int )result[3], (unsigned int )result[4], (unsigned int )result[5]); } return wxEmptyString; } }; #endif |
Sample of usage:
1 | m_MACAddress = wxMACAddressUtility::GetMACAddress(); |
As you can see, core class does not depend on wxWidgets and can be used in native applications written without wxWidgets.
3 Comments
Alatar
А можно немного покритиковать?
1) Если уж делаем кросплатформенно, то пусть будет кроссплатформенно, а то что это такое? Под виндой у меня не собирается, говорит, что не знает кто такой UuidCreateSequential, про мою любимую FreeBSD вообще забыли… Под Линуксом и Маком не проверял – под рукой не оказалось. Кстати, если не секрет, зачем так сложно под Mac?
Короче, мой вариант см тут – http://paste.org/pastebin/view/10239
источники, из которых собирал:
http://support.microsoft.com/kb/118623
http://www.cs.williams.edu/~morgan/code/C++/getip.cpp
2) Что вообще такое MAC-адрес компьютера? MAC-адрес может быть сетевой карточки, а их в компе может быть несколько. Получается, адрес первой попавшейся карточки. А если я захочу ещё одну сетевушку воткнуть? Или VPN поднять? Или ещё чего? Аутентификация может начать обламывать, что не есть хорошо. =) Для идентификации лучше бы использовать что-нить типа идентификатора материнки, хотя это, конечно, уже сложнее. Что же касается этих функций – неплохо бы было в качестве параметра передовать интерфейс, для которого нужно получить MAC. Либо IP – по нему найти нужный интерфейс уже не сложно.
Всё это, конечно, моё личное ИМХО =)
T-Rex
Под винду не собирается? А какая у вас винда?
http://msdn.microsoft.com/en-us/library/aa379322%28VS.85%29.aspx
говорит что минимальное требование Windows 2000 Professional.
Ммм? Так это.. не надо было мне ФриБСД, только линух/мак/винда. Если есть вариант под BSD – ок.
я только за.
Угу.. может. Задача у меня стояла как “запретить повторный коннект с одного мак адреса”. Если два раза одним и тем же алгоритмом выбрать первый мак-адрес то не важно какой там интерфейс идет первым.
Ну а так – код в public domain – каждый дорабатывает как хочет.
Даете вариант с параметром? Согласен выложить для общего обозрения если есть
И еще, на пасторге сорцы долго хранятся? А то если удалят вариант для FreeBSD будет печально. Может сюда запостите?
Alatar
Я не знаю, сколько там хранят – первый раз пользуюсь, раньше не было нужды. Просто постить большие куски кода в комменты не красиво, да и не знаю я, как тут форматирование нормальное сделать, а для отдельного поста в своём ЖЖ материала маловато. Так что Вы уж сами запостите оттуда сюда, если не сложно.
Винда у меня XP. Проверял на двух компах –
g++ main.cc
main.cc: In static member function `static long int MACAddressUtility::GetMACAddressMSW(unsigned char*)’:
main.cc:67: error: `UuidCreateSequential’ was not declared in this scope
При этом если заменить UuidCreateSequential на UuidCreate – собирается, но, естественно, выдаёт ерунду. По всей видимости, надо ставить VS со свежим SDK. (у меня стоит только MinGW)
По поводу BSD – это я просто справедливости ради – всё время про неё забывают. Тем более, обратите внимание, в моём варианте для *BSD/MacOS/Linux используется одна и таже функция с небольшим ifdef`ом.
Вариант с параметрами? Был бы – выложил бы его, чего мудрить =) Я этот-то на скорую руку слепил из Вашего кода. Будет свободное время, сделаю, интереса ради =) Только у меня возможности по тестированию ограничены – Windows, FreeBSD и, в ограниченных количествах, MacOS.