how to print the MAC address - c++

i created a client server program that displays the MAC and ip addresses of the clients and i have a function that gets the MAC address of the computer but I'm having trouble printing the MAC address in a listbox. I created a print function that did work before but i changed the code for finding the mac address and now its not working (also i don't know if i'm calling printMACaddress in the right place). I have two list boxes - one displays the ip address the other displays the MAC address. When i click the ip address i want the MAC address of that computer to be displayed but right now its not displaying. nothing happens when i click the ip address
void CmfcServerDlg::OnLbnSelchangeListClientaddr()
{
bool GetMACFromIP(BYTE Address[MAX_ADAPTER_ADDRESS_LENGTH], const std::string &selected_ip_adr);
}
void CmfcServerDlg::PrintMACaddress(unsigned char MACData[])
{
CString
strText;
strText.Format("%02X-%02X-%02X-%02X-%02X-%02X\n",MACData[0], MACData[1], MACData[2], MACData[3], MACData[4], MACData[5]);
m_ClientIdList.AddString(strText);
}
bool CmfcServerDlg::GetMACFromIP(BYTE Address[MAX_ADAPTER_ADDRESS_LENGTH], const std::string &selected_ip_adr)
{
IP_ADAPTER_INFO AdapterInfo[16];
DWORD dwBufLen = sizeof(AdapterInfo);
DWORD dwStatus = GetAdaptersInfo(
AdapterInfo,
&dwBufLen);
assert(dwStatus == ERROR_SUCCESS);
PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo;
bool found = false;
do {
const IP_ADDR_STRING *addr_str = &pAdapterInfo->IpAddressList;
while(addr_str != NULL)
{
if(selected_ip_adr == addr_str->IpAddress.String)
{
found = true;
break;
}
}
if(found)
{
memcpy(Address, pAdapterInfo->Address, MAX_ADAPTER_ADDRESS_LENGTH);
PrintMACaddress(pAdapterInfo->Address); //problem here
break;
}
else
{
PrintMACaddress(pAdapterInfo->Address);
pAdapterInfo = pAdapterInfo->Next;
}
}
while(pAdapterInfo);
return found;
}

You do realize that your stated goal in the comment ("i am trying to find the MAC address of any computer that connects to the server") is not possible? MAC addresses are lower-level than IP addresses, they identify the "media port", i.e. the place where the cable sits.
You cannot get the MAC address for a client on the other side of a switch even, much less across the Internet.

Related

WNetOpenEnum returns ERROR_NETWORK_UNREACHABLE for the "Microsoft Windows Network" node

