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