From 649537ef53fa76ad3ddf4d29d61136f909a49458 Mon Sep 17 00:00:00 2001 From: mstolle629 <81071220+mstolle629@users.noreply.github.com> Date: Wed, 31 Mar 2021 06:47:43 +0200 Subject: [PATCH 1/7] Add files via upload ipv6 support --- src/client.cpp | 7 +- src/client.h | 4 +- src/global.h | 18 +---- src/main.cpp | 63 ++++++++++------- src/settings.h | 2 + src/socket.cpp | 180 ++++++++++++++++++++++++++++++++++++++----------- src/util.cpp | 58 +++++++++++++--- src/util.h | 18 +++-- 8 files changed, 253 insertions(+), 97 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index cf8da89978..52504e8f79 100755 --- a/src/client.cpp +++ b/src/client.cpp @@ -31,7 +31,8 @@ CClient::CClient ( const quint16 iPortNumber, const QString& strMIDISetup, const bool bNoAutoJackConnect, const QString& strNClientName, - const bool bNMuteMeInPersonalMix ) : + const bool bNMuteMeInPersonalMix, + int ipmode ) : ChannelInfo ( ), strClientName ( strNClientName ), Channel ( false ), /* we need a client channel -> "false" */ @@ -62,6 +63,7 @@ CClient::CClient ( const quint16 iPortNumber, bEnableOPUS64 ( false ), bJitterBufferOK ( true ), bNuteMeInPersonalMix ( bNMuteMeInPersonalMix ), + iipmode ( ipmode ), iServerSockBufNumFrames ( DEF_NET_BUF_SIZE_NUM_BL ), pSignalHandler ( CSignalHandler::getSingletonP() ) { @@ -389,7 +391,8 @@ bool CClient::SetServerAddr ( QString strNAddr ) { CHostAddress HostAddress; if ( NetworkUtil().ParseNetworkAddress ( strNAddr, - HostAddress ) ) + HostAddress, + iipmode ) ) { // apply address to the channel Channel.SetAddress ( HostAddress ); diff --git a/src/client.h b/src/client.h index 7d83a54440..06a507d745 100755 --- a/src/client.h +++ b/src/client.h @@ -113,7 +113,8 @@ class CClient : public QObject const QString& strMIDISetup, const bool bNoAutoJackConnect, const QString& strNClientName, - const bool bNMuteMeInPersonalMix ); + const bool bNMuteMeInPersonalMix, + int ipmode=0 ); virtual ~CClient(); @@ -355,6 +356,7 @@ class CClient : public QObject bool bJitterBufferOK; bool bNuteMeInPersonalMix; + int iipmode; QMutex MutexDriverReinit; // server settings diff --git a/src/global.h b/src/global.h index 044fb1c9a6..490a2fabca 100755 --- a/src/global.h +++ b/src/global.h @@ -93,9 +93,6 @@ LED bar: lbr #define SYSTEM_FRAME_SIZE_SAMPLES 64 #define DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES ( 2 * SYSTEM_FRAME_SIZE_SAMPLES ) -// additional buffer for delay panning -#define MAX_DELAY_PANNING_SAMPLES 64 - // default server address and port numbers #define DEFAULT_SERVER_ADDRESS "anygenre1.jamulus.io" #define DEFAULT_PORT_NUMBER 22124 @@ -155,19 +152,6 @@ LED bar: lbr #define AUD_MIX_FADER_MAX 100 #define AUD_MIX_PAN_MAX 100 -// range of audio mixer fader -#define AUD_MIX_FADER_RANGE_DB 35.0f - -// coefficient for averaging channel levels for automatic fader adjustment -#define AUTO_FADER_ADJUST_ALPHA 0.2f - -// target level for auto fader adjustment in decibels -#define AUTO_FADER_TARGET_LEVEL_DB -30.0f - -// threshold in decibels below which the channel is considered as noise -// and not adjusted -#define AUTO_FADER_NOISE_THRESHOLD_DB -40.0f - // maximum number of fader groups (must be consistent to audiomixerboard implementation) #define MAX_NUM_FADER_GROUPS 4 @@ -249,7 +233,7 @@ LED bar: lbr #define MAX_LEN_CHAT_TEXT 1600 #define MAX_LEN_CHAT_TEXT_PLUS_HTML 1800 #define MAX_LEN_SERVER_NAME 20 -#define MAX_LEN_IP_ADDRESS 15 +#define MAX_LEN_IP_ADDRESS 40 #define MAX_LEN_SERVER_CITY 20 #define MAX_LEN_VERSION_TEXT 30 diff --git a/src/main.cpp b/src/main.cpp index 5484a63b85..b4326ff990 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -69,7 +69,6 @@ int main ( int argc, char** argv ) bool bMuteStream = false; bool bMuteMeInPersonalMix = false; bool bDisableRecording = false; - bool bDelayPan = false; bool bNoAutoJackConnect = false; bool bUseTranslation = true; bool bCustomPortNumberGiven = false; @@ -88,6 +87,7 @@ int main ( int argc, char** argv ) QString strServerListFilter = ""; QString strWelcomeMessage = ""; QString strClientName = ""; + int ipmode = 0; #if !defined(HEADLESS) && defined(_WIN32) if (AttachConsole(ATTACH_PARENT_PROCESS)) { @@ -140,6 +140,41 @@ int main ( int argc, char** argv ) continue; } + // Use ipv4 protocol + if ( GetFlagArgument ( argv, + i, + "-4", + "--ipv4" )) + { + switch (ipmode) + { + case 0: ipmode = 1; break; + case 1: break; + case 2: ipmode = 3; break; + case 3: break; + default: break; + } + CommandLineOptions << "--ipv4"; + continue; + } + + // Use ipv6 protocol + if ( GetFlagArgument ( argv, + i, + "-6", + "--ipv6" )) + { + switch (ipmode) + { + case 0: ipmode = 2; break; + case 1: ipmode = 0; break; + case 2: break; + case 3: break; + default: break; + } + CommandLineOptions << "--ipv6"; + continue; + } // Use 64 samples frame size mode -------------------------------------- if ( GetFlagArgument ( argv, @@ -382,17 +417,6 @@ int main ( int argc, char** argv ) continue; } - // Enable delay panning on startup ---------------------------------------- - if ( GetFlagArgument ( argv, - i, - "-P", - "--delaypan" ) ) - { - bDelayPan = true; - qInfo() << "- starting with delay panning"; - CommandLineOptions << "--delaypan"; - continue; - } // Central server ------------------------------------------------------ if ( GetStringArgument ( argc, @@ -577,14 +601,6 @@ int main ( int argc, char** argv ) Q_UNUSED ( bMuteStream ) // avoid compiler warnings #endif -#ifdef SERVER_ONLY - if ( bIsClient ) - { - qCritical() << "Only --server mode is supported in this build with nosound."; - exit ( 1 ); - } -#endif - // the inifile is not supported for the headless server mode if ( !bIsClient && !bUseGUI && !strIniFileName.isEmpty() ) { @@ -697,7 +713,8 @@ int main ( int argc, char** argv ) strMIDISetup, bNoAutoJackConnect, strClientName, - bMuteMeInPersonalMix ); + bMuteMeInPersonalMix, + ipmode ); // load settings from init-file (command line options override) CClientSettings Settings ( &Client, strIniFileName ); @@ -754,7 +771,6 @@ int main ( int argc, char** argv ) bUseDoubleSystemFrameSize, bUseMultithreading, bDisableRecording, - bDelayPan, eLicenceType ); #ifndef HEADLESS @@ -846,6 +862,8 @@ QString UsageArguments ( char **argv ) " -p, --port set your local port number\n" " -t, --notranslation disable translation (use English language)\n" " -v, --version output version information and exit\n" + " -4, --ipv4 use IPv4 Protocol\n" + " -6 --ipv6 use IPv6 Protocol (order of -4 and -6 will be followed\n" "\nServer only:\n" " -d, --discononquit disconnect all clients on quit\n" " -e, --centralserver address of the server list on which to register\n" @@ -858,7 +876,6 @@ QString UsageArguments ( char **argv ) " -m, --htmlstatus enable HTML status file, set file name\n" " -o, --serverinfo infos of this server in the format:\n" " [name];[city];[country as QLocale ID]\n" - " -P, --delaypan start with delay panning enabled\n" " -R, --recording sets directory to contain recorded jams\n" " --norecord disables recording (when enabled by default by -R)\n" " -s, --server start server\n" diff --git a/src/settings.h b/src/settings.h index 55a5aecfc6..da39fbd681 100755 --- a/src/settings.h +++ b/src/settings.h @@ -46,6 +46,7 @@ class CSettings : public QObject CSettings() : vecWindowPosMain ( ), // empty array strLanguage ( "" ), + iipmode ( 0 ), strFileName ( "" ) { QObject::connect ( QCoreApplication::instance(), &QCoreApplication::aboutToQuit, @@ -58,6 +59,7 @@ class CSettings : public QObject // common settings QByteArray vecWindowPosMain; QString strLanguage; + int iipmode; protected: virtual void WriteSettingsToXML ( QDomDocument& IniXMLDocument ) = 0; diff --git a/src/socket.cpp b/src/socket.cpp index 2aa7643800..eefaee02ee 100755 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -24,8 +24,7 @@ #include "socket.h" #include "server.h" - - +#include /* Implementation *************************************************************/ void CSocket::Init ( const quint16 iPortNumber ) { @@ -39,16 +38,23 @@ void CSocket::Init ( const quint16 iPortNumber ) #endif // create the UDP socket - UdpSocket = socket ( AF_INET, SOCK_DGRAM, 0 ); - + UdpSocket = socket ( AF_INET6, SOCK_DGRAM, 0); + int on = 1; + int off = 0; + setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on)); + // allow to receive ipv4 packet info (i.e. destination ip address through recvmsg + setsockopt(UdpSocket, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); + // allow to receive ipv6 packetinfo + setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); + // allow to receive v6 and v4 requests + setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)); // allocate memory for network receive and send buffer in samples vecbyRecBuf.Init ( MAX_SIZE_BYTES_NETW_BUF ); // preinitialize socket in address (only the port number is missing) - sockaddr_in UdpSocketInAddr; - UdpSocketInAddr.sin_family = AF_INET; - UdpSocketInAddr.sin_addr.s_addr = INADDR_ANY; - + sockaddr_in6 UdpSocketInAddr; + UdpSocketInAddr.sin6_family = AF_INET6; + UdpSocketInAddr.sin6_addr = in6addr_any; // initialize the listening socket bool bSuccess; @@ -57,11 +63,10 @@ void CSocket::Init ( const quint16 iPortNumber ) if ( iPortNumber == 0 ) { // if port number is 0, bind the client to a random available port - UdpSocketInAddr.sin_port = htons ( 0 ); - + UdpSocketInAddr.sin6_port = htons ( 0 ); bSuccess = ( ::bind ( UdpSocket , (sockaddr*) &UdpSocketInAddr, - sizeof ( sockaddr_in ) ) == 0 ); + sizeof ( sockaddr_in6 ) ) == 0); } else { @@ -77,11 +82,10 @@ void CSocket::Init ( const quint16 iPortNumber ) while ( !bSuccess && ( iClientPortIncrement <= NUM_SOCKET_PORTS_TO_TRY ) ) { - UdpSocketInAddr.sin_port = htons ( startingPortNumber + iClientPortIncrement ); - + UdpSocketInAddr.sin6_port = htons ( startingPortNumber + iClientPortIncrement ); bSuccess = ( ::bind ( UdpSocket , (sockaddr*) &UdpSocketInAddr, - sizeof ( sockaddr_in ) ) == 0 ); + sizeof ( sockaddr_in6 ) ) == 0 ); iClientPortIncrement++; } @@ -92,11 +96,10 @@ void CSocket::Init ( const quint16 iPortNumber ) // for the server, only try the given port number and do not try out // other port numbers to bind since it is important that the server // gets the desired port number - UdpSocketInAddr.sin_port = htons ( iPortNumber ); - + UdpSocketInAddr.sin6_port = htons ( iPortNumber ); bSuccess = ( ::bind ( UdpSocket , (sockaddr*) &UdpSocketInAddr, - sizeof ( sockaddr_in ) ) == 0 ); + sizeof ( sockaddr_in6 ) ) == 0 ); } if ( !bSuccess ) @@ -182,18 +185,69 @@ void CSocket::SendPacket ( const CVector& vecbySendBuf, // char vector in "const char*", for this we first convert the const // uint8_t vector in a read/write uint8_t vector and then do the cast to // const char*) - sockaddr_in UdpSocketOutAddr; - - UdpSocketOutAddr.sin_family = AF_INET; - UdpSocketOutAddr.sin_port = htons ( HostAddr.iPort ); - UdpSocketOutAddr.sin_addr.s_addr = htonl ( HostAddr.InetAddr.toIPv4Address() ); - - sendto ( UdpSocket, - (const char*) &( (CVector) vecbySendBuf )[0], - iVecSizeOut, - 0, - (sockaddr*) &UdpSocketOutAddr, - sizeof ( sockaddr_in ) ); + sockaddr_in6 UdpSocketOutAddr; + UdpSocketOutAddr.sin6_family = AF_INET6; + UdpSocketOutAddr.sin6_port = htons ( HostAddr.iPort ); + +// memcpy(&UdpSocketOutAddr.sin6_addr.s6_addr,(void *)&HostAddr.InetAddr.toIPv6Address(),16); + Q_IPV6ADDR ipv6addr_h = HostAddr.InetAddr.toIPv6Address(); + memcpy (&UdpSocketOutAddr.sin6_addr.s6_addr,&ipv6addr_h,16); + msghdr m; + struct iovec iov[1]; + int rv; + char pktinfo[100]; + m.msg_name = (sockaddr*) &UdpSocketOutAddr; + m.msg_namelen = sizeof ( sockaddr_in6 ); + + iov[0].iov_base = (void *) &( vecbySendBuf )[0]; + iov[0].iov_len = iVecSizeOut; + m.msg_iov = iov; + m.msg_iovlen = 1; + //m.msg_control = pktinfo; + m.msg_control = NULL; + m.msg_controllen = 0; + bool isv4; + quint32 ipaddr4_l = HostAddr.LocalAddr.toIPv4Address(&isv4); + if (isv4) + { + if (ipaddr4_l != 0) + { + struct in_addr ia; + struct in_addr da; + memset(pktinfo,0,sizeof(pktinfo)); + struct in_pktinfo *pktinfo2; + m.msg_control = pktinfo; + struct cmsghdr *cmsg = (struct cmsghdr *) pktinfo; + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + + pktinfo2 = (struct in_pktinfo*) CMSG_DATA(cmsg); + pktinfo2->ipi_ifindex = 0; + ia.s_addr = ipaddr4_l; + da.s_addr = 0; + pktinfo2->ipi_spec_dst = ia; + pktinfo2->ipi_addr = da; + m.msg_controllen += CMSG_SPACE(sizeof(struct in_pktinfo)); + + } else + { + ; + } + } + else + { + memset(pktinfo,0,sizeof(pktinfo)); + m.msg_control = pktinfo; + struct cmsghdr *cmsg = (struct cmsghdr *)pktinfo; + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(16); + memcpy(CMSG_DATA(cmsg),&HostAddr.LocalAddr,16); + m.msg_controllen += CMSG_SPACE(16); + } + rv=sendmsg(UdpSocket,&m,0); + if (rv<0) fprintf(stderr, "%s\n", strerror(errno)); } } @@ -223,19 +277,53 @@ void CSocket::OnDataReceived() */ // read block from network interface and query address of sender - sockaddr_in SenderAddr; + sockaddr_in6 SenderAddr; + in6_addr LocalInterfaceAddr; #ifdef _WIN32 int SenderAddrSize = sizeof ( sockaddr_in ); #else - socklen_t SenderAddrSize = sizeof ( sockaddr_in ); + socklen_t SenderAddrSize = sizeof ( sockaddr_in6 ); #endif + //const long iNumBytesRead = recvfrom ( UdpSocket, + // (char*) &vecbyRecBuf[0], + // MAX_SIZE_BYTES_NETW_BUF, + // f 0, + // (sockaddr*) &SenderAddr, + // &SenderAddrSize ); + struct msghdr msgh; + struct iovec iov[1]; + + struct cmsghdr *cmsg; + // Prepare message header structure + memset( &msgh, 0, sizeof(msgh)); + msgh.msg_name = &SenderAddr; + msgh.msg_namelen = SenderAddrSize; + msgh.msg_iovlen = 1; + msgh.msg_iov = iov; + iov[0].iov_base = (char *) &vecbyRecBuf[0]; + iov[0].iov_len = MAX_SIZE_BYTES_NETW_BUF; + char controlbuffer[4096]; + memset( controlbuffer, 0, sizeof(controlbuffer)); + msgh.msg_control = controlbuffer; + msgh.msg_controllen = sizeof(controlbuffer); + const long iNumBytesRead = ::recvmsg(UdpSocket, &msgh, 0); + + /* Receive auxiliary data in msgh */ + bool la_found = false; + for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msgh, cmsg)) { + //printf("d=%d ",cmsg->cmsg_level); + if (cmsg->cmsg_level == IPPROTO_IPV6 + && cmsg->cmsg_type == IPV6_PKTINFO) { + memcpy(&LocalInterfaceAddr, CMSG_DATA(cmsg), sizeof(LocalInterfaceAddr)); + char lesbareadresse[40]; + inet_ntop(AF_INET6,&LocalInterfaceAddr,lesbareadresse,sizeof(lesbareadresse)); + la_found = true; + //fprintf(stderr,"inet6addresse = %s",lesbareadresse); + break; + } + } - const long iNumBytesRead = recvfrom ( UdpSocket, - (char*) &vecbyRecBuf[0], - MAX_SIZE_BYTES_NETW_BUF, - 0, - (sockaddr*) &SenderAddr, - &SenderAddrSize ); // check if an error occurred or no data could be read if ( iNumBytesRead <= 0 ) @@ -244,8 +332,22 @@ void CSocket::OnDataReceived() } // convert address of client - RecHostAddr.InetAddr.setAddress ( ntohl ( SenderAddr.sin_addr.s_addr ) ); - RecHostAddr.iPort = ntohs ( SenderAddr.sin_port ); + Q_IPV6ADDR saddr6_tmp; + Q_IPV6ADDR iaddr6_tmp; + bool isv4 = false; + quint32 i4a; + memcpy(&saddr6_tmp,&SenderAddr.sin6_addr,16); + memcpy(&iaddr6_tmp,&LocalInterfaceAddr,16); + // Behandle ipv6-mapped ipv4-Adressen + RecHostAddr.InetAddr.setAddress(saddr6_tmp); + i4a = RecHostAddr.InetAddr.toIPv4Address(&isv4); + if (isv4) + { + RecHostAddr.InetAddr.setAddress(i4a); + } + isv4 = false; + if (la_found) RecHostAddr.LocalAddr.setAddress(iaddr6_tmp); + RecHostAddr.iPort = ntohs ( SenderAddr.sin6_port ); // check if this is a protocol message diff --git a/src/util.cpp b/src/util.cpp index e8edee54be..5519680005 100755 --- a/src/util.cpp +++ b/src/util.cpp @@ -24,7 +24,7 @@ #include "util.h" #include "client.h" - +#include "socket.h" /* Implementation *************************************************************/ // Input level meter implementation -------------------------------------------- @@ -485,8 +485,6 @@ CAboutDlg::CAboutDlg ( QWidget* parent ) : CBaseDlg ( parent ) "

David Kastrup (dakhubgit)

" "

Jordan Lum (mulyaj)

" "

Noam Postavsky (npostavs)

" - "

Johannes Brauers (JohannesBrx)

" - "

Henk De Groot (henkdegroot)

" "
" + tr ( "For details on the contributions check out the " ) + "" + tr ( "Github Contributors list" ) + "." ); @@ -502,7 +500,6 @@ CAboutDlg::CAboutDlg ( QWidget* parent ) : CBaseDlg ( parent ) "

Melcon Moraes (melcon)

" "

" + tr ( "Dutch" ) + "

" "

Jeroen Geertzen (jerogee)

" - "

Henk De Groot (henkdegroot)

" "

" + tr ( "Italian" ) + "

" "

Giuseppe Sapienza (dzpex)

" "

" + tr ( "German" ) + "

" @@ -953,7 +950,8 @@ void CLanguageComboBox::OnLanguageActivated ( int iLanguageIdx ) \******************************************************************************/ // Network utility functions --------------------------------------------------- bool NetworkUtil::ParseNetworkAddress ( QString strAddress, - CHostAddress& HostAddress ) + CHostAddress& HostAddress, + int inetmode) { QHostAddress InetAddr; quint16 iNetPort = DEFAULT_PORT_NUMBER; @@ -1004,21 +1002,62 @@ bool NetworkUtil::ParseNetworkAddress ( QString strAddress, } // use the first IPv4 address, if any - bool bFoundIPv4 = false; + bool bFoundIPx = false; foreach ( const QHostAddress HostAddr, HostInfo.addresses() ) { - if ( HostAddr.protocol() == QAbstractSocket::IPv4Protocol ) + if ( (inetmode <= 1) && (HostAddr.protocol() == QAbstractSocket::IPv4Protocol )) { InetAddr = HostAddr; - bFoundIPv4 = true; + bFoundIPx = true; break; } + if ((inetmode >= 2) && (HostAddr.protocol() == QAbstractSocket::IPv6Protocol )) + { + InetAddr = HostAddr; + bFoundIPx = true; + break; + } } - if ( !bFoundIPv4 ) + if ( !bFoundIPx ) { + switch (inetmode) + { + case 0: // ipv4 first then ipv6 + foreach (const QHostAddress Hostaddr, HostInfo.addresses() ) + { + if (Hostaddr.protocol() == QAbstractSocket::IPv6Protocol ) + { + InetAddr = Hostaddr; + bFoundIPx = true; + break; + } + } + break; + case 1: // ipv4 only + case 2: // ipv6 only + return false; + break; + case 3: // ipv6 first, then ipv4 + + foreach (const QHostAddress Hostaddr, HostInfo.addresses() ) + { + if (Hostaddr.protocol() == QAbstractSocket::IPv4Protocol ) + { + InetAddr = Hostaddr; + bFoundIPx = true; + break; + } + } + break; + default: break; + } // switch // only found IPv6 addresses + //return false; + } // if !bfoundIPx + if (!bFoundIPx) + { return false; } } @@ -1028,6 +1067,7 @@ bool NetworkUtil::ParseNetworkAddress ( QString strAddress, return true; } + CHostAddress NetworkUtil::GetLocalAddress() { QUdpSocket socket; diff --git a/src/util.h b/src/util.h index 70a05b7e39..bcd75bd5f0 100755 --- a/src/util.h +++ b/src/util.h @@ -768,16 +768,19 @@ class CHostAddress CHostAddress() : InetAddr ( static_cast ( 0 ) ), - iPort ( 0 ) {} + iPort ( 0 ), + LocalAddr ( static_cast ( 0 ) ) {} CHostAddress ( const QHostAddress NInetAddr, const quint16 iNPort ) : InetAddr ( NInetAddr ), - iPort ( iNPort ) {} + iPort ( iNPort ), + LocalAddr ( static_cast ( 0 )) {} CHostAddress ( const CHostAddress& NHAddr ) : InetAddr ( NHAddr.InetAddr ), - iPort ( NHAddr.iPort ) {} + iPort ( NHAddr.iPort ), + LocalAddr ( static_cast ( 0 )) {} // copy operator CHostAddress& operator= ( const CHostAddress& NHAddr ) @@ -819,6 +822,8 @@ class CHostAddress QHostAddress InetAddr; quint16 iPort; + QHostAddress LocalAddr; + quint16 lPort; }; @@ -1097,7 +1102,9 @@ class NetworkUtil { public: static bool ParseNetworkAddress ( QString strAddress, - CHostAddress& HostAddress ); + CHostAddress& HostAddress, + int inetmode=0); + static QString FixAddress ( const QString& strAddress ); static CHostAddress GetLocalAddress(); @@ -1281,8 +1288,7 @@ class MathUtils } else { - return powf ( 10.0f, ( fInValueRange0_1 - 1.0f ) * - AUD_MIX_FADER_RANGE_DB / 20.0f ); + return powf ( 10.0f, ( fInValueRange0_1 * 35.0f - 35.0f ) / 20.0f ); } } }; From 585ea3c7ad85ff50ba67d4f22e203d55ab8623f3 Mon Sep 17 00:00:00 2001 From: mstolle629 <81071220+mstolle629@users.noreply.github.com> Date: Wed, 31 Mar 2021 19:43:52 +0200 Subject: [PATCH 2/7] Add files via upload --- src/global.h | 16 ++++++++++++++++ src/main.cpp | 22 ++++++++++++++++++++++ src/util.cpp | 3 +++ src/util.h | 3 ++- 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/global.h b/src/global.h index 490a2fabca..ecdd8c135a 100755 --- a/src/global.h +++ b/src/global.h @@ -93,6 +93,9 @@ LED bar: lbr #define SYSTEM_FRAME_SIZE_SAMPLES 64 #define DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES ( 2 * SYSTEM_FRAME_SIZE_SAMPLES ) +// additional buffer for delay panning +#define MAX_DELAY_PANNING_SAMPLES 64 + // default server address and port numbers #define DEFAULT_SERVER_ADDRESS "anygenre1.jamulus.io" #define DEFAULT_PORT_NUMBER 22124 @@ -152,6 +155,19 @@ LED bar: lbr #define AUD_MIX_FADER_MAX 100 #define AUD_MIX_PAN_MAX 100 +// range of audio mixer fader +#define AUD_MIX_FADER_RANGE_DB 35.0f + +// coefficient for averaging channel levels for automatic fader adjustment +#define AUTO_FADER_ADJUST_ALPHA 0.2f + +// target level for auto fader adjustment in decibels +#define AUTO_FADER_TARGET_LEVEL_DB -30.0f + +// threshold in decibels below which the channel is considered as noise +// and not adjusted +#define AUTO_FADER_NOISE_THRESHOLD_DB -40.0f + // maximum number of fader groups (must be consistent to audiomixerboard implementation) #define MAX_NUM_FADER_GROUPS 4 diff --git a/src/main.cpp b/src/main.cpp index b4326ff990..c9613e0035 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -69,6 +69,7 @@ int main ( int argc, char** argv ) bool bMuteStream = false; bool bMuteMeInPersonalMix = false; bool bDisableRecording = false; + bool bDelayPan = false; bool bNoAutoJackConnect = false; bool bUseTranslation = true; bool bCustomPortNumberGiven = false; @@ -417,6 +418,17 @@ int main ( int argc, char** argv ) continue; } + // Enable delay panning on startup ---------------------------------------- + if ( GetFlagArgument ( argv, + i, + "-P", + "--delaypan" ) ) + { + bDelayPan = true; + qInfo() << "- starting with delay panning"; + CommandLineOptions << "--delaypan"; + continue; + } // Central server ------------------------------------------------------ if ( GetStringArgument ( argc, @@ -601,6 +613,14 @@ int main ( int argc, char** argv ) Q_UNUSED ( bMuteStream ) // avoid compiler warnings #endif +#ifdef SERVER_ONLY + if ( bIsClient ) + { + qCritical() << "Only --server mode is supported in this build with nosound."; + exit ( 1 ); + } +#endif + // the inifile is not supported for the headless server mode if ( !bIsClient && !bUseGUI && !strIniFileName.isEmpty() ) { @@ -771,6 +791,7 @@ int main ( int argc, char** argv ) bUseDoubleSystemFrameSize, bUseMultithreading, bDisableRecording, + bDelayPan, eLicenceType ); #ifndef HEADLESS @@ -876,6 +897,7 @@ QString UsageArguments ( char **argv ) " -m, --htmlstatus enable HTML status file, set file name\n" " -o, --serverinfo infos of this server in the format:\n" " [name];[city];[country as QLocale ID]\n" + " -P, --delaypan start with delay panning enabled\n" " -R, --recording sets directory to contain recorded jams\n" " --norecord disables recording (when enabled by default by -R)\n" " -s, --server start server\n" diff --git a/src/util.cpp b/src/util.cpp index 5519680005..9c831383ad 100755 --- a/src/util.cpp +++ b/src/util.cpp @@ -485,6 +485,8 @@ CAboutDlg::CAboutDlg ( QWidget* parent ) : CBaseDlg ( parent ) "

David Kastrup (dakhubgit)

" "

Jordan Lum (mulyaj)

" "

Noam Postavsky (npostavs)

" + "

Johannes Brauers (JohannesBrx)

" + "

Henk De Groot (henkdegroot)

" "
" + tr ( "For details on the contributions check out the " ) + "" + tr ( "Github Contributors list" ) + "." ); @@ -500,6 +502,7 @@ CAboutDlg::CAboutDlg ( QWidget* parent ) : CBaseDlg ( parent ) "

Melcon Moraes (melcon)

" "

" + tr ( "Dutch" ) + "

" "

Jeroen Geertzen (jerogee)

" + "

Henk De Groot (henkdegroot)

" "

" + tr ( "Italian" ) + "

" "

Giuseppe Sapienza (dzpex)

" "

" + tr ( "German" ) + "

" diff --git a/src/util.h b/src/util.h index bcd75bd5f0..cba6f4c5a9 100755 --- a/src/util.h +++ b/src/util.h @@ -1288,7 +1288,8 @@ class MathUtils } else { - return powf ( 10.0f, ( fInValueRange0_1 * 35.0f - 35.0f ) / 20.0f ); + return powf ( 10.0f, ( fInValueRange0_1 - 1.0f ) * + AUD_MIX_FADER_RANGE_DB / 20.0f ); } } }; From 490d36fb7446ee6b8742f594c8c79e375b81e7a2 Mon Sep 17 00:00:00 2001 From: mstolle629 <81071220+mstolle629@users.noreply.github.com> Date: Mon, 5 Apr 2021 20:12:22 +0200 Subject: [PATCH 3/7] socket.cpp ipv6 support, socket.h windows extension socket.cpp ipv6 support for Linux/Windows/Mac socket.h extension for Windows Pointers to WSASendMsg and WSARecvMsg --- src/socket.cpp | 1060 ++++++++++++++++++++++++++++-------------------- src/socket.h | 462 ++++++++++----------- 2 files changed, 857 insertions(+), 665 deletions(-) diff --git a/src/socket.cpp b/src/socket.cpp index eefaee02ee..eb71234706 100755 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -1,437 +1,623 @@ -/******************************************************************************\ - * Copyright (c) 2004-2020 - * - * Author(s): - * Volker Fischer - * - ****************************************************************************** - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - * -\******************************************************************************/ - -#include "socket.h" -#include "server.h" -#include -/* Implementation *************************************************************/ -void CSocket::Init ( const quint16 iPortNumber ) -{ -#ifdef _WIN32 - // for the Windows socket usage we have to start it up first - -// TODO check for error and exit application on error - - WSADATA wsa; - WSAStartup ( MAKEWORD(1, 0), &wsa ); -#endif - - // create the UDP socket - UdpSocket = socket ( AF_INET6, SOCK_DGRAM, 0); - int on = 1; - int off = 0; - setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on)); - // allow to receive ipv4 packet info (i.e. destination ip address through recvmsg - setsockopt(UdpSocket, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); - // allow to receive ipv6 packetinfo - setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); - // allow to receive v6 and v4 requests - setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)); - // allocate memory for network receive and send buffer in samples - vecbyRecBuf.Init ( MAX_SIZE_BYTES_NETW_BUF ); - - // preinitialize socket in address (only the port number is missing) - sockaddr_in6 UdpSocketInAddr; - UdpSocketInAddr.sin6_family = AF_INET6; - UdpSocketInAddr.sin6_addr = in6addr_any; - // initialize the listening socket - bool bSuccess; - - if ( bIsClient ) - { - if ( iPortNumber == 0 ) - { - // if port number is 0, bind the client to a random available port - UdpSocketInAddr.sin6_port = htons ( 0 ); - bSuccess = ( ::bind ( UdpSocket , - (sockaddr*) &UdpSocketInAddr, - sizeof ( sockaddr_in6 ) ) == 0); - } - else - { - // If the port is not available, try "NUM_SOCKET_PORTS_TO_TRY" times - // with incremented port numbers. Randomize the start port, in case a - // faulty router gets stuck and confused by a particular port (like - // the starting port). Might work around frustrating "cannot connect" - // problems (#568) - const quint16 startingPortNumber = iPortNumber + rand() % NUM_SOCKET_PORTS_TO_TRY; - - quint16 iClientPortIncrement = 0; - bSuccess = false; // initialization for while loop - - while ( !bSuccess && ( iClientPortIncrement <= NUM_SOCKET_PORTS_TO_TRY ) ) - { - UdpSocketInAddr.sin6_port = htons ( startingPortNumber + iClientPortIncrement ); - bSuccess = ( ::bind ( UdpSocket , - (sockaddr*) &UdpSocketInAddr, - sizeof ( sockaddr_in6 ) ) == 0 ); - - iClientPortIncrement++; - } - } - } - else - { - // for the server, only try the given port number and do not try out - // other port numbers to bind since it is important that the server - // gets the desired port number - UdpSocketInAddr.sin6_port = htons ( iPortNumber ); - bSuccess = ( ::bind ( UdpSocket , - (sockaddr*) &UdpSocketInAddr, - sizeof ( sockaddr_in6 ) ) == 0 ); - } - - if ( !bSuccess ) - { - // we cannot bind socket, throw error - throw CGenErr ( "Cannot bind the socket (maybe " - "the software is already running).", "Network Error" ); - } - - - // Connections ------------------------------------------------------------- - // it is important to do the following connections in this class since we - // have a thread transition - - // we have different connections for client and server - if ( bIsClient ) - { - // client connections: - - QObject::connect ( this, &CSocket::ProtcolMessageReceived, - pChannel, &CChannel::OnProtcolMessageReceived ); - - QObject::connect ( this, &CSocket::ProtcolCLMessageReceived, - pChannel, &CChannel::OnProtcolCLMessageReceived ); - - QObject::connect ( this, static_cast ( &CSocket::NewConnection ), - pChannel, &CChannel::OnNewConnection ); - } - else - { - // server connections: - - QObject::connect ( this, &CSocket::ProtcolMessageReceived, - pServer, &CServer::OnProtcolMessageReceived ); - - QObject::connect ( this, &CSocket::ProtcolCLMessageReceived, - pServer, &CServer::OnProtcolCLMessageReceived ); - - QObject::connect ( this, static_cast ( &CSocket::NewConnection ), - pServer, &CServer::OnNewConnection ); - - QObject::connect ( this, &CSocket::ServerFull, - pServer, &CServer::OnServerFull ); - } -} - -void CSocket::Close() -{ -#ifdef _WIN32 - // closesocket will cause recvfrom to return with an error because the - // socket is closed -> then the thread can safely be shut down - closesocket ( UdpSocket ); -#elif defined ( __APPLE__ ) || defined ( __MACOSX ) - // on Mac the general close has the same effect as closesocket on Windows - close ( UdpSocket ); -#else - // on Linux the shutdown call cancels the recvfrom - shutdown ( UdpSocket, SHUT_RDWR ); -#endif -} - -CSocket::~CSocket() -{ - // cleanup the socket (on Windows the WSA cleanup must also be called) -#ifdef _WIN32 - closesocket ( UdpSocket ); - WSACleanup(); -#else - close ( UdpSocket ); -#endif -} - -void CSocket::SendPacket ( const CVector& vecbySendBuf, - const CHostAddress& HostAddr ) -{ - QMutexLocker locker ( &Mutex ); - - const int iVecSizeOut = vecbySendBuf.Size(); - - if ( iVecSizeOut > 0 ) - { - // send packet through network (we have to convert the constant unsigned - // char vector in "const char*", for this we first convert the const - // uint8_t vector in a read/write uint8_t vector and then do the cast to - // const char*) - sockaddr_in6 UdpSocketOutAddr; - UdpSocketOutAddr.sin6_family = AF_INET6; - UdpSocketOutAddr.sin6_port = htons ( HostAddr.iPort ); - -// memcpy(&UdpSocketOutAddr.sin6_addr.s6_addr,(void *)&HostAddr.InetAddr.toIPv6Address(),16); - Q_IPV6ADDR ipv6addr_h = HostAddr.InetAddr.toIPv6Address(); - memcpy (&UdpSocketOutAddr.sin6_addr.s6_addr,&ipv6addr_h,16); - msghdr m; - struct iovec iov[1]; - int rv; - char pktinfo[100]; - m.msg_name = (sockaddr*) &UdpSocketOutAddr; - m.msg_namelen = sizeof ( sockaddr_in6 ); - - iov[0].iov_base = (void *) &( vecbySendBuf )[0]; - iov[0].iov_len = iVecSizeOut; - m.msg_iov = iov; - m.msg_iovlen = 1; - //m.msg_control = pktinfo; - m.msg_control = NULL; - m.msg_controllen = 0; - bool isv4; - quint32 ipaddr4_l = HostAddr.LocalAddr.toIPv4Address(&isv4); - if (isv4) - { - if (ipaddr4_l != 0) - { - struct in_addr ia; - struct in_addr da; - memset(pktinfo,0,sizeof(pktinfo)); - struct in_pktinfo *pktinfo2; - m.msg_control = pktinfo; - struct cmsghdr *cmsg = (struct cmsghdr *) pktinfo; - cmsg->cmsg_level = IPPROTO_IP; - cmsg->cmsg_type = IP_PKTINFO; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); - - pktinfo2 = (struct in_pktinfo*) CMSG_DATA(cmsg); - pktinfo2->ipi_ifindex = 0; - ia.s_addr = ipaddr4_l; - da.s_addr = 0; - pktinfo2->ipi_spec_dst = ia; - pktinfo2->ipi_addr = da; - m.msg_controllen += CMSG_SPACE(sizeof(struct in_pktinfo)); - - } else - { - ; - } - } - else - { - memset(pktinfo,0,sizeof(pktinfo)); - m.msg_control = pktinfo; - struct cmsghdr *cmsg = (struct cmsghdr *)pktinfo; - cmsg->cmsg_level = IPPROTO_IPV6; - cmsg->cmsg_type = IPV6_PKTINFO; - cmsg->cmsg_len = CMSG_LEN(16); - memcpy(CMSG_DATA(cmsg),&HostAddr.LocalAddr,16); - m.msg_controllen += CMSG_SPACE(16); - } - rv=sendmsg(UdpSocket,&m,0); - if (rv<0) fprintf(stderr, "%s\n", strerror(errno)); - } -} - -bool CSocket::GetAndResetbJitterBufferOKFlag() -{ - // check jitter buffer status - if ( !bJitterBufferOK ) - { - // reset flag and return "not OK" status - bJitterBufferOK = true; - return false; - } - - // the buffer was OK, we do not have to reset anything and just return the - // OK status - return true; -} - -void CSocket::OnDataReceived() -{ -/* - The strategy of this function is that only the "put audio" function is - called directly (i.e. the high thread priority is used) and all other less - important things like protocol parsing and acting on protocol messages is - done in the low priority thread. To get a thread transition, we have to - use the signal/slot mechanism (i.e. we use messages for that). -*/ - - // read block from network interface and query address of sender - sockaddr_in6 SenderAddr; - in6_addr LocalInterfaceAddr; -#ifdef _WIN32 - int SenderAddrSize = sizeof ( sockaddr_in ); -#else - socklen_t SenderAddrSize = sizeof ( sockaddr_in6 ); -#endif - //const long iNumBytesRead = recvfrom ( UdpSocket, - // (char*) &vecbyRecBuf[0], - // MAX_SIZE_BYTES_NETW_BUF, - // f 0, - // (sockaddr*) &SenderAddr, - // &SenderAddrSize ); - struct msghdr msgh; - struct iovec iov[1]; - - struct cmsghdr *cmsg; - // Prepare message header structure - memset( &msgh, 0, sizeof(msgh)); - msgh.msg_name = &SenderAddr; - msgh.msg_namelen = SenderAddrSize; - msgh.msg_iovlen = 1; - msgh.msg_iov = iov; - iov[0].iov_base = (char *) &vecbyRecBuf[0]; - iov[0].iov_len = MAX_SIZE_BYTES_NETW_BUF; - char controlbuffer[4096]; - memset( controlbuffer, 0, sizeof(controlbuffer)); - msgh.msg_control = controlbuffer; - msgh.msg_controllen = sizeof(controlbuffer); - const long iNumBytesRead = ::recvmsg(UdpSocket, &msgh, 0); - - /* Receive auxiliary data in msgh */ - bool la_found = false; - for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; - cmsg = CMSG_NXTHDR(&msgh, cmsg)) { - //printf("d=%d ",cmsg->cmsg_level); - if (cmsg->cmsg_level == IPPROTO_IPV6 - && cmsg->cmsg_type == IPV6_PKTINFO) { - memcpy(&LocalInterfaceAddr, CMSG_DATA(cmsg), sizeof(LocalInterfaceAddr)); - char lesbareadresse[40]; - inet_ntop(AF_INET6,&LocalInterfaceAddr,lesbareadresse,sizeof(lesbareadresse)); - la_found = true; - //fprintf(stderr,"inet6addresse = %s",lesbareadresse); - break; - } - } - - - // check if an error occurred or no data could be read - if ( iNumBytesRead <= 0 ) - { - return; - } - - // convert address of client - Q_IPV6ADDR saddr6_tmp; - Q_IPV6ADDR iaddr6_tmp; - bool isv4 = false; - quint32 i4a; - memcpy(&saddr6_tmp,&SenderAddr.sin6_addr,16); - memcpy(&iaddr6_tmp,&LocalInterfaceAddr,16); - // Behandle ipv6-mapped ipv4-Adressen - RecHostAddr.InetAddr.setAddress(saddr6_tmp); - i4a = RecHostAddr.InetAddr.toIPv4Address(&isv4); - if (isv4) - { - RecHostAddr.InetAddr.setAddress(i4a); - } - isv4 = false; - if (la_found) RecHostAddr.LocalAddr.setAddress(iaddr6_tmp); - RecHostAddr.iPort = ntohs ( SenderAddr.sin6_port ); - - - // check if this is a protocol message - int iRecCounter; - int iRecID; - CVector vecbyMesBodyData; - - if ( !CProtocol::ParseMessageFrame ( vecbyRecBuf, - iNumBytesRead, - vecbyMesBodyData, - iRecCounter, - iRecID ) ) - { - // this is a protocol message, check the type of the message - if ( CProtocol::IsConnectionLessMessageID ( iRecID ) ) - { - -// TODO a copy of the vector is used -> avoid malloc in real-time routine - - emit ProtcolCLMessageReceived ( iRecID, vecbyMesBodyData, RecHostAddr ); - } - else - { - -// TODO a copy of the vector is used -> avoid malloc in real-time routine - - emit ProtcolMessageReceived ( iRecCounter, iRecID, vecbyMesBodyData, RecHostAddr ); - } - } - else - { - // this is most probably a regular audio packet - if ( bIsClient ) - { - // client: - - switch ( pChannel->PutAudioData ( vecbyRecBuf, iNumBytesRead, RecHostAddr ) ) - { - case PS_AUDIO_ERR: - case PS_GEN_ERROR: - bJitterBufferOK = false; - break; - - case PS_NEW_CONNECTION: - // inform other objects that new connection was established - emit NewConnection(); - break; - - case PS_AUDIO_INVALID: - // inform about received invalid packet by fireing an event - emit InvalidPacketReceived ( RecHostAddr ); - break; - - default: - // do nothing - break; - } - } - else - { - // server: - - int iCurChanID; - - if ( pServer->PutAudioData ( vecbyRecBuf, iNumBytesRead, RecHostAddr, iCurChanID ) ) - { - // we have a new connection, emit a signal - emit NewConnection ( iCurChanID, RecHostAddr ); - - // this was an audio packet, start server if it is in sleep mode - if ( !pServer->IsRunning() ) - { - // (note that Qt will delete the event object when done) - QCoreApplication::postEvent ( pServer, - new CCustomEvent ( MS_PACKET_RECEIVED, 0, 0 ) ); - } - } - - // check if no channel is available - if ( iCurChanID == INVALID_CHANNEL_ID ) - { - // fire message for the state that no free channel is available - emit ServerFull ( RecHostAddr ); - } - } - } -} +/******************************************************************************\ + * Copyright (c) 2004-2020 + * + * Author(s): + * Volker Fischer + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * +\******************************************************************************/ + +#include "socket.h" +#include "server.h" +#ifdef _WIN32 +#include +#include +#include +#include +#else +#include +#endif +/* Implementation *************************************************************/ +void CSocket::Init ( const quint16 iPortNumber ) +{ +#ifdef _WIN32 + // for the Windows socket usage we have to start it up first + +// TODO check for error and exit application on error + WSADATA wsa; + WSAStartup ( MAKEWORD(1, 0), &wsa ); +#else +#include +#endif + + // create the UDP socket + UdpSocket = socket ( AF_INET6, SOCK_DGRAM, 0); +#ifndef WIN32 + int on = 1; + int off = 0; + setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on)); + // allow to receive ipv4 packet info (i.e. destination ip address through recvmsg + setsockopt(UdpSocket, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); + // allow to receive ipv6 packetinfo + setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); + // allow to receive v6 and v4 requests + setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)); + // allocate memory for network receive and send buffer in samples +#else + char on = 1; + char off = 0; + setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)); + setsockopt(UdpSocket, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); + setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on)); + GUID g = WSAID_WSARECVMSG; + DWORD dwBytesReturned = 0; + if (WSAIoctl(UdpSocket, SIO_GET_EXTENSION_FUNCTION_POINTER, &g, sizeof(g), &lpWSARecvMsg, sizeof(lpWSARecvMsg), &dwBytesReturned, NULL, NULL) != 0) + { + // WSARecvMsg is not available... + } + g = WSAID_WSASENDMSG; + dwBytesReturned = 0; + WSAIoctl(UdpSocket, SIO_GET_EXTENSION_FUNCTION_POINTER, &g, sizeof(g), &lpWSASendMsg, sizeof(lpWSASendMsg), &dwBytesReturned, NULL, NULL ); + +#endif + vecbyRecBuf.Init ( MAX_SIZE_BYTES_NETW_BUF ); + // preinitialize socket in address (only the port number is missing) + sockaddr_in6 UdpSocketInAddr; + { + int i=0; + for (char *a=(char *)&UdpSocketInAddr; i<(int)sizeof(sockaddr_in6); ++i, ++a) *a=0; + } + //memset(&UdpSocketInAddr,0,sizeof(UdpSocketInAddr)); + UdpSocketInAddr.sin6_family = AF_INET6; + UdpSocketInAddr.sin6_addr = in6addr_any; + // initialize the listening socket + bool bSuccess; + + if ( bIsClient ) + { + if ( iPortNumber == 0 ) + { + // if port number is 0, bind the client to a random available port + UdpSocketInAddr.sin6_port = htons ( 0 ); + bSuccess = ( ::bind ( UdpSocket , + (sockaddr*) &UdpSocketInAddr, + sizeof ( sockaddr_in6 ) ) == 0); + } + else + { + // If the port is not available, try "NUM_SOCKET_PORTS_TO_TRY" times + // with incremented port numbers. Randomize the start port, in case a + // faulty router gets stuck and confused by a particular port (like + // the starting port). Might work around frustrating "cannot connect" + // problems (#568) + const quint16 startingPortNumber = iPortNumber + rand() % NUM_SOCKET_PORTS_TO_TRY; + + quint16 iClientPortIncrement = 0; + bSuccess = false; // initialization for while loop + + while ( !bSuccess && ( iClientPortIncrement <= NUM_SOCKET_PORTS_TO_TRY ) ) + { + UdpSocketInAddr.sin6_port = htons ( startingPortNumber + iClientPortIncrement ); + bSuccess = ( ::bind ( UdpSocket , + (sockaddr*) &UdpSocketInAddr, + sizeof ( sockaddr_in6 ) ) == 0 ); + + iClientPortIncrement++; + } + } + } + else + { + // for the server, only try the given port number and do not try out + // other port numbers to bind since it is important that the server + // gets the desired port number + UdpSocketInAddr.sin6_port = htons ( iPortNumber ); + bSuccess = ( ::bind ( UdpSocket , + (sockaddr*) &UdpSocketInAddr, + sizeof ( sockaddr_in6 ) ) == 0 ); + } + + if ( !bSuccess ) + { + // we cannot bind socket, throw error + throw CGenErr ( "Cannot bind the socket (maybe " + "the software is already running).", "Network Error" ); + } + + + // Connections ------------------------------------------------------------- + // it is important to do the following connections in this class since we + // have a thread transition + + // we have different connections for client and server + if ( bIsClient ) + { + // client connections: + + QObject::connect ( this, &CSocket::ProtcolMessageReceived, + pChannel, &CChannel::OnProtcolMessageReceived ); + + QObject::connect ( this, &CSocket::ProtcolCLMessageReceived, + pChannel, &CChannel::OnProtcolCLMessageReceived ); + + QObject::connect ( this, static_cast ( &CSocket::NewConnection ), + pChannel, &CChannel::OnNewConnection ); + } + else + { + // server connections: + + QObject::connect ( this, &CSocket::ProtcolMessageReceived, + pServer, &CServer::OnProtcolMessageReceived ); + + QObject::connect ( this, &CSocket::ProtcolCLMessageReceived, + pServer, &CServer::OnProtcolCLMessageReceived ); + + QObject::connect ( this, static_cast ( &CSocket::NewConnection ), + pServer, &CServer::OnNewConnection ); + + QObject::connect ( this, &CSocket::ServerFull, + pServer, &CServer::OnServerFull ); + } +} + +void CSocket::Close() +{ +#ifdef _WIN32 + // closesocket will cause recvfrom to return with an error because the + // socket is closed -> then the thread can safely be shut down + closesocket ( UdpSocket ); +#elif defined ( __APPLE__ ) || defined ( __MACOSX ) + // on Mac the general close has the same effect as closesocket on Windows + close ( UdpSocket ); +#else + // on Linux the shutdown call cancels the recvfrom + shutdown ( UdpSocket, SHUT_RDWR ); +#endif +} + +CSocket::~CSocket() +{ + // cleanup the socket (on Windows the WSA cleanup must also be called) +#ifdef _WIN32 + closesocket ( UdpSocket ); + WSACleanup(); +#else + close ( UdpSocket ); +#endif +} + +void CSocket::SendPacket ( const CVector& vecbySendBuf, + const CHostAddress& HostAddr ) +{ + QMutexLocker locker ( &Mutex ); + + const int iVecSizeOut = vecbySendBuf.Size(); + + if ( iVecSizeOut > 0 ) + { + // send packet through network (we have to convert the constant unsigned + // char vector in "const char*", for this we first convert the const + // uint8_t vector in a read/write uint8_t vector and then do the cast to + // const char*) + sockaddr_in6 UdpSocketOutAddr = {}; + { + int i=0; + for (char *a=(char *)&UdpSocketOutAddr; i<(int)sizeof(sockaddr_in6); ++i, ++a) *a=0; + } + UdpSocketOutAddr.sin6_family = AF_INET6; + UdpSocketOutAddr.sin6_port = htons ( HostAddr.iPort ); + +// memcpy(&UdpSocketOutAddr.sin6_addr.s6_addr,(void *)&HostAddr.InetAddr.toIPv6Address(),16); + Q_IPV6ADDR ipv6addr_h = HostAddr.InetAddr.toIPv6Address(); + memcpy (&UdpSocketOutAddr.sin6_addr.s6_addr,&ipv6addr_h,16); +#ifndef _WIN32 + msghdr m; + struct iovec iov[1]; + int rv; + char pktinfo[100]; + m.msg_name = (sockaddr*) &UdpSocketOutAddr; + m.msg_namelen = sizeof ( sockaddr_in6 ); + + iov[0].iov_base = (void *) &( vecbySendBuf )[0]; + iov[0].iov_len = iVecSizeOut; + m.msg_iov = iov; + m.msg_iovlen = 1; + //m.msg_control = pktinfo; + m.msg_control = NULL; + m.msg_controllen = 0; + bool isv4; + quint32 ipaddr4_l = HostAddr.LocalAddr.toIPv4Address(&isv4); + if (isv4) + { + if (ipaddr4_l != 0) + { + struct in_addr ia; + struct in_addr da; + memset(pktinfo,0,sizeof(pktinfo)); + struct in_pktinfo *pktinfo2; + m.msg_control = pktinfo; + struct cmsghdr *cmsg = (struct cmsghdr *) pktinfo; + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + + pktinfo2 = (struct in_pktinfo*) CMSG_DATA(cmsg); + pktinfo2->ipi_ifindex = 0; + ia.s_addr = ipaddr4_l; + da.s_addr = 0; + pktinfo2->ipi_spec_dst = ia; + pktinfo2->ipi_addr = da; + m.msg_controllen += CMSG_SPACE(sizeof(struct in_pktinfo)); + + } else + { + ; + } + } + else + { + memset(pktinfo,0,sizeof(pktinfo)); + m.msg_control = pktinfo; + struct cmsghdr *cmsg = (struct cmsghdr *)pktinfo; + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(16); + memcpy(CMSG_DATA(cmsg),&HostAddr.LocalAddr,16); + m.msg_controllen += CMSG_SPACE(16); + } + rv=sendmsg(UdpSocket,&m,0); + if (rv<0) fprintf(stderr, "%s\n", strerror(errno)); +#else + WSAMSG m; + WSABUF iov[1]; + char pktinfo[100]; + WSABUF controlbuf; + m.name = (sockaddr*) &UdpSocketOutAddr; + m.namelen = sizeof ( sockaddr_in6 ); + iov[0].buf = (char *) &( vecbySendBuf )[0]; + iov[0].len = iVecSizeOut; + m.lpBuffers = iov; + m.dwBufferCount = 1; + controlbuf.len = 0; + controlbuf.buf = NULL; + m.Control = controlbuf; + m.dwFlags = 0; + bool isv4; + quint32 ipaddr4_l = HostAddr.LocalAddr.toIPv4Address(&isv4); + if (isv4) + { + if (ipaddr4_l != 0) + { + //struct in_addr ia; + //struct in_addr da; + memset(pktinfo,0,sizeof(pktinfo)); + m.Control.buf = pktinfo; + m.Control.len = sizeof(pktinfo); + WSACMSGHDR *cmsg = WSA_CMSG_FIRSTHDR(&m); + if (cmsg != NULL) + { + struct in_pktinfo *pktinfo2; + //m.msg_control = pktinfo; + memset(cmsg, 0, WSA_CMSG_SPACE(sizeof(struct in_pktinfo))); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = WSA_CMSG_LEN(sizeof(struct in_pktinfo)); + + pktinfo2 = (struct in_pktinfo*) WSA_CMSG_DATA(cmsg); + pktinfo2->ipi_ifindex = 0; + pktinfo2->ipi_addr.s_addr = ipaddr4_l; + //ia.s_addr = ipaddr4_l; + //da.s_addr = 0; + + // pktinfo2->ipi_spec_dst = ia; + // pktinfo2->ipi_addr = da; + // m.msg_controllen += CMSG_SPACE(sizeof(struct in_pktinfo)); + //m.Control.len += CMSG_SPACE(sizeof(struct in_pktinfo)); + } + + } else + { + m.Control.buf = NULL; + m.Control.len = 0; + } + } + else + { + memset(pktinfo,0,sizeof(pktinfo)); + m.Control.buf = pktinfo; + m.Control.len = sizeof(pktinfo); + WSACMSGHDR *cmsg = WSA_CMSG_FIRSTHDR(&m); + //struct cmsghdr *cmsg = (struct cmsghdr *)pktinfo; + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = WSA_CMSG_LEN(16); + memcpy(WSA_CMSG_DATA(cmsg),&HostAddr.LocalAddr,16); + //m.Control.len += CMSG_SPACE(16); + } + { + int rv; + //WSASendMsg(UdpSocket,&m,0,) + DWORD bytes_sent; + rv = lpWSASendMsg(UdpSocket,&m,0,&bytes_sent,NULL,NULL); + if (rv != 0) + { + int err; + char msgbuf [256]; // for a message up to 255 bytes. + + msgbuf [0] = '\0'; // Microsoft doesn't guarantee this on man page. + err = WSAGetLastError (); + + FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // flags + NULL, // lpsource + err, // message id + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), // languageid + msgbuf, // output buffer + sizeof (msgbuf), // size of msgbuf, bytes + NULL); // va_list of arguments + + CGenErr ( "WSASendMsg failed" ); + } + } +#endif + } +} + +bool CSocket::GetAndResetbJitterBufferOKFlag() +{ + // check jitter buffer status + if ( !bJitterBufferOK ) + { + // reset flag and return "not OK" status + bJitterBufferOK = true; + return false; + } + + // the buffer was OK, we do not have to reset anything and just return the + // OK status + return true; +} + +void CSocket::OnDataReceived() +{ +/* + The strategy of this function is that only the "put audio" function is + called directly (i.e. the high thread priority is used) and all other less + important things like protocol parsing and acting on protocol messages is + done in the low priority thread. To get a thread transition, we have to + use the signal/slot mechanism (i.e. we use messages for that). +*/ + + // read block from network interface and query address of sender + sockaddr_in6 SenderAddr; + in6_addr LocalInterfaceAddr; +#ifdef _WIN32 + int SenderAddrSize = sizeof ( sockaddr_in6 ); +#else + socklen_t SenderAddrSize = sizeof ( sockaddr_in6 ); +#endif + //const long iNumBytesRead = recvfrom ( UdpSocket, + // (char*) &vecbyRecBuf[0], + // MAX_SIZE_BYTES_NETW_BUF, + // f 0, + // (sockaddr*) &SenderAddr, + // &SenderAddrSize ); +#ifdef _WIN32 + WSAMSG msgh; + WSABUF iov[1]; +#else + struct msghdr msgh; + struct iovec iov[1]; +#endif + bool la_found = false; +#ifdef _WIN32 + WSACMSGHDR *cmsg; + // Prepare message header structure, fill it with zero (memset) + { + int i=0; + for (char *a=(char *)&msgh; i<(int)sizeof(WSAMSG); ++i, ++a) *a=0; + } + + msgh.name = (sockaddr *)&SenderAddr; + msgh.namelen = SenderAddrSize; + msgh.dwBufferCount = 1; + msgh.lpBuffers = iov; + iov[0].buf = (char *) &vecbyRecBuf[0]; + iov[0].len = MAX_SIZE_BYTES_NETW_BUF; + WSABUF controlbuf; + char controlbuffer[512]; + controlbuf.buf = controlbuffer; + controlbuf.len = sizeof(controlbuffer); + { + int i=0; + for (char *a=(char *)controlbuffer; i<(int)sizeof(controlbuffer); ++i, ++a) *a=0; + } + + msgh.Control = controlbuf; + DWORD iNumBytesRead; + { + int rv = lpWSARecvMsg(UdpSocket, &msgh, &iNumBytesRead, NULL, NULL); + if (rv != 0) + { + int err; + char msgbuf [256]; // for a message up to 255 bytes. + msgbuf [0] = '\0'; // Microsoft doesn't guarantee this on man page. + err = WSAGetLastError (); + FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // flags + NULL, // lpsource + err, // message id + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), // languageid + msgbuf, // output buffer + sizeof (msgbuf), // size of msgbuf, bytes + NULL); // va_list of arguments + return; + } + else + { + /* Receive auxiliary data in msgh */ + //fprintf(stderr,"hallo"); + for (cmsg = WSA_CMSG_FIRSTHDR(&msgh); cmsg != NULL; + cmsg = WSA_CMSG_NXTHDR(&msgh, cmsg)) { + //fprintf(stderr,"d=%d ",cmsg->cmsg_level); + if (cmsg->cmsg_level == IPPROTO_IPV6 + && cmsg->cmsg_type == IPV6_PKTINFO) { + memcpy(&LocalInterfaceAddr, WSA_CMSG_DATA(cmsg), sizeof(LocalInterfaceAddr)); + la_found = true; + break; + } + } + + } + } + + // check if an error occurred or no data could be read +#else + struct cmsghdr *cmsg; + // Prepare message header structure + memset( &msgh, 0, sizeof(msgh)); + msgh.msg_name = &SenderAddr; + msgh.msg_namelen = SenderAddrSize; + msgh.msg_iovlen = 1; + msgh.msg_iov = iov; + iov[0].iov_base = (char *) &vecbyRecBuf[0]; + iov[0].iov_len = MAX_SIZE_BYTES_NETW_BUF; + char controlbuffer[4096]; + memset( controlbuffer, 0, sizeof(controlbuffer)); + msgh.msg_control = controlbuffer; + msgh.msg_controllen = sizeof(controlbuffer); + const long iNumBytesRead = ::recvmsg(UdpSocket, &msgh, 0); + + /* Receive auxiliary data in msgh */ + for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msgh, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_IPV6 + && cmsg->cmsg_type == IPV6_PKTINFO) { + memcpy(&LocalInterfaceAddr, CMSG_DATA(cmsg), sizeof(LocalInterfaceAddr)); + la_found = true; + break; + } + } +#endif + if ( iNumBytesRead <= 0 ) + { + return; + } + // convert address of client + Q_IPV6ADDR saddr6_tmp; + Q_IPV6ADDR iaddr6_tmp; + bool isv4 = false; + quint32 i4a; + memcpy(&saddr6_tmp,&SenderAddr.sin6_addr,16); + if (la_found) memcpy(&iaddr6_tmp,&LocalInterfaceAddr,16); + // Behandle ipv6-mapped ipv4-Adressen + RecHostAddr.InetAddr.setAddress(saddr6_tmp); + i4a = RecHostAddr.InetAddr.toIPv4Address(&isv4); + if (isv4) + { + RecHostAddr.InetAddr.setAddress(i4a); + } + isv4 = false; + if (la_found) RecHostAddr.LocalAddr.setAddress(iaddr6_tmp); + RecHostAddr.iPort = ntohs ( SenderAddr.sin6_port ); + + + // check if this is a protocol message + int iRecCounter; + int iRecID; + CVector vecbyMesBodyData; + + if ( !CProtocol::ParseMessageFrame ( vecbyRecBuf, + iNumBytesRead, + vecbyMesBodyData, + iRecCounter, + iRecID ) ) + { + // this is a protocol message, check the type of the message + if ( CProtocol::IsConnectionLessMessageID ( iRecID ) ) + { + +// TODO a copy of the vector is used -> avoid malloc in real-time routine + + emit ProtcolCLMessageReceived ( iRecID, vecbyMesBodyData, RecHostAddr ); + } + else + { + +// TODO a copy of the vector is used -> avoid malloc in real-time routine + + emit ProtcolMessageReceived ( iRecCounter, iRecID, vecbyMesBodyData, RecHostAddr ); + } + } + else + { + // this is most probably a regular audio packet + if ( bIsClient ) + { + // client: + + switch ( pChannel->PutAudioData ( vecbyRecBuf, iNumBytesRead, RecHostAddr ) ) + { + case PS_AUDIO_ERR: + case PS_GEN_ERROR: + bJitterBufferOK = false; + break; + + case PS_NEW_CONNECTION: + // inform other objects that new connection was established + emit NewConnection(); + break; + + case PS_AUDIO_INVALID: + // inform about received invalid packet by fireing an event + emit InvalidPacketReceived ( RecHostAddr ); + break; + + default: + // do nothing + break; + } + } + else + { + // server: + + int iCurChanID; + + if ( pServer->PutAudioData ( vecbyRecBuf, iNumBytesRead, RecHostAddr, iCurChanID ) ) + { + // we have a new connection, emit a signal + emit NewConnection ( iCurChanID, RecHostAddr ); + + // this was an audio packet, start server if it is in sleep mode + if ( !pServer->IsRunning() ) + { + // (note that Qt will delete the event object when done) + QCoreApplication::postEvent ( pServer, + new CCustomEvent ( MS_PACKET_RECEIVED, 0, 0 ) ); + } + } + + // check if no channel is available + if ( iCurChanID == INVALID_CHANNEL_ID ) + { + // fire message for the state that no free channel is available + emit ServerFull ( RecHostAddr ); + } + } + } +} diff --git a/src/socket.h b/src/socket.h index ba07613c33..56b930da54 100755 --- a/src/socket.h +++ b/src/socket.h @@ -1,228 +1,234 @@ -/******************************************************************************\ - * Copyright (c) 2004-2020 - * - * Author(s): - * Volker Fischer - * - ****************************************************************************** - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - * -\******************************************************************************/ - -#pragma once - -#include -#include -#include -#include -#include "global.h" -#include "protocol.h" -#include "util.h" -#ifndef _WIN32 -# include -# include -#endif - - -// The header files channel.h and server.h require to include this header file -// so we get a cyclic dependency. To solve this issue, a prototype of the -// channel class and server class is defined here. -class CServer; // forward declaration of CServer -class CChannel; // forward declaration of CChannel - - -/* Definitions ****************************************************************/ -// number of ports we try to bind until we give up -#define NUM_SOCKET_PORTS_TO_TRY 100 - - -/* Classes ********************************************************************/ -/* Base socket class -------------------------------------------------------- */ -class CSocket : public QObject -{ - Q_OBJECT - -public: - CSocket ( CChannel* pNewChannel, - const quint16 iPortNumber ) - : pChannel ( pNewChannel ), - bIsClient ( true ), - bJitterBufferOK ( true ) { Init ( iPortNumber ); } - - CSocket ( CServer* pNServP, - const quint16 iPortNumber ) - : pServer ( pNServP ), - bIsClient ( false ), - bJitterBufferOK ( true ) { Init ( iPortNumber ); } - - virtual ~CSocket(); - - void SendPacket ( const CVector& vecbySendBuf, - const CHostAddress& HostAddr ); - - bool GetAndResetbJitterBufferOKFlag(); - void Close(); - -protected: - void Init ( const quint16 iPortNumber ); - -#ifdef _WIN32 - SOCKET UdpSocket; -#else - int UdpSocket; -#endif - - QMutex Mutex; - - CVector vecbyRecBuf; - CHostAddress RecHostAddr; - QHostAddress SenderAddress; - quint16 SenderPort; - - CChannel* pChannel; // for client - CServer* pServer; // for server - - bool bIsClient; - - bool bJitterBufferOK; - -public: - void OnDataReceived(); - -signals: - void NewConnection(); // for the client - - void NewConnection ( int iChID, - CHostAddress RecHostAddr ); // for the server - - void ServerFull ( CHostAddress RecHostAddr ); - - void InvalidPacketReceived ( CHostAddress RecHostAddr ); - - void ProtcolMessageReceived ( int iRecCounter, - int iRecID, - CVector vecbyMesBodyData, - CHostAddress HostAdr ); - - void ProtcolCLMessageReceived ( int iRecID, - CVector vecbyMesBodyData, - CHostAddress HostAdr ); -}; - - -/* Socket which runs in a separate high priority thread --------------------- */ -// The receive socket should be put in a high priority thread to ensure the GUI -// does not effect the stability of the audio stream (e.g. if the GUI is on -// high load because of a table update, the incoming network packets must still -// be put in the jitter buffer with highest priority). -class CHighPrioSocket : public QObject -{ - Q_OBJECT - -public: - CHighPrioSocket ( CChannel* pNewChannel, - const quint16 iPortNumber ) - : Socket ( pNewChannel, iPortNumber ) { Init(); } - - CHighPrioSocket ( CServer* pNewServer, - const quint16 iPortNumber ) - : Socket ( pNewServer, iPortNumber ) { Init(); } - - virtual ~CHighPrioSocket() - { - NetworkWorkerThread.Stop(); - } - - void Start() - { - // starts the high priority socket receive thread (with using blocking - // socket request call) - NetworkWorkerThread.start ( QThread::TimeCriticalPriority ); - } - - void SendPacket ( const CVector& vecbySendBuf, - const CHostAddress& HostAddr ) - { - Socket.SendPacket ( vecbySendBuf, HostAddr ); - } - - bool GetAndResetbJitterBufferOKFlag() - { - return Socket.GetAndResetbJitterBufferOKFlag(); - } - -protected: - class CSocketThread : public QThread - { - public: - CSocketThread ( CSocket* pNewSocket = nullptr, QObject* parent = nullptr ) : - QThread ( parent ), pSocket ( pNewSocket ), bRun ( true ) { setObjectName ( "CSocketThread" ); } - - void Stop() - { - // disable run flag so that the thread loop can be exit - bRun = false; - - // to leave blocking wait for receive - pSocket->Close(); - - // give thread some time to terminate - wait ( 5000 ); - } - - void SetSocket ( CSocket* pNewSocket ) { pSocket = pNewSocket; } - - protected: - void run() { - // make sure the socket pointer is initialized (should be always the - // case) - if ( pSocket != nullptr ) - { - while ( bRun ) - { - // this function is a blocking function (waiting for network - // packets to be received and processed) - pSocket->OnDataReceived(); - } - } - } - - CSocket* pSocket; - bool bRun; - }; - - void Init() - { - // Creation of the new socket thread which has to have the highest - // possible thread priority to make sure the jitter buffer is reliably - // filled with the network audio packets and does not get interrupted - // by other GUI threads. The following code is based on: - // http://qt-project.org/wiki/Threads_Events_QObjects - Socket.moveToThread ( &NetworkWorkerThread ); - - NetworkWorkerThread.SetSocket ( &Socket ); - - // connect the "InvalidPacketReceived" signal - QObject::connect ( &Socket, &CSocket::InvalidPacketReceived, - this, &CHighPrioSocket::InvalidPacketReceived ); - } - - CSocketThread NetworkWorkerThread; - CSocket Socket; - -signals: - void InvalidPacketReceived ( CHostAddress RecHostAddr ); -}; +/******************************************************************************\ + * Copyright (c) 2004-2020 + * + * Author(s): + * Volker Fischer + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * +\******************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include "global.h" +#include "protocol.h" +#include "util.h" +#ifndef _WIN32 +# include +# include +#else +# include +# include +#endif + + +// The header files channel.h and server.h require to include this header file +// so we get a cyclic dependency. To solve this issue, a prototype of the +// channel class and server class is defined here. +class CServer; // forward declaration of CServer +class CChannel; // forward declaration of CChannel + + +/* Definitions ****************************************************************/ +// number of ports we try to bind until we give up +#define NUM_SOCKET_PORTS_TO_TRY 100 + + +/* Classes ********************************************************************/ +/* Base socket class -------------------------------------------------------- */ +class CSocket : public QObject +{ + Q_OBJECT + +public: + CSocket ( CChannel* pNewChannel, + const quint16 iPortNumber ) + : pChannel ( pNewChannel ), + bIsClient ( true ), + bJitterBufferOK ( true ) { Init ( iPortNumber ); } + + CSocket ( CServer* pNServP, + const quint16 iPortNumber ) + : pServer ( pNServP ), + bIsClient ( false ), + bJitterBufferOK ( true ) { Init ( iPortNumber ); } + + virtual ~CSocket(); + + void SendPacket ( const CVector& vecbySendBuf, + const CHostAddress& HostAddr ); + + bool GetAndResetbJitterBufferOKFlag(); + void Close(); + +protected: + void Init ( const quint16 iPortNumber ); + +#ifdef _WIN32 + SOCKET UdpSocket; +#else + int UdpSocket; +#endif + + QMutex Mutex; + + CVector vecbyRecBuf; + CHostAddress RecHostAddr; + QHostAddress SenderAddress; + quint16 SenderPort; + + CChannel* pChannel; // for client + CServer* pServer; // for server + + bool bIsClient; + + bool bJitterBufferOK; +#ifdef _WIN32 + LPFN_WSARECVMSG lpWSARecvMsg = NULL; + LPFN_WSASENDMSG lpWSASendMsg = NULL; +#endif +public: + void OnDataReceived(); + +signals: + void NewConnection(); // for the client + + void NewConnection ( int iChID, + CHostAddress RecHostAddr ); // for the server + + void ServerFull ( CHostAddress RecHostAddr ); + + void InvalidPacketReceived ( CHostAddress RecHostAddr ); + + void ProtcolMessageReceived ( int iRecCounter, + int iRecID, + CVector vecbyMesBodyData, + CHostAddress HostAdr ); + + void ProtcolCLMessageReceived ( int iRecID, + CVector vecbyMesBodyData, + CHostAddress HostAdr ); +}; + + +/* Socket which runs in a separate high priority thread --------------------- */ +// The receive socket should be put in a high priority thread to ensure the GUI +// does not effect the stability of the audio stream (e.g. if the GUI is on +// high load because of a table update, the incoming network packets must still +// be put in the jitter buffer with highest priority). +class CHighPrioSocket : public QObject +{ + Q_OBJECT + +public: + CHighPrioSocket ( CChannel* pNewChannel, + const quint16 iPortNumber ) + : Socket ( pNewChannel, iPortNumber ) { Init(); } + + CHighPrioSocket ( CServer* pNewServer, + const quint16 iPortNumber ) + : Socket ( pNewServer, iPortNumber ) { Init(); } + + virtual ~CHighPrioSocket() + { + NetworkWorkerThread.Stop(); + } + + void Start() + { + // starts the high priority socket receive thread (with using blocking + // socket request call) + NetworkWorkerThread.start ( QThread::TimeCriticalPriority ); + } + + void SendPacket ( const CVector& vecbySendBuf, + const CHostAddress& HostAddr ) + { + Socket.SendPacket ( vecbySendBuf, HostAddr ); + } + + bool GetAndResetbJitterBufferOKFlag() + { + return Socket.GetAndResetbJitterBufferOKFlag(); + } + +protected: + class CSocketThread : public QThread + { + public: + CSocketThread ( CSocket* pNewSocket = nullptr, QObject* parent = nullptr ) : + QThread ( parent ), pSocket ( pNewSocket ), bRun ( true ) { setObjectName ( "CSocketThread" ); } + + void Stop() + { + // disable run flag so that the thread loop can be exit + bRun = false; + + // to leave blocking wait for receive + pSocket->Close(); + + // give thread some time to terminate + wait ( 5000 ); + } + + void SetSocket ( CSocket* pNewSocket ) { pSocket = pNewSocket; } + + protected: + void run() { + // make sure the socket pointer is initialized (should be always the + // case) + if ( pSocket != nullptr ) + { + while ( bRun ) + { + // this function is a blocking function (waiting for network + // packets to be received and processed) + pSocket->OnDataReceived(); + } + } + } + + CSocket* pSocket; + bool bRun; + }; + + void Init() + { + // Creation of the new socket thread which has to have the highest + // possible thread priority to make sure the jitter buffer is reliably + // filled with the network audio packets and does not get interrupted + // by other GUI threads. The following code is based on: + // http://qt-project.org/wiki/Threads_Events_QObjects + Socket.moveToThread ( &NetworkWorkerThread ); + + NetworkWorkerThread.SetSocket ( &Socket ); + + // connect the "InvalidPacketReceived" signal + QObject::connect ( &Socket, &CSocket::InvalidPacketReceived, + this, &CHighPrioSocket::InvalidPacketReceived ); + } + + CSocketThread NetworkWorkerThread; + CSocket Socket; + +signals: + void InvalidPacketReceived ( CHostAddress RecHostAddr ); +}; From f19713d8ecfd0211634823770480af3e078e334a Mon Sep 17 00:00:00 2001 From: mstolle629 <81071220+mstolle629@users.noreply.github.com> Date: Mon, 5 Apr 2021 20:17:22 +0200 Subject: [PATCH 4/7] Remove some unnecessary comments after ipv6 support --- socket.cpp | 607 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 607 insertions(+) create mode 100644 socket.cpp diff --git a/socket.cpp b/socket.cpp new file mode 100644 index 0000000000..467a232118 --- /dev/null +++ b/socket.cpp @@ -0,0 +1,607 @@ +/******************************************************************************\ + * Copyright (c) 2004-2020 + * + * Author(s): + * Volker Fischer + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * +\******************************************************************************/ + +#include "socket.h" +#include "server.h" +#ifdef _WIN32 +#include +#include +#include +#include +#else +#include +#endif +/* Implementation *************************************************************/ +void CSocket::Init ( const quint16 iPortNumber ) +{ +#ifdef _WIN32 + // for the Windows socket usage we have to start it up first + +// TODO check for error and exit application on error + WSADATA wsa; + WSAStartup ( MAKEWORD(1, 0), &wsa ); +#else +#include +#endif + + // create the UDP socket + UdpSocket = socket ( AF_INET6, SOCK_DGRAM, 0); +#ifndef WIN32 + int on = 1; + int off = 0; + setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on)); + // allow to receive ipv4 packet info (i.e. destination ip address through recvmsg + setsockopt(UdpSocket, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); + // allow to receive ipv6 packetinfo + setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); + // allow to receive v6 and v4 requests + setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)); + // allocate memory for network receive and send buffer in samples +#else + char on = 1; + char off = 0; + setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)); + setsockopt(UdpSocket, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); + setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on)); + GUID g = WSAID_WSARECVMSG; + DWORD dwBytesReturned = 0; + if (WSAIoctl(UdpSocket, SIO_GET_EXTENSION_FUNCTION_POINTER, &g, sizeof(g), &lpWSARecvMsg, sizeof(lpWSARecvMsg), &dwBytesReturned, NULL, NULL) != 0) + { + // WSARecvMsg is not available... + } + g = WSAID_WSASENDMSG; + dwBytesReturned = 0; + WSAIoctl(UdpSocket, SIO_GET_EXTENSION_FUNCTION_POINTER, &g, sizeof(g), &lpWSASendMsg, sizeof(lpWSASendMsg), &dwBytesReturned, NULL, NULL ); + +#endif + vecbyRecBuf.Init ( MAX_SIZE_BYTES_NETW_BUF ); + // preinitialize socket in address (only the port number is missing) + sockaddr_in6 UdpSocketInAddr; + { + int i=0; + for (char *a=(char *)&UdpSocketInAddr; i<(int)sizeof(sockaddr_in6); ++i, ++a) *a=0; + } + //memset(&UdpSocketInAddr,0,sizeof(UdpSocketInAddr)); + UdpSocketInAddr.sin6_family = AF_INET6; + UdpSocketInAddr.sin6_addr = in6addr_any; + // initialize the listening socket + bool bSuccess; + + if ( bIsClient ) + { + if ( iPortNumber == 0 ) + { + // if port number is 0, bind the client to a random available port + UdpSocketInAddr.sin6_port = htons ( 0 ); + bSuccess = ( ::bind ( UdpSocket , + (sockaddr*) &UdpSocketInAddr, + sizeof ( sockaddr_in6 ) ) == 0); + } + else + { + // If the port is not available, try "NUM_SOCKET_PORTS_TO_TRY" times + // with incremented port numbers. Randomize the start port, in case a + // faulty router gets stuck and confused by a particular port (like + // the starting port). Might work around frustrating "cannot connect" + // problems (#568) + const quint16 startingPortNumber = iPortNumber + rand() % NUM_SOCKET_PORTS_TO_TRY; + + quint16 iClientPortIncrement = 0; + bSuccess = false; // initialization for while loop + + while ( !bSuccess && ( iClientPortIncrement <= NUM_SOCKET_PORTS_TO_TRY ) ) + { + UdpSocketInAddr.sin6_port = htons ( startingPortNumber + iClientPortIncrement ); + bSuccess = ( ::bind ( UdpSocket , + (sockaddr*) &UdpSocketInAddr, + sizeof ( sockaddr_in6 ) ) == 0 ); + + iClientPortIncrement++; + } + } + } + else + { + // for the server, only try the given port number and do not try out + // other port numbers to bind since it is important that the server + // gets the desired port number + UdpSocketInAddr.sin6_port = htons ( iPortNumber ); + bSuccess = ( ::bind ( UdpSocket , + (sockaddr*) &UdpSocketInAddr, + sizeof ( sockaddr_in6 ) ) == 0 ); + } + + if ( !bSuccess ) + { + // we cannot bind socket, throw error + throw CGenErr ( "Cannot bind the socket (maybe " + "the software is already running).", "Network Error" ); + } + + + // Connections ------------------------------------------------------------- + // it is important to do the following connections in this class since we + // have a thread transition + + // we have different connections for client and server + if ( bIsClient ) + { + // client connections: + + QObject::connect ( this, &CSocket::ProtcolMessageReceived, + pChannel, &CChannel::OnProtcolMessageReceived ); + + QObject::connect ( this, &CSocket::ProtcolCLMessageReceived, + pChannel, &CChannel::OnProtcolCLMessageReceived ); + + QObject::connect ( this, static_cast ( &CSocket::NewConnection ), + pChannel, &CChannel::OnNewConnection ); + } + else + { + // server connections: + + QObject::connect ( this, &CSocket::ProtcolMessageReceived, + pServer, &CServer::OnProtcolMessageReceived ); + + QObject::connect ( this, &CSocket::ProtcolCLMessageReceived, + pServer, &CServer::OnProtcolCLMessageReceived ); + + QObject::connect ( this, static_cast ( &CSocket::NewConnection ), + pServer, &CServer::OnNewConnection ); + + QObject::connect ( this, &CSocket::ServerFull, + pServer, &CServer::OnServerFull ); + } +} + +void CSocket::Close() +{ +#ifdef _WIN32 + // closesocket will cause recvfrom to return with an error because the + // socket is closed -> then the thread can safely be shut down + closesocket ( UdpSocket ); +#elif defined ( __APPLE__ ) || defined ( __MACOSX ) + // on Mac the general close has the same effect as closesocket on Windows + close ( UdpSocket ); +#else + // on Linux the shutdown call cancels the recvfrom + shutdown ( UdpSocket, SHUT_RDWR ); +#endif +} + +CSocket::~CSocket() +{ + // cleanup the socket (on Windows the WSA cleanup must also be called) +#ifdef _WIN32 + closesocket ( UdpSocket ); + WSACleanup(); +#else + close ( UdpSocket ); +#endif +} + +void CSocket::SendPacket ( const CVector& vecbySendBuf, + const CHostAddress& HostAddr ) +{ + QMutexLocker locker ( &Mutex ); + + const int iVecSizeOut = vecbySendBuf.Size(); + + if ( iVecSizeOut > 0 ) + { + // send packet through network (we have to convert the constant unsigned + // char vector in "const char*", for this we first convert the const + // uint8_t vector in a read/write uint8_t vector and then do the cast to + // const char*) + sockaddr_in6 UdpSocketOutAddr = {}; + { + int i=0; + for (char *a=(char *)&UdpSocketOutAddr; i<(int)sizeof(sockaddr_in6); ++i, ++a) *a=0; + } + UdpSocketOutAddr.sin6_family = AF_INET6; + UdpSocketOutAddr.sin6_port = htons ( HostAddr.iPort ); + +// memcpy(&UdpSocketOutAddr.sin6_addr.s6_addr,(void *)&HostAddr.InetAddr.toIPv6Address(),16); + Q_IPV6ADDR ipv6addr_h = HostAddr.InetAddr.toIPv6Address(); + memcpy (&UdpSocketOutAddr.sin6_addr.s6_addr,&ipv6addr_h,16); +#ifndef _WIN32 + msghdr m; + struct iovec iov[1]; + int rv; + char pktinfo[100]; + m.msg_name = (sockaddr*) &UdpSocketOutAddr; + m.msg_namelen = sizeof ( sockaddr_in6 ); + + iov[0].iov_base = (void *) &( vecbySendBuf )[0]; + iov[0].iov_len = iVecSizeOut; + m.msg_iov = iov; + m.msg_iovlen = 1; + //m.msg_control = pktinfo; + m.msg_control = NULL; + m.msg_controllen = 0; + bool isv4; + quint32 ipaddr4_l = HostAddr.LocalAddr.toIPv4Address(&isv4); + if (isv4) + { + if (ipaddr4_l != 0) + { + struct in_addr ia; + struct in_addr da; + memset(pktinfo,0,sizeof(pktinfo)); + struct in_pktinfo *pktinfo2; + m.msg_control = pktinfo; + struct cmsghdr *cmsg = (struct cmsghdr *) pktinfo; + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + + pktinfo2 = (struct in_pktinfo*) CMSG_DATA(cmsg); + pktinfo2->ipi_ifindex = 0; + ia.s_addr = ipaddr4_l; + da.s_addr = 0; + pktinfo2->ipi_spec_dst = ia; + pktinfo2->ipi_addr = da; + m.msg_controllen += CMSG_SPACE(sizeof(struct in_pktinfo)); + + } else + { + ; + } + } + else + { + memset(pktinfo,0,sizeof(pktinfo)); + m.msg_control = pktinfo; + struct cmsghdr *cmsg = (struct cmsghdr *)pktinfo; + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(16); + memcpy(CMSG_DATA(cmsg),&HostAddr.LocalAddr,16); + m.msg_controllen += CMSG_SPACE(16); + } + rv=sendmsg(UdpSocket,&m,0); + if (rv<0) fprintf(stderr, "%s\n", strerror(errno)); +#else + WSAMSG m; + WSABUF iov[1]; + char pktinfo[100]; + WSABUF controlbuf; + m.name = (sockaddr*) &UdpSocketOutAddr; + m.namelen = sizeof ( sockaddr_in6 ); + iov[0].buf = (char *) &( vecbySendBuf )[0]; + iov[0].len = iVecSizeOut; + m.lpBuffers = iov; + m.dwBufferCount = 1; + controlbuf.len = 0; + controlbuf.buf = NULL; + m.Control = controlbuf; + m.dwFlags = 0; + bool isv4; + quint32 ipaddr4_l = HostAddr.LocalAddr.toIPv4Address(&isv4); + if (isv4) + { + if (ipaddr4_l != 0) + { + //struct in_addr ia; + //struct in_addr da; + memset(pktinfo,0,sizeof(pktinfo)); + m.Control.buf = pktinfo; + m.Control.len = sizeof(pktinfo); + WSACMSGHDR *cmsg = WSA_CMSG_FIRSTHDR(&m); + if (cmsg != NULL) + { + struct in_pktinfo *pktinfo2; + memset(cmsg, 0, WSA_CMSG_SPACE(sizeof(struct in_pktinfo))); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = WSA_CMSG_LEN(sizeof(struct in_pktinfo)); + + pktinfo2 = (struct in_pktinfo*) WSA_CMSG_DATA(cmsg); + pktinfo2->ipi_ifindex = 0; + pktinfo2->ipi_addr.s_addr = ipaddr4_l; + + } + + } else + { + m.Control.buf = NULL; + m.Control.len = 0; + } + } + else + { + memset(pktinfo,0,sizeof(pktinfo)); + m.Control.buf = pktinfo; + m.Control.len = sizeof(pktinfo); + WSACMSGHDR *cmsg = WSA_CMSG_FIRSTHDR(&m); + //struct cmsghdr *cmsg = (struct cmsghdr *)pktinfo; + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = WSA_CMSG_LEN(16); + memcpy(WSA_CMSG_DATA(cmsg),&HostAddr.LocalAddr,16); + } + { + int rv; + DWORD bytes_sent; + rv = lpWSASendMsg(UdpSocket,&m,0,&bytes_sent,NULL,NULL); + if (rv != 0) + { + int err; + char msgbuf [256]; // for a message up to 255 bytes. + + msgbuf [0] = '\0'; // Microsoft doesn't guarantee this on man page. + err = WSAGetLastError (); + + FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // flags + NULL, // lpsource + err, // message id + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), // languageid + msgbuf, // output buffer + sizeof (msgbuf), // size of msgbuf, bytes + NULL); // va_list of arguments + + CGenErr ( "WSASendMsg failed" ); + } + } +#endif + } +} + +bool CSocket::GetAndResetbJitterBufferOKFlag() +{ + // check jitter buffer status + if ( !bJitterBufferOK ) + { + // reset flag and return "not OK" status + bJitterBufferOK = true; + return false; + } + + // the buffer was OK, we do not have to reset anything and just return the + // OK status + return true; +} + +void CSocket::OnDataReceived() +{ +/* + The strategy of this function is that only the "put audio" function is + called directly (i.e. the high thread priority is used) and all other less + important things like protocol parsing and acting on protocol messages is + done in the low priority thread. To get a thread transition, we have to + use the signal/slot mechanism (i.e. we use messages for that). +*/ + + // read block from network interface and query address of sender + sockaddr_in6 SenderAddr; + in6_addr LocalInterfaceAddr; +#ifdef _WIN32 + int SenderAddrSize = sizeof ( sockaddr_in6 ); +#else + socklen_t SenderAddrSize = sizeof ( sockaddr_in6 ); +#endif + +#ifdef _WIN32 + WSAMSG msgh; + WSABUF iov[1]; +#else + struct msghdr msgh; + struct iovec iov[1]; +#endif + bool la_found = false; +#ifdef _WIN32 + WSACMSGHDR *cmsg; + // Prepare message header structure, fill it with zero (memset) + { + int i=0; + for (char *a=(char *)&msgh; i<(int)sizeof(WSAMSG); ++i, ++a) *a=0; + } + + msgh.name = (sockaddr *)&SenderAddr; + msgh.namelen = SenderAddrSize; + msgh.dwBufferCount = 1; + msgh.lpBuffers = iov; + iov[0].buf = (char *) &vecbyRecBuf[0]; + iov[0].len = MAX_SIZE_BYTES_NETW_BUF; + WSABUF controlbuf; + char controlbuffer[512]; + controlbuf.buf = controlbuffer; + controlbuf.len = sizeof(controlbuffer); + { + int i=0; + for (char *a=(char *)controlbuffer; i<(int)sizeof(controlbuffer); ++i, ++a) *a=0; + } + + msgh.Control = controlbuf; + DWORD iNumBytesRead; + { + int rv = lpWSARecvMsg(UdpSocket, &msgh, &iNumBytesRead, NULL, NULL); + if (rv != 0) + { + int err; + char msgbuf [256]; // for a message up to 255 bytes. + msgbuf [0] = '\0'; // Microsoft doesn't guarantee this on man page. + err = WSAGetLastError (); + FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // flags + NULL, // lpsource + err, // message id + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), // languageid + msgbuf, // output buffer + sizeof (msgbuf), // size of msgbuf, bytes + NULL); // va_list of arguments + return; + } + else + { + /* Receive auxiliary data in msgh */ + for (cmsg = WSA_CMSG_FIRSTHDR(&msgh); cmsg != NULL; + cmsg = WSA_CMSG_NXTHDR(&msgh, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_IPV6 + && cmsg->cmsg_type == IPV6_PKTINFO) { + memcpy(&LocalInterfaceAddr, WSA_CMSG_DATA(cmsg), sizeof(LocalInterfaceAddr)); + la_found = true; + break; + } + } + + } + } + + // check if an error occurred or no data could be read +#else + struct cmsghdr *cmsg; + // Prepare message header structure + memset( &msgh, 0, sizeof(msgh)); + msgh.msg_name = &SenderAddr; + msgh.msg_namelen = SenderAddrSize; + msgh.msg_iovlen = 1; + msgh.msg_iov = iov; + iov[0].iov_base = (char *) &vecbyRecBuf[0]; + iov[0].iov_len = MAX_SIZE_BYTES_NETW_BUF; + char controlbuffer[4096]; + memset( controlbuffer, 0, sizeof(controlbuffer)); + msgh.msg_control = controlbuffer; + msgh.msg_controllen = sizeof(controlbuffer); + const long iNumBytesRead = ::recvmsg(UdpSocket, &msgh, 0); + + /* Receive auxiliary data in msgh */ + for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msgh, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_IPV6 + && cmsg->cmsg_type == IPV6_PKTINFO) { + memcpy(&LocalInterfaceAddr, CMSG_DATA(cmsg), sizeof(LocalInterfaceAddr)); + la_found = true; + break; + } + } +#endif + if ( iNumBytesRead <= 0 ) + { + return; + } + // convert address of client + Q_IPV6ADDR saddr6_tmp; + Q_IPV6ADDR iaddr6_tmp; + bool isv4 = false; + quint32 i4a; + memcpy(&saddr6_tmp,&SenderAddr.sin6_addr,16); + if (la_found) memcpy(&iaddr6_tmp,&LocalInterfaceAddr,16); + // Handle ipv6 mapped ipv4 addresses + RecHostAddr.InetAddr.setAddress(saddr6_tmp); + i4a = RecHostAddr.InetAddr.toIPv4Address(&isv4); + if (isv4) + { + RecHostAddr.InetAddr.setAddress(i4a); + } + isv4 = false; + if (la_found) RecHostAddr.LocalAddr.setAddress(iaddr6_tmp); + RecHostAddr.iPort = ntohs ( SenderAddr.sin6_port ); + + + // check if this is a protocol message + int iRecCounter; + int iRecID; + CVector vecbyMesBodyData; + + if ( !CProtocol::ParseMessageFrame ( vecbyRecBuf, + iNumBytesRead, + vecbyMesBodyData, + iRecCounter, + iRecID ) ) + { + // this is a protocol message, check the type of the message + if ( CProtocol::IsConnectionLessMessageID ( iRecID ) ) + { + +// TODO a copy of the vector is used -> avoid malloc in real-time routine + + emit ProtcolCLMessageReceived ( iRecID, vecbyMesBodyData, RecHostAddr ); + } + else + { + +// TODO a copy of the vector is used -> avoid malloc in real-time routine + + emit ProtcolMessageReceived ( iRecCounter, iRecID, vecbyMesBodyData, RecHostAddr ); + } + } + else + { + // this is most probably a regular audio packet + if ( bIsClient ) + { + // client: + + switch ( pChannel->PutAudioData ( vecbyRecBuf, iNumBytesRead, RecHostAddr ) ) + { + case PS_AUDIO_ERR: + case PS_GEN_ERROR: + bJitterBufferOK = false; + break; + + case PS_NEW_CONNECTION: + // inform other objects that new connection was established + emit NewConnection(); + break; + + case PS_AUDIO_INVALID: + // inform about received invalid packet by fireing an event + emit InvalidPacketReceived ( RecHostAddr ); + break; + + default: + // do nothing + break; + } + } + else + { + // server: + + int iCurChanID; + + if ( pServer->PutAudioData ( vecbyRecBuf, iNumBytesRead, RecHostAddr, iCurChanID ) ) + { + // we have a new connection, emit a signal + emit NewConnection ( iCurChanID, RecHostAddr ); + + // this was an audio packet, start server if it is in sleep mode + if ( !pServer->IsRunning() ) + { + // (note that Qt will delete the event object when done) + QCoreApplication::postEvent ( pServer, + new CCustomEvent ( MS_PACKET_RECEIVED, 0, 0 ) ); + } + } + + // check if no channel is available + if ( iCurChanID == INVALID_CHANNEL_ID ) + { + // fire message for the state that no free channel is available + emit ServerFull ( RecHostAddr ); + } + } + } +} From 45a02ffcbbd824232c29a91a69905fcc16abce22 Mon Sep 17 00:00:00 2001 From: mstolle629 <81071220+mstolle629@users.noreply.github.com> Date: Tue, 6 Apr 2021 19:58:48 +0200 Subject: [PATCH 5/7] added autosensing, if there is no ipv6 available added autosensing, if there is no ipv6 stack available in OS, fall back to ipv4 communication works with mac os, windows and linux --- src/socket.cpp | 674 +++++++++++++++++++++++++++++-------------------- src/socket.h | 1 + 2 files changed, 397 insertions(+), 278 deletions(-) diff --git a/src/socket.cpp b/src/socket.cpp index eb71234706..1336573969 100755 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -47,23 +47,47 @@ void CSocket::Init ( const quint16 iPortNumber ) // create the UDP socket UdpSocket = socket ( AF_INET6, SOCK_DGRAM, 0); + if (UdpSocket < 0) + { + UdpSocket = socket ( AF_INET, SOCK_DGRAM, 0); + sockmode = 4; + } + else + { + sockmode = 6; + } #ifndef WIN32 int on = 1; int off = 0; - setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on)); - // allow to receive ipv4 packet info (i.e. destination ip address through recvmsg - setsockopt(UdpSocket, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); - // allow to receive ipv6 packetinfo - setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); - // allow to receive v6 and v4 requests - setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)); - // allocate memory for network receive and send buffer in samples + if (sockmode == 6) + { + setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on)); + // allow to receive ipv4 packet info (i.e. destination ip address through recvmsg + setsockopt(UdpSocket, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); + // allow to receive ipv6 packetinfo + setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); + // allow to receive v6 and v4 requests + setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)); + // allocate memory for network receive and send buffer in samples + } + else + { + // allow to receive ipv4 packet info (i.e. destination ip address through recvmsg + setsockopt(UdpSocket, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); + } #else char on = 1; char off = 0; - setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)); - setsockopt(UdpSocket, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); - setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on)); + if (sockmode == 6) + { + setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)); + setsockopt(UdpSocket, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); + setsockopt(UdpSocket, IPPROTO_IPV6, IPV6_PKTINFO, &on, sizeof(on)); + } + else + { + setsockopt(UdpSocket, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); + } GUID g = WSAID_WSARECVMSG; DWORD dwBytesReturned = 0; if (WSAIoctl(UdpSocket, SIO_GET_EXTENSION_FUNCTION_POINTER, &g, sizeof(g), &lpWSARecvMsg, sizeof(lpWSARecvMsg), &dwBytesReturned, NULL, NULL) != 0) @@ -73,7 +97,6 @@ void CSocket::Init ( const quint16 iPortNumber ) g = WSAID_WSASENDMSG; dwBytesReturned = 0; WSAIoctl(UdpSocket, SIO_GET_EXTENSION_FUNCTION_POINTER, &g, sizeof(g), &lpWSASendMsg, sizeof(lpWSASendMsg), &dwBytesReturned, NULL, NULL ); - #endif vecbyRecBuf.Init ( MAX_SIZE_BYTES_NETW_BUF ); // preinitialize socket in address (only the port number is missing) @@ -82,9 +105,20 @@ void CSocket::Init ( const quint16 iPortNumber ) int i=0; for (char *a=(char *)&UdpSocketInAddr; i<(int)sizeof(sockaddr_in6); ++i, ++a) *a=0; } + sockaddr_in UdpSocket4InAddr; + if ( sockmode == 4) + { + UdpSocket4InAddr.sin_family = AF_INET; + UdpSocket4InAddr.sin_addr.s_addr = INADDR_ANY; + } //memset(&UdpSocketInAddr,0,sizeof(UdpSocketInAddr)); UdpSocketInAddr.sin6_family = AF_INET6; UdpSocketInAddr.sin6_addr = in6addr_any; + if ( sockmode == 4) + { + UdpSocket4InAddr.sin_family = AF_INET; + UdpSocket4InAddr.sin_addr.s_addr = INADDR_ANY; + } // initialize the listening socket bool bSuccess; @@ -93,10 +127,22 @@ void CSocket::Init ( const quint16 iPortNumber ) if ( iPortNumber == 0 ) { // if port number is 0, bind the client to a random available port - UdpSocketInAddr.sin6_port = htons ( 0 ); - bSuccess = ( ::bind ( UdpSocket , + if ( sockmode == 6) + { + UdpSocketInAddr.sin6_port = htons ( 0 ); + bSuccess = ( ::bind ( UdpSocket , (sockaddr*) &UdpSocketInAddr, sizeof ( sockaddr_in6 ) ) == 0); + } + else + { + UdpSocket4InAddr.sin_port = htons ( 0 ); + + bSuccess = ( ::bind ( UdpSocket , + (sockaddr*) &UdpSocket4InAddr, + sizeof ( sockaddr_in ) ) == 0 ); + + } } else { @@ -112,10 +158,21 @@ void CSocket::Init ( const quint16 iPortNumber ) while ( !bSuccess && ( iClientPortIncrement <= NUM_SOCKET_PORTS_TO_TRY ) ) { - UdpSocketInAddr.sin6_port = htons ( startingPortNumber + iClientPortIncrement ); - bSuccess = ( ::bind ( UdpSocket , + if ( sockmode == 6) + { + UdpSocketInAddr.sin6_port = htons ( startingPortNumber + iClientPortIncrement ); + bSuccess = ( ::bind ( UdpSocket , (sockaddr*) &UdpSocketInAddr, sizeof ( sockaddr_in6 ) ) == 0 ); + } + else + { + UdpSocket4InAddr.sin_port = htons ( startingPortNumber + iClientPortIncrement ); + + bSuccess = ( ::bind ( UdpSocket , + (sockaddr*) &UdpSocket4InAddr, + sizeof ( sockaddr_in ) ) == 0 ); + } iClientPortIncrement++; } @@ -126,10 +183,22 @@ void CSocket::Init ( const quint16 iPortNumber ) // for the server, only try the given port number and do not try out // other port numbers to bind since it is important that the server // gets the desired port number - UdpSocketInAddr.sin6_port = htons ( iPortNumber ); - bSuccess = ( ::bind ( UdpSocket , + if ( sockmode == 6) + { + UdpSocketInAddr.sin6_port = htons ( iPortNumber ); + bSuccess = ( ::bind ( UdpSocket , (sockaddr*) &UdpSocketInAddr, sizeof ( sockaddr_in6 ) ) == 0 ); + } + else + { + UdpSocket4InAddr.sin_port = htons ( iPortNumber ); + + bSuccess = ( ::bind ( UdpSocket , + (sockaddr*) &UdpSocket4InAddr, + sizeof ( sockaddr_in ) ) == 0 ); + + } } if ( !bSuccess ) @@ -215,168 +284,190 @@ void CSocket::SendPacket ( const CVector& vecbySendBuf, // char vector in "const char*", for this we first convert the const // uint8_t vector in a read/write uint8_t vector and then do the cast to // const char*) - sockaddr_in6 UdpSocketOutAddr = {}; - { - int i=0; - for (char *a=(char *)&UdpSocketOutAddr; i<(int)sizeof(sockaddr_in6); ++i, ++a) *a=0; - } - UdpSocketOutAddr.sin6_family = AF_INET6; - UdpSocketOutAddr.sin6_port = htons ( HostAddr.iPort ); - -// memcpy(&UdpSocketOutAddr.sin6_addr.s6_addr,(void *)&HostAddr.InetAddr.toIPv6Address(),16); - Q_IPV6ADDR ipv6addr_h = HostAddr.InetAddr.toIPv6Address(); - memcpy (&UdpSocketOutAddr.sin6_addr.s6_addr,&ipv6addr_h,16); + if ( sockmode == 6 ) + { + sockaddr_in6 UdpSocketOutAddr = {}; + { + int i=0; + for (char *a=(char *)&UdpSocketOutAddr; i<(int)sizeof(sockaddr_in6); ++i, ++a) *a=0; + } + UdpSocketOutAddr.sin6_family = AF_INET6; + UdpSocketOutAddr.sin6_port = htons ( HostAddr.iPort ); + + // memcpy(&UdpSocketOutAddr.sin6_addr.s6_addr,(void *)&HostAddr.InetAddr.toIPv6Address(),16); + Q_IPV6ADDR ipv6addr_h = HostAddr.InetAddr.toIPv6Address(); + memcpy (&UdpSocketOutAddr.sin6_addr.s6_addr,&ipv6addr_h,16); #ifndef _WIN32 - msghdr m; - struct iovec iov[1]; - int rv; - char pktinfo[100]; - m.msg_name = (sockaddr*) &UdpSocketOutAddr; - m.msg_namelen = sizeof ( sockaddr_in6 ); - - iov[0].iov_base = (void *) &( vecbySendBuf )[0]; - iov[0].iov_len = iVecSizeOut; - m.msg_iov = iov; - m.msg_iovlen = 1; - //m.msg_control = pktinfo; - m.msg_control = NULL; - m.msg_controllen = 0; - bool isv4; - quint32 ipaddr4_l = HostAddr.LocalAddr.toIPv4Address(&isv4); - if (isv4) - { - if (ipaddr4_l != 0) + msghdr m; + struct iovec iov[1]; + int rv; + char pktinfo[100]; + m.msg_name = (sockaddr*) &UdpSocketOutAddr; + m.msg_namelen = sizeof ( sockaddr_in6 ); + + iov[0].iov_base = (void *) &( vecbySendBuf )[0]; + iov[0].iov_len = iVecSizeOut; + m.msg_iov = iov; + m.msg_iovlen = 1; + //m.msg_control = pktinfo; + m.msg_control = NULL; + m.msg_controllen = 0; + bool isv4; + quint32 ipaddr4_l = HostAddr.LocalAddr.toIPv4Address(&isv4); + if (isv4) + { + if (ipaddr4_l != 0) + { + struct in_addr ia; + struct in_addr da; + memset(pktinfo,0,sizeof(pktinfo)); + struct in_pktinfo *pktinfo2; + m.msg_control = pktinfo; + struct cmsghdr *cmsg = (struct cmsghdr *) pktinfo; + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); + + pktinfo2 = (struct in_pktinfo*) CMSG_DATA(cmsg); + pktinfo2->ipi_ifindex = 0; + ia.s_addr = ipaddr4_l; + da.s_addr = 0; + pktinfo2->ipi_spec_dst = ia; + pktinfo2->ipi_addr = da; + m.msg_controllen += CMSG_SPACE(sizeof(struct in_pktinfo)); + + } else + { + ; + } + } + else { - struct in_addr ia; - struct in_addr da; memset(pktinfo,0,sizeof(pktinfo)); - struct in_pktinfo *pktinfo2; m.msg_control = pktinfo; - struct cmsghdr *cmsg = (struct cmsghdr *) pktinfo; - cmsg->cmsg_level = IPPROTO_IP; - cmsg->cmsg_type = IP_PKTINFO; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); - - pktinfo2 = (struct in_pktinfo*) CMSG_DATA(cmsg); - pktinfo2->ipi_ifindex = 0; - ia.s_addr = ipaddr4_l; - da.s_addr = 0; - pktinfo2->ipi_spec_dst = ia; - pktinfo2->ipi_addr = da; - m.msg_controllen += CMSG_SPACE(sizeof(struct in_pktinfo)); - - } else - { - ; + struct cmsghdr *cmsg = (struct cmsghdr *)pktinfo; + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = CMSG_LEN(16); + memcpy(CMSG_DATA(cmsg),&HostAddr.LocalAddr,16); + m.msg_controllen += CMSG_SPACE(16); } - } - else - { - memset(pktinfo,0,sizeof(pktinfo)); - m.msg_control = pktinfo; - struct cmsghdr *cmsg = (struct cmsghdr *)pktinfo; - cmsg->cmsg_level = IPPROTO_IPV6; - cmsg->cmsg_type = IPV6_PKTINFO; - cmsg->cmsg_len = CMSG_LEN(16); - memcpy(CMSG_DATA(cmsg),&HostAddr.LocalAddr,16); - m.msg_controllen += CMSG_SPACE(16); - } - rv=sendmsg(UdpSocket,&m,0); - if (rv<0) fprintf(stderr, "%s\n", strerror(errno)); + rv=sendmsg(UdpSocket,&m,0); + if (rv<0) fprintf(stderr, "%s\n", strerror(errno)); #else - WSAMSG m; - WSABUF iov[1]; - char pktinfo[100]; - WSABUF controlbuf; - m.name = (sockaddr*) &UdpSocketOutAddr; - m.namelen = sizeof ( sockaddr_in6 ); - iov[0].buf = (char *) &( vecbySendBuf )[0]; - iov[0].len = iVecSizeOut; - m.lpBuffers = iov; - m.dwBufferCount = 1; - controlbuf.len = 0; - controlbuf.buf = NULL; - m.Control = controlbuf; - m.dwFlags = 0; - bool isv4; - quint32 ipaddr4_l = HostAddr.LocalAddr.toIPv4Address(&isv4); - if (isv4) - { - if (ipaddr4_l != 0) + WSAMSG m; + WSABUF iov[1]; + char pktinfo[100]; + WSABUF controlbuf; + m.name = (sockaddr*) &UdpSocketOutAddr; + m.namelen = sizeof ( sockaddr_in6 ); + iov[0].buf = (char *) &( vecbySendBuf )[0]; + iov[0].len = iVecSizeOut; + m.lpBuffers = iov; + m.dwBufferCount = 1; + controlbuf.len = 0; + controlbuf.buf = NULL; + m.Control = controlbuf; + m.dwFlags = 0; + bool isv4; + quint32 ipaddr4_l = HostAddr.LocalAddr.toIPv4Address(&isv4); + if (isv4) + { + if (ipaddr4_l != 0) + { + //struct in_addr ia; + //struct in_addr da; + memset(pktinfo,0,sizeof(pktinfo)); + m.Control.buf = pktinfo; + m.Control.len = sizeof(pktinfo); + WSACMSGHDR *cmsg = WSA_CMSG_FIRSTHDR(&m); + if (cmsg != NULL) + { + struct in_pktinfo *pktinfo2; + //m.msg_control = pktinfo; + memset(cmsg, 0, WSA_CMSG_SPACE(sizeof(struct in_pktinfo))); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_PKTINFO; + cmsg->cmsg_len = WSA_CMSG_LEN(sizeof(struct in_pktinfo)); + + pktinfo2 = (struct in_pktinfo*) WSA_CMSG_DATA(cmsg); + pktinfo2->ipi_ifindex = 0; + pktinfo2->ipi_addr.s_addr = ipaddr4_l; + //ia.s_addr = ipaddr4_l; + //da.s_addr = 0; + + // pktinfo2->ipi_spec_dst = ia; + // pktinfo2->ipi_addr = da; + // m.msg_controllen += CMSG_SPACE(sizeof(struct in_pktinfo)); + //m.Control.len += CMSG_SPACE(sizeof(struct in_pktinfo)); + } + + } else + { + m.Control.buf = NULL; + m.Control.len = 0; + } + } + else { - //struct in_addr ia; - //struct in_addr da; memset(pktinfo,0,sizeof(pktinfo)); m.Control.buf = pktinfo; m.Control.len = sizeof(pktinfo); WSACMSGHDR *cmsg = WSA_CMSG_FIRSTHDR(&m); - if (cmsg != NULL) - { - struct in_pktinfo *pktinfo2; - //m.msg_control = pktinfo; - memset(cmsg, 0, WSA_CMSG_SPACE(sizeof(struct in_pktinfo))); - cmsg->cmsg_level = IPPROTO_IP; - cmsg->cmsg_type = IP_PKTINFO; - cmsg->cmsg_len = WSA_CMSG_LEN(sizeof(struct in_pktinfo)); - - pktinfo2 = (struct in_pktinfo*) WSA_CMSG_DATA(cmsg); - pktinfo2->ipi_ifindex = 0; - pktinfo2->ipi_addr.s_addr = ipaddr4_l; - //ia.s_addr = ipaddr4_l; - //da.s_addr = 0; - - // pktinfo2->ipi_spec_dst = ia; - // pktinfo2->ipi_addr = da; - // m.msg_controllen += CMSG_SPACE(sizeof(struct in_pktinfo)); - //m.Control.len += CMSG_SPACE(sizeof(struct in_pktinfo)); - } - - } else - { - m.Control.buf = NULL; - m.Control.len = 0; + //struct cmsghdr *cmsg = (struct cmsghdr *)pktinfo; + cmsg->cmsg_level = IPPROTO_IPV6; + cmsg->cmsg_type = IPV6_PKTINFO; + cmsg->cmsg_len = WSA_CMSG_LEN(16); + memcpy(WSA_CMSG_DATA(cmsg),&HostAddr.LocalAddr,16); + //m.Control.len += CMSG_SPACE(16); } - } - else - { - memset(pktinfo,0,sizeof(pktinfo)); - m.Control.buf = pktinfo; - m.Control.len = sizeof(pktinfo); - WSACMSGHDR *cmsg = WSA_CMSG_FIRSTHDR(&m); - //struct cmsghdr *cmsg = (struct cmsghdr *)pktinfo; - cmsg->cmsg_level = IPPROTO_IPV6; - cmsg->cmsg_type = IPV6_PKTINFO; - cmsg->cmsg_len = WSA_CMSG_LEN(16); - memcpy(WSA_CMSG_DATA(cmsg),&HostAddr.LocalAddr,16); - //m.Control.len += CMSG_SPACE(16); - } - { - int rv; - //WSASendMsg(UdpSocket,&m,0,) - DWORD bytes_sent; - rv = lpWSASendMsg(UdpSocket,&m,0,&bytes_sent,NULL,NULL); - if (rv != 0) { - int err; - char msgbuf [256]; // for a message up to 255 bytes. - - msgbuf [0] = '\0'; // Microsoft doesn't guarantee this on man page. - err = WSAGetLastError (); - - FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // flags - NULL, // lpsource - err, // message id - MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), // languageid - msgbuf, // output buffer - sizeof (msgbuf), // size of msgbuf, bytes - NULL); // va_list of arguments - - CGenErr ( "WSASendMsg failed" ); + int rv; + //WSASendMsg(UdpSocket,&m,0,) + DWORD bytes_sent; + rv = lpWSASendMsg(UdpSocket,&m,0,&bytes_sent,NULL,NULL); + if (rv != 0) + { + int err; + char msgbuf [256]; // for a message up to 255 bytes. + + msgbuf [0] = '\0'; // Microsoft doesn't guarantee this on man page. + err = WSAGetLastError (); + + FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // flags + NULL, // lpsource + err, // message id + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), // languageid + msgbuf, // output buffer + sizeof (msgbuf), // size of msgbuf, bytes + NULL); // va_list of arguments + + CGenErr ( "WSASendMsg failed" ); + } } - } #endif - } -} + } /* if sockmode 6 */ + else /* sockmode 4 */ + { + // send packet through network (we have to convert the constant unsigned + // char vector in "const char*", for this we first convert the const + // uint8_t vector in a read/write uint8_t vector and then do the cast to + // const char*) + sockaddr_in UdpSocketOutAddr; + + UdpSocketOutAddr.sin_family = AF_INET; + UdpSocketOutAddr.sin_port = htons ( HostAddr.iPort ); + UdpSocketOutAddr.sin_addr.s_addr = htonl ( HostAddr.InetAddr.toIPv4Address() ); + + sendto ( UdpSocket, + (const char*) &( (CVector) vecbySendBuf )[0], + iVecSizeOut, + 0, + (sockaddr*) &UdpSocketOutAddr, + sizeof ( sockaddr_in ) ); + } /* else sockmode 6*/ + } /* if iVecSizeOut */ +} /* SendPacket */ bool CSocket::GetAndResetbJitterBufferOKFlag() { @@ -404,137 +495,164 @@ void CSocket::OnDataReceived() */ // read block from network interface and query address of sender - sockaddr_in6 SenderAddr; - in6_addr LocalInterfaceAddr; #ifdef _WIN32 - int SenderAddrSize = sizeof ( sockaddr_in6 ); + DWORD iNumBytesRead; #else - socklen_t SenderAddrSize = sizeof ( sockaddr_in6 ); + long iNumBytesRead; #endif - //const long iNumBytesRead = recvfrom ( UdpSocket, - // (char*) &vecbyRecBuf[0], - // MAX_SIZE_BYTES_NETW_BUF, - // f 0, - // (sockaddr*) &SenderAddr, - // &SenderAddrSize ); + if (sockmode == 6) + { + sockaddr_in6 SenderAddr; + in6_addr LocalInterfaceAddr; #ifdef _WIN32 - WSAMSG msgh; - WSABUF iov[1]; + int SenderAddrSize = sizeof ( sockaddr_in6 ); #else - struct msghdr msgh; - struct iovec iov[1]; + socklen_t SenderAddrSize = sizeof ( sockaddr_in6 ); #endif - bool la_found = false; #ifdef _WIN32 - WSACMSGHDR *cmsg; - // Prepare message header structure, fill it with zero (memset) - { - int i=0; - for (char *a=(char *)&msgh; i<(int)sizeof(WSAMSG); ++i, ++a) *a=0; - } - - msgh.name = (sockaddr *)&SenderAddr; - msgh.namelen = SenderAddrSize; - msgh.dwBufferCount = 1; - msgh.lpBuffers = iov; - iov[0].buf = (char *) &vecbyRecBuf[0]; - iov[0].len = MAX_SIZE_BYTES_NETW_BUF; - WSABUF controlbuf; - char controlbuffer[512]; - controlbuf.buf = controlbuffer; - controlbuf.len = sizeof(controlbuffer); - { - int i=0; - for (char *a=(char *)controlbuffer; i<(int)sizeof(controlbuffer); ++i, ++a) *a=0; - } - - msgh.Control = controlbuf; - DWORD iNumBytesRead; - { - int rv = lpWSARecvMsg(UdpSocket, &msgh, &iNumBytesRead, NULL, NULL); - if (rv != 0) + WSAMSG msgh; + WSABUF iov[1]; +#else + struct msghdr msgh; + struct iovec iov[1]; +#endif + bool la_found = false; +#ifdef _WIN32 + WSACMSGHDR *cmsg; + // Prepare message header structure, fill it with zero (memset) + { + int i=0; + for (char *a=(char *)&msgh; i<(int)sizeof(WSAMSG); ++i, ++a) *a=0; + } + + msgh.name = (sockaddr *)&SenderAddr; + msgh.namelen = SenderAddrSize; + msgh.dwBufferCount = 1; + msgh.lpBuffers = iov; + iov[0].buf = (char *) &vecbyRecBuf[0]; + iov[0].len = MAX_SIZE_BYTES_NETW_BUF; + WSABUF controlbuf; + char controlbuffer[512]; + controlbuf.buf = controlbuffer; + controlbuf.len = sizeof(controlbuffer); { - int err; - char msgbuf [256]; // for a message up to 255 bytes. - msgbuf [0] = '\0'; // Microsoft doesn't guarantee this on man page. - err = WSAGetLastError (); - FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // flags - NULL, // lpsource - err, // message id - MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), // languageid - msgbuf, // output buffer - sizeof (msgbuf), // size of msgbuf, bytes - NULL); // va_list of arguments - return; + int i=0; + for (char *a=(char *)controlbuffer; i<(int)sizeof(controlbuffer); ++i, ++a) *a=0; } - else + + msgh.Control = controlbuf; { - /* Receive auxiliary data in msgh */ - //fprintf(stderr,"hallo"); - for (cmsg = WSA_CMSG_FIRSTHDR(&msgh); cmsg != NULL; - cmsg = WSA_CMSG_NXTHDR(&msgh, cmsg)) { - //fprintf(stderr,"d=%d ",cmsg->cmsg_level); - if (cmsg->cmsg_level == IPPROTO_IPV6 - && cmsg->cmsg_type == IPV6_PKTINFO) { - memcpy(&LocalInterfaceAddr, WSA_CMSG_DATA(cmsg), sizeof(LocalInterfaceAddr)); - la_found = true; - break; + int rv = lpWSARecvMsg(UdpSocket, &msgh, &iNumBytesRead, NULL, NULL); + if (rv != 0) + { + int err; + char msgbuf [256]; // for a message up to 255 bytes. + msgbuf [0] = '\0'; // Microsoft doesn't guarantee this on man page. + err = WSAGetLastError (); + FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // flags + NULL, // lpsource + err, // message id + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), // languageid + msgbuf, // output buffer + sizeof (msgbuf), // size of msgbuf, bytes + NULL); // va_list of arguments + return; + } + else + { + /* Receive auxiliary data in msgh */ + //fprintf(stderr,"hallo"); + for (cmsg = WSA_CMSG_FIRSTHDR(&msgh); cmsg != NULL; + cmsg = WSA_CMSG_NXTHDR(&msgh, cmsg)) { + //fprintf(stderr,"d=%d ",cmsg->cmsg_level); + if (cmsg->cmsg_level == IPPROTO_IPV6 + && cmsg->cmsg_type == IPV6_PKTINFO) { + memcpy(&LocalInterfaceAddr, WSA_CMSG_DATA(cmsg), sizeof(LocalInterfaceAddr)); + la_found = true; + break; + } } + } - } - } - - // check if an error occurred or no data could be read + + // check if an error occurred or no data could be read #else - struct cmsghdr *cmsg; - // Prepare message header structure - memset( &msgh, 0, sizeof(msgh)); - msgh.msg_name = &SenderAddr; - msgh.msg_namelen = SenderAddrSize; - msgh.msg_iovlen = 1; - msgh.msg_iov = iov; - iov[0].iov_base = (char *) &vecbyRecBuf[0]; - iov[0].iov_len = MAX_SIZE_BYTES_NETW_BUF; - char controlbuffer[4096]; - memset( controlbuffer, 0, sizeof(controlbuffer)); - msgh.msg_control = controlbuffer; - msgh.msg_controllen = sizeof(controlbuffer); - const long iNumBytesRead = ::recvmsg(UdpSocket, &msgh, 0); - - /* Receive auxiliary data in msgh */ - for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; - cmsg = CMSG_NXTHDR(&msgh, cmsg)) { - if (cmsg->cmsg_level == IPPROTO_IPV6 - && cmsg->cmsg_type == IPV6_PKTINFO) { - memcpy(&LocalInterfaceAddr, CMSG_DATA(cmsg), sizeof(LocalInterfaceAddr)); - la_found = true; - break; + struct cmsghdr *cmsg; + // Prepare message header structure + memset( &msgh, 0, sizeof(msgh)); + msgh.msg_name = &SenderAddr; + msgh.msg_namelen = SenderAddrSize; + msgh.msg_iovlen = 1; + msgh.msg_iov = iov; + iov[0].iov_base = (char *) &vecbyRecBuf[0]; + iov[0].iov_len = MAX_SIZE_BYTES_NETW_BUF; + char controlbuffer[4096]; + memset( controlbuffer, 0, sizeof(controlbuffer)); + msgh.msg_control = controlbuffer; + msgh.msg_controllen = sizeof(controlbuffer); + iNumBytesRead = ::recvmsg(UdpSocket, &msgh, 0); + + /* Receive auxiliary data in msgh */ + for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msgh, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_IPV6 + && cmsg->cmsg_type == IPV6_PKTINFO) { + memcpy(&LocalInterfaceAddr, CMSG_DATA(cmsg), sizeof(LocalInterfaceAddr)); + la_found = true; + break; + } } - } #endif - if ( iNumBytesRead <= 0 ) - { - return; - } - // convert address of client - Q_IPV6ADDR saddr6_tmp; - Q_IPV6ADDR iaddr6_tmp; - bool isv4 = false; - quint32 i4a; - memcpy(&saddr6_tmp,&SenderAddr.sin6_addr,16); - if (la_found) memcpy(&iaddr6_tmp,&LocalInterfaceAddr,16); - // Behandle ipv6-mapped ipv4-Adressen - RecHostAddr.InetAddr.setAddress(saddr6_tmp); - i4a = RecHostAddr.InetAddr.toIPv4Address(&isv4); - if (isv4) + if ( iNumBytesRead <= 0 ) + { + return; + } + // convert address of client + Q_IPV6ADDR saddr6_tmp; + Q_IPV6ADDR iaddr6_tmp; + bool isv4 = false; + quint32 i4a; + memcpy(&saddr6_tmp,&SenderAddr.sin6_addr,16); + if (la_found) memcpy(&iaddr6_tmp,&LocalInterfaceAddr,16); + // Behandle ipv6-mapped ipv4-Adressen + RecHostAddr.InetAddr.setAddress(saddr6_tmp); + i4a = RecHostAddr.InetAddr.toIPv4Address(&isv4); + if (isv4) + { + RecHostAddr.InetAddr.setAddress(i4a); + } + isv4 = false; + if (la_found) RecHostAddr.LocalAddr.setAddress(iaddr6_tmp); + RecHostAddr.iPort = ntohs ( SenderAddr.sin6_port ); + } // if sockmode 6 + else { - RecHostAddr.InetAddr.setAddress(i4a); - } - isv4 = false; - if (la_found) RecHostAddr.LocalAddr.setAddress(iaddr6_tmp); - RecHostAddr.iPort = ntohs ( SenderAddr.sin6_port ); + sockaddr_in SenderAddr; +#ifdef _WIN32 + int SenderAddrSize = sizeof ( sockaddr_in ); +#else + socklen_t SenderAddrSize = sizeof ( sockaddr_in ); +#endif + + iNumBytesRead = recvfrom ( UdpSocket, + (char*) &vecbyRecBuf[0], + MAX_SIZE_BYTES_NETW_BUF, + 0, + (sockaddr*) &SenderAddr, + &SenderAddrSize ); + + // check if an error occurred or no data could be read + if ( iNumBytesRead <= 0 ) + { + return; + } + + // convert address of client + RecHostAddr.InetAddr.setAddress ( ntohl ( SenderAddr.sin_addr.s_addr ) ); + RecHostAddr.iPort = ntohs ( SenderAddr.sin_port ); + } // else sockaddr == 6 // check if this is a protocol message int iRecCounter; diff --git a/src/socket.h b/src/socket.h index 56b930da54..04fa96b3e1 100755 --- a/src/socket.h +++ b/src/socket.h @@ -101,6 +101,7 @@ class CSocket : public QObject bool bIsClient; bool bJitterBufferOK; + char sockmode = 6; #ifdef _WIN32 LPFN_WSARECVMSG lpWSARecvMsg = NULL; LPFN_WSASENDMSG lpWSASendMsg = NULL; From 45a3d93df3239cd634d137f083ed18a8455359fb Mon Sep 17 00:00:00 2001 From: mstolle629 <81071220+mstolle629@users.noreply.github.com> Date: Wed, 7 Apr 2021 05:23:40 +0200 Subject: [PATCH 6/7] added Define -D__APPLE_USE_RFC_3542 for elder Mac compilers and removed one fprintf debug message --- src/socket.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/socket.cpp b/src/socket.cpp index 1336573969..05f6d75624 100755 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -300,7 +300,6 @@ void CSocket::SendPacket ( const CVector& vecbySendBuf, #ifndef _WIN32 msghdr m; struct iovec iov[1]; - int rv; char pktinfo[100]; m.msg_name = (sockaddr*) &UdpSocketOutAddr; m.msg_namelen = sizeof ( sockaddr_in6 ); @@ -352,8 +351,7 @@ void CSocket::SendPacket ( const CVector& vecbySendBuf, memcpy(CMSG_DATA(cmsg),&HostAddr.LocalAddr,16); m.msg_controllen += CMSG_SPACE(16); } - rv=sendmsg(UdpSocket,&m,0); - if (rv<0) fprintf(stderr, "%s\n", strerror(errno)); + sendmsg(UdpSocket,&m,0); #else WSAMSG m; WSABUF iov[1]; From bcfd5d0b197dd3227cae76c1ea1152919d6f8623 Mon Sep 17 00:00:00 2001 From: mstolle629 <81071220+mstolle629@users.noreply.github.com> Date: Wed, 7 Apr 2021 05:43:37 +0200 Subject: [PATCH 7/7] added __APPLE_USE_RFC_3542 in socket.h for compatibility with elder mac --- src/socket.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/socket.h b/src/socket.h index 04fa96b3e1..e8c68cf1de 100755 --- a/src/socket.h +++ b/src/socket.h @@ -23,6 +23,9 @@ \******************************************************************************/ #pragma once +#ifndef __APLLE_USE_RFC_3542 +#define __APPLE_USE_RFC_3542 +#endif #include #include