Our program has a piece of code that calculates the list of computers on our local network. It uses the Windows Networking API (WNetOpenEnum/WNetEnumResource) to unwind the network. For many years, the resulting list was identical to the one that can be seen in Windows Explorer under the "Network" entry. However, recently we have noticed that the same code returns an empty list. During debugging I found that WNetOpenEnum returns error 1231 (ERROR_NETWORK_UNREACHABLE) when it is called for the "Microsoft Windows Network" under the root node.
I have to mention, though I'm pretty sure it has nothing to do with the matter, that the network unwinding is done multithreaded, to avoid possible delays in the main GUI thread. Each time a node of type RESOURCEUSAGE_CONTAINER is encountered, a new worker thread is launched. The thread function calls the following procedure:
DWORD WINAPI EnumNetwork(NETRESOURCE_M* lpNR)
{
const int BUF_SIZE = 16384; // 16K is a good size.
HANDLE hEnum;
DWORD Result;
// Call the WNetOpenEnum function to begin the enumeration.
Result = ::WNetOpenEnum(RESOURCE_GLOBALNET, // all network
RESOURCETYPE_ANY, // all resource types
0, // enumerate all
(LPNETRESOURCE)lpNR,// parent resource
&hEnum); // enumeration handle
if (Result != NO_ERROR) // -> for "Microsoft Windows Network" Result = 1231
return Result;
std::vector<std::wstring> SrvList;
// Allocate buffer for enumeration.
LPNETRESOURCE lpEnumNR = (LPNETRESOURCE)new char[BUF_SIZE];
if (lpEnumNR == 0)
Result = ERROR_OUTOFMEMORY;
else
{
while (1)
{
::ZeroMemory(lpEnumNR, BUF_SIZE); // Initialize the buffer.
DWORD NumEntries = -1; // Enumerate all entries.
DWORD BufSize = BUF_SIZE;
// Call WNetEnumResource to continue the enumeration.
Result = ::WNetEnumResource(hEnum, // enumeration handle
&NumEntries,// number of entries to enumerate
lpEnumNR, // array of resources to return
&BufSize); // buffer size
if (Result == NO_ERROR)
{
// If the call succeeds, loop through the array.
for (unsigned i = 0; i < NumEntries; ++i)
{
if (lpEnumNR[i].dwDisplayType == RESOURCEDISPLAYTYPE_SERVER)
{
// Collect servers.
LPCWSTR SrvName = lpEnumNR[i].lpRemoteName;
if (PathHelpers::IsFullPath(SrvName))
SrvList.push_back(SrvName);
}
else if ((lpEnumNR[i].dwUsage & RESOURCEUSAGE_CONTAINER) &&
lpEnumNR[i].lpRemoteName != 0)
{
TCHAR PathBuf[1024] = {0};
if (lpNR && lpNR->Path)
{
_tcscpy(PathBuf, lpNR->Path);
::PathAddBackslash(PathBuf);
}
_tcscat(PathBuf, lpEnumNR[i].lpRemoteName);
if (RegisterServer(PathBuf))
{
// Start new thread for recursive enumeration.
NETRESOURCE_M* lpChildNR = DeepCopyNR(&lpEnumNR[i], PathBuf);
ExploreNetwork(lpChildNR); // -> this starts a worker thread
}
else
{
GetLogger().LogMessage(
_T("Cycles found while unwinding network: %s"), PathBuf);
}
}
}
}
else
{
if (Result == ERROR_NO_MORE_ITEMS)
Result = NO_ERROR;
break;
}
} // end while
delete [] (char*)lpEnumNR;
} // end if
::WNetCloseEnum(hEnum);
if (!SrvList.empty())
NotifyServerAdded(SrvList);
return Result;
}
where NETRESOURCE_M is the structure
struct NETRESOURCE_M
{
NETRESOURCE NR;
LPTSTR Path;
};
Trying to figure out what could have caused such a sudden change in behavior, I found in Google that a few years ago Microsoft disabled the SMB1 protocol, which could affect Network Discovery. However, I can't believe they could have damaged their own API without saying a word in the documentation.
EDIT: At the same time, Windows Explorer has a bunch of computers under its "Network" node. In the network settings, the network type is "Domain", and the network discovery is ON. Services "Function Discovery Provider Host" and "Function Discovery Resources Publication" are running. Windows OS build is 19042.685.
Edit 2: The Sysinternals' "ShareEnum" tool also fails with the error: "No domains or workgroups where found on your network". Because of this, and also because some time ago our company moved all of its computers to a different network, I got the feeling that the problem is in the network configuration. Such as though the network is declared as "Domain", the computers were not enrolled to this domain. I do not understand much in that, but something like this.

Why the value is changing unexpectedly ? inet_ntoa

