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 "network.h"
4
5#include <base/dbg.h>
6#include <base/mem.h>
7#include <base/net.h>
8#include <base/time.h>
9#include <base/types.h>
10
11#include <engine/shared/protocol7.h>
12
13bool CNetClient::Open(NETADDR BindAddr)
14{
15 // open socket
16 NETSOCKET Socket;
17 Socket = net_udp_create(bindaddr: BindAddr);
18 if(!Socket)
19 return false;
20 Close();
21 // clean it
22 *this = CNetClient{};
23
24 // init
25 m_Socket = Socket;
26 m_pStun = new CStun(m_Socket);
27 m_Connection.Init(Socket: m_Socket, BlockCloseMsg: false);
28 m_TokenCache.Init(Socket: m_Socket);
29
30 return true;
31}
32
33void CNetClient::Close()
34{
35 if(!m_Socket)
36 {
37 return;
38 }
39 if(m_pStun)
40 {
41 delete m_pStun;
42 m_pStun = nullptr;
43 }
44 net_udp_close(sock: m_Socket);
45 m_Socket = nullptr;
46}
47
48void CNetClient::Disconnect(const char *pReason)
49{
50 m_Connection.Disconnect(pReason);
51}
52
53void CNetClient::Update()
54{
55 m_Connection.Update();
56 if(m_Connection.State() == CNetConnection::EState::ERROR)
57 Disconnect(pReason: m_Connection.ErrorString());
58 m_pStun->Update();
59 m_TokenCache.Update();
60}
61
62void CNetClient::Connect(const NETADDR *pAddr, int NumAddrs)
63{
64 m_Connection.Connect(pAddr, NumAddrs);
65}
66
67void CNetClient::Connect7(const NETADDR *pAddr, int NumAddrs)
68{
69 m_Connection.Connect7(pAddr, NumAddrs);
70}
71
72void CNetClient::ResetErrorString()
73{
74 m_Connection.ResetErrorString();
75}
76
77int CNetClient::Recv(CNetChunk *pChunk, SECURITY_TOKEN *pResponseToken, bool Sixup)
78{
79 while(true)
80 {
81 // Unpack next chunk from stored packet if available
82 if(m_PacketChunkUnpacker.UnpackNextChunk(pChunk))
83 {
84 // Only return the pending packet if the peer is still
85 // available, the caller might have dropped them in
86 // response to the previous chunk.
87 if(m_Connection.State() != CNetConnection::EState::OFFLINE)
88 {
89 return 1;
90 }
91 else
92 {
93 m_PacketChunkUnpacker.Reset();
94 }
95 }
96
97 // TODO: empty the recvinfo
98 NETADDR Addr;
99 unsigned char *pData;
100 int Bytes = net_udp_recv(sock: m_Socket, addr: &Addr, data: &pData);
101
102 // no more packets for now
103 if(Bytes <= 0)
104 break;
105
106 if(m_pStun->OnPacket(Addr, pData, DataSize: Bytes))
107 {
108 continue;
109 }
110
111 SECURITY_TOKEN Token;
112 if(CNetBase::UnpackPacket(pBuffer: pData, Size: Bytes, pPacket: &m_RecvBuffer, Sixup, pSecurityToken: &Token, pResponseToken) == 0)
113 {
114 if(Sixup)
115 {
116 Addr.type |= NETTYPE_TW7;
117 }
118 if(m_RecvBuffer.m_Flags & NET_PACKETFLAG_CONNLESS)
119 {
120 pChunk->m_Flags = NETSENDFLAG_CONNLESS;
121 pChunk->m_ClientId = -1;
122 pChunk->m_Address = Addr;
123 pChunk->m_DataSize = m_RecvBuffer.m_DataSize;
124 pChunk->m_pData = m_RecvBuffer.m_aChunkData;
125 if(m_RecvBuffer.m_Flags & NET_PACKETFLAG_EXTENDED)
126 {
127 pChunk->m_Flags |= NETSENDFLAG_EXTENDED;
128 mem_copy(dest: pChunk->m_aExtraData, source: m_RecvBuffer.m_aExtraData, size: sizeof(pChunk->m_aExtraData));
129 }
130 return 1;
131 }
132 else
133 {
134 const bool Control = (m_RecvBuffer.m_Flags & NET_PACKETFLAG_CONTROL) != 0;
135 if(Sixup &&
136 Control &&
137 m_RecvBuffer.m_DataSize >= 1 + (int)sizeof(SECURITY_TOKEN) &&
138 m_RecvBuffer.m_aChunkData[0] == protocol7::NET_CTRLMSG_TOKEN)
139 {
140 m_TokenCache.AddToken(pAddr: &Addr, Token: *pResponseToken);
141 }
142 if(m_Connection.State() != CNetConnection::EState::OFFLINE &&
143 m_Connection.State() != CNetConnection::EState::ERROR &&
144 m_Connection.Feed(pPacket: &m_RecvBuffer, pAddr: &Addr, SecurityToken: Token, ResponseToken: *pResponseToken))
145 {
146 if(!Control &&
147 m_RecvBuffer.m_DataSize > 0 &&
148 m_RecvBuffer.m_NumChunks > 0)
149 {
150 m_PacketChunkUnpacker.FeedPacket(Addr, Packet: m_RecvBuffer, pConnection: &m_Connection, ClientId: 0);
151 }
152 }
153 }
154 }
155 }
156 return 0;
157}
158
159int CNetClient::Send(CNetChunk *pChunk)
160{
161 pChunk->AssertSizeSanity();
162
163 if(pChunk->m_Flags & NETSENDFLAG_CONNLESS)
164 {
165 // send connectionless packet
166 if(pChunk->m_Address.type & NETTYPE_TW7)
167 {
168 m_TokenCache.SendPacketConnless(pChunk);
169 }
170 else
171 {
172 CNetBase::SendPacketConnless(Socket: m_Socket, pAddr: &pChunk->m_Address, pData: pChunk->m_pData, DataSize: pChunk->m_DataSize,
173 Extended: pChunk->m_Flags & NETSENDFLAG_EXTENDED, aExtra: pChunk->m_aExtraData);
174 }
175 }
176 else
177 {
178 int Flags = 0;
179 dbg_assert(pChunk->m_ClientId == 0, "erroneous client id");
180
181 if(pChunk->m_Flags & NETSENDFLAG_VITAL)
182 Flags = NET_CHUNKFLAG_VITAL;
183
184 m_Connection.QueueChunk(Flags, DataSize: pChunk->m_DataSize, pData: pChunk->m_pData);
185
186 if(pChunk->m_Flags & NETSENDFLAG_FLUSH)
187 m_Connection.Flush();
188 }
189 return 0;
190}
191
192int CNetClient::State()
193{
194 if(m_Connection.State() == CNetConnection::EState::ONLINE)
195 return NETSTATE_ONLINE;
196 if(m_Connection.State() == CNetConnection::EState::OFFLINE)
197 return NETSTATE_OFFLINE;
198 return NETSTATE_CONNECTING;
199}
200
201int CNetClient::Flush()
202{
203 return m_Connection.Flush();
204}
205
206bool CNetClient::GotProblems(int64_t MaxLatency) const
207{
208 return time_get() - m_Connection.LastRecvTime() > MaxLatency;
209}
210
211const char *CNetClient::ErrorString() const
212{
213 return m_Connection.ErrorString();
214}
215
216void CNetClient::FeedStunServer(NETADDR StunServer)
217{
218 m_pStun->FeedStunServer(StunServer);
219}
220
221void CNetClient::RefreshStun()
222{
223 m_pStun->Refresh();
224}
225
226CONNECTIVITY CNetClient::GetConnectivity(int NetType, NETADDR *pGlobalAddr)
227{
228 return m_pStun->GetConnectivity(NetType, pGlobalAddr);
229}
230