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
#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
#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
#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:
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.
Введение Уже долгое время не пишу статьи о разработке, хотя сам процесс написания мне очень…
I can see that there is still a lot of topics at wxWidgets forums related…
I've just published the source code of wxToolBox component and a couple of sample apps at…
Microsoft released their Kinect SDK several days ago. So, for those wxWidgets developers who are…
JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to…
Вдохновленнный читаемой нынче книгой My Job Went to India: 52 Ways to Save Your Job…
View Comments
А можно немного покритиковать?
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 - по нему найти нужный интерфейс уже не сложно.
Всё это, конечно, моё личное ИМХО =)
Под винду не собирается? А какая у вас винда?
http://msdn.microsoft.com/en-us/library/aa379322%28VS.85%29.aspx
говорит что минимальное требование Windows 2000 Professional.
Ммм? Так это.. не надо было мне ФриБСД, только линух/мак/винда. Если есть вариант под BSD - ок. :) я только за.
Угу.. может. Задача у меня стояла как "запретить повторный коннект с одного мак адреса". Если два раза одним и тем же алгоритмом выбрать первый мак-адрес то не важно какой там интерфейс идет первым.
Ну а так - код в public domain - каждый дорабатывает как хочет. :)
Даете вариант с параметром? Согласен выложить для общего обозрения если есть :)
И еще, на пасторге сорцы долго хранятся? А то если удалят вариант для FreeBSD будет печально. Может сюда запостите?
Я не знаю, сколько там хранят - первый раз пользуюсь, раньше не было нужды. Просто постить большие куски кода в комменты не красиво, да и не знаю я, как тут форматирование нормальное сделать, а для отдельного поста в своём ЖЖ материала маловато. Так что Вы уж сами запостите оттуда сюда, если не сложно.
Винда у меня 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.