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