diff options
author | rubidium <rubidium@openttd.org> | 2010-10-15 21:56:06 +0000 |
---|---|---|
committer | rubidium <rubidium@openttd.org> | 2010-10-15 21:56:06 +0000 |
commit | 234bee0858b3c135f0827be63e904b8edacdee90 (patch) | |
tree | 0de110f2ffd193c3461ad837700d43dbb7c7e091 /src/network/core | |
parent | c9cbab14ea4ad07354524dd1a7e7df4126722ba1 (diff) | |
download | openttd-234bee0858b3c135f0827be63e904b8edacdee90.tar.xz |
(svn r20938) -Codechange: make the code for listening on a socket (more) reusable
Diffstat (limited to 'src/network/core')
-rw-r--r-- | src/network/core/tcp_listen.h | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/src/network/core/tcp_listen.h b/src/network/core/tcp_listen.h new file mode 100644 index 000000000..800f81219 --- /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 <http://www.gnu.org/licenses/>. + */ + +/** + * @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 Tsocket, PacketType Tfull_packet, PacketType Tban_packet> +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 <class Tsocket, PacketType Tfull_packet, PacketType Tban_packet> SocketList TCPListenHandler<Tsocket, Tfull_packet, Tban_packet>::sockets; + +#endif /* ENABLE_NETWORK */ + +#endif /* NETWORK_CORE_TCP_LISTEN_H */ |