/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ // // Filename: nicenum.cpp // Author: Tom Spencer-Smith // Date: Dec 1999 // Description: // #include "nicenum.h" #include #include #include "wwdebug.h" #include "netutil.h" #include "useroptions.h" #include "GameSpy_QnR.h" // // Class statics // ULONG cNicEnum::NicList[]; USHORT cNicEnum::NumNics = 0; ULONG cNicEnum::GSNicList[]; USHORT cNicEnum::NumGSNics = 0; //---------------------------------------------------------------------------------- void cNicEnum::Init ( void ) { WWDEBUG_SAY(("cNicEnum::Init\n")); WSADATA wsa_data; int startup_rc = ::WSAStartup(MAKEWORD(1, 1), &wsa_data); if (startup_rc != 0) { WWDEBUG_SAY((" WSAStartup failed!\n")); return; } // // Retrieve list of nic's // ULONG local_addresses[MAX_NICS]; ULONG num_addresses = Enumerate_Nics(local_addresses, MAX_NICS); WWASSERT(num_addresses <= MAX_NICS); WWDEBUG_SAY((" Found %d NIC(s)\n", num_addresses)); USHORT index = 0; USHORT class_1 = 0; USHORT class_2 = 0; // // First, extract the non-internet addressable nicks, ordered on general usage. // ULONG lan_addresses[MAX_NICS]; ULONG num_lan_addresses = 0; // // First, scan for 10.*.*.* addresses // for (index = 0; index < num_addresses; index++) { class_1 = (::ntohl(local_addresses[index]) & 0xff000000) >> 24; if (class_1 == 10) { lan_addresses[num_lan_addresses++] = local_addresses[index]; local_addresses[index] = 0; } } // // Next, scan for 192.168.*.* addresses // for (index = 0; index < num_addresses; index++) { class_1 = (::ntohl(local_addresses[index]) & 0xff000000) >> 24; class_2 = (::ntohl(local_addresses[index]) & 0x00ff0000) >> 16; if (class_1 == 192 && class_2 == 168) { lan_addresses[num_lan_addresses++] = local_addresses[index]; local_addresses[index] = 0; } } // // Next, scan for 172.16-31.*.* addresses // for (index = 0; index < num_addresses; index++) { class_1 = (::ntohl(local_addresses[index]) & 0xff000000) >> 24; class_2 = (::ntohl(local_addresses[index]) & 0x00ff0000) >> 16; if (class_1 == 172 && class_2 >= 16 && class_2 <= 31) { lan_addresses[num_lan_addresses++] = local_addresses[index]; local_addresses[index] = 0; } } // // Finally, scan for 169.254.*.* addresses (IP autoconfiguration) // for (index = 0; index < num_addresses; index++) { class_1 = (::ntohl(local_addresses[index]) & 0xff000000) >> 24; class_2 = (::ntohl(local_addresses[index]) & 0x00ff0000) >> 16; if (class_1 == 169 && class_2 == 254) { lan_addresses[num_lan_addresses++] = local_addresses[index]; local_addresses[index] = 0; } } WWDEBUG_SAY((" Of which %d are non-internet addressable.\n", num_lan_addresses)); // // Next, copy the Internet addressable addresses. Weed out localhost and multicast // addresses. // ULONG internet_addresses[MAX_NICS]; ULONG num_internet_addresses = 0; for (index = 0; index < num_addresses; index++) { if (local_addresses[index] != 0) { class_1 = (::ntohl(local_addresses[index]) & 0xff000000) >> 24; if (class_1 != 127 && class_1 != 224) { internet_addresses[num_internet_addresses++] = local_addresses[index]; local_addresses[index] = 0; } } } // // Now build the LAN and GameSpy NIC lists. They contain the same entries. // The only difference is that the LAN list puts the non-internet addressable // NIC's first, the GameSpy list puts them last. // NumNics = 0; NumGSNics = 0; for (index = 0; index < num_lan_addresses; index++) { NicList[NumNics++] = lan_addresses[index]; } for (index = 0; index < num_internet_addresses; index++) { NicList[NumNics++] = internet_addresses[index]; } for (index = 0; index < num_internet_addresses; index++) { GSNicList[NumGSNics++] = internet_addresses[index]; } for (index = 0; index < num_lan_addresses; index++) { GSNicList[NumGSNics++] = lan_addresses[index]; } WWASSERT(NumNics == NumGSNics); // // Initialize or update PreferredLanNic if required. // bool is_nic_valid = false; for (index = 0; index < NumNics; index++) { if ((ULONG) cUserOptions::PreferredLanNic.Get() == NicList[index]) { is_nic_valid = true; break; } } if (!is_nic_valid) { if (NumNics > 0) { cUserOptions::PreferredLanNic.Set(NicList[0]); } else { cUserOptions::PreferredLanNic.Set(0); } } // // Initialize or update PreferredGameSpyNic if required. // is_nic_valid = false; for (index = 0; index < NumGSNics; index++) { if ((ULONG) cUserOptions::PreferredGameSpyNic.Get() == GSNicList[index]) { is_nic_valid = true; break; } } if (!is_nic_valid) { if (NumGSNics > 0) { cUserOptions::PreferredGameSpyNic.Set(GSNicList[0]); } else { cUserOptions::PreferredGameSpyNic.Set(0); } } WWDEBUG_SAY((" PreferredLanNic is %u (%s)\n", (ULONG) cUserOptions::PreferredLanNic.Get(), cNetUtil::Address_To_String(cUserOptions::PreferredLanNic.Get()))); WWDEBUG_SAY((" PreferredGameSpyNic is %u (%s)\n", (ULONG) cUserOptions::PreferredGameSpyNic.Get(), cNetUtil::Address_To_String(cUserOptions::PreferredGameSpyNic.Get()))); int cleanup_rc = ::WSACleanup(); WWASSERT(cleanup_rc != SOCKET_ERROR); } //--------------------------------------------------------------------------- ULONG cNicEnum::Enumerate_Nics ( ULONG * addresses, ULONG max_nics ) { WWASSERT(addresses != NULL); WWASSERT(max_nics > 0); WWDEBUG_SAY(("cNicEnum::Enumerate_Nics\n")); // // Get the local hostname // char local_host_name[300]; int gethostname_rc = ::gethostname(local_host_name, sizeof(local_host_name)); WWASSERT(gethostname_rc != SOCKET_ERROR); // // Resolve hostname for local adapter addresses. // This does a DNS lookup (name resolution) // LPHOSTENT p_hostent = ::gethostbyname(local_host_name); if (p_hostent == NULL) { DIE; } ULONG num_addresses = 0; while (num_addresses < max_nics && p_hostent->h_addr_list[num_addresses] != NULL) { IN_ADDR in_addr; ::memcpy(&in_addr, p_hostent->h_addr_list[num_addresses], sizeof(in_addr)); addresses[num_addresses] = in_addr.s_addr; WWDEBUG_SAY((" NIC: %s\n", cNetUtil::Address_To_String(addresses[num_addresses]))); num_addresses++; } return num_addresses; } /* void cNicEnum::Init ( void ) { WWDEBUG_SAY(("cNicEnum::Init\n")); WSADATA wsa_data; int startup_rc = ::WSAStartup(MAKEWORD(1, 1), &wsa_data); if (startup_rc != 0) { WWDEBUG_SAY((" WSAStartup failed!\n")); return; } // // Retrieve list of nic's // ULONG local_addresses[MAX_NICS]; ULONG num_addresses = Enumerate_Nics(local_addresses, MAX_NICS); WWASSERT(num_addresses <= MAX_NICS); WWDEBUG_SAY((" Found %d NIC(s)\n", num_addresses)); // // We are going to discard anything that is internet addressable. // The non-internet-addressable NIC's are: // 10.*.*.* // 192.168.*.* // 172.16-31.*.* // We will order these as above to promote the more common choice. // USHORT index = 0; USHORT class_1 = 0; USHORT class_2 = 0; NumNics = 0; NumGSNics = 0; // // Create a seperate list of GameSpy compatible NIC's that includes // local and internet addressable // // Don't add the following interfaces to the list. // 127.* (localhost) // 224.* (multicast) // for (index = 0; index < num_addresses; index++) { class_1 = (::ntohl(local_addresses[index]) & 0xff000000) >> 24; if (class_1 != 127 && class_1 != 224) { GSNicList[NumGSNics++] = local_addresses[index]; } } // // First, scan for 10.*.*.* addresses // for (index = 0; index < num_addresses; index++) { class_1 = (::ntohl(local_addresses[index]) & 0xff000000) >> 24; if (class_1 == 10) { NicList[NumNics++] = local_addresses[index]; } } // // Next, scan for 192.168.*.* addresses // for (index = 0; index < num_addresses; index++) { class_1 = (::ntohl(local_addresses[index]) & 0xff000000) >> 24; class_2 = (::ntohl(local_addresses[index]) & 0x00ff0000) >> 16; if (class_1 == 192 && class_2 == 168) { NicList[NumNics++] = local_addresses[index]; } } // // Finally, scan for 172.16-31.*.* addresses // for (index = 0; index < num_addresses; index++) { class_1 = (::ntohl(local_addresses[index]) & 0xff000000) >> 24; class_2 = (::ntohl(local_addresses[index]) & 0x00ff0000) >> 16; if (class_1 == 172 && class_2 >= 16 && class_2 <= 31) { NicList[NumNics++] = local_addresses[index]; } } WWDEBUG_SAY((" Of which %d are non-internet addressable.\n", NumNics)); // // Initialize or update PreferredLanNic if required. // bool is_nic_valid = false; for (index = 0; index < NumNics; index++) { if ((ULONG) cUserOptions::PreferredLanNic.Get() == NicList[index]) { is_nic_valid = true; break; } } if (!is_nic_valid) { if (NumNics > 0) { cUserOptions::PreferredLanNic.Set(NicList[0]); } else { cUserOptions::PreferredLanNic.Set(0); } } is_nic_valid = false; for (index = 0; index < NumGSNics; index++) { if ((ULONG) cUserOptions::PreferredGameSpyNic.Get() == GSNicList[index]) { is_nic_valid = true; break; } } if (!is_nic_valid) { if (NumGSNics > 0) { cUserOptions::PreferredGameSpyNic.Set(GSNicList[0]); } else { cUserOptions::PreferredGameSpyNic.Set(0); } } WWDEBUG_SAY((" PreferredLanNic is %u (%s)\n", (ULONG) cUserOptions::PreferredLanNic.Get(), cNetUtil::Address_To_String(cUserOptions::PreferredLanNic.Get()))); WWDEBUG_SAY((" PreferredGameSpyNic is %u (%s)\n", (ULONG) cUserOptions::PreferredGameSpyNic.Get(), cNetUtil::Address_To_String(cUserOptions::PreferredGameSpyNic.Get()))); int cleanup_rc = ::WSACleanup(); WWASSERT(cleanup_rc != SOCKET_ERROR); } */