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
8bool 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
35void 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
42void 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
56void 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
64int 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
116void 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
134int 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
148int 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