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/mem.h>
6#include <base/net.h>
7#include <base/str.h>
8
9void CConsoleNetConnection::SetPeerAddr(const NETADDR *pAddr)
10{
11 m_PeerAddr = *pAddr;
12 net_addr_str(addr: pAddr, string: m_aPeerAddrStr.data(), max_length: m_aPeerAddrStr.size(), add_port: true);
13}
14
15void CConsoleNetConnection::ClearPeerAddr()
16{
17 mem_zero(block: &m_PeerAddr, size: sizeof(m_PeerAddr));
18 m_aPeerAddrStr[0] = '\0';
19}
20
21void CConsoleNetConnection::Reset()
22{
23 m_State = EState::OFFLINE;
24 ClearPeerAddr();
25 m_aErrorString[0] = 0;
26
27 m_Socket = nullptr;
28 m_aBuffer[0] = 0;
29 m_BufferOffset = 0;
30
31 m_LineEndingDetected = false;
32#if defined(CONF_FAMILY_WINDOWS)
33 m_aLineEnding[0] = '\r';
34 m_aLineEnding[1] = '\n';
35 m_aLineEnding[2] = 0;
36#else
37 m_aLineEnding[0] = '\n';
38 m_aLineEnding[1] = 0;
39 m_aLineEnding[2] = 0;
40#endif
41}
42
43int CConsoleNetConnection::Init(NETSOCKET Socket, const NETADDR *pAddr)
44{
45 Reset();
46
47 if(net_set_non_blocking(sock: Socket) != 0)
48 {
49 return -1;
50 }
51
52 m_Socket = Socket;
53 SetPeerAddr(pAddr);
54 m_State = EState::ONLINE;
55 return 0;
56}
57
58void CConsoleNetConnection::Disconnect(const char *pReason)
59{
60 if(State() == EState::OFFLINE)
61 return;
62
63 if(pReason && pReason[0])
64 Send(pLine: pReason);
65
66 net_tcp_close(sock: m_Socket);
67
68 Reset();
69}
70
71int CConsoleNetConnection::Update()
72{
73 if(State() == EState::ONLINE)
74 {
75 if((int)(sizeof(m_aBuffer)) <= m_BufferOffset)
76 {
77 m_State = EState::ERROR;
78 str_copy(dst&: m_aErrorString, src: "too weak connection (out of buffer)");
79 return -1;
80 }
81
82 int Bytes = net_tcp_recv(sock: m_Socket, data: m_aBuffer + m_BufferOffset, maxsize: (int)(sizeof(m_aBuffer)) - m_BufferOffset);
83
84 if(Bytes > 0)
85 {
86 m_BufferOffset += Bytes;
87 }
88 else if(Bytes < 0)
89 {
90 if(net_would_block()) // no data received
91 return 0;
92
93 m_State = EState::ERROR;
94 str_copy(dst&: m_aErrorString, src: "connection failure");
95 return -1;
96 }
97 else
98 {
99 m_State = EState::ERROR;
100 str_copy(dst&: m_aErrorString, src: "remote end closed the connection");
101 return -1;
102 }
103 }
104
105 return 0;
106}
107
108int CConsoleNetConnection::Recv(char *pLine, int MaxLength)
109{
110 if(State() == EState::ONLINE)
111 {
112 if(m_BufferOffset)
113 {
114 // find message start
115 int StartOffset = 0;
116 while(m_aBuffer[StartOffset] == '\r' || m_aBuffer[StartOffset] == '\n')
117 {
118 // detect clients line ending format
119 if(!m_LineEndingDetected)
120 {
121 m_aLineEnding[0] = m_aBuffer[StartOffset];
122 if(StartOffset + 1 < m_BufferOffset && (m_aBuffer[StartOffset + 1] == '\r' || m_aBuffer[StartOffset + 1] == '\n') &&
123 m_aBuffer[StartOffset] != m_aBuffer[StartOffset + 1])
124 m_aLineEnding[1] = m_aBuffer[StartOffset + 1];
125 m_LineEndingDetected = true;
126 }
127
128 if(++StartOffset >= m_BufferOffset)
129 {
130 m_BufferOffset = 0;
131 return 0;
132 }
133 }
134
135 // find message end
136 int EndOffset = StartOffset;
137 while(m_aBuffer[EndOffset] != '\r' && m_aBuffer[EndOffset] != '\n')
138 {
139 if(++EndOffset >= m_BufferOffset)
140 {
141 if(StartOffset > 0)
142 {
143 mem_move(dest: m_aBuffer, source: m_aBuffer + StartOffset, size: m_BufferOffset - StartOffset);
144 m_BufferOffset -= StartOffset;
145 }
146 return 0;
147 }
148 }
149
150 // extract message and update buffer
151 if(MaxLength - 1 < EndOffset - StartOffset)
152 {
153 if(StartOffset > 0)
154 {
155 mem_move(dest: m_aBuffer, source: m_aBuffer + StartOffset, size: m_BufferOffset - StartOffset);
156 m_BufferOffset -= StartOffset;
157 }
158 return 0;
159 }
160 mem_copy(dest: pLine, source: m_aBuffer + StartOffset, size: EndOffset - StartOffset);
161 pLine[EndOffset - StartOffset] = 0;
162 str_sanitize_cc(str: pLine);
163 mem_move(dest: m_aBuffer, source: m_aBuffer + EndOffset, size: m_BufferOffset - EndOffset);
164 m_BufferOffset -= EndOffset;
165 return str_utf8_check(str: pLine);
166 }
167 }
168 return 0;
169}
170
171int CConsoleNetConnection::Send(const char *pLine)
172{
173 if(State() != EState::ONLINE)
174 return -1;
175
176 char aBuf[1024];
177 str_copy(dst: aBuf, src: pLine, dst_size: (int)(sizeof(aBuf)) - 2);
178 int Length = str_length(str: aBuf);
179 aBuf[Length] = m_aLineEnding[0];
180 aBuf[Length + 1] = m_aLineEnding[1];
181 aBuf[Length + 2] = m_aLineEnding[2];
182 Length += 3;
183 const char *pData = aBuf;
184
185 while(true)
186 {
187 int Send = net_tcp_send(sock: m_Socket, data: pData, size: Length);
188 if(Send < 0)
189 {
190 m_State = EState::ERROR;
191 str_copy(dst&: m_aErrorString, src: "failed to send packet");
192 return -1;
193 }
194
195 if(Send >= Length)
196 break;
197
198 pData += Send;
199 Length -= Send;
200 }
201
202 return 0;
203}
204