/* ** 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 . */ /*********************************************************************************************** *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** *********************************************************************************************** * * * Project Name : Command & Conquer * * * * $Archive:: /Commando/Code/Commando/natter.cpp $* * * * $Author:: Steve_t $* * * * $Modtime:: 3/22/02 4:08p $* * * * $Revision:: 28 $* * * * * *---------------------------------------------------------------------------------------------* * * * * *---------------------------------------------------------------------------------------------* * * * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* ** Disable warning about exception handling not being enabled. */ #pragma warning(disable : 4530) #include "always.h" #include "_globals.h" #include "wwdebug.h" #include "natter.h" #include "nat.h" #include "natsock.h" #include "crandom.h" #include "registry.h" #include "wollogonmgr.h" #include "packettype.h" #include "cnetwork.h" #include "fromaddress.h" #include "packetmgr.h" #include "..\wwonline\wolchannel.h" #include "..\wwonline\wolgameoptions.h" #include "..\wwonline\wollogininfo.h" #include "..\wwonline\wolproduct.h" #include "..\wwonline\wolserver.h" WOLNATInterfaceClass WOLNATInterface; /*********************************************************************************************** * WOLNATInterfaceClass::WOLNATInterfaceClass -- Class constructor * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/7/2001 7:45PM ST : Created * *=============================================================================================*/ WOLNATInterfaceClass::WOLNATInterfaceClass(void) { GameOptionsMutex = CreateMutex(NULL, false, NULL); ServiceSocketHandler = NULL; IsServer = false; PortBase = 0; ForcePort = 0; RegExternalIP = 0; RegExternalPort = 0; ChatExternalIP = 0; } /*********************************************************************************************** * WOLNATInterfaceClass::~WOLNATInterfaceClass -- Class destructor * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/7/2001 7:45PM ST : Created * *=============================================================================================*/ WOLNATInterfaceClass::~WOLNATInterfaceClass(void) { CloseHandle(GameOptionsMutex); } /*********************************************************************************************** * WOLNATInterfaceClass::Init -- Get the class ready for use. * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/9/2001 1:20PM ST : Created * *=============================================================================================*/ void WOLNATInterfaceClass::Init(void) { /* ** Start up the firewall helper class. */ FirewallHelper.Startup(); /* ** Get the session pointer. */ SessionPtr = WWOnline::Session::GetInstance(false); /* ** Register as an observer. */ Observer::NotifyMe(*SessionPtr); Observer::NotifyMe(*SessionPtr); Observer::NotifyMe(*SessionPtr); Observer::NotifyMe(*SessionPtr); /* ** Read default values from the registry. */ RegistryClass reg(APPLICATION_SUB_KEY_NAME_NET_FIREWALL); fw_assert(reg.Is_Valid()); if (reg.Is_Valid()) { /* ** Read the FirewallHelper values from the registry. */ int last_behavior = reg.Get_Int("Behavior", 0); int last_source_port_allocation_delta = reg.Get_Int("PortDelta", 1); int source_port_pool = reg.Get_Int("PortPool", 0); int confidence = reg.Get_Int("Confidence", 0); bool send_delay; // = reg.Get_Bool("SendDelay", 0); int force_port; Get_Config(®, force_port, send_delay); ForcePort = (unsigned short) force_port; RegExternalIP = (unsigned long) reg.Get_Int("ExternalIP", RegExternalIP); RegExternalPort = (unsigned short) reg.Get_Int("ExternalPort", RegExternalPort); /* ** Set the values into the firewall helper. */ FirewallHelper.Set_Firewall_Info((unsigned long)last_behavior, last_source_port_allocation_delta, (unsigned short)source_port_pool, send_delay, confidence); /* ** Read the local class values from the registry. */ PortBase = reg.Get_Int("PortBase", PortBase); //ForcePort = reg.Get_Int("ForcePort", ForcePort); } /* ** Make sure the base port is reasonable. */ if (PortBase < 1024) { PortBase = FreeRandom.Get_Int(PORT_BASE_MIN, PORT_BASE_MAX - 32); } fw_assert(PortBase >= PORT_BASE_MIN && PortBase < PORT_BASE_MAX); /* ** Use the next base port. Add 2 not 1 since the server requires 2 ports if not dedicated. ** Make sure the port isn't in use. */ bool got_port = false; unsigned short start_port = PortBase; unsigned long timeout = TIMEGETTIME() + TIMER_SECOND * 5; do { PortBase += 2; if (PortBase > PORT_BASE_MAX - 1) { PortBase = PORT_BASE_MIN; } /* ** If we went all the way around and couldn't find a port then give up. */ if (PortBase == start_port) { break; } /* ** Try binding the ports to sockets to see if they are useable. */ SocketHandlerClass socket1, socket2; if (socket1.Open(PortBase, 0)) { if (socket2.Open(PortBase+1, 0)) { got_port = true; socket2.Close(); } else { WWDEBUG_SAY(("WOLNATInterface::Init - unable to open port %d\n", (int)PortBase+1)); } socket1.Close(); } else { WWDEBUG_SAY(("WOLNATInterface::Init - unable to open port %d\n", (int)PortBase)); } } while (!got_port && timeout > TIMEGETTIME()); } /*********************************************************************************************** * WOLNATInterfaceClass::Get_Port_As_Server -- Get the current server port number * * * * * * * * INPUT: Nothing * * * * OUTPUT: Server port * * * * WARNINGS: Use on server side only * * * * HISTORY: * * 8/20/2001 12:27PM ST : Created * *=============================================================================================*/ unsigned short WOLNATInterfaceClass::Get_Port_As_Server(void) { fw_assert(PortBase != 0); /* ** If the user specified a port then use that. */ if (ForcePort) { WWDEBUG_SAY(("WOLNATInterface::Get_Port_As_Server - returning port %d\n", (int)ForcePort)); return(ForcePort); } /* ** Use the rolling port number scheme. */ WWDEBUG_SAY(("WOLNATInterface::Get_Port_As_Server - returning port %d\n", (int)PortBase)); return(PortBase); } /*********************************************************************************************** * WOLNATInterfaceClass::Get_Port_As_Server_Client -- Get the current client port number * * * * * * * * INPUT: Nothing * * * * OUTPUT: Client port for server player * * * * WARNINGS: Use on server side only * * * * HISTORY: * * 8/20/2001 12:27PM ST : Created * *=============================================================================================*/ unsigned short WOLNATInterfaceClass::Get_Port_As_Server_Client(void) { fw_assert(PortBase != 0); /* ** If the user specified a port then use that. */ if (ForcePort) { WWDEBUG_SAY(("WOLNATInterface::Get_Port_As_Server_Client - returning port %d\n", (int)ForcePort+1)); return(ForcePort + 1); } /* ** Use the rolling port number scheme. */ WWDEBUG_SAY(("WOLNATInterface::Get_Port_As_Server_Client - returning port %d\n", (int)PortBase+1)); return(PortBase + 1); } /*********************************************************************************************** * WOLNATInterfaceClass::Set_Server -- Set the IsServer flag for the class * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/14/2001 2:20PM ST : Created * *=============================================================================================*/ void WOLNATInterfaceClass::Set_Server(bool is_server) { IsServer = is_server; if (IsServer) { FirewallHelper.Reset_Server(); } } /*********************************************************************************************** * WOLNATInterfaceClass::Get_Mangler_Server_List -- Get list if mangler servers * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/21/2001 9:59PM ST : Created * *=============================================================================================*/ DynamicVectorClass WOLNATInterfaceClass::Get_Mangler_Server_List(void) { DynamicVectorClass return_list; const WWOnline::MGLServerList &server_list = SessionPtr->GetManglerServerList(); int num_servers = server_list.size(); /* ** Copy each server from the Session server list into our own vector. */ for (int i=0 ; iGetData()), sizeof(*server)); return_list.Add(server); } return(return_list); } /*********************************************************************************************** * WOLNATInterfaceClass::Get_Num_Mangler_Servers -- Get the number of mangler servers we have * * * * * * * * INPUT: Nothing * * * * OUTPUT: Number of mangler servers * * * * WARNINGS: None * * * * HISTORY: * * 8/21/2001 9:59PM ST : Created * *=============================================================================================*/ int WOLNATInterfaceClass::Get_Num_Mangler_Servers(void) { const WWOnline::MGLServerList &server_list = SessionPtr->GetManglerServerList(); return(server_list.size()); } /*********************************************************************************************** * WOLNATInterfaceClass::Get_Mangler_Name_By_Index -- Get mangler name from servserv * * * * * * * * INPUT: Index of name to retrieve * * Buffer to return name in * * * * OUTPUT: True if we found the name * * * * WARNINGS: None * * * * HISTORY: * * 8/21/2001 10:17PM ST : Created * *=============================================================================================*/ bool WOLNATInterfaceClass::Get_Mangler_Name_By_Index(int index, char *mangler_name) { /* ** Get the server list. */ DynamicVectorClass server_list = WOLNATInterface.Get_Mangler_Server_List(); /* ** Make sure it contains the server index we want. */ if (server_list.Count() <= index) { for (int i=0 ; iconndata) < 128); strcpy(conn_data, (char*) server_list[index]->conndata); for (int i=0 ; i server_list = WOLNATInterface.Get_Mangler_Server_List(); /* ** Make sure it contains the server index we want. */ if (server_list.Count() <= index) { for (int i=0 ; iconndata) < 128); strcpy(conn_data, (char*) server_list[index]->conndata); for (int i=0 ; i::StopObserving(); Observer::StopObserving(); Observer::StopObserving(); /* ** Relase the session pointer */ SessionPtr.Release(); /* ** Now we need to write out any registry values that might have changed. */ RegistryClass reg(APPLICATION_SUB_KEY_NAME_NET_FIREWALL); fw_assert(reg.Is_Valid()); if (reg.Is_Valid()) { /* ** Read the FirewallHelper values from the class. */ unsigned long last_behavior = 0; int last_source_port_allocation_delta = 1; unsigned short source_port_pool = PORT_POOL_MIN; bool send_delay = false; int confidence = 0; FirewallHelper.Get_Firewall_Info(last_behavior, last_source_port_allocation_delta, source_port_pool, send_delay, confidence); reg.Set_Int("Behavior", (int)last_behavior); reg.Set_Int("PortDelta", last_source_port_allocation_delta); reg.Set_Int("PortPool", (int)source_port_pool); reg.Set_Int("Confidence", confidence); //reg.Set_Bool("SendDelay", send_delay); /* ** Set the local class values into the registry. */ reg.Set_Int("PortBase", PortBase); //reg.Set_Int("ForcePort", ForcePort); Set_Config(®, ForcePort, send_delay); } /* ** Shut down the firewall helper class. */ FirewallHelper.Shutdown(); } /*********************************************************************************************** * WOLNATInterfaceClass::Get_Config -- Get config settings from the registry. * * * * * * * * INPUT: Ptr to registry entry object (null for default) * * Reference to port number to set * * Reference to send delay flag to set * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 9/24/2001 12:38PM ST : Created * *=============================================================================================*/ void WOLNATInterfaceClass::Get_Config(RegistryClass *reg, int &port_number, bool &send_delay) { RegistryClass *local_reg = reg; if (!local_reg) { local_reg = new RegistryClass(APPLICATION_SUB_KEY_NAME_NET_FIREWALL); } send_delay = local_reg->Get_Bool("SendDelay", FirewallHelper.Get_Send_Delay()); port_number = local_reg->Get_Int("ForcePort", ForcePort); FirewallHelper.Set_Send_Delay(send_delay); ForcePort = port_number; if (!reg) { delete local_reg; } } /*********************************************************************************************** * WOLNATInterfaceClass::Set_Config -- Set config settings into the registry * * * * * * * * INPUT: Ptr to registry entry object (null for default) * * Port number to set * * Send delay flag to set * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 9/24/2001 12:39PM ST : Created * *=============================================================================================*/ void WOLNATInterfaceClass::Set_Config(RegistryClass *reg, int port_number, bool send_delay) { RegistryClass *local_reg = reg; if (!local_reg) { local_reg = new RegistryClass(APPLICATION_SUB_KEY_NAME_NET_FIREWALL); } FirewallHelper.Set_Send_Delay(send_delay); ForcePort = port_number; local_reg->Set_Bool("SendDelay", send_delay); local_reg->Set_Int("ForcePort", ForcePort); if (!reg) { delete local_reg; } } /*********************************************************************************************** * WOLNATInterfaceClass::HandleNotification -- Handle notification for user event. * * * * * * * * INPUT: UserEvent thingy * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/7/2001 6:57PM ST : Created * *=============================================================================================*/ void WOLNATInterfaceClass::HandleNotification(WWOnline::UserEvent& event) { WWDEBUG_SAY(("WOLNATInterface: UserEvent received.\n")); /* ** If we are in a channel the request our IP. */ if (ChatExternalIP == 0) { if (event.GetEvent() == WWOnline::UserEvent::Join) { const RefPtr& joiner = event.Subject(); WOL::User const &user = joiner->GetData(); /* ** Is this me? */ WideStringClass nick; if (WOLLogonMgr::GetLoginName(nick)) { WWDEBUG_SAY(("WOLNATInterface: User is %s\n", (char*)user.name)); char name_buffer[256]; name_buffer[0] = 0; Get_Silly_String(&nick, name_buffer, 256); if (stricmp(name_buffer, (char*)user.name) == 0) { /* ** Ask the chat server what my IP is. */ SessionPtr->RequestUserIP((char*)user.name); } } } } /* ** If someone is leaving the channel, and we are a server they are waiting to negotiate with, then we need to remove them ** from our pending negotiation list. */ if (Am_I_Server() && event.GetEvent() == WWOnline::UserEvent::Leave) { const RefPtr& joiner = event.Subject(); WOL::User const &user = joiner->GetData(); if (FirewallHelper.Remove_Player_From_Negotiation_Queue_If_Mutex_Available((char *)user.name)) { WWDEBUG_SAY(("WOLNATInterface: UserEvent Handler: Removed %s from player queue.\n", (char*)user.name)); } } #if (0) /* ** Don't do anything here unless we are the server. */ if (Am_I_Server()) { //if (CombatManager::I_Am_Server()) { /* ** If someone is joining our channel and we are a game server then we need to start talking to them. */ if (event.GetEvent() == WWOnline::UserEvent::Join) { WWDEBUG_SAY(("WOLNATInterface: UserEvent is 'Join'.\n")); /* ** Make sure this is a game channel by comparing the channel type to the game ID in our SKU. */ /* ** Get the channel type and our product SKU. */ const RefPtr &channel = SessionPtr->GetCurrentChannel(); WOL::Channel &wol_channel = channel->GetData(); RefPtrConst product = WWOnline::Product::Current(); if (product.IsValid()) { int sku = product->GetSKU(); /* ** Compare only the product type field of the sku with the game channel type. */ if (wol_channel.type == ((sku & 0xff00) >> 8)) { WWDEBUG_SAY(("WOLNATInterface: Channel is of our game type\n")); /* ** Get the original user structure from the event. */ const RefPtr& joiner = event.Subject(); WOL::User const &user = joiner->GetData(); /* ** Is this me? If so, we don't want to invite ourselves. */ WideStringClass nick; if (WOLLogonMgr::GetLoginName(nick)) { WWDEBUG_SAY(("WOLNATInterface: User is %s\n", (char*)user.name)); char name_buffer[256]; name_buffer[0] = 0; Get_Silly_String(&nick, name_buffer, 256); if (stricmp(name_buffer, (char*)user.name) != 0) { WWDEBUG_SAY(("WOLNATInterface: User isn't me.\n", (char*)user.name)); /* ** Call the join function. */ FirewallHelper.Talk_To_New_Player((WOL::User*)&user); } } } } } } #endif //(0) } /*********************************************************************************************** * WOLNATInterfaceClass::HandleNotification -- Handle notification for game options. * * * * * * * * INPUT: Game options message * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/7/2001 7:50PM ST : Created * *=============================================================================================*/ void WOLNATInterfaceClass::HandleNotification(WWOnline::GameOptionsMessage &message) { WWDEBUG_SAY(("WOLNATInterface: GameOptionsMessage received.\n")); if (message.IsPrivate()) { /* ** Get the message. */ const WOL::User& user = message.GetWOLUser(); WideStringClass porky_options = message.GetOptions(); /* ** Convert to standard string. */ char slender_options[OPTIONS_STAGING_BUFFER_SIZE]; #ifdef WWDEBUG char *ptr = #endif //WWDEBUG Get_Silly_String(&porky_options, slender_options, sizeof(slender_options)); fw_assert(ptr == slender_options); if (strnicmp(slender_options, "NAT:", 4) == 0) { /* ** Add it to the options staging area. */ ThreadLockClass locker(this); GameOptionsStagingStruct *new_options = new GameOptionsStagingStruct; new_options->User = user; strcpy(new_options->Options, slender_options); IncomingOptions.Add(new_options); } } } /*********************************************************************************************** * WOLNATInterfaceClass::HandleNotification -- Handle notification for connection status. * * * * * * * * INPUT: Connection status * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/7/2001 7:50PM ST : Created * *=============================================================================================*/ void WOLNATInterfaceClass::HandleNotification(WWOnline::ConnectionStatus &status) { WWDEBUG_SAY(("WOLNATInterface: ConnectionStatus received.\n")); if (status == WWOnline::ConnectionConnected) { FirewallHelper.Connected_To_WWOnline_Server(); } } /*********************************************************************************************** * WOLNATInterfaceClass::HandleNotification -- Handle notification for user IP events * * * * * * * * INPUT: User IP Event * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/7/2001 7:50PM ST : Created * *=============================================================================================*/ void WOLNATInterfaceClass::HandleNotification(WWOnline::UserIPEvent &ipevent) { WWDEBUG_SAY(("WOLNATInterface: ConnectionStatus received.\n")); if (ipevent.GetEvent() == WWOnline::UserIPEvent::GotIP) { WOL::User user = ipevent(); char namebuf[32]; if (Get_My_Name(namebuf)) { if (stricmp((char*)user.name, namebuf) == 0) { unsigned long ip = user.ipaddr; WWDEBUG_SAY(("WOLNATInterfaceClass::HandleNotification(WWOnline::UserIPEvent &ipevent) : ip = %08x\n")); ChatExternalIP = ip; if (ForcePort || !FirewallHelper.Get_External_Address().Is_Valid() || FirewallHelper.Get_External_Address().Get_Address() == 0) { IPAddressClass ipaddr(ip, 0); FirewallHelper.Set_External_Address(ipaddr); } } } } } /*********************************************************************************************** * WOLNATInterfaceClass::Get_My_Name -- Get name of local user. * * * * * * * * INPUT: Ptr to return buffer * * * * OUTPUT: True if got name * * * * WARNINGS: None * * * * HISTORY: * * 8/13/2001 12:08PM ST : Created * *=============================================================================================*/ bool WOLNATInterfaceClass::Get_My_Name(char *namebuf) { WideStringClass nick; if (WOLLogonMgr::GetLoginName(nick)) { namebuf[0] = 0; Get_Silly_String(&nick, namebuf, 256); return(true); } return(false); } /*********************************************************************************************** * WOLNATInterfaceClass::Send_Private_Game_Options -- Send private game options * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/7/2001 7:18PM ST : Created * *=============================================================================================*/ void WOLNATInterfaceClass::Send_Private_Game_Options(WOL::User *user, char *options) { /* ** This can be called from the firewall thread so all it does is add the options to a queue. We can't send them directly ** here since WOLAPI doesn't have a multi-threaded interface. */ ThreadLockClass locker(this); fw_assert(user != NULL); fw_assert(options != NULL); fw_assert(strlen(options) < OPTIONS_STAGING_BUFFER_SIZE); GameOptionsStagingStruct *new_options = new GameOptionsStagingStruct; new_options->User = *user; strcpy(new_options->Options, options); WWDEBUG_SAY(("WOLNATInterface: Send options string: %s\n", options)); OutgoingOptions.Add(new_options); } /*********************************************************************************************** * WOLNATInterfaceClass::Get_Private_Game_Options -- Return next game options packet * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/9/2001 9:40PM ST : Created * *=============================================================================================*/ bool WOLNATInterfaceClass::Get_Private_Game_Options(WOL::User *user, char *options_buffer, int option_buffer_len) { ThreadLockClass locker(this); fw_assert(user != NULL); fw_assert(options_buffer != NULL); fw_assert(option_buffer_len >= OPTIONS_STAGING_BUFFER_SIZE); if (IncomingOptions.Count()) { GameOptionsStagingStruct *options = IncomingOptions[0]; memcpy(user, &options->User, sizeof(*user)); strcpy(options_buffer, options->Options); delete IncomingOptions[0]; IncomingOptions.Delete(0); return(true); } return(false); } /*********************************************************************************************** * WOLNATInterfaceClass::Tell_Server_That_Client_Is_In_Channel -- Client initiates port neg. * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 11/13/2001 2:15PM ST : Created * *=============================================================================================*/ void WOLNATInterfaceClass::Tell_Server_That_Client_Is_In_Channel(void) { /* ** Get the channel name (could be passed in maybe). */ RefPtr wol_session = WWOnline::Session::GetInstance(false); WideStringClass name_string(wol_session->GetCurrentChannel()->GetName(), true); char namebuf[256]; int buffer_size = sizeof(namebuf); Get_Silly_String(&name_string, namebuf, buffer_size); /* ** Fill in a ClientInChannel options packet to send to the host. */ PrivateGameOptionsStruct options; strcpy(options.NATOptionsPrefix, "NAT:"); options.Option = WOLNATInterfaceClass::OPTION_CLIENT_IN_CHANNEL; char my_name[64]; Get_My_Name(my_name); strcpy(options.OptionData.ClientInChannel.Name, my_name); /* ** Send it. */ WOL::User user; memset(&user, 0, sizeof(user)); strcpy((char*)user.name, namebuf); WOLNATInterface.Send_Private_Game_Options(&user, (char*)&options); } /*********************************************************************************************** * WOLNATInterfaceClass::Service -- Game options service * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/7/2001 8:17PM ST : Created * *=============================================================================================*/ void WOLNATInterfaceClass::Service(void) { ThreadLockClass locker(this); /* ** Remove any names from the client queue that represent players who have already left the channel. */ FirewallHelper.Cleanup_Client_Queue(); /* ** Send any pending game options strings. */ while (OutgoingOptions.Count()) { GameOptionsStagingStruct *options = OutgoingOptions[0]; #ifdef WWDEBUG HRESULT res = #endif //WWDEBUG SessionPtr->GetChatObject()->RequestPrivateGameOptions(&options->User, options->Options); fw_assert(res == S_OK); OutgoingOptions.Delete(0); delete options; } /* ** Do any socket level servicing needed. */ if (ServiceSocketHandler) { Service_Receive_Queue(ServiceSocketHandler); } } /*********************************************************************************************** * WOLNATInterfaceClass::Set_Service_Socket_Handler -- Set socket handler for service * * * * * * * * INPUT: Ptr to socket handler class * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/13/2001 3:10PM ST : Created * *=============================================================================================*/ void WOLNATInterfaceClass::Set_Service_Socket_Handler(SocketHandlerClass *socket) { ThreadLockClass locker(this); fw_assert((socket == NULL && ServiceSocketHandler != NULL) || (socket != NULL && ServiceSocketHandler == NULL)); ServiceSocketHandler = socket; } /*********************************************************************************************** * WOLNATInterfaceClass::Get_Silly_String -- Convert wide string to char[] * * * * * * * * INPUT: Ptr to wide string * * Buffer to return string in * * Size of buffer * * * * OUTPUT: Ptr to input buffer * * * * WARNINGS: None * * * * HISTORY: * * 8/7/2001 8:19PM ST : Created * *=============================================================================================*/ char *WOLNATInterfaceClass::Get_Silly_String(WideStringClass *silly_string, char *buffer, int buffer_size) { StringClass string; silly_string->Convert_To(string); char *string_buffer = string.Peek_Buffer(); fw_assert(strlen(string_buffer) < (unsigned)buffer_size); strcpy(buffer, string_buffer); return(buffer); } /*********************************************************************************************** * WOLNATInterfaceClass::Get_Current_Server -- Get server we are logged into * * * * * * * * INPUT: Nothing * * * * OUTPUT: Current server * * * * WARNINGS: None * * * * HISTORY: * * 8/7/2001 9:44PM ST : Created * *=============================================================================================*/ void WOLNATInterfaceClass::Get_Current_Server_ConnData(char *buffer, int size) { const RefPtr& server = SessionPtr->GetCurrentServer(); if (server.IsValid()) { WOL::Server& data = server->GetData(); fw_assert(buffer != NULL); fw_assert(size >= sizeof(data.conndata)); strncpy(buffer, (char*)data.conndata, size); } else { buffer[0] = 0; } } /*********************************************************************************************** * WOLNATInterfaceClass::Send_Game_Format_Packet_To -- Send game packet with our payload * * * * * * * * INPUT: Address to send to * * Packet to send * * Size of packet * * Socket to send from as client * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/10/2001 11:12AM ST : Created * *=============================================================================================*/ bool WOLNATInterfaceClass::Send_Game_Format_Packet_To(IPAddressClass *address, char *payload, int payload_size, SocketHandlerClass *socket_handler) { ThreadLockClass locker(this); payload_size = payload_size; fw_assert(address != NULL); fw_assert(address->Is_Valid()); fw_assert(payload != NULL); /* ** Build the packet. */ cPacket packet; packet.Set_Type(PACKETTYPE_FIREWALL_PROBE); packet.Set_Id(cPacket::UNDEFINED_ID+1); packet.Add_Terminated_String(payload); /* ** Convert the address to game format. */ struct sockaddr_in sock_address; sock_address.sin_family = AF_INET; sock_address.sin_port = (unsigned short) htons((unsigned short)address->Get_Port()); address->Get_Address((unsigned char*)&sock_address.sin_addr.s_addr); /* ** Send the packet. */ if (Am_I_Server()) { /* ** On the server, we can use the regular game packet send. */ fw_assert(cNetwork::PServerConnection != NULL); cNetwork::PServerConnection->Send_Packet_To_Address(packet, &sock_address); } else { fw_assert(socket != NULL); /* ** If we are the client then the game comms isn't initialised and we need to send directly. Ugh. ** ** Calculate the packet CRC. */ cPacket full_packet; cPacket::Construct_Full_Packet(full_packet, packet); /* ** Send it. */ PacketManager.Take_Packet((unsigned char *)full_packet.Get_Data(), full_packet.Get_Compressed_Size_Bytes(), (unsigned char*)&sock_address.sin_addr.s_addr, sock_address.sin_port, socket_handler->Get_Socket()); PacketManager.Flush(true); //Hmmmmm is this going to send a bunch of other packets to the wrong place? #if (0) WWDEBUG_SAY(("WOLNATInterface - sendto %s\n", address->As_String())); int result = sendto(socket_handler->Get_Socket(), full_packet.Get_Data(), full_packet.Get_Compressed_Size_Bytes(), 0, (LPSOCKADDR) &sock_address, sizeof(SOCKADDR_IN)); if (result == SOCKET_ERROR){ if (LAST_ERROR != WSAEWOULDBLOCK) { WWDEBUG_SAY(("WOLNATInterface - sendto returned error code %d\n", LAST_ERROR)); } else { /* ** No more room for outgoing packets. */ WWDEBUG_SAY(("WOLNATInterface - sendto returned WSAEWOULDBLOCK\n", LAST_ERROR)); } } #endif //(0) } return(true); } /*********************************************************************************************** * WOLNATInterfaceClass::Intercept_Game_Packet -- Handle one of our packets coming in * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/10/2001 12:08PM ST : Created * *=============================================================================================*/ void WOLNATInterfaceClass::Intercept_Game_Packet(cPacket &packet) { ThreadLockClass locker(this); /* ** All we care about is the from address and the payload string to extract those. */ char payload[512]; payload[0] = 0; int payload_size = 512; packet.Get_Terminated_String(payload, payload_size, true); fw_assert(strlen(payload) > 0); /* ** Get the address. */ cFromAddress const *from_address = packet.Get_From_Address_Wrapper(); IPAddressClass my_address; my_address.Set_Address((unsigned char*)&(from_address->FromAddress.sin_addr.s_addr), (unsigned short) ntohs(from_address->FromAddress.sin_port)); WWDEBUG_SAY(("WOLNATInterface: Got game packet from address %s - %s\n", my_address.As_String(), payload)); /* ** Add it to the incoming packet queue. */ GamePacketStruct *new_packet = new GamePacketStruct; new_packet->FromAddress = my_address; fw_assert(strlen(payload) < sizeof(new_packet->Payload)); strncpy((char*)(new_packet->Payload), payload, sizeof(new_packet->Payload)-1); IncomingPackets.Add(new_packet); } /*********************************************************************************************** * WOLNATInterfaceClass::Service_Receive_Queue -- Pick up game packets for clients only * * * * * * * * INPUT: Socket to check on * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/10/2001 1:42PM ST : Created * *=============================================================================================*/ void WOLNATInterfaceClass::Service_Receive_Queue(SocketHandlerClass *socket) { ThreadLockClass locker(this); fw_assert(socket != NULL); fw_assert(socket->Get_Socket() != INVALID_SOCKET); /* ** If we are not the server, just try to pick up a single packet. */ if (!Am_I_Server()) { cPacket packet; cPacket full_packet; unsigned char ip_address[4]; unsigned short port; int bytes = PacketManager.Get_Packet(socket->Get_Socket(), (unsigned char *)full_packet.Get_Data(), full_packet.Get_Max_Size(), ip_address, port); if (bytes > 0) { sockaddr_in *addr_ptr = (LPSOCKADDR_IN) &full_packet.Get_From_Address_Wrapper()->FromAddress; memcpy(&addr_ptr->sin_addr.s_addr, ip_address, 4); addr_ptr->sin_port = port; } if (bytes > 0) { full_packet.Set_Bit_Length(bytes*8); cPacket::Construct_App_Packet(packet, full_packet); Intercept_Game_Packet(packet); } #if (0) unsigned long bytes = 0; int result = ioctlsocket(socket->Get_Socket(), FIONREAD, &bytes); /* ** Result of 0 is success. */ fw_assert(result == 0); /* ** If there is outstanding data, 'bytes' will contain the size of the next queued datagram. */ if (bytes != 0) { /* ** Call recvfrom function to get the outstanding packet. */ int addr_len = sizeof(sockaddr_in); result = recvfrom(socket->Get_Socket(), full_packet.Get_Data(), full_packet.Get_Max_Size(), 0, (LPSOCKADDR) &full_packet.Get_From_Address_Wrapper()->FromAddress, &addr_len); /* ** See if we got an error. */ if (result != SOCKET_ERROR) { full_packet.Set_Bit_Length(result*8); cPacket::Construct_App_Packet(packet, full_packet); Intercept_Game_Packet(packet); } } #endif //(0) } } /*********************************************************************************************** * WOLNATInterfaceClass::Get_Packet -- Get the next queued packet. * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/10/2001 12:25PM ST : Created * *=============================================================================================*/ bool WOLNATInterfaceClass::Get_Packet(char *packet_buffer, int buffer_size, IPAddressClass &address) { ThreadLockClass locker(this); /* ** If there is a packet in the queue then return it. */ if (IncomingPackets.Count()) { GamePacketStruct *packet = IncomingPackets[0]; fw_assert(strlen((char*)(packet->Payload)) < (unsigned)buffer_size); strncpy(packet_buffer, (char*)(packet->Payload), buffer_size-1); address = packet->FromAddress; delete packet; IncomingPackets.Delete(0); return(true); } return(false); } /*********************************************************************************************** * WOLNATInterfaceClass::Get_Next_Client_Port -- Get next port client should use * * * * * * * * INPUT: Nothing * * * * OUTPUT: Next client port * * * * WARNINGS: None * * * * HISTORY: * * 8/14/2001 10:12PM ST : Created * *=============================================================================================*/ unsigned short WOLNATInterfaceClass::Get_Next_Client_Port(void) { /* ** If the user specified a port then use that. */ if (ForcePort) { WWDEBUG_SAY(("WOLNATInterface::Get_Next_Client_Port - returning port %d\n", (int)ForcePort)); return(ForcePort); } /* ** Use the rolling port number scheme. ** Make sure the port isn't in use. */ bool got_port = false; unsigned short start_port = PortBase; unsigned long timeout = TIMEGETTIME() + TIMER_SECOND * 5; do { PortBase++; if (PortBase > PORT_BASE_MAX - 1) { PortBase = PORT_BASE_MIN; } /* ** If we went all the way around and couldn't find a port then give up. */ if (PortBase == start_port) { break; } /* ** Try binding the port to a socket to see if it is useable. */ SocketHandlerClass socket; if (socket.Open(PortBase, 0)) { got_port = true; socket.Close(); } else { WWDEBUG_SAY(("WOLNATInterface::Get_Next_Client_Port - unable to open port %d\n", (int)PortBase)); } } while (!got_port && timeout > TIMEGETTIME()); WWDEBUG_SAY(("WOLNATInterface::Get_Next_Client_Port - returning port %d\n", (int)PortBase)); return(PortBase); } /*********************************************************************************************** * WOLNATInterfaceClass::Set_Server_Negotiated_Address -- Write server ip and port back to game* * * * * * * * INPUT: Server address * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 8/15/2001 6:43PM ST : Created * *=============================================================================================*/ void WOLNATInterfaceClass::Set_Server_Negotiated_Address(IPAddressClass *server_address) { fw_assert(server_address != NULL); fw_assert(server_address->Is_Valid()); if (server_address && server_address->Is_Valid()) { ServerNegotiatedAddress = *server_address; WWASSERT(PTheGameData != NULL); The_Game()->Set_Ip_Address((unsigned long)server_address->Get_Address()); The_Game()->Set_Port(server_address->Get_Port()); } } /*********************************************************************************************** * WOLNATInterfaceClass::Save_Firewall_Info_To_Registry -- Save detection info to registry * * * * * * * * INPUT: Nothing * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 11/28/2001 9:39PM ST : Created * *=============================================================================================*/ void WOLNATInterfaceClass::Save_Firewall_Info_To_Registry(void) { RegistryClass reg(APPLICATION_SUB_KEY_NAME_NET_FIREWALL); fw_assert(reg.Is_Valid()); if (reg.Is_Valid()) { IPAddressClass addr = FirewallHelper.Get_External_Address(); if (addr.Is_Valid()) { reg.Set_Int("ExternalIP", addr.Get_Address()); reg.Set_Int("ExternalPort", addr.Get_Port()); } } } /*********************************************************************************************** * WOLNATInterfaceClass::Get_Compact_Log -- Get basic log information to send to server * * * * * * * * INPUT: String to add info to * * * * OUTPUT: Nothing * * * * WARNINGS: None * * * * HISTORY: * * 12/4/2001 1:12PM ST : Created * *=============================================================================================*/ void WOLNATInterfaceClass::Get_Compact_Log(StringClass &log_string) { char temp[1024]; /* ** Port Base ** Force Port ** Send Delay ** Local IP ** External IP ** Raw firewall behavior ** Source port allocation delta ** Source port pool ** Server negotiated IP ** Server negotiated port. */ sprintf(temp, "%d\t%d\t%d\t%08X\t%08X\t%02X\t%d\t%d\t%08X\t%d\t", PortBase, ForcePort, FirewallHelper.Get_Send_Delay(), ntohl(FirewallHelper.Get_Local_Address()), (unsigned long) FirewallHelper.Get_External_Address().Get_Address(), (int) FirewallHelper.Get_Raw_Firewall_Behavior(), (int) FirewallHelper.Get_Source_Port_Allocation_Delta(), FirewallHelper.Get_Source_Port_Pool(), (unsigned long) ServerNegotiatedAddress.Get_Address(), (int) ServerNegotiatedAddress.Get_Port()); log_string = temp; } /*********************************************************************************************** * WOLNATInterfaceClass::Is_NAT_Thread_Busy -- Is the NAY thread not in it's idle state? * * * * * * * * INPUT: Nothing * * * * OUTPUT: bool - true if the NAT thread not in it's idle state * * * * WARNINGS: None * * * * HISTORY: * * 1/30/2002 12:52PM ST : Created * *=============================================================================================*/ bool WOLNATInterfaceClass::Is_NAT_Thread_Busy(void) { return(FirewallHelper.Is_Busy()); }