diff --git a/checkin_notes b/checkin_notes index f5d48aa6ea..4c38456626 100644 --- a/checkin_notes +++ b/checkin_notes @@ -9233,3 +9233,19 @@ Rom 5 Nov 2008 clientgui/ BOINCGUIApp.cpp DlgExitMessage.cpp + +David 5 Nov 2008 + - client: make host CPID a function of: + MAC addresses + hostname + IP addr + This means that a given host will generally always get the same CPID. + Helpful e.g. on grids where the client gets installed repeatedly. + From Artyom Sharov. + + client/ + hostinfo_network.cpp + lib/ + hostinfo.cpp + mac_address.cpp,h + win_build/ + boinc_cli_curl.vcproj + libboinc.vcproj diff --git a/client/hostinfo_network.cpp b/client/hostinfo_network.cpp index f9bfe3808c..ac5bdd0fa1 100644 --- a/client/hostinfo_network.cpp +++ b/client/hostinfo_network.cpp @@ -47,9 +47,11 @@ #include "str_util.h" #include "parse.h" +#include "util.h" #include "file_names.h" #include "client_msgs.h" #include "error_numbers.h" +#include "mac_address.h" #include "hostinfo.h" @@ -76,4 +78,39 @@ int HOST_INFO::get_local_network_info() { return 0; } +// make a random string using host info. +// Not recommended for password generation; +// use as a last resort if more secure methods fail +// +void HOST_INFO::make_random_string(const char* salt, char* out) { + char buf[1024]; + + sprintf(buf, "%f%s%s%f%s", dtime(), domain_name, ip_addr, d_free, salt); + md5_block((const unsigned char*) buf, (int)strlen(buf), out); +} + +// make a host cross-project ID. +// Should be unique across hosts with very high probability +// +void HOST_INFO::generate_host_cpid() { +#if defined(__linux__) || defined(_WIN32) || defined(__APPLE__ ) + char buffer[8192] = ""; + // must be big enough to accommodate aa:bb:cc:dd:ee:ff + // times the number of network interfaces, + // plus the domain name, plus the ip addr. + // 8K should suffice + + if (!get_mac_addresses(buffer) || ! strcmp(buffer, "")) { + make_random_string("", host_cpid); + return; + } + strcat(buffer, domain_name); + strcat(buffer, ip_addr); + md5_block((unsigned char*)buffer, (int)strlen(buffer), host_cpid); +#else + make_random_string("", host_cpid); +#endif +} + + const char *BOINC_RCSID_9275b20aa5 = "$Id$"; diff --git a/lib/hostinfo.cpp b/lib/hostinfo.cpp index 3580289431..4253361982 100644 --- a/lib/hostinfo.cpp +++ b/lib/hostinfo.cpp @@ -201,23 +201,4 @@ int HOST_INFO::write_cpu_benchmarks(FILE* out) { return 0; } -// make a random string using host info. -// Not recommended for password generation; -// use as a last resort if more secure methods fail -// -void HOST_INFO::make_random_string(const char* salt, char* out) { - char buf[1024]; - - sprintf(buf, "%f%s%s%f%s", dtime(), domain_name, ip_addr, d_free, salt); - md5_block((const unsigned char*) buf, (int)strlen(buf), out); -} - -// make a host cross-project ID. -// Should be unique across hosts with very high probability -// -void HOST_INFO::generate_host_cpid() { - make_random_string("", host_cpid); -} - - const char *BOINC_RCSID_edf7e5c147 = "$Id$"; diff --git a/lib/mac_address.cpp b/lib/mac_address.cpp new file mode 100644 index 0000000000..9c7fafde49 --- /dev/null +++ b/lib/mac_address.cpp @@ -0,0 +1,200 @@ +#include "mac_address.h" + +#include + +#if defined(__linux__) +#include +#include +#include +#include +#include +#elif defined(_WIN32) +#include +#include +#elif defined(__APPLE__) +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +// Returns an iterator across all known Ethernet interfaces. Caller is responsible for +// releasing the iterator when iteration is complete. +static kern_return_t +FindEthernetInterfaces(io_iterator_t *matchingServices) +{ + kern_return_t kernResult; + mach_port_t masterPort; + CFMutableDictionaryRef classesToMatch; + + kernResult = IOMasterPort(MACH_PORT_NULL, &masterPort); + if (KERN_SUCCESS != kernResult) fprintf(stderr, "IOMasterPort returned %d\n", kernResult); + // Ethernet interfaces are instances of class kIOEthernetInterfaceClass + classesToMatch = IOServiceMatching(kIOEthernetInterfaceClass); + // Note that another option here would be: classesToMatch = IOBSDMatching("enX"); + // where X is a number from 0 to the number of Ethernet interfaces on the system - 1. + if (classesToMatch == NULL) fprintf(stderr, "IOServiceMatching returned a NULL dictionary.\n"); + + kernResult = IOServiceGetMatchingServices(masterPort, classesToMatch, matchingServices); + if (KERN_SUCCESS != kernResult) fprintf(stderr, "IOServiceGetMatchingServices returned %d\n", kernResult); + + return kernResult; +} + +// Given an iterator across a set of Ethernet interfaces, return the MAC address of the first one. +// If no interfaces are found the MAC address is set to an empty string. +static kern_return_t +GetMACAddress(io_iterator_t intfIterator, char* buffer) +{ + io_object_t intfService; + io_object_t controllerService; + kern_return_t kernResult = KERN_FAILURE; + char delimiter[2] = "\0"; + + while (intfService = IOIteratorNext(intfIterator)) + { + CFTypeRef MACAddressAsCFData; + // IONetworkControllers can't be found directly by the IOServiceGetMatchingServices call, + // matching mechanism. So we've found the IONetworkInterface and will get its parent controller + // by asking for it specifically. + kernResult = IORegistryEntryGetParentEntry( intfService, kIOServicePlane, &controllerService ); + + if (KERN_SUCCESS != kernResult) fprintf(stderr, "IORegistryEntryGetParentEntry returned 0x%08x\n", kernResult); + else { + MACAddressAsCFData = IORegistryEntryCreateCFProperty( controllerService, CFSTR(kIOMACAddress), kCFAllocatorDefault, 0); + + if (MACAddressAsCFData) + { + const __CFData* refData = (const __CFData*)MACAddressAsCFData; + UInt8 MACAddress[ kIOEthernetAddressSize ]; + + CFDataGetBytes(refData, CFRangeMake(0,CFDataGetLength(refData)), MACAddress); + sprintf(buffer, "%s%s%02x:%02x:%02x:%02x:%02x:%02x", + buffer, delimiter, + MACAddress[0], MACAddress[1], MACAddress[2], MACAddress[3], MACAddress[4], MACAddress[5]); + CFRelease(MACAddressAsCFData); + delimiter[0] = ':'; + delimiter[1] = '\0'; + } + (void) IOObjectRelease(controllerService); + } + // We have sucked this service dry of information so release it now. + (void) IOObjectRelease(intfService); + // We're just interested in the first interface so exit the loop. + //break; + } + return kernResult; +} +#endif + +bool +get_mac_addresses(char* addresses) { +#if defined(_WIN32) + IP_ADAPTER_INFO AdapterInfo[16]; // Allocate information for up to 16 NICs + DWORD dwBufLen = sizeof(AdapterInfo); // Save memory size of buffer + // Call GetAdapterInfo + DWORD dwStatus = GetAdaptersInfo(AdapterInfo, &dwBufLen); + + if(dwStatus == ERROR_SUCCESS) + { + strcpy(addresses, ""); + char delimiter[2] = "\0"; + // valid, no buffer overflow + PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo; // Contains pointer to current adapter info + do { + sprintf(addresses, "%s%s%02x:%02x:%02x:%02x:%02x:%02x", addresses, delimiter, + pAdapterInfo->Address[0], pAdapterInfo->Address[1], pAdapterInfo->Address[2], + pAdapterInfo->Address[3], pAdapterInfo->Address[4], pAdapterInfo->Address[5]); + delimiter[0] = ':'; + delimiter[1] = '\0'; + + pAdapterInfo = pAdapterInfo->Next; + } + while(pAdapterInfo); + } + else + { + fprintf(stderr, "Adapters information not found\n"); + + return false; + } + return true; + + #elif defined(__linux__) + char buf[1024]; + struct ifconf ifc; + struct ifreq *ifr; + int sck; + int nInterfaces; + int i; + /* Get a socket handle. */ + sck = socket(AF_INET, SOCK_DGRAM, 0); + if(sck < 0) + { + perror("socket"); + + return false; + } + /* Query available interfaces. */ + ifc.ifc_len = sizeof(buf); + ifc.ifc_buf = buf; + if(ioctl(sck, SIOCGIFCONF, &ifc) < 0) + { + perror("ioctl(SIOCGIFCONF)"); + + return false; + } + /* Iterate through the list of interfaces. */ + ifr = ifc.ifc_req; + nInterfaces = ifc.ifc_len / sizeof(struct ifreq); + strcpy(addresses, ""); + char delimiter[2] = "\0"; + + for(i = 0; i < nInterfaces; i++) + { + struct ifreq *item = &ifr[i]; + /* Get the MAC address */ + if(ioctl(sck, SIOCGIFHWADDR, item) < 0) + { + perror("ioctl(SIOCGIFHWADDR)"); + + return false; + } + strcat(addresses, delimiter); + delimiter[0] = ':'; + delimiter[1] = '\0'; + strcat(addresses, ether_ntoa((struct ether_addr *)item->ifr_hwaddr.sa_data)); + } + + return true; + #elif defined(__APPLE__) + kern_return_t kernResult = KERN_SUCCESS; // on PowerPC this is an int (4 bytes) + /* + * error number layout as follows (see mach/error.h and IOKitLib/IOReturn.h): + * + * hi lo + * | system(6) | subsystem(12) | code(14) | + */ + io_iterator_t intfIterator; + + kernResult = FindEthernetInterfaces(&intfIterator); + if (KERN_SUCCESS != kernResult) fprintf(stderr, "FindEthernetInterfaces returned 0x%08x\n", kernResult); + else + { + kernResult = GetMACAddress(intfIterator, addresses); + if (KERN_SUCCESS != kernResult) fprintf(stderr, "GetMACAddress returned 0x%08x\n", kernResult); + } + IOObjectRelease(intfIterator); + + return kernResult; + #endif +} + diff --git a/lib/mac_address.h b/lib/mac_address.h new file mode 100644 index 0000000000..0feffb07d6 --- /dev/null +++ b/lib/mac_address.h @@ -0,0 +1,9 @@ +#ifndef MAC_ADDRESS_H +#define MAC_ADDRESS_H + +// Note: the code on Mac OS X requires the following linkage flags +// -framework CoreFoundation -lIOKit +bool +get_mac_addresses(char* addresses); + +#endif // MAC_ADDRESS_H diff --git a/win_build/boinc_cli_curl.vcproj b/win_build/boinc_cli_curl.vcproj index 068984f161..d006f419db 100644 --- a/win_build/boinc_cli_curl.vcproj +++ b/win_build/boinc_cli_curl.vcproj @@ -85,7 +85,7 @@ /> + + @@ -1297,6 +1301,10 @@ RelativePath="..\lib\hostinfo.h" > + +