| 1 | /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ |
| 2 | /* If you are missing that file, acquire a complete release at teeworlds.com. */ |
| 3 | #include "netban.h" |
| 4 | #include "network.h" |
| 5 | |
| 6 | #include <base/system.h> |
| 7 | |
| 8 | bool CNetConsole::Open(NETADDR BindAddr, CNetBan *pNetBan) |
| 9 | { |
| 10 | // zero out the whole structure |
| 11 | *this = CNetConsole{}; |
| 12 | m_pNetBan = pNetBan; |
| 13 | |
| 14 | m_Socket = net_tcp_create(bindaddr: BindAddr); |
| 15 | if(!m_Socket) |
| 16 | { |
| 17 | return false; |
| 18 | } |
| 19 | if(net_tcp_listen(sock: m_Socket, backlog: NET_MAX_CONSOLE_CLIENTS) != 0 || |
| 20 | net_set_non_blocking(sock: m_Socket) != 0) |
| 21 | { |
| 22 | net_tcp_close(sock: m_Socket); |
| 23 | m_Socket = nullptr; |
| 24 | return false; |
| 25 | } |
| 26 | |
| 27 | for(auto &Slot : m_aSlots) |
| 28 | { |
| 29 | Slot.m_Connection.Reset(); |
| 30 | } |
| 31 | |
| 32 | return true; |
| 33 | } |
| 34 | |
| 35 | void CNetConsole::SetCallbacks(NETFUNC_NEWCLIENT_CON pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser) |
| 36 | { |
| 37 | m_pfnNewClient = pfnNewClient; |
| 38 | m_pfnDelClient = pfnDelClient; |
| 39 | m_pUser = pUser; |
| 40 | } |
| 41 | |
| 42 | void CNetConsole::Close() |
| 43 | { |
| 44 | if(!m_Socket) |
| 45 | { |
| 46 | return; |
| 47 | } |
| 48 | for(auto &Slot : m_aSlots) |
| 49 | { |
| 50 | Slot.m_Connection.Disconnect(pReason: "closing console" ); |
| 51 | } |
| 52 | net_tcp_close(sock: m_Socket); |
| 53 | m_Socket = nullptr; |
| 54 | } |
| 55 | |
| 56 | void CNetConsole::Drop(int ClientId, const char *pReason) |
| 57 | { |
| 58 | if(m_pfnDelClient) |
| 59 | m_pfnDelClient(ClientId, pReason, m_pUser); |
| 60 | |
| 61 | m_aSlots[ClientId].m_Connection.Disconnect(pReason); |
| 62 | } |
| 63 | |
| 64 | int CNetConsole::AcceptClient(NETSOCKET Socket, const NETADDR *pAddr) |
| 65 | { |
| 66 | const auto &DropClient = [&](const char *pError) { |
| 67 | net_tcp_send(sock: Socket, data: pError, size: str_length(str: pError)); |
| 68 | net_tcp_close(sock: Socket); |
| 69 | }; |
| 70 | |
| 71 | // check if address is banned |
| 72 | char aBanMessage[256]; |
| 73 | if(NetBan() && NetBan()->IsBanned(pOrigAddr: pAddr, pBuf: aBanMessage, BufferSize: sizeof(aBanMessage))) |
| 74 | { |
| 75 | DropClient(aBanMessage); |
| 76 | return -1; |
| 77 | } |
| 78 | |
| 79 | // look for free slot or multiple clients with same address |
| 80 | int FreeSlot = -1; |
| 81 | for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) |
| 82 | { |
| 83 | if(m_aSlots[i].m_Connection.State() == CConsoleNetConnection::EState::OFFLINE) |
| 84 | { |
| 85 | if(FreeSlot == -1) |
| 86 | { |
| 87 | FreeSlot = i; |
| 88 | } |
| 89 | } |
| 90 | else if(net_addr_comp_noport(a: pAddr, b: m_aSlots[i].m_Connection.PeerAddress()) == 0) |
| 91 | { |
| 92 | DropClient("only one client per IP allowed" ); |
| 93 | return -1; |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | if(FreeSlot == -1) |
| 98 | { |
| 99 | DropClient("no free slot available" ); |
| 100 | return -1; |
| 101 | } |
| 102 | |
| 103 | // accept client |
| 104 | if(m_aSlots[FreeSlot].m_Connection.Init(Socket, pAddr) != 0) |
| 105 | { |
| 106 | DropClient("failed to initialize client connection" ); |
| 107 | return -1; |
| 108 | } |
| 109 | if(m_pfnNewClient) |
| 110 | { |
| 111 | m_pfnNewClient(FreeSlot, m_pUser); |
| 112 | } |
| 113 | return 0; |
| 114 | } |
| 115 | |
| 116 | void CNetConsole::Update() |
| 117 | { |
| 118 | NETSOCKET Socket; |
| 119 | NETADDR Addr; |
| 120 | if(net_tcp_accept(sock: m_Socket, new_sock: &Socket, addr: &Addr) > 0) |
| 121 | { |
| 122 | AcceptClient(Socket, pAddr: &Addr); |
| 123 | } |
| 124 | |
| 125 | for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) |
| 126 | { |
| 127 | if(m_aSlots[i].m_Connection.State() == CConsoleNetConnection::EState::ONLINE) |
| 128 | m_aSlots[i].m_Connection.Update(); |
| 129 | if(m_aSlots[i].m_Connection.State() == CConsoleNetConnection::EState::ERROR) |
| 130 | Drop(ClientId: i, pReason: m_aSlots[i].m_Connection.ErrorString()); |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | int CNetConsole::Recv(char *pLine, int MaxLength, int *pClientId) |
| 135 | { |
| 136 | for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) |
| 137 | { |
| 138 | if(m_aSlots[i].m_Connection.State() == CConsoleNetConnection::EState::ONLINE && m_aSlots[i].m_Connection.Recv(pLine, MaxLength)) |
| 139 | { |
| 140 | if(pClientId) |
| 141 | *pClientId = i; |
| 142 | return 1; |
| 143 | } |
| 144 | } |
| 145 | return 0; |
| 146 | } |
| 147 | |
| 148 | int CNetConsole::Send(int ClientId, const char *pLine) |
| 149 | { |
| 150 | if(m_aSlots[ClientId].m_Connection.State() == CConsoleNetConnection::EState::ONLINE) |
| 151 | return m_aSlots[ClientId].m_Connection.Send(pLine); |
| 152 | else |
| 153 | return -1; |
| 154 | } |
| 155 | |