I'm trying to create a function which gathers all IPs present on the machine and will be able to detect when the IP in parameter is present. 
The Issue is that the value of the variable containing my IP is changing after an inet_ntoa done on another IN_ADDR.
I tried to use std::string without success. I know inet_ntoa is deprecated but I tried something else without success. Also I would like to understand what I'm doing wrong.
void CIPUtilities::AwaitIPAddress(struct sockaddr_in WFIAddr) {
// also tried with AwaitIPAddress(char * IPName)
char* IPName = inet_ntoa(WFIAddr.sin_addr);
printf("\n Waiting for the IP: %s to be ready.\n", IPName);
int i;
bool ctn = TRUE;
/* Variables used by GetIpAddrTable */
PMIB_IPADDRTABLE pIPAddrTable;
DWORD dwSize = 0;
DWORD dwRetVal = 0;
IN_ADDR IPAddr;
ULONG outBufLen = 0;
ULONG Iterations = 0;
/* Variables used to return error message */
LPVOID lpMsgBuf;
// Allocate a 15 KB buffer to start with.
outBufLen = WORKING_BUFFER_SIZE;
do {
do {
//printf("inside do\n");
Sleep(300); //to be removed ? Only there to avaoid to loop too much.
pIPAddrTable = (MIB_IPADDRTABLE*)MALLOC(outBufLen);
if (pIPAddrTable == NULL) {
printf("Memory allocation failed for GetIpAddrTable\n");
//exit(1); // no need to exit we need debug
}
dwRetVal = GetIpAddrTable(pIPAddrTable, &outBufLen, 0);
if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
FREE(pIPAddrTable);
pIPAddrTable = NULL;
}
else {
break;
}
} while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < MAX_TRIES));
if (dwRetVal == NO_ERROR) {
// If successful, search the IP from the data we retrived
for (i = 0; i < (int)pIPAddrTable->dwNumEntries; i++) {
IPAddr.S_un.S_addr = (u_long)pIPAddrTable->table[i].dwAddr;
printf("1- The value of IPName is %s\n", IPName);
printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr));
printf("2- The value of IPName is %s\n", IPName);
//if (strcmp(IPName, inet_ntoa(IPAddr)) == 0) {
// printf("IP adress[%s] is found in AddrTable\n", inet_ntoa(IPAddr));
// ctn = FALSE; // We exit the loop because the adress is created.
// break; // no need to parse more addresses.
//}
}
}
else
{
printf("GetIpAddrTable failed with error %d\n", dwRetVal);
printf("Required size should be %d\n", dwSize);
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwRetVal, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR)&lpMsgBuf, 0, NULL)) {
printf("\tError: %s", (char*)lpMsgBuf);
LocalFree(lpMsgBuf);
}
//exit(1); // no need to exit we need debug
}
if (pIPAddrTable) {
FREE(pIPAddrTable);
pIPAddrTable = NULL;
}
} while (ctn);}
Basically the output will be :
Waiting for the IP: 10.0.4.3 to be ready.
1- The value of IPName is 10.0.4.3
IP Address[0]: 160.223.17.135
2- The value of IPName is 160.223.17.135
1- The value of IPName is 160.223.17.135
IP Address[1]: 169.254.165.50
2- The value of IPName is 169.254.165.50
1- The value of IPName is 169.254.165.50
IP Address[2]: 10.0.4.3
2- The value of IPName is 10.0.4.3
1- The value of IPName is 10.0.4.3
IP Address[3]: 10.0.12.44
2- The value of IPName is 10.0.12.44
1- The value of IPName is 10.0.12.44
IP Address[4]: 192.168.0.17
2- The value of IPName is 192.168.0.17
1- The value of IPName is 192.168.0.17
IP Address[5]: 127.0.0.1
2- The value of IPName is 127.0.0.1
1- The value of IPName is 127.0.0.1
I removed some parts of the function to make it more readable.
In short: this happens because you use the returned pointer directly instead of making a copy of the string.
char* IPName = inet_ntoa(WFIAddr.sin_addr);
As stated in the documentation of inet_ntoa (see here for Windows or here for Linux) the result is a pointer to a static buffer:
The string returned is guaranteed to be valid only until the next Windows Sockets function call is made within the same thread. Therefore, the data should be copied before another Windows Sockets call is made.
All you need to do, is copy the contents of the buffer into a string variable.
char IPName[18];
char* temp = inet_ntoa(WFIAddr.sin_addr);
if (temp != NULL)
{
strcpy(IPName, temp);
}
(For details about the size of your buffer see here)
And you should, of course, treat the error case (if temp is NULL)

Faster processing of SendARP function

