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