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 <base/system.h> |
4 | |
5 | #include "netban.h" |
6 | #include "network.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 | // open socket |
15 | m_Socket = net_tcp_create(bindaddr: BindAddr); |
16 | if(!m_Socket) |
17 | return false; |
18 | if(net_tcp_listen(sock: m_Socket, backlog: NET_MAX_CONSOLE_CLIENTS)) |
19 | return false; |
20 | net_set_non_blocking(sock: m_Socket); |
21 | |
22 | for(auto &Slot : m_aSlots) |
23 | Slot.m_Connection.Reset(); |
24 | |
25 | return true; |
26 | } |
27 | |
28 | void CNetConsole::SetCallbacks(NETFUNC_NEWCLIENT_CON pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser) |
29 | { |
30 | m_pfnNewClient = pfnNewClient; |
31 | m_pfnDelClient = pfnDelClient; |
32 | m_pUser = pUser; |
33 | } |
34 | |
35 | int CNetConsole::Close() |
36 | { |
37 | for(auto &Slot : m_aSlots) |
38 | Slot.m_Connection.Disconnect(pReason: "closing console" ); |
39 | |
40 | net_tcp_close(sock: m_Socket); |
41 | |
42 | return 0; |
43 | } |
44 | |
45 | int CNetConsole::Drop(int ClientId, const char *pReason) |
46 | { |
47 | if(m_pfnDelClient) |
48 | m_pfnDelClient(ClientId, pReason, m_pUser); |
49 | |
50 | m_aSlots[ClientId].m_Connection.Disconnect(pReason); |
51 | |
52 | return 0; |
53 | } |
54 | |
55 | int CNetConsole::AcceptClient(NETSOCKET Socket, const NETADDR *pAddr) |
56 | { |
57 | char aError[256] = {0}; |
58 | int FreeSlot = -1; |
59 | |
60 | // look for free slot or multiple client |
61 | for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) |
62 | { |
63 | if(FreeSlot == -1 && m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE) |
64 | FreeSlot = i; |
65 | if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE) |
66 | { |
67 | if(net_addr_comp(a: pAddr, b: m_aSlots[i].m_Connection.PeerAddress()) == 0) |
68 | { |
69 | str_copy(dst&: aError, src: "only one client per IP allowed" ); |
70 | break; |
71 | } |
72 | } |
73 | } |
74 | |
75 | // accept client |
76 | if(!aError[0] && FreeSlot != -1) |
77 | { |
78 | m_aSlots[FreeSlot].m_Connection.Init(Socket, pAddr); |
79 | if(m_pfnNewClient) |
80 | m_pfnNewClient(FreeSlot, m_pUser); |
81 | return 0; |
82 | } |
83 | |
84 | // reject client |
85 | if(!aError[0]) |
86 | str_copy(dst&: aError, src: "no free slot available" ); |
87 | |
88 | net_tcp_send(sock: Socket, data: aError, size: str_length(str: aError)); |
89 | net_tcp_close(sock: Socket); |
90 | |
91 | return -1; |
92 | } |
93 | |
94 | int CNetConsole::Update() |
95 | { |
96 | NETSOCKET Socket; |
97 | NETADDR Addr; |
98 | |
99 | if(net_tcp_accept(sock: m_Socket, new_sock: &Socket, addr: &Addr) > 0) |
100 | { |
101 | // check if we just should drop the packet |
102 | char aBuf[128]; |
103 | if(NetBan() && NetBan()->IsBanned(pOrigAddr: &Addr, pBuf: aBuf, BufferSize: sizeof(aBuf))) |
104 | { |
105 | // banned, reply with a message and drop |
106 | net_tcp_send(sock: Socket, data: aBuf, size: str_length(str: aBuf)); |
107 | net_tcp_close(sock: Socket); |
108 | } |
109 | else |
110 | AcceptClient(Socket, pAddr: &Addr); |
111 | } |
112 | |
113 | for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) |
114 | { |
115 | if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ONLINE) |
116 | m_aSlots[i].m_Connection.Update(); |
117 | if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR) |
118 | Drop(ClientId: i, pReason: m_aSlots[i].m_Connection.ErrorString()); |
119 | } |
120 | |
121 | return 0; |
122 | } |
123 | |
124 | int CNetConsole::Recv(char *pLine, int MaxLength, int *pClientId) |
125 | { |
126 | for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++) |
127 | { |
128 | if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ONLINE && m_aSlots[i].m_Connection.Recv(pLine, MaxLength)) |
129 | { |
130 | if(pClientId) |
131 | *pClientId = i; |
132 | return 1; |
133 | } |
134 | } |
135 | return 0; |
136 | } |
137 | |
138 | int CNetConsole::Send(int ClientId, const char *pLine) |
139 | { |
140 | if(m_aSlots[ClientId].m_Connection.State() == NET_CONNSTATE_ONLINE) |
141 | return m_aSlots[ClientId].m_Connection.Send(pLine); |
142 | else |
143 | return -1; |
144 | } |
145 | |