Originally posted here, but found off topic: https://serverfault.com/questions/617459/faster-processing-of-sendarp-function
I've been working on a network scanner for Windows. I have successfully written the code, but the problem is it takes too much time to scan the hosts that aren't up. When I tried scanning a subnet (1 to 255), it took more than half hour. I couldn't find a function to control the time limit or a way to control the time-out of SendARP function.
DestIp = inet_addr(strn.c_str()); //Setting Destination IPv4 dotted-decimal address into a proper address for the IN_ADDR structure.
SrcIp = inet_addr(SrcIpString);
memset(&MacAddr, 0xff, sizeof(MacAddr)); //Initializing MAC Address to ff-ff-ff-ff-ff-ff
dwRetVal = SendARP(DestIp, SrcIp, &MacAddr, &PhysAddrLen); //Sending ARP request to the destined IP Address
if (dwRetVal == NO_ERROR) {
bPhysAddr = (BYTE *)& MacAddr;
if (PhysAddrLen) {
std::cout << strn<<std::endl;
for (int i = 0; i < (int)PhysAddrLen; i++) {
if (i == ((int)PhysAddrLen - 1))
printf("%.2X\n", (int)bPhysAddr[i]);
else
printf("%.2X-", (int)bPhysAddr[i]);
}
}
}
You're using a convenience function from the "IP Helper" library. That's not performance-oriented.
The ServerFault comments actually hit the mail on the head: use threads. With <thread> that's nowadays quite simple. Just do 255 std::async calls to your function. Of course, make sure that all the MacAddr and PhysAddrLen references aren't invalidated.

Bluetooth MAC address in C

How do I find MAC address (hardware address) of Bluetooth network interface in Windows? The problem is not to find out an address, the problem is to identify if network interface type is Bluetooth.
Both approaches I tried does not distinguish between ethernet card and bluetooth (at least I don't see a difference) - GetAdaptersAddresses returns bluetooth interface as IF_TYPE_ETHERNET_CSMACD and WMI as AdapterTypeID of Ethernet 802.3 (same as WiFi, eventhough Wireless type exists).
Only possibility I currently see is to search a name or description string for text "bluetooth" but this does not seem as OK solution ;-)
You can use BluetoothFindFirstRadio, BluetoothFindNextRadio and BluetoothGetRadioInfo. The local MAC adress of the adapter is then in the field address of BLUETOOTH_RADIO_INFO:
BLUETOOTH_FIND_RADIO_PARAMS btfrp;
btfrp.dwSize = sizeof(btfrp);
HANDLE hRadio;
HBLUETOOTH_RADIO_FIND hFind = BluetoothFindFirstRadio(&btfrp, &hRadio);
if(hFind == NULL)
{
DWORD err = GetLastError();
switch(err)
{
case ERROR_NO_MORE_ITEMS:
// No bluetooth radio found
break;
default:
// Error finding radios
}
return;
}
do
{
BLUETOOTH_RADIO_INFO radioInfo;
radioInfo.dwSize = sizeof(radioInfo);
DWORD err = BluetoothGetRadioInfo(hRadio, &radioInfo);
if(err != ERROR_SUCCESS)
{
// Error during BluetoothGetRadioInfo
continue;
}
// The mac address is in radioInfo.address
}
while(BluetoothFindNextRadio(hFind, &hRadio));
BluetoothFindRadioClose(hFind);

Generate machine-specific key for Mac

