1 /******************************************************************************\
2 * Copyright (c) 2004-2020
3 *
4 * Author(s):
5 * Volker Fischer
6 *
7 ******************************************************************************
8 *
9 * This program is free software; you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License as published by the Free Software
11 * Foundation; either version 2 of the License, or (at your option) any later
12 * version.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 *
23 \******************************************************************************/
24
25 #include "socket.h"
26 #include "server.h"
27
28 #ifdef _WIN32
29 # include <winsock2.h>
30 # include <ws2tcpip.h>
31 #else
32 # include <arpa/inet.h>
33 #endif
34
35 /* Implementation *************************************************************/
36
37 // Connections -------------------------------------------------------------
38 // it is important to do the following connections in this class since we
39 // have a thread transition
40
41 // we have different connections for client and server, created after Init in corresponding constructor
42
CSocket(CChannel * pNewChannel,const quint16 iPortNumber,const quint16 iQosNumber,const QString & strServerBindIP,bool bEnableIPv6)43 CSocket::CSocket ( CChannel* pNewChannel, const quint16 iPortNumber, const quint16 iQosNumber, const QString& strServerBindIP, bool bEnableIPv6 ) :
44 pChannel ( pNewChannel ),
45 bIsClient ( true ),
46 bJitterBufferOK ( true ),
47 bEnableIPv6 ( bEnableIPv6 )
48 {
49 Init ( iPortNumber, iQosNumber, strServerBindIP );
50
51 // client connections:
52 QObject::connect ( this, &CSocket::ProtcolMessageReceived, pChannel, &CChannel::OnProtcolMessageReceived );
53
54 QObject::connect ( this, &CSocket::ProtcolCLMessageReceived, pChannel, &CChannel::OnProtcolCLMessageReceived );
55
56 QObject::connect ( this, static_cast<void ( CSocket::* )()> ( &CSocket::NewConnection ), pChannel, &CChannel::OnNewConnection );
57 }
58
CSocket(CServer * pNServP,const quint16 iPortNumber,const quint16 iQosNumber,const QString & strServerBindIP,bool bEnableIPv6)59 CSocket::CSocket ( CServer* pNServP, const quint16 iPortNumber, const quint16 iQosNumber, const QString& strServerBindIP, bool bEnableIPv6 ) :
60 pServer ( pNServP ),
61 bIsClient ( false ),
62 bJitterBufferOK ( true ),
63 bEnableIPv6 ( bEnableIPv6 )
64 {
65 Init ( iPortNumber, iQosNumber, strServerBindIP );
66
67 // server connections:
68 QObject::connect ( this, &CSocket::ProtcolMessageReceived, pServer, &CServer::OnProtcolMessageReceived );
69
70 QObject::connect ( this, &CSocket::ProtcolCLMessageReceived, pServer, &CServer::OnProtcolCLMessageReceived );
71
72 QObject::connect ( this,
73 static_cast<void ( CSocket::* ) ( int, int, CHostAddress )> ( &CSocket::NewConnection ),
74 pServer,
75 &CServer::OnNewConnection );
76
77 QObject::connect ( this, &CSocket::ServerFull, pServer, &CServer::OnServerFull );
78 }
79
Init(const quint16 iNewPortNumber,const quint16 iNewQosNumber,const QString & strNewServerBindIP)80 void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber, const QString& strNewServerBindIP )
81 {
82 uSockAddr UdpSocketAddr;
83
84 int UdpSocketAddrLen;
85 uint16_t* UdpPort;
86
87 // first store parameters, in case reinit is required (mostly for iOS)
88 iPortNumber = iNewPortNumber;
89 iQosNumber = iNewQosNumber;
90 strServerBindIP = strNewServerBindIP;
91
92 #ifdef _WIN32
93 // for the Windows socket usage we have to start it up first
94
95 // clang-format off
96 // TODO check for error and exit application on error
97 // clang-format on
98
99 WSADATA wsa;
100 WSAStartup ( MAKEWORD ( 1, 0 ), &wsa );
101 #endif
102
103 memset ( &UdpSocketAddr, 0, sizeof ( UdpSocketAddr ) );
104
105 if ( bEnableIPv6 )
106 {
107 // try to create a IPv6 UDP socket
108 UdpSocket = socket ( AF_INET6, SOCK_DGRAM, 0 );
109 if ( UdpSocket == -1 )
110 {
111 // IPv6 requested but not available, throw error
112 throw CGenErr ( "IPv6 requested but not available on this system.", "Network Error" );
113 }
114
115 // The IPV6_V6ONLY socket option must be false in order for the socket to listen on both protocols.
116 // On Linux it's false by default on most (all?) distros, but on Windows it is true by default
117 const uint8_t no = 0;
118 setsockopt ( UdpSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*) &no, sizeof ( no ) );
119
120 // set the QoS
121 const char tos = (char) iQosNumber; // Quality of Service
122 setsockopt ( UdpSocket, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof ( tos ) );
123
124 UdpSocketAddr.sa6.sin6_family = AF_INET6;
125 UdpSocketAddr.sa6.sin6_addr = in6addr_any;
126 UdpSocketAddrLen = sizeof ( UdpSocketAddr.sa6 );
127
128 UdpPort = &UdpSocketAddr.sa6.sin6_port; // where to put the port number
129
130 // FIXME: If binding a dual-protocol interface to a specific address, does it cease to be dual-protocol?
131
132 // TODO - ALLOW IPV6 ADDRESS
133 // if ( !strServerBindIP.isEmpty() )
134 //{
135 // UdpSocketInAddr.sin_addr.s_addr = htonl ( QHostAddress ( strServerBindIP ).toIPv4Address() );
136 //}
137 // END TODO - ALLOW IPV6 ADDRESS
138 }
139 else
140 {
141 // create the UDP socket for IPv4
142 UdpSocket = socket ( AF_INET, SOCK_DGRAM, 0 );
143 if ( UdpSocket == -1 )
144 {
145 // IPv4 requested but not available, throw error (should never happen, but check anyway)
146 throw CGenErr ( "IPv4 requested but not available on this system.", "Network Error" );
147 }
148
149 // set the QoS
150 const char tos = (char) iQosNumber; // Quality of Service
151 setsockopt ( UdpSocket, IPPROTO_IP, IP_TOS, &tos, sizeof ( tos ) );
152
153 // preinitialize socket in address (only the port number is missing)
154 UdpSocketAddr.sa4.sin_family = AF_INET;
155 UdpSocketAddr.sa4.sin_addr.s_addr = INADDR_ANY;
156 UdpSocketAddrLen = sizeof ( UdpSocketAddr.sa4 );
157
158 UdpPort = &UdpSocketAddr.sa4.sin_port; // where to put the port number
159
160 if ( !strServerBindIP.isEmpty() )
161 {
162 UdpSocketAddr.sa4.sin_addr.s_addr = htonl ( QHostAddress ( strServerBindIP ).toIPv4Address() );
163 }
164 }
165
166 #ifdef Q_OS_IOS
167 // ignore the broken pipe signal to avoid crash (iOS)
168 int valueone = 1;
169 setsockopt ( UdpSocket, SOL_SOCKET, SO_NOSIGPIPE, &valueone, sizeof ( valueone ) );
170 #endif
171
172 // allocate memory for network receive and send buffer in samples
173 vecbyRecBuf.Init ( MAX_SIZE_BYTES_NETW_BUF );
174
175 // initialize the listening socket
176 bool bSuccess;
177
178 if ( bIsClient )
179 {
180 if ( iPortNumber == 0 )
181 {
182 // if port number is 0, bind the client to a random available port
183 *UdpPort = htons ( 0 );
184
185 bSuccess = ( ::bind ( UdpSocket, &UdpSocketAddr.sa, UdpSocketAddrLen ) == 0 );
186 }
187 else
188 {
189 // If the port is not available, try "NUM_SOCKET_PORTS_TO_TRY" times
190 // with incremented port numbers. Randomize the start port, in case a
191 // faulty router gets stuck and confused by a particular port (like
192 // the starting port). Might work around frustrating "cannot connect"
193 // problems (#568)
194 const quint16 startingPortNumber = iPortNumber + rand() % NUM_SOCKET_PORTS_TO_TRY;
195
196 quint16 iClientPortIncrement = 0;
197 bSuccess = false; // initialization for while loop
198
199 while ( !bSuccess && ( iClientPortIncrement <= NUM_SOCKET_PORTS_TO_TRY ) )
200 {
201 *UdpPort = htons ( startingPortNumber + iClientPortIncrement );
202
203 bSuccess = ( ::bind ( UdpSocket, &UdpSocketAddr.sa, UdpSocketAddrLen ) == 0 );
204
205 iClientPortIncrement++;
206 }
207 }
208 }
209 else
210 {
211 // for the server, only try the given port number and do not try out
212 // other port numbers to bind since it is important that the server
213 // gets the desired port number
214 *UdpPort = htons ( iPortNumber );
215
216 bSuccess = ( ::bind ( UdpSocket, &UdpSocketAddr.sa, UdpSocketAddrLen ) == 0 );
217 }
218
219 if ( !bSuccess )
220 {
221 // we cannot bind socket, throw error
222 throw CGenErr ( "Cannot bind the socket (maybe "
223 "the software is already running).",
224 "Network Error" );
225 }
226 }
227
Close()228 void CSocket::Close()
229 {
230 #ifdef _WIN32
231 // closesocket will cause recvfrom to return with an error because the
232 // socket is closed -> then the thread can safely be shut down
233 closesocket ( UdpSocket );
234 #elif defined( __APPLE__ ) || defined( __MACOSX )
235 // on Mac the general close has the same effect as closesocket on Windows
236 close ( UdpSocket );
237 #else
238 // on Linux the shutdown call cancels the recvfrom
239 shutdown ( UdpSocket, SHUT_RDWR );
240 #endif
241 }
242
~CSocket()243 CSocket::~CSocket()
244 {
245 // cleanup the socket (on Windows the WSA cleanup must also be called)
246 #ifdef _WIN32
247 closesocket ( UdpSocket );
248 WSACleanup();
249 #else
250 close ( UdpSocket );
251 #endif
252 }
253
SendPacket(const CVector<uint8_t> & vecbySendBuf,const CHostAddress & HostAddr)254 void CSocket::SendPacket ( const CVector<uint8_t>& vecbySendBuf, const CHostAddress& HostAddr )
255 {
256 int status = 0;
257
258 uSockAddr UdpSocketAddr;
259
260 memset ( &UdpSocketAddr, 0, sizeof ( UdpSocketAddr ) );
261
262 QMutexLocker locker ( &Mutex );
263
264 const int iVecSizeOut = vecbySendBuf.Size();
265
266 if ( iVecSizeOut > 0 )
267 {
268 // send packet through network (we have to convert the constant unsigned
269 // char vector in "const char*", for this we first convert the const
270 // uint8_t vector in a read/write uint8_t vector and then do the cast to
271 // const char *)
272
273 for ( int tries = 0; tries < 2; tries++ ) // retry loop in case send fails on iOS
274 {
275 if ( HostAddr.InetAddr.protocol() == QAbstractSocket::IPv4Protocol )
276 {
277 if ( bEnableIPv6 )
278 {
279 // Linux and Mac allow to pass an AF_INET address to a dual-stack socket,
280 // but Windows does not. So use a V4MAPPED address in an AF_INET6 sockaddr,
281 // which works on all platforms.
282
283 UdpSocketAddr.sa6.sin6_family = AF_INET6;
284 UdpSocketAddr.sa6.sin6_port = htons ( HostAddr.iPort );
285
286 uint32_t* addr = (uint32_t*) &UdpSocketAddr.sa6.sin6_addr;
287
288 addr[0] = 0;
289 addr[1] = 0;
290 addr[2] = htonl ( 0xFFFF );
291 addr[3] = htonl ( HostAddr.InetAddr.toIPv4Address() );
292
293 status = sendto ( UdpSocket,
294 (const char*) &( (CVector<uint8_t>) vecbySendBuf )[0],
295 iVecSizeOut,
296 0,
297 &UdpSocketAddr.sa,
298 sizeof ( UdpSocketAddr.sa6 ) );
299 }
300 else
301 {
302 UdpSocketAddr.sa4.sin_family = AF_INET;
303 UdpSocketAddr.sa4.sin_port = htons ( HostAddr.iPort );
304 UdpSocketAddr.sa4.sin_addr.s_addr = htonl ( HostAddr.InetAddr.toIPv4Address() );
305
306 status = sendto ( UdpSocket,
307 (const char*) &( (CVector<uint8_t>) vecbySendBuf )[0],
308 iVecSizeOut,
309 0,
310 &UdpSocketAddr.sa,
311 sizeof ( UdpSocketAddr.sa4 ) );
312 }
313 }
314 else if ( bEnableIPv6 )
315 {
316 UdpSocketAddr.sa6.sin6_family = AF_INET6;
317 UdpSocketAddr.sa6.sin6_port = htons ( HostAddr.iPort );
318 inet_pton ( AF_INET6, HostAddr.InetAddr.toString().toLocal8Bit().constData(), &UdpSocketAddr.sa6.sin6_addr );
319
320 status = sendto ( UdpSocket,
321 (const char*) &( (CVector<uint8_t>) vecbySendBuf )[0],
322 iVecSizeOut,
323 0,
324 &UdpSocketAddr.sa,
325 sizeof ( UdpSocketAddr.sa6 ) );
326 }
327
328 if ( status >= 0 )
329 {
330 break; // do not retry if success
331 }
332
333 #ifdef Q_OS_IOS
334 // qDebug("Socket send exception - mostly happens in iOS when returning from idle");
335 Init ( iPortNumber, iQosNumber, strServerBindIP ); // reinit
336
337 // loop back to retry
338 #endif
339 }
340 }
341 }
342
GetAndResetbJitterBufferOKFlag()343 bool CSocket::GetAndResetbJitterBufferOKFlag()
344 {
345 // check jitter buffer status
346 if ( !bJitterBufferOK )
347 {
348 // reset flag and return "not OK" status
349 bJitterBufferOK = true;
350 return false;
351 }
352
353 // the buffer was OK, we do not have to reset anything and just return the
354 // OK status
355 return true;
356 }
357
OnDataReceived()358 void CSocket::OnDataReceived()
359 {
360 /*
361 The strategy of this function is that only the "put audio" function is
362 called directly (i.e. the high thread priority is used) and all other less
363 important things like protocol parsing and acting on protocol messages is
364 done in the low priority thread. To get a thread transition, we have to
365 use the signal/slot mechanism (i.e. we use messages for that).
366 */
367
368 // read block from network interface and query address of sender
369 uSockAddr UdpSocketAddr;
370 #ifdef _WIN32
371 int SenderAddrSize = sizeof ( UdpSocketAddr );
372 #else
373 socklen_t SenderAddrSize = sizeof ( UdpSocketAddr );
374 #endif
375
376 const long iNumBytesRead = recvfrom ( UdpSocket, (char*) &vecbyRecBuf[0], MAX_SIZE_BYTES_NETW_BUF, 0, &UdpSocketAddr.sa, &SenderAddrSize );
377
378 // check if an error occurred or no data could be read
379 if ( iNumBytesRead <= 0 )
380 {
381 return;
382 }
383
384 if ( UdpSocketAddr.sa.sa_family == AF_INET6 )
385 {
386 if ( IN6_IS_ADDR_V4MAPPED ( &( UdpSocketAddr.sa6.sin6_addr ) ) )
387 {
388 const uint32_t addr = ( (const uint32_t*) ( &( UdpSocketAddr.sa6.sin6_addr ) ) )[3];
389 RecHostAddr.InetAddr.setAddress ( ntohl ( addr ) );
390 }
391 else
392 {
393 RecHostAddr.InetAddr.setAddress ( UdpSocketAddr.sa6.sin6_addr.s6_addr );
394 }
395 RecHostAddr.iPort = ntohs ( UdpSocketAddr.sa6.sin6_port );
396 }
397 else
398 {
399 // convert address of client
400 RecHostAddr.InetAddr.setAddress ( ntohl ( UdpSocketAddr.sa4.sin_addr.s_addr ) );
401 RecHostAddr.iPort = ntohs ( UdpSocketAddr.sa4.sin_port );
402 }
403
404 // check if this is a protocol message
405 int iRecCounter;
406 int iRecID;
407 CVector<uint8_t> vecbyMesBodyData;
408
409 if ( !CProtocol::ParseMessageFrame ( vecbyRecBuf, iNumBytesRead, vecbyMesBodyData, iRecCounter, iRecID ) )
410 {
411 // this is a protocol message, check the type of the message
412 if ( CProtocol::IsConnectionLessMessageID ( iRecID ) )
413 {
414
415 // clang-format off
416 // TODO a copy of the vector is used -> avoid malloc in real-time routine
417 // clang-format on
418
419 emit ProtcolCLMessageReceived ( iRecID, vecbyMesBodyData, RecHostAddr );
420 }
421 else
422 {
423
424 // clang-format off
425 // TODO a copy of the vector is used -> avoid malloc in real-time routine
426 // clang-format on
427
428 emit ProtcolMessageReceived ( iRecCounter, iRecID, vecbyMesBodyData, RecHostAddr );
429 }
430 }
431 else
432 {
433 // this is most probably a regular audio packet
434 if ( bIsClient )
435 {
436 // client:
437
438 switch ( pChannel->PutAudioData ( vecbyRecBuf, iNumBytesRead, RecHostAddr ) )
439 {
440 case PS_AUDIO_ERR:
441 case PS_GEN_ERROR:
442 bJitterBufferOK = false;
443 break;
444
445 case PS_NEW_CONNECTION:
446 // inform other objects that new connection was established
447 emit NewConnection();
448 break;
449
450 case PS_AUDIO_INVALID:
451 // inform about received invalid packet by fireing an event
452 emit InvalidPacketReceived ( RecHostAddr );
453 break;
454
455 default:
456 // do nothing
457 break;
458 }
459 }
460 else
461 {
462 // server:
463
464 int iCurChanID;
465
466 if ( pServer->PutAudioData ( vecbyRecBuf, iNumBytesRead, RecHostAddr, iCurChanID ) )
467 {
468 // we have a new connection, emit a signal
469 emit NewConnection ( iCurChanID, pServer->GetNumberOfConnectedClients(), RecHostAddr );
470
471 // this was an audio packet, start server if it is in sleep mode
472 if ( !pServer->IsRunning() )
473 {
474 // (note that Qt will delete the event object when done)
475 QCoreApplication::postEvent ( pServer, new CCustomEvent ( MS_PACKET_RECEIVED, 0, 0 ) );
476 }
477 }
478
479 // check if no channel is available
480 if ( iCurChanID == INVALID_CHANNEL_ID )
481 {
482 // fire message for the state that no free channel is available
483 emit ServerFull ( RecHostAddr );
484 }
485 }
486 }
487 }
488