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 return 1;
84
85 // TODO: empty the recvinfo
86 NETADDR Addr;
87 unsigned char *pData;
88 int Bytes = net_udp_recv(sock: m_Socket, addr: &Addr, data: &pData);
89
90 // no more packets for now
91 if(Bytes <= 0)
92 break;
93
94 if(m_pStun->OnPacket(Addr, pData, DataSize: Bytes))
95 {
96 continue;
97 }
98
99 SECURITY_TOKEN Token;
100 *pResponseToken = NET_SECURITY_TOKEN_UNKNOWN;
101 if(CNetBase::UnpackPacket(pBuffer: pData, Size: Bytes, pPacket: &m_RecvBuffer, Sixup, pSecurityToken: &Token, pResponseToken) == 0)
102 {
103 if(Sixup)
104 {
105 Addr.type |= NETTYPE_TW7;
106 }
107 if(m_RecvBuffer.m_Flags & NET_PACKETFLAG_CONNLESS)
108 {
109 pChunk->m_Flags = NETSENDFLAG_CONNLESS;
110 pChunk->m_ClientId = -1;
111 pChunk->m_Address = Addr;
112 pChunk->m_DataSize = m_RecvBuffer.m_DataSize;
113 pChunk->m_pData = m_RecvBuffer.m_aChunkData;
114 if(m_RecvBuffer.m_Flags & NET_PACKETFLAG_EXTENDED)
115 {
116 pChunk->m_Flags |= NETSENDFLAG_EXTENDED;
117 mem_copy(dest: pChunk->m_aExtraData, source: m_RecvBuffer.m_aExtraData, size: sizeof(pChunk->m_aExtraData));
118 }
119 return 1;
120 }
121 else
122 {
123 const bool Control = (m_RecvBuffer.m_Flags & NET_PACKETFLAG_CONTROL) != 0;
124 if(Sixup &&
125 Control &&
126 m_RecvBuffer.m_DataSize >= 1 + (int)sizeof(SECURITY_TOKEN) &&
127 m_RecvBuffer.m_aChunkData[0] == protocol7::NET_CTRLMSG_TOKEN)
128 {
129 m_TokenCache.AddToken(pAddr: &Addr, Token: *pResponseToken);
130 }
131 if(m_Connection.State() != CNetConnection::EState::OFFLINE &&
132 m_Connection.State() != CNetConnection::EState::ERROR &&
133 m_Connection.Feed(pPacket: &m_RecvBuffer, pAddr: &Addr, SecurityToken: Token, ResponseToken: *pResponseToken))
134 {
135 if(!Control &&
136 m_RecvBuffer.m_DataSize > 0 &&
137 m_RecvBuffer.m_NumChunks > 0)
138 {
139 m_PacketChunkUnpacker.FeedPacket(Addr, Packet: m_RecvBuffer, pConnection: &m_Connection, ClientId: 0);
140 }
141 }
142 }
143 }
144 }
145 return 0;
146}
147
148int CNetClient::Send(CNetChunk *pChunk)
149{
150 if(pChunk->m_DataSize >= NET_MAX_PAYLOAD)
151 {
152 dbg_msg(sys: "netclient", fmt: "chunk payload too big. %d. dropping chunk", pChunk->m_DataSize);
153 return -1;
154 }
155
156 if(pChunk->m_Flags & NETSENDFLAG_CONNLESS)
157 {
158 // send connectionless packet
159 if(pChunk->m_Address.type & NETTYPE_TW7)
160 {
161 m_TokenCache.SendPacketConnless(pChunk);
162 }
163 else
164 {
165 CNetBase::SendPacketConnless(Socket: m_Socket, pAddr: &pChunk->m_Address, pData: pChunk->m_pData, DataSize: pChunk->m_DataSize,
166 Extended: pChunk->m_Flags & NETSENDFLAG_EXTENDED, aExtra: pChunk->m_aExtraData);
167 }
168 }
169 else
170 {
171 int Flags = 0;
172 dbg_assert(pChunk->m_ClientId == 0, "erroneous client id");
173
174 if(pChunk->m_Flags & NETSENDFLAG_VITAL)
175 Flags = NET_CHUNKFLAG_VITAL;
176
177 m_Connection.QueueChunk(Flags, DataSize: pChunk->m_DataSize, pData: pChunk->m_pData);
178
179 if(pChunk->m_Flags & NETSENDFLAG_FLUSH)
180 m_Connection.Flush();
181 }
182 return 0;
183}
184
185int CNetClient::State()
186{
187 if(m_Connection.State() == CNetConnection::EState::ONLINE)
188 return NETSTATE_ONLINE;
189 if(m_Connection.State() == CNetConnection::EState::OFFLINE)
190 return NETSTATE_OFFLINE;
191 return NETSTATE_CONNECTING;
192}
193
194int CNetClient::Flush()
195{
196 return m_Connection.Flush();
197}
198
199bool CNetClient::GotProblems(int64_t MaxLatency) const
200{
201 return time_get() - m_Connection.LastRecvTime() > MaxLatency;
202}
203
204const char *CNetClient::ErrorString() const
205{
206 return m_Connection.ErrorString();
207}
208
209void CNetClient::FeedStunServer(NETADDR StunServer)
210{
211 m_pStun->FeedStunServer(StunServer);
212}
213
214void CNetClient::RefreshStun()
215{
216 m_pStun->Refresh();
217}
218
219CONNECTIVITY CNetClient::GetConnectivity(int NetType, NETADDR *pGlobalAddr)
220{
221 return m_pStun->GetConnectivity(NetType, pGlobalAddr);
222}
223