Cross-Platform Programming with wxWidgets
Just Make It Cross-Platform
Subscribe to Feed
  • Home
  • Projects
  • Links

Cross-Platform Way of Obtaining MAC Address of Your Machine

Components, Libraries, wxWidgets Add comments |

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();

Get MAC address programmatically

As you can see, core class does not depend on wxWidgets and can be used in native applications written without wxWidgets.

Download sample application


August 30th, 2009 |

Tags: Networking, wxWidgets, wxWinCE

Related Posts

  • Работа с базами данных в wxWidgets – DatabaseLayer – Минимальное приложение
  • Taking Screenshots with wxWidgets under Mac OS is Really Tricky.
  • Перевод книги Julian’а Smart’а – Глава X – Работа с Изображениями – Часть 1
  • Классы редактирования даты и времени в ячейках wxGrid
  • Перевод книги Julian’а Smart’а – Глава XIII – Структуры данных (Часть 1)

3 Responses to “Cross-Platform Way of Obtaining MAC Address of Your Machine”

  1. Alatar
    September 4th, 2009 at 14:09:08

    А можно немного покритиковать?
    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 – по нему найти нужный интерфейс уже не сложно.
    Всё это, конечно, моё личное ИМХО =)


  2. T-Rex
    September 4th, 2009 at 15:09:43

    Под винду не собирается? А какая у вас винда?
    http://msdn.microsoft.com/en-us/library/aa379322%28VS.85%29.aspx
    говорит что минимальное требование Windows 2000 Professional.

    FreeBSD вообще забыли…

    Ммм? Так это.. не надо было мне ФриБСД, только линух/мак/винда. Если есть вариант под BSD – ок. :) я только за.

    MAC-адрес может быть сетевой карточки, а их в компе может быть несколько

    Угу.. может. Задача у меня стояла как “запретить повторный коннект с одного мак адреса”. Если два раза одним и тем же алгоритмом выбрать первый мак-адрес то не важно какой там интерфейс идет первым.
    Ну а так – код в public domain – каждый дорабатывает как хочет. :)

    то же касается этих функций – неплохо бы было в качестве параметра передовать интерфейс, для которого нужно получить MAC. Либо IP – по нему найти нужный интерфейс уже не сложно.

    Даете вариант с параметром? Согласен выложить для общего обозрения если есть :)

    И еще, на пасторге сорцы долго хранятся? А то если удалят вариант для FreeBSD будет печально. Может сюда запостите?


  3. Alatar
    September 4th, 2009 at 18:09:44

    Я не знаю, сколько там хранят – первый раз пользуюсь, раньше не было нужды. Просто постить большие куски кода в комменты не красиво, да и не знаю я, как тут форматирование нормальное сделать, а для отдельного поста в своём ЖЖ материала маловато. Так что Вы уж сами запостите оттуда сюда, если не сложно.

    Винда у меня 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.


Leave a Reply

  • This blog is about…

    Библиотека Книги Статьи Articles Code::Blocks Components Controls Database DatabaseLayer Document/View Eclipse Localization NetBeans Networking News Printing Reports SQLite Tutorilas Video Visual Studio wxAUI wxButton wxDev-CPP wxGrid wxHelpController wxJavaScript wxJSON wxLocale wxLog wxPaintDC wxPropertyGrid wxRuby wxSQLite3 wxThread wxValidator wxWidgets wxWinCE wxZipInputStream wxZipOutputStream XML
  • Showcase

    Visit wxToolBox Homepage

    Buy wxToolBox (with source code)

  • Archives

    • November 2009
    • September 2009
    • August 2009
    • May 2009
    • April 2009
    • March 2009
    • February 2009
    • January 2009
    • December 2008
    • September 2008
    • August 2008
    • July 2008
    • June 2008
    • May 2008
    • April 2008
    • March 2008
    • February 2008
    • January 2008
    • December 2007
    • June 2007
    • May 2007
    • January 2007
  • Recent Comments

    • T-Rex on Сделайте мне красиво – Часть II – wxAUI в Multi-View приложений
    • Vanya on Сделайте мне красиво – Часть II – wxAUI в Multi-View приложений
    • Sash on wxWidgets-2.8 and Code::Blocks (Windows)
    • T-Rex on Быстрый способ упаковки содержимого папки в ZIP-архив
    • Gerald on Быстрый способ упаковки содержимого папки в ZIP-архив
  • Buttons

    Locations of visitors to this page

    Rambler's Top100
    Рейтинг@Mail.ru

Copyright © 2010 Cross-Platform Programming with wxWidgets All Rights Reserved
RSS Log in