// Copyright (c) 2001 Microsoft Corporation // // File: NetworkInterface.cpp // // Synopsis: Defines a NetworkInterface // This object has the knowledge of an // IP enabled network connection including // IP address, DHCP information, etc. // // History: 03/01/2001 JeffJon Created #include "pch.h" #include "NetworkInterface.h" #include // SetIpForwardEntryToStack #include // Adapter type info #include // RAS error codes #define MPR50 1 // Needed in order to include routprot.h #include // Router protocols #define CYS_WMIPROP_IPADDRESS L"IPAddress" #define CYS_WMIPROP_IPSUBNET L"IPSubnet" #define CYS_WMIPROP_DHCPENABLED L"DHCPEnabled" #define CYS_WMIPROP_DESCRIPTION L"Description" #define CYS_WMIPROP_DNSSERVERS L"DNSServerSearchOrder" #define CYS_WMIPROP_INDEX L"Index" // An array of strings to ease the logging of the adapter type static PCWSTR adapterTypes[] = { L"IF_TYPE_OTHER", // 1 None of the below L"IF_TYPE_REGULAR_1822", // 2 L"IF_TYPE_HDH_1822", // 3 L"IF_TYPE_DDN_X25", // 4 L"IF_TYPE_RFC877_X25", // 5 L"IF_TYPE_ETHERNET_CSMACD", // 6 L"IF_TYPE_IS088023_CSMACD", // 7 L"IF_TYPE_ISO88024_TOKENBUS", // 8 L"IF_TYPE_ISO88025_TOKENRING", // 9 L"IF_TYPE_ISO88026_MAN", // 10 L"IF_TYPE_STARLAN", // 11 L"IF_TYPE_PROTEON_10MBIT", // 12 L"IF_TYPE_PROTEON_80MBIT", // 13 L"IF_TYPE_HYPERCHANNEL", // 14 L"IF_TYPE_FDDI", // 15 L"IF_TYPE_LAP_B", // 16 L"IF_TYPE_SDLC", // 17 L"IF_TYPE_DS1", // 18 DS1-MIB L"IF_TYPE_E1", // 19 Obsolete; see DS1-MIB L"IF_TYPE_BASIC_ISDN", // 20 L"IF_TYPE_PRIMARY_ISDN", // 21 L"IF_TYPE_PROP_POINT2POINT_SERIAL", // 22 proprietary serial L"IF_TYPE_PPP", // 23 L"IF_TYPE_SOFTWARE_LOOPBACK", // 24 L"IF_TYPE_EON", // 25 CLNP over IP L"IF_TYPE_ETHERNET_3MBIT", // 26 L"IF_TYPE_NSIP", // 27 XNS over IP L"IF_TYPE_SLIP", // 28 Generic Slip L"IF_TYPE_ULTRA", // 29 ULTRA Technologies L"IF_TYPE_DS3", // 30 DS3-MIB L"IF_TYPE_SIP", // 31 SMDS, coffee L"IF_TYPE_FRAMERELAY", // 32 DTE only L"IF_TYPE_RS232", // 33 L"IF_TYPE_PARA", // 34 Parallel port L"IF_TYPE_ARCNET", // 35 L"IF_TYPE_ARCNET_PLUS", // 36 L"IF_TYPE_ATM", // 37 ATM cells L"IF_TYPE_MIO_X25", // 38 L"IF_TYPE_SONET", // 39 SONET or SDH L"IF_TYPE_X25_PLE", // 40 L"IF_TYPE_ISO88022_LLC", // 41 L"IF_TYPE_LOCALTALK", // 42 L"IF_TYPE_SMDS_DXI", // 43 L"IF_TYPE_FRAMERELAY_SERVICE", // 44 FRNETSERV-MIB L"IF_TYPE_V35", // 45 L"IF_TYPE_HSSI", // 46 L"IF_TYPE_HIPPI", // 47 L"IF_TYPE_MODEM", // 48 Generic Modem L"IF_TYPE_AAL5", // 49 AAL5 over ATM L"IF_TYPE_SONET_PATH", // 50 L"IF_TYPE_SONET_VT", // 51 L"IF_TYPE_SMDS_ICIP", // 52 SMDS InterCarrier Interface L"IF_TYPE_PROP_VIRTUAL", // 53 Proprietary virtual/internal L"IF_TYPE_PROP_MULTIPLEXOR", // 54 Proprietary multiplexing L"IF_TYPE_IEEE80212", // 55 100BaseVG L"IF_TYPE_FIBRECHANNEL", // 56 L"IF_TYPE_HIPPIINTERFACE", // 57 L"IF_TYPE_FRAMERELAY_INTERCONNECT", // 58 Obsolete, use 32 or 44 L"IF_TYPE_AFLANE_8023", // 59 ATM Emulated LAN for 802.3 L"IF_TYPE_AFLANE_8025", // 60 ATM Emulated LAN for 802.5 L"IF_TYPE_CCTEMUL", // 61 ATM Emulated circuit L"IF_TYPE_FASTETHER", // 62 Fast Ethernet (100BaseT) L"IF_TYPE_ISDN", // 63 ISDN and X.25 L"IF_TYPE_V11", // 64 CCITT V.11/X.21 L"IF_TYPE_V36", // 65 CCITT V.36 L"IF_TYPE_G703_64K", // 66 CCITT G703 at 64Kbps L"IF_TYPE_G703_2MB", // 67 Obsolete; see DS1-MIB L"IF_TYPE_QLLC", // 68 SNA QLLC L"IF_TYPE_FASTETHER_FX", // 69 Fast Ethernet (100BaseFX) L"IF_TYPE_CHANNEL", // 70 L"IF_TYPE_IEEE80211", // 71 Radio spread spectrum L"IF_TYPE_IBM370PARCHAN", // 72 IBM System 360/370 OEMI Channel L"IF_TYPE_ESCON", // 73 IBM Enterprise Systems Connection L"IF_TYPE_DLSW", // 74 Data Link Switching L"IF_TYPE_ISDN_S", // 75 ISDN S/T interface L"IF_TYPE_ISDN_U", // 76 ISDN U interface L"IF_TYPE_LAP_D", // 77 Link Access Protocol D L"IF_TYPE_IPSWITCH", // 78 IP Switching Objects L"IF_TYPE_RSRB", // 79 Remote Source Route Bridging L"IF_TYPE_ATM_LOGICAL", // 80 ATM Logical Port L"IF_TYPE_DS0", // 81 Digital Signal Level 0 L"IF_TYPE_DS0_BUNDLE", // 82 Group of ds0s on the same ds1 L"IF_TYPE_BSC", // 83 Bisynchronous Protocol L"IF_TYPE_ASYNC", // 84 Asynchronous Protocol L"IF_TYPE_CNR", // 85 Combat Net Radio L"IF_TYPE_ISO88025R_DTR", // 86 ISO 802.5r DTR L"IF_TYPE_EPLRS", // 87 Ext Pos Loc Report Sys L"IF_TYPE_ARAP", // 88 Appletalk Remote Access Protocol L"IF_TYPE_PROP_CNLS", // 89 Proprietary Connectionless Proto L"IF_TYPE_HOSTPAD", // 90 CCITT-ITU X.29 PAD Protocol L"IF_TYPE_TERMPAD", // 91 CCITT-ITU X.3 PAD Facility L"IF_TYPE_FRAMERELAY_MPI", // 92 Multiproto Interconnect over FR L"IF_TYPE_X213", // 93 CCITT-ITU X213 L"IF_TYPE_ADSL", // 94 Asymmetric Digital Subscrbr Loop L"IF_TYPE_RADSL", // 95 Rate-Adapt Digital Subscrbr Loop L"IF_TYPE_SDSL", // 96 Symmetric Digital Subscriber Loop L"IF_TYPE_VDSL", // 97 Very H-Speed Digital Subscrb Loop L"IF_TYPE_ISO88025_CRFPRINT", // 98 ISO 802.5 CRFP L"IF_TYPE_MYRINET", // 99 Myricom Myrinet L"IF_TYPE_VOICE_EM", // 100 Voice recEive and transMit L"IF_TYPE_VOICE_FXO", // 101 Voice Foreign Exchange Office L"IF_TYPE_VOICE_FXS", // 102 Voice Foreign Exchange Station L"IF_TYPE_VOICE_ENCAP", // 103 Voice encapsulation L"IF_TYPE_VOICE_OVERIP", // 104 Voice over IP encapsulation L"IF_TYPE_ATM_DXI", // 105 ATM DXI L"IF_TYPE_ATM_FUNI", // 106 ATM FUNI L"IF_TYPE_ATM_IMA", // 107 ATM IMA L"IF_TYPE_PPPMULTILINKBUNDLE", // 108 PPP Multilink Bundle L"IF_TYPE_IPOVER_CDLC", // 109 IBM ipOverCdlc L"IF_TYPE_IPOVER_CLAW", // 110 IBM Common Link Access to Workstn L"IF_TYPE_STACKTOSTACK", // 111 IBM stackToStack L"IF_TYPE_VIRTUALIPADDRESS", // 112 IBM VIPA L"IF_TYPE_MPC", // 113 IBM multi-proto channel support L"IF_TYPE_IPOVER_ATM", // 114 IBM ipOverAtm L"IF_TYPE_ISO88025_FIBER", // 115 ISO 802.5j Fiber Token Ring L"IF_TYPE_TDLC", // 116 IBM twinaxial data link control L"IF_TYPE_GIGABITETHERNET", // 117 L"IF_TYPE_HDLC", // 118 L"IF_TYPE_LAP_F", // 119 L"IF_TYPE_V37", // 120 L"IF_TYPE_X25_MLP", // 121 Multi-Link Protocol L"IF_TYPE_X25_HUNTGROUP", // 122 X.25 Hunt Group L"IF_TYPE_TRANSPHDLC", // 123 L"IF_TYPE_INTERLEAVE", // 124 Interleave channel L"IF_TYPE_FAST", // 125 Fast channel L"IF_TYPE_IP", // 126 IP (for APPN HPR in IP networks) L"IF_TYPE_DOCSCABLE_MACLAYER", // 127 CATV Mac Layer L"IF_TYPE_DOCSCABLE_DOWNSTREAM", // 128 CATV Downstream interface L"IF_TYPE_DOCSCABLE_UPSTREAM", // 129 CATV Upstream interface L"IF_TYPE_A12MPPSWITCH", // 130 Avalon Parallel Processor L"IF_TYPE_TUNNEL", // 131 Encapsulation interface L"IF_TYPE_COFFEE", // 132 Coffee pot L"IF_TYPE_CES", // 133 Circuit Emulation Service L"IF_TYPE_ATM_SUBINTERFACE", // 134 ATM Sub Interface L"IF_TYPE_L2_VLAN", // 135 Layer 2 Virtual LAN using 802.1Q L"IF_TYPE_L3_IPVLAN", // 136 Layer 3 Virtual LAN using IP L"IF_TYPE_L3_IPXVLAN", // 137 Layer 3 Virtual LAN using IPX L"IF_TYPE_DIGITALPOWERLINE", // 138 IP over Power Lines L"IF_TYPE_MEDIAMAILOVERIP", // 139 Multimedia Mail over IP L"IF_TYPE_DTM", // 140 Dynamic syncronous Transfer Mode L"IF_TYPE_DCN", // 141 Data Communications Network L"IF_TYPE_IPFORWARD", // 142 IP Forwarding Interface L"IF_TYPE_MSDSL", // 143 Multi-rate Symmetric DSL L"IF_TYPE_IEEE1394", // 144 IEEE1394 High Perf Serial Bus L"IF_TYPE_RECEIVE_ONLY", // 145 TV adapter type }; // macro that uses the string table above to log the adapter type #define LOG_ADAPTER_TYPE(type) \ if (type >= 145 || type <= 0) \ { \ LOG(String::format(L"adapterType = %1", adapterTypes[0])); \ } \ else \ { \ LOG(String::format(L"adapterType = %1", adapterTypes[type-1])); \ } NetworkInterface::NetworkInterface() : initialized(false), dhcpEnabled(false), dhcpServerAvailable(false), index(0) { LOG_CTOR(NetworkInterface); } NetworkInterface::~NetworkInterface() { LOG_DTOR(NetworkInterface); if (!ipaddresses.empty()) { ipaddresses.clear(); } if (!subnetMasks.empty()) { subnetMasks.clear(); } } NetworkInterface::NetworkInterface(const NetworkInterface &nic) { LOG_CTOR2(NetworkInterface, L"Copy constructor"); if (this == &nic) { return; } name = nic.name; description = nic.description; initialized = nic.initialized; dhcpEnabled = nic.dhcpEnabled; index = nic.index; ipaddressStringList = nic.ipaddressStringList; subnetMaskStringList = nic.subnetMaskStringList; dnsServerSearchOrder = nic.dnsServerSearchOrder; // Make a copy of the ipaddress array ipaddresses = nic.ipaddresses; subnetMasks = nic.subnetMasks; } NetworkInterface& NetworkInterface::operator=(const NetworkInterface& rhs) { LOG_FUNCTION(NetworkInterface::operator=); if (this == &rhs) { return *this; } name = rhs.name; description = rhs.description; initialized = rhs.initialized; dhcpEnabled = rhs.dhcpEnabled; index = rhs.index; ipaddressStringList = rhs.ipaddressStringList; subnetMaskStringList = rhs.subnetMaskStringList; dnsServerSearchOrder = rhs.dnsServerSearchOrder; // Make a copy of the ipaddress array ipaddresses = rhs.ipaddresses; subnetMasks = rhs.subnetMasks; return *this; } HRESULT NetworkInterface::Initialize(const IP_ADAPTER_INFO& adapterInfo) { LOG_FUNCTION(NetworkInterface::Initialize); HRESULT hr = S_OK; do { if (initialized) { ASSERT(!initialized); hr = E_UNEXPECTED; } else { // Get the name name = adapterInfo.AdapterName; LOG(String::format( L"name = %1", name.c_str())); // the description description = adapterInfo.Description; LOG(String::format( L"description = %1", description.c_str())); // the type type = adapterInfo.Type; LOG_ADAPTER_TYPE(type); // the index index = adapterInfo.Index; LOG(String::format( L"index = %1!d!", index)); // Is DHCP enabled? dhcpEnabled = (adapterInfo.DhcpEnabled != 0); LOG_BOOL(dhcpEnabled); hr = SetIPList(adapterInfo.IpAddressList); if (FAILED(hr)) { LOG(String::format( L"Failed to set the IP and subnet mask: hr = 0x%1!x!", hr)); break; } // Now retrieve the rest of the info from the registry // Note: this has to be done after getting the name from // the Adapter Info hr = RetrieveAdapterInfoFromRegistry(); if (FAILED(hr)) { LOG(String::format( L"Failed to retrieve adapter info from registry: hr = 0x%1!x!", hr)); break; } } } while (false); // If we succeeded in retrieving the data we need, // mark the object initialized if (SUCCEEDED(hr)) { initialized = true; } LOG_HRESULT(hr); return hr; } HRESULT NetworkInterface::RetrieveAdapterInfoFromRegistry() { LOG_FUNCTION(NetworkInterface::RetrieveAdapterInfoFromRegistry); HRESULT hr = S_OK; do { String keyName = CYS_NETWORK_INTERFACES_KEY; keyName += String(name); RegistryKey key; hr = key.Open( HKEY_LOCAL_MACHINE, keyName, KEY_READ); if (FAILED(hr)) { LOG(String::format( L"Failed to read the interfaces regkey: hr = 0x%1!x!", hr)); break; } // Read the NameServer key String nameServers; hr = key.GetValue( CYS_NETWORK_NAME_SERVERS, nameServers); if (SUCCEEDED(hr)) { LOG(String::format( L"nameServers = %1", nameServers.c_str())); // Adds the name servers to the dnsServerSearchOrder member nameServers.tokenize(std::back_inserter(dnsServerSearchOrder)); } else { LOG(String::format( L"Failed to read the NameServer regkey: hr = 0x%1!x!", hr)); // This is not a breaking condition. We still can try the // DhcpNameServer key } // Read the DhcpNameServer key String dhcpNameServers; hr = key.GetValue( CYS_NETWORK_DHCP_NAME_SERVERS, dhcpNameServers); if (SUCCEEDED(hr)) { LOG(String::format( L"dhcpNameServers = %1", dhcpNameServers.c_str())); // Adds the name servers to the dnsServerSearchOrder member dhcpNameServers.tokenize(std::back_inserter(dnsServerSearchOrder)); } else { LOG(String::format( L"Failed to read the DhcpNameServer regkey: hr = 0x%1!x!", hr)); } // It doesn't matter if we were not able to retrieve the name servers // These are just used as suggestions for DNS forwarding. hr = S_OK; } while (false); LOG_HRESULT(hr); return hr; } HRESULT NetworkInterface::SetIPList( const IP_ADDR_STRING& ipList) { LOG_FUNCTION(NetworkInterface::SetIPList); HRESULT hr = S_OK; // if the list already contains some entries, delete them and start over if (!ipaddresses.empty()) { ipaddresses.erase(ipaddresses.begin()); } if (!subnetMasks.empty()) { subnetMasks.erase(subnetMasks.begin()); } const IP_ADDR_STRING* current = &ipList; while (current) { // IP Address - convert and add String ipAddress(current->IpAddress.String); DWORD newAddress = StringToIPAddress(ipAddress); ASSERT(newAddress != INADDR_NONE); // StringToIPAddress returns an address of 1.2.3.4 as 04030201. The UI // controls return the same address as 01020304. So to make // things consistent convert to the UI way ipaddresses.push_back(ConvertIPAddressOrder(newAddress)); // also add it to the string list ipaddressStringList.push_back(ipAddress); LOG(String::format( L"Adding address: %1", ipAddress.c_str())); // Subnet Mask - convert and add String subnetMask(current->IpMask.String); DWORD newMask = StringToIPAddress(subnetMask); // NTBUG#NTRAID-561163-2002/03/19-JeffJon // Do not assert the subnet mask is not INADDR_NONE because // if there is an RRAS connection then the subnet mask may be // 255.255.255.255 which is the same as INADDR_NONE. The appropriate // fix would be to use WSAStringToAddress inside StringToIPAddress // but that would require rearchitecting all of CYS since IP addresses // have a different structure for use to with that API // ASSERT(newMask != INADDR_NONE); // StringToIPAddress returns an address of 1.2.3.4 as 04030201. The UI // controls return the same address as 01020304. So to make // things consistent convert to the UI way subnetMasks.push_back(ConvertIPAddressOrder(newMask)); // also add it to the string list subnetMaskStringList.push_back(subnetMask); LOG(String::format( L"Adding subnet: %1", subnetMask.c_str())); current = current->Next; } LOG_HRESULT(hr); return hr; } DWORD NetworkInterface::GetIPAddress(DWORD addressIndex) const { LOG_FUNCTION2( NetworkInterface::GetIPAddress, String::format( L"%1!d!", addressIndex)); ASSERT(initialized); DWORD result = 0; if (addressIndex < ipaddresses.size()) { result = ipaddresses[addressIndex]; } LOG(IPAddressToString(result)); return result; } String NetworkInterface::GetStringIPAddress(DWORD addressIndex) const { LOG_FUNCTION2( NetworkInterface::GetStringIPAddress, String::format(L"%1!d!", addressIndex)); ASSERT(addressIndex < ipaddressStringList.size()); String result; if (addressIndex < ipaddressStringList.size()) { result = ipaddressStringList[addressIndex]; } LOG(result); return result; } DWORD NetworkInterface::GetSubnetMask(DWORD addressIndex) const { LOG_FUNCTION2( NetworkInterface::GetSubnetMask, String::format( L"%1!d!", addressIndex)); ASSERT(initialized); DWORD result = 0; if (addressIndex < subnetMasks.size()) { result = subnetMasks[addressIndex]; } LOG(IPAddressToString(result)); return result; } String NetworkInterface::GetStringSubnetMask(DWORD addressIndex) const { LOG_FUNCTION2( NetworkInterface::GetStringSubnetMask, String::format(L"%1!d!", addressIndex)); ASSERT(addressIndex < subnetMaskStringList.size()); String result; if (addressIndex < subnetMaskStringList.size()) { result = subnetMaskStringList[addressIndex]; } LOG(result); return result; } String NetworkInterface::GetName() const { LOG_FUNCTION(NetworkInterface::GetName); ASSERT(initialized); LOG(name); return name; } String NetworkInterface::GetFriendlyName( const String defaultName) const { LOG_FUNCTION(NetworkInterface::GetFriendlyName); DWORD dwRet = 0; HANDLE hMprConfig = 0; static const unsigned friendlyNameLength = 128; wchar_t wszFriendlyName[friendlyNameLength]; ZeroMemory(wszFriendlyName, sizeof(wchar_t) * friendlyNameLength); String result; String guidName = GetName(); dwRet = MprConfigServerConnect(0, &hMprConfig); if (NO_ERROR == dwRet) { dwRet = MprConfigGetFriendlyName( hMprConfig, const_cast(guidName.c_str()), wszFriendlyName, sizeof(wchar_t) * friendlyNameLength); if (NO_ERROR != dwRet) { LOG(String::format( L"MprConfigGetFriendlyName() failed: error = %1!x!", dwRet)); *wszFriendlyName = 0; } else { LOG(String::format( L"MprConfigGetFriendlyName() failed: error = 0x%1!x!", dwRet)); } } else { LOG(String::format( L"MprConfigServerConnect() failed: error = 0x%1!x!", dwRet)); } MprConfigServerDisconnect(hMprConfig); if (!*wszFriendlyName) { // we failed to get a friendly name, so use the default one result = defaultName; } else { result = wszFriendlyName; } LOG(result); return result; } HRESULT NetworkInterface::GetNameAsGUID(GUID& guid) const { LOG_FUNCTION(NetworkInterface::GetNameAsGUID); ASSERT(initialized); LPOLESTR oleString = 0; HRESULT hr = name.as_OLESTR(oleString); if (SUCCEEDED(hr)) { hr = ::CLSIDFromString( oleString, &guid); ASSERT(SUCCEEDED(hr)); ::CoTaskMemFree(oleString); } LOG_HRESULT(hr); return hr; } String NetworkInterface::GetDescription() const { LOG_FUNCTION(NetworkInterface::GetDescription); ASSERT(initialized); LOG(description); return description; } UINT NetworkInterface::GetType() const { LOG_FUNCTION(NetworkInterface::GetType); UINT result = type; LOG_ADAPTER_TYPE(result); return result; } DWORD NetworkInterface::GetIndex() const { LOG_FUNCTION(NetworkInterface::GetIndex); DWORD result = index; LOG(String::format(L"index = %1!d!", result)); return result; } void NetworkInterface::SetIPAddress(DWORD address, String addressString) { LOG_FUNCTION2( NetworkInterface::SetIPAddress, addressString); DWORD newIPAddress = ConvertIPAddressOrder(address); LOG(IPAddressToString(newIPAddress)); // Clear out the old values if (!ipaddresses.empty()) { ipaddresses.clear(); } if (!ipaddressStringList.empty()) { ipaddressStringList.clear(); } // Now add the new values ipaddresses.push_back(newIPAddress); ipaddressStringList.push_back(addressString); } void NetworkInterface::SetSubnetMask(DWORD address, String addressString) { LOG_FUNCTION2( NetworkInterface::SetSubnetMask, addressString); LOG(IPAddressToString(address)); // Clear out the old values if (!subnetMasks.empty()) { subnetMasks.clear(); } if (!subnetMaskStringList.empty()) { subnetMaskStringList.clear(); } // Now add the new values subnetMasks.push_back(address); subnetMaskStringList.push_back(addressString); } String NetworkInterface::GetDNSServerString(DWORD index) { LOG_FUNCTION2( NetworkInterface::GetDNSServerString, String::format( L"%1!d!", index)); String dnsServer; if (dnsServerSearchOrder.empty()) { } if (index < dnsServerSearchOrder.size()) { dnsServer = dnsServerSearchOrder[index]; } else { LOG(String::format( L"Index to large for dnsServerSearchOrder vector: index = %1!d!, size = %2!d!", index, dnsServerSearchOrder.size())); } LOG(dnsServer); return dnsServer; } void NetworkInterface::GetDNSServers(IPAddressList& servers) { LOG_FUNCTION(NetworkInterface::GetDNSServers); // Note: this will read the values from WMI if they // haven't already been retrieved String server = GetDNSServerString(0); if (!dnsServerSearchOrder.empty()) { for (StringVector::iterator itr = dnsServerSearchOrder.begin(); itr != dnsServerSearchOrder.end(); ++itr) { server = *itr; if (!server.empty()) { DWORD newAddress = StringToIPAddress(server); if (newAddress != INADDR_NONE) { // Don't add the current IP address of this server DWORD newInorderAddress = ConvertIPAddressOrder(newAddress); if (newInorderAddress != GetIPAddress(0)) { LOG(String::format( L"Adding server: %1", IPAddressToString(newInorderAddress).c_str())); servers.push_back(newInorderAddress); } } } } } } bool NetworkInterface::IsDHCPAvailable() const { LOG_FUNCTION(NetworkInterface::IsDHCPAvailable); LOG_BOOL(dhcpServerAvailable); return dhcpServerAvailable; } bool NetworkInterface::CanDetectDHCPServer() { LOG_FUNCTION(NetworkInterface::CanDetectDHCPServer); bool result = false; do { if (!IsConnected()) { // Since the NIC isn't connected there // is no reason to make the check break; } ULONG serverIPAddress = 0; DWORD interfaceIPAddress = ConvertIPAddressOrder(GetIPAddress(0)); // This will perform a DHCP_INFORM to try // to detect the DHCP server, if that fails // it will attemp a DHCP_DISCOVER to attempt // to detect the DHCP server. DWORD error = AnyDHCPServerRunning( interfaceIPAddress, &serverIPAddress); if (error == ERROR_SUCCESS) { // DHCP server found result = true; LOG( String::format( L"DHCP server found at the following IP: %1", IPAddressToString( ConvertIPAddressOrder(serverIPAddress)).c_str())); } else { LOG( String::format( L"DHCP server not found: error = 0x%1!x!", error)); } } while (false); dhcpServerAvailable = result; LOG_BOOL(result); return result; } bool NetworkInterface::IsConnected() const { LOG_FUNCTION(NetworkInterface::IsConnected); bool result = true; do { // Convert the name to a GUID GUID guid; ZeroMemory(&guid, sizeof(GUID)); HRESULT hr = GetNameAsGUID(guid); if (FAILED(hr)) { LOG(String::format( L"Failed to get name as guid: hr = 0x%1!x!", hr)); result = false; break; } // Different adapter types require different // ways of detecting connection state if (IsModem()) { // We only know about modems if they are connected result = true; } else { result = IsStandardAdapterConnected(guid); } } while(false); LOG_BOOL(result); return result; } bool NetworkInterface::IsStandardAdapterConnected(const GUID& guid) const { LOG_FUNCTION(NetworkInterface::IsStandardAdapterConnected); bool result = true; do { // Now get the status using the GUID NETCON_STATUS status = NCS_CONNECTED; HRESULT hr = HrGetPnpDeviceStatus(&guid, &status); if (FAILED(hr)) { LOG(String::format( L"Failed to get device status: hr = 0x%1!x!", hr)); result = false; break; } // The status values are defined in netcon.h LOG(String::format( L"Device status = %1!d!", status)); if (status == NCS_DISCONNECTED || status == NCS_DISCONNECTING || status == NCS_HARDWARE_NOT_PRESENT || status == NCS_HARDWARE_DISABLED || status == NCS_HARDWARE_MALFUNCTION || status == NCS_MEDIA_DISCONNECTED) { result = false; } } while (false); LOG_BOOL(result); return result; } bool NetworkInterface::IsModem() const { LOG_FUNCTION(NetworkInterface::IsModem); bool isModem = false; switch (GetType()) { case IF_TYPE_PPP: isModem = true; break; default: isModem = false; } LOG_BOOL(isModem); return isModem; } DWORD NetworkInterface::GetNextAvailableIPAddress( DWORD startAddress, DWORD subnetMask) { LOG_FUNCTION2( NetworkInterface::GetNextAvailableIPAddress, IPAddressToString(startAddress)); DWORD result = startAddress; DWORD currentAddress = startAddress; bool isIPInUse = false; do { isIPInUse = IsIPAddressInUse( currentAddress, subnetMask); if (!isIPInUse) { break; } ++currentAddress; if ((currentAddress & subnetMask) != (startAddress & subnetMask)) { // REVIEW_JEFFJON : what should the behavior be if there are // no available addresses? Is this likely to happen? // Since we couldn't find an available address in this subnet // use the start address currentAddress = startAddress; break; } } while (isIPInUse); result = currentAddress; LOG(IPAddressToString(result)); return result; } bool NetworkInterface::IsIPAddressInUse( DWORD ipaddress, DWORD subnetMask) { LOG_FUNCTION2( NetworkInterface::IsIPAddressInUse, IPAddressToString(ipaddress)); bool result = false; do { if (!IsConnected()) { break; } // Before calling SendARP we have to set a route // on the interface for the subnet we are interested // in so that SendARP will work even if the interface // IP address is completely different than the IP // address we are looking for. SetRoute( ipaddress, subnetMask); // Use the interface IP address to tell TCP which interface // to send the ARP on DWORD interfaceIPAddress = GetIPAddress(0); if (interfaceIPAddress == ipaddress) { // If the IP address is in use by the NIC then // of course it is in use on the network result = true; break; } // MSDN doesn't mention whether or not the buffer needs to be // there but it appears that SendArp() is now failing if we // don't pass it a buffer even though we ignore the macAddress. // The size was taken from the example in MSDN. ULONG macAddress[2]; ULONG macLength = sizeof(macAddress); memset(macAddress, 0xff, macLength); DWORD error = SendARP( ConvertIPAddressOrder(ipaddress), ConvertIPAddressOrder(interfaceIPAddress), macAddress, &macLength); LOG( String::format( L"SendARP returned: error = 0x%1!x!", error)); if (error == NO_ERROR) { result = true; } // Now that we are done with the SendARP // remove the route so that the TCP stack // reverts to normal behavior RemoveRoute( ipaddress, subnetMask); } while (false); LOG_BOOL(result); return result; } void NetworkInterface::SetRoute( DWORD ipaddress, DWORD subnetMask) { LOG_FUNCTION2( NetworkInterface::SetRoute, IPAddressToString(ipaddress)); LOG( String::format( L"subnetMask = %1", IPAddressToString(subnetMask).c_str())); MIB_IPFORWARDROW routerTableEntry; ZeroMemory(&routerTableEntry, sizeof(MIB_IPFORWARDROW)); // Destination routerTableEntry.dwForwardDest = ConvertIPAddressOrder(ipaddress) & ConvertIPAddressOrder(subnetMask); LOG( String::format( L"dwForwardDest = %1", IPAddressToString(ipaddress & subnetMask).c_str())); // net mask routerTableEntry.dwForwardMask = ConvertIPAddressOrder(subnetMask); LOG( String::format( L"dwForwardMask = %1", IPAddressToString(subnetMask).c_str())); // Interface index routerTableEntry.dwForwardIfIndex = GetIndex(); LOG( String::format( L"dwForwardIfIndex = %1!d!", GetIndex())); // Gateway routerTableEntry.dwForwardNextHop = ConvertIPAddressOrder(GetIPAddress(0)); LOG( String::format( L"dwForwardNextHop = %1", IPAddressToString(GetIPAddress(0)).c_str())); // Protocol generator (must be PROTO_IP_NETMGMT according to MSDN) routerTableEntry.dwForwardProto = PROTO_IP_NETMGMT; // Taking these from %sdxroot%\net\rras\cm\customactions\cmroute\cmroute.cpp // since I can't get the API to succeed without them routerTableEntry.dwForwardType = 3; routerTableEntry.dwForwardAge = INFINITE; routerTableEntry.dwForwardMetric1 = 1; routerTableEntry.dwForwardMetric2 = 0xFFFFFFFF; routerTableEntry.dwForwardMetric3 = 0xFFFFFFFF; routerTableEntry.dwForwardMetric4 = 0xFFFFFFFF; routerTableEntry.dwForwardMetric5 = 0xFFFFFFFF; // Create the table entry // NTRAID#NTBUG9-667088-2002/09/25-JeffJon // Do not use CreateIpForwardEntry here because if // RRAS is running the call will become asynchronous // and the route may not be in the table by the time // we call SendARP() DWORD error = SetIpForwardEntryToStack(&routerTableEntry); if (error != NO_ERROR) { LOG( String::format( L"SetIpForwardEntryToStack failed: error = 0x%1!x!", error)); } } void NetworkInterface::RemoveRoute( DWORD ipaddress, DWORD subnetMask) { LOG_FUNCTION2( NetworkInterface::RemoveRoute, IPAddressToString(ipaddress)); LOG( String::format( L"subnetMask = %1", IPAddressToString(subnetMask).c_str())); MIB_IPFORWARDROW routerTableEntry; ZeroMemory(&routerTableEntry, sizeof(MIB_IPFORWARDROW)); // Destination routerTableEntry.dwForwardDest = ConvertIPAddressOrder(ipaddress) & ConvertIPAddressOrder(subnetMask); LOG( String::format( L"dwForwardDest = %1", IPAddressToString(ipaddress & subnetMask).c_str())); // net mask routerTableEntry.dwForwardMask = ConvertIPAddressOrder(subnetMask); LOG( String::format( L"dwForwardMask = %1", IPAddressToString(subnetMask).c_str())); // Interface index routerTableEntry.dwForwardIfIndex = GetIndex(); LOG( String::format( L"dwForwardIfIndex = %1!d!", GetIndex())); // Gateway routerTableEntry.dwForwardNextHop = ConvertIPAddressOrder(GetIPAddress(0)); LOG( String::format( L"dwForwardNextHop = %1", IPAddressToString(GetIPAddress(0)).c_str())); // Delete the table entry DWORD error = DeleteIpForwardEntry(&routerTableEntry); if (error != NO_ERROR) { LOG( String::format( L"DeleteIpForwardEntry failed: error = 0x%1!x!", error)); } }