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