diff --git a/projects/openttd_vs100.vcxproj b/projects/openttd_vs100.vcxproj index 7ad2ebabc3..69e635eedf 100644 --- a/projects/openttd_vs100.vcxproj +++ b/projects/openttd_vs100.vcxproj @@ -1041,6 +1041,7 @@ + diff --git a/projects/openttd_vs100.vcxproj.filters b/projects/openttd_vs100.vcxproj.filters index 1da3154f4f..98740669e2 100644 --- a/projects/openttd_vs100.vcxproj.filters +++ b/projects/openttd_vs100.vcxproj.filters @@ -2343,6 +2343,9 @@ Network Core + + Network Core + Network Core diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj index a0f9fc6537..1e8fa9d975 100644 --- a/projects/openttd_vs80.vcproj +++ b/projects/openttd_vs80.vcproj @@ -3518,6 +3518,10 @@ RelativePath=".\..\src\network\core\tcp_http.h" > + + diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj index 2f88e6139d..dde8192f0d 100644 --- a/projects/openttd_vs90.vcproj +++ b/projects/openttd_vs90.vcproj @@ -3515,6 +3515,10 @@ RelativePath=".\..\src\network\core\tcp_http.h" > + + diff --git a/source.list b/source.list index 33a4bd6839..c59f96d4d0 100644 --- a/source.list +++ b/source.list @@ -836,6 +836,7 @@ network/core/tcp_game.cpp network/core/tcp_game.h network/core/tcp_http.cpp network/core/tcp_http.h +network/core/tcp_listen.h network/core/udp.cpp network/core/udp.h diff --git a/src/network/core/tcp_listen.h b/src/network/core/tcp_listen.h new file mode 100644 index 0000000000..800f812199 --- /dev/null +++ b/src/network/core/tcp_listen.h @@ -0,0 +1,178 @@ +/* $Id: tcp.h 20933 2010-10-15 19:33:08Z rubidium $ */ + +/* + * This file is part of OpenTTD. + * OpenTTD 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, version 2. + * OpenTTD 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 OpenTTD. If not, see . + */ + +/** + * @file tcp.h Basic functions to listen for TCP connections. + */ + +#ifndef NETWORK_CORE_TCP_LISTEN_H +#define NETWORK_CORE_TCP_LISTEN_H + +#include "tcp.h" +#include "../network.h" +#include "../../core/pool_type.hpp" +#include "../../debug.h" +#include "table/strings.h" + +#ifdef ENABLE_NETWORK + +/** + * Template for TCP listeners. + * @param Tsocket The class we create sockets for. + * @param Tfull_packet The packet type to return when we don't allow more sockets. + * @param Tban_packet The packet type to return when the client is banned. + */ +template +class TCPListenHandler { + /** List of sockets we listen on. */ + static SocketList sockets; + +public: + /** + * Accepts clients from the sockets. + * @param ls Socket to accept clients from. + */ + static void AcceptClient(SOCKET ls) + { + for (;;) { + struct sockaddr_storage sin; + memset(&sin, 0, sizeof(sin)); + socklen_t sin_len = sizeof(sin); + SOCKET s = accept(ls, (struct sockaddr*)&sin, &sin_len); + if (s == INVALID_SOCKET) return; + + SetNonBlocking(s); // XXX error handling? + + NetworkAddress address(sin, sin_len); + DEBUG(net, 1, "[%s] Client connected from %s on frame %d", Tsocket::GetName(), address.GetHostname(), _frame_counter); + + SetNoDelay(s); // XXX error handling? + + /* Check if the client is banned */ + bool banned = false; + for (char **iter = _network_ban_list.Begin(); iter != _network_ban_list.End(); iter++) { + banned = address.IsInNetmask(*iter); + if (banned) { + Packet p(Tban_packet); + p.PrepareToSend(); + + DEBUG(net, 1, "[%s] Banned ip tried to join (%s), refused", Tsocket::GetName(), *iter); + + send(s, (const char*)p.buffer, p.size, 0); + closesocket(s); + break; + } + } + /* If this client is banned, continue with next client */ + if (banned) continue; + + /* Can we handle a new client? */ + if (!Tsocket::AllowConnection()) { + /* no more clients allowed? + * Send to the client that we are full! */ + Packet p(Tfull_packet); + p.PrepareToSend(); + + send(s, (const char*)p.buffer, p.size, 0); + closesocket(s); + + continue; + } + + Tsocket::AcceptConnection(s, address); + } + } + + /** + * Handle the receiving of packets. + * @return true if everything went okay. + */ + static bool Receive() + { + fd_set read_fd, write_fd; + struct timeval tv; + + FD_ZERO(&read_fd); + FD_ZERO(&write_fd); + + + Tsocket *cs; + FOR_ALL_ITEMS_FROM(Tsocket, idx, cs, 0) { + FD_SET(cs->sock, &read_fd); + FD_SET(cs->sock, &write_fd); + } + + /* take care of listener port */ + for (SocketList::iterator s = sockets.Begin(); s != sockets.End(); s++) { + FD_SET(s->second, &read_fd); + } + + tv.tv_sec = tv.tv_usec = 0; // don't block at all. +#if !defined(__MORPHOS__) && !defined(__AMIGA__) + select(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv); +#else + WaitSelect(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv, NULL); +#endif + + /* accept clients.. */ + for (SocketList::iterator s = sockets.Begin(); s != sockets.End(); s++) { + if (FD_ISSET(s->second, &read_fd)) AcceptClient(s->second); + } + + /* read stuff from clients */ + FOR_ALL_ITEMS_FROM(Tsocket, idx, cs, 0) { + cs->writable = !!FD_ISSET(cs->sock, &write_fd); + if (FD_ISSET(cs->sock, &read_fd)) { + cs->Recv_Packets(); + } + } + return _networking; + } + + /** + * Listen on a particular port. + * @param port The port to listen on. + * @return true if listening succeeded. + */ + static bool Listen(uint16 port) + { + assert(sockets.Length() == 0); + + NetworkAddressList addresses; + GetBindAddresses(&addresses, port); + + for (NetworkAddress *address = addresses.Begin(); address != addresses.End(); address++) { + address->Listen(SOCK_STREAM, &sockets); + } + + if (sockets.Length() == 0) { + DEBUG(net, 0, "[server] could not start network: could not create listening socket"); + NetworkError(STR_NETWORK_ERROR_SERVER_START); + return false; + } + + return true; + } + + /** Close the sockets we're listening on. */ + static void CloseListeners() + { + for (SocketList::iterator s = sockets.Begin(); s != sockets.End(); s++) { + closesocket(s->second); + } + sockets.Clear(); + DEBUG(net, 1, "[%s] closed listeners", Tsocket::GetName()); + } +}; + +template SocketList TCPListenHandler::sockets; + +#endif /* ENABLE_NETWORK */ + +#endif /* NETWORK_CORE_TCP_LISTEN_H */ diff --git a/src/network/network.cpp b/src/network/network.cpp index 435a8eb91d..309a9f0252 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -87,9 +87,6 @@ extern NetworkUDPSocketHandler *_udp_client_socket; ///< udp client socket extern NetworkUDPSocketHandler *_udp_server_socket; ///< udp server socket extern NetworkUDPSocketHandler *_udp_master_socket; ///< udp master socket -/* The listen socket for the server */ -static SocketList _listensockets; - /* The amount of clients connected */ byte _network_clients_connected = 0; @@ -234,12 +231,6 @@ void NetworkError(StringID error_string) _switch_mode_errorstr = error_string; } -static void ServerStartError(const char *error) -{ - DEBUG(net, 0, "[server] could not start network: %s",error); - NetworkError(STR_NETWORK_ERROR_SERVER_START); -} - /** * Retrieve the string id of an internal error number * @param err NetworkErrorCode @@ -428,82 +419,14 @@ void ParseConnectionString(const char **company, const char **port, char *connec } } -/* For the server, to accept new clients */ -static void NetworkAcceptClients(SOCKET ls) +/* static */ void ServerNetworkGameSocketHandler::AcceptConnection(SOCKET s, const NetworkAddress &address) { - for (;;) { - struct sockaddr_storage sin; - memset(&sin, 0, sizeof(sin)); - socklen_t sin_len = sizeof(sin); - SOCKET s = accept(ls, (struct sockaddr*)&sin, &sin_len); - if (s == INVALID_SOCKET) return; - - SetNonBlocking(s); // XXX error handling? - - NetworkAddress address(sin, sin_len); - DEBUG(net, 1, "Client connected from %s on frame %d", address.GetHostname(), _frame_counter); - - SetNoDelay(s); // XXX error handling? - - /* Check if the client is banned */ - bool banned = false; - for (char **iter = _network_ban_list.Begin(); iter != _network_ban_list.End(); iter++) { - banned = address.IsInNetmask(*iter); - if (banned) { - Packet p(PACKET_SERVER_BANNED); - p.PrepareToSend(); - - DEBUG(net, 1, "Banned ip tried to join (%s), refused", *iter); - - send(s, (const char*)p.buffer, p.size, 0); - closesocket(s); - break; - } - } - /* If this client is banned, continue with next client */ - if (banned) continue; - - /* Can we handle a new client? */ - if (_network_clients_connected >= MAX_CLIENTS || - _network_game_info.clients_on >= _settings_client.network.max_clients) { - /* no more clients allowed? - * Send to the client that we are full! */ - Packet p(PACKET_SERVER_FULL); - p.PrepareToSend(); - - send(s, (const char*)p.buffer, p.size, 0); - closesocket(s); + /* Register the login */ + _network_clients_connected++; - continue; - } - - /* Register the login */ - _network_clients_connected++; - - SetWindowDirty(WC_CLIENT_LIST, 0); - ServerNetworkGameSocketHandler *cs = new ServerNetworkGameSocketHandler(s); - cs->GetInfo()->client_address = address; // Save the IP of the client - } -} - -/* Set up the listen socket for the server */ -static bool NetworkListen() -{ - assert(_listensockets.Length() == 0); - - NetworkAddressList addresses; - GetBindAddresses(&addresses, _settings_client.network.server_port); - - for (NetworkAddress *address = addresses.Begin(); address != addresses.End(); address++) { - address->Listen(SOCK_STREAM, &_listensockets); - } - - if (_listensockets.Length() == 0) { - ServerStartError("Could not create listening socket"); - return false; - } - - return true; + SetWindowDirty(WC_CLIENT_LIST, 0); + ServerNetworkGameSocketHandler *cs = new ServerNetworkGameSocketHandler(s); + cs->GetInfo()->client_address = address; // Save the IP of the client } /** Resets both pools used for network clients */ @@ -521,12 +444,7 @@ void NetworkClose() FOR_ALL_CLIENT_SOCKETS(cs) { cs->CloseConnection(NETWORK_RECV_STATUS_CONN_LOST); } - /* We are a server, also close the listensocket */ - for (SocketList::iterator s = _listensockets.Begin(); s != _listensockets.End(); s++) { - closesocket(s->second); - } - _listensockets.Clear(); - DEBUG(net, 1, "[tcp] closed listeners"); + ServerNetworkGameSocketHandler::CloseListeners(); } else if (MyClient::my_client != NULL) { MyClient::SendQuit(); MyClient::my_client->Send_Packets(); @@ -713,7 +631,7 @@ bool NetworkServerStart() NetworkDisconnect(); NetworkInitialize(); - if (!NetworkListen()) return false; + if (!ServerNetworkGameSocketHandler::Listen(_settings_client.network.server_port)) return false; /* Try to start UDP-server */ _network_udp_server = _udp_server_socket->Listen(); @@ -789,67 +707,20 @@ void NetworkDisconnect(bool blocking) */ static bool NetworkReceive() { - if (!_network_server) { + if (_network_server) { + return ServerNetworkGameSocketHandler::Receive(); + } else { return ClientNetworkGameSocketHandler::Receive(); } - NetworkClientSocket *cs; - fd_set read_fd, write_fd; - struct timeval tv; - - FD_ZERO(&read_fd); - FD_ZERO(&write_fd); - - FOR_ALL_CLIENT_SOCKETS(cs) { - FD_SET(cs->sock, &read_fd); - FD_SET(cs->sock, &write_fd); - } - - /* take care of listener port */ - for (SocketList::iterator s = _listensockets.Begin(); s != _listensockets.End(); s++) { - FD_SET(s->second, &read_fd); - } - - tv.tv_sec = tv.tv_usec = 0; // don't block at all. -#if !defined(__MORPHOS__) && !defined(__AMIGA__) - int n = select(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv); -#else - int n = WaitSelect(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv, NULL); -#endif - if (n == -1 && !_network_server) NetworkError(STR_NETWORK_ERROR_LOSTCONNECTION); - - /* accept clients.. */ - for (SocketList::iterator s = _listensockets.Begin(); s != _listensockets.End(); s++) { - if (FD_ISSET(s->second, &read_fd)) NetworkAcceptClients(s->second); - } - - /* read stuff from clients */ - FOR_ALL_CLIENT_SOCKETS(cs) { - cs->writable = !!FD_ISSET(cs->sock, &write_fd); - if (FD_ISSET(cs->sock, &read_fd)) { - cs->Recv_Packets(); - } - } - return _networking; } /* This sends all buffered commands (if possible) */ static void NetworkSend() { - if (!_network_server) { + if (_network_server) { + ServerNetworkGameSocketHandler::Send(); + } else { ClientNetworkGameSocketHandler::Send(); - return; - } - - NetworkClientSocket *cs; - FOR_ALL_CLIENT_SOCKETS(cs) { - if (cs->writable) { - cs->Send_Packets(); - - if (cs->status == STATUS_MAP) { - /* This client is in the middle of a map-send, call the function for that */ - cs->SendMap(); - } - } } } diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index ccb1bb37ed..ab2bd1632c 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -49,6 +49,9 @@ assert_compile(NetworkClientSocketPool::MAX_SIZE == MAX_CLIENT_SLOTS); NetworkClientSocketPool _networkclientsocket_pool("NetworkClientSocket"); INSTANTIATE_POOL_METHODS(NetworkClientSocket) +/** Instantiate the listen sockets. */ +template SocketList TCPListenHandler::sockets; + /** * Create a new socket for the server side of the game connection. * @param s The socket to connect with. @@ -118,6 +121,32 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::CloseConnection(NetworkRecvSta return status; } +/** + * Whether an connection is allowed or not at this moment. + * @return true if the connection is allowed. + */ +/* static */ bool ServerNetworkGameSocketHandler::AllowConnection() +{ + extern byte _network_clients_connected; + return _network_clients_connected < MAX_CLIENTS && _network_game_info.clients_on < _settings_client.network.max_clients; +} + +/** Send the packets for the server sockets. */ +/* static */ void ServerNetworkGameSocketHandler::Send() +{ + NetworkClientSocket *cs; + FOR_ALL_CLIENT_SOCKETS(cs) { + if (cs->writable) { + cs->Send_Packets(); + + if (cs->status == STATUS_MAP) { + /* This client is in the middle of a map-send, call the function for that */ + cs->SendMap(); + } + } + } +} + static void NetworkHandleCommandQueue(NetworkClientSocket *cs); /*********** diff --git a/src/network/network_server.h b/src/network/network_server.h index 691411519d..c5bc049f17 100644 --- a/src/network/network_server.h +++ b/src/network/network_server.h @@ -15,6 +15,7 @@ #ifdef ENABLE_NETWORK #include "network_internal.h" +#include "core/tcp_listen.h" class ServerNetworkGameSocketHandler; typedef ServerNetworkGameSocketHandler NetworkClientSocket; @@ -22,7 +23,7 @@ typedef Pool NetworkClien extern NetworkClientSocketPool _networkclientsocket_pool; /** Class for handling the server side of the game connection. */ -class ServerNetworkGameSocketHandler : public NetworkClientSocketPool::PoolItem<&_networkclientsocket_pool>, public NetworkGameSocketHandler { +class ServerNetworkGameSocketHandler : public NetworkClientSocketPool::PoolItem<&_networkclientsocket_pool>, public NetworkGameSocketHandler, public TCPListenHandler { protected: DECLARE_GAME_RECEIVE_COMMAND(PACKET_CLIENT_JOIN); DECLARE_GAME_RECEIVE_COMMAND(PACKET_CLIENT_COMPANY_INFO); @@ -76,6 +77,19 @@ public: NetworkRecvStatus SendCommand(const CommandPacket *cp); NetworkRecvStatus SendCompanyUpdate(); NetworkRecvStatus SendConfigUpdate(); + + static void Send(); + static void AcceptConnection(SOCKET s, const NetworkAddress &address); + static bool AllowConnection(); + + /** + * Get the name used by the listener. + * @return the name to show in debug logs and the like. + */ + static const char *GetName() + { + return "server"; + } }; void NetworkServer_Tick(bool send_frame);