On Windows, we generate a PC-specific unique key used to tie a license to a PC. It's a C++ app using wxWidgets, which is theoretically cross-platform compatible but not been maintained on the Mac side. We use some Win32-specific code for generating a key... how might I do something comparable on the Mac?
Looking more into whitelionV and blahdiblah's asnwers, I found this useful page:
Accessing the system serial number programmatically
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
// Returns the serial number as a CFString.
// It is the caller's responsibility to release the returned CFString when done with it.
void CopySerialNumber(CFStringRef *serialNumber)
{
if (serialNumber != NULL) {
*serialNumber = NULL;
io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault,
IOServiceMatching("IOPlatformExpertDevice"));
if (platformExpert) {
CFTypeRef serialNumberAsCFString =
IORegistryEntryCreateCFProperty(platformExpert,
CFSTR(kIOPlatformSerialNumberKey),
kCFAllocatorDefault, 0);
if (serialNumberAsCFString) {
*serialNumber = serialNumberAsCFString;
}
IOObjectRelease(platformExpert);
}
}
}
Accessing the built-in MAC address programmatically
#include <stdio.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/network/IOEthernetInterface.h>
#include <IOKit/network/IONetworkInterface.h>
#include <IOKit/network/IOEthernetController.h>
static kern_return_t FindEthernetInterfaces(io_iterator_t *matchingServices);
static kern_return_t GetMACAddress(io_iterator_t intfIterator, UInt8 *MACAddress, UInt8 bufferSize);
static kern_return_t FindEthernetInterfaces(io_iterator_t *matchingServices)
{
kern_return_t kernResult;
CFMutableDictionaryRef matchingDict;
CFMutableDictionaryRef propertyMatchDict;
matchingDict = IOServiceMatching(kIOEthernetInterfaceClass);
if (NULL == matchingDict) {
printf("IOServiceMatching returned a NULL dictionary.\n");
}
else {
propertyMatchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (NULL == propertyMatchDict) {
printf("CFDictionaryCreateMutable returned a NULL dictionary.\n");
}
else {
CFDictionarySetValue(matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict);
CFRelease(propertyMatchDict);
}
}
kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, matchingServices);
if (KERN_SUCCESS != kernResult) {
printf("IOServiceGetMatchingServices returned 0x%08x\n", kernResult);
}
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;
kernResult = IORegistryEntryGetParentEntry(intfService,
kIOServicePlane,
&controllerService);
if (KERN_SUCCESS != kernResult) {
printf("IORegistryEntryGetParentEntry returned 0x%08x\n", kernResult);
}
else {
MACAddressAsCFData = IORegistryEntryCreateCFProperty(controllerService,
CFSTR(kIOMACAddress),
kCFAllocatorDefault,
0);
if (MACAddressAsCFData) {
CFShow(MACAddressAsCFData); // for display purposes only; output goes to stderr
CFDataGetBytes(MACAddressAsCFData, CFRangeMake(0, kIOEthernetAddressSize), MACAddress);
CFRelease(MACAddressAsCFData);
}
(void) IOObjectRelease(controllerService);
}
(void) IOObjectRelease(intfService);
}
return kernResult;
}
int main(int argc, char *argv[])
{
kern_return_t kernResult = KERN_SUCCESS;
io_iterator_t intfIterator;
UInt8 MACAddress[kIOEthernetAddressSize];
kernResult = FindEthernetInterfaces(&intfIterator);
if (KERN_SUCCESS != kernResult) {
printf("FindEthernetInterfaces returned 0x%08x\n", kernResult);
}
else {
kernResult = GetMACAddress(intfIterator, MACAddress, sizeof(MACAddress));
if (KERN_SUCCESS != kernResult) {
printf("GetMACAddress returned 0x%08x\n", kernResult);
}
else {
printf("This system's built-in MAC address is %02x:%02x:%02x:%02x:%02x:%02x.\n",
MACAddress[0], MACAddress[1], MACAddress[2], MACAddress[3], MACAddress[4], MACAddress[5]);
}
}
(void) IOObjectRelease(intfIterator); // Release the iterator.
return kernResult;
}
While MAC is on the face of it probably preferable as being more predictable, they warn that:
Netbooting introduces a wrinkle with systems with multiple built-in
Ethernet ports. The primary Ethernet port on these systems is the one
that is connected to the NetBoot server. This means that a search for
the primary port may return either of the built-in MAC addresses
depending on which port was used for netbooting. Note that "built-in"
does not include Ethernet ports that reside on an expansion card.
It concerns me this might mean you don't always get the same value back?
You could just call system_profiler and look for "Serial Number"
/usr/sbin/system_profiler | grep "Serial Number (system)"
There might well be a programmatic way to get the same information, but I don't know it offhand.
To uniquely identify any machine you could try to use the MAC address. The process, although not trivial, its quite simple. There are a lot of cross platform open source libraries.
In fact you could try this Apple dev example