From bdd65af1a43c2ff90c3e9ae844f286f282a838ac Mon Sep 17 00:00:00 2001 From: Rick Dicaire Date: Sat, 7 Feb 2026 13:55:31 -0500 Subject: [PATCH 1/8] Correct data size for IPV6_TCLASS --- src/socket.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/socket.cpp b/src/socket.cpp index e2de7313e6..22d01cc6a6 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -118,8 +118,8 @@ void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber, setsockopt ( UdpSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*) &no, sizeof ( no ) ); // set the QoS - const char tos = (char) iQosNumber; // Quality of Service - setsockopt ( UdpSocket, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof ( tos ) ); + const int tos = (int) iQosNumber; // Quality of Service + setsockopt ( UdpSocket, IPPROTO_IPV6, IPV6_TCLASS, (const char*) &tos, sizeof ( tos ) ); UdpSocketAddr.sa6.sin6_family = AF_INET6; UdpSocketAddr.sa6.sin6_addr = in6addr_any; From e39af2d0a2b078a67102ea947b8191d0a7922d21 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sun, 8 Feb 2026 22:02:19 +0000 Subject: [PATCH 2/8] Add error checking for setsockopt() --- src/socket.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/socket.cpp b/src/socket.cpp index 22d01cc6a6..aa452fffbd 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -115,11 +115,17 @@ void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber, // The IPV6_V6ONLY socket option must be false in order for the socket to listen on both protocols. // On Linux it's false by default on most (all?) distros, but on Windows it is true by default const uint8_t no = 0; - setsockopt ( UdpSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*) &no, sizeof ( no ) ); + if ( setsockopt ( UdpSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*) &no, sizeof ( no ) ) == -1 ) + { + throw CGenErr ( "setsockopt for IPV6_V6ONLY failed", "Network Error" ); + } // set the QoS const int tos = (int) iQosNumber; // Quality of Service - setsockopt ( UdpSocket, IPPROTO_IPV6, IPV6_TCLASS, (const char*) &tos, sizeof ( tos ) ); + if ( setsockopt ( UdpSocket, IPPROTO_IPV6, IPV6_TCLASS, (const char*) &tos, sizeof ( tos ) ) == -1 ) + { + throw CGenErr ( "setsockopt for IPV6_TCLASS failed", "Network Error" ); + } UdpSocketAddr.sa6.sin6_family = AF_INET6; UdpSocketAddr.sa6.sin6_addr = in6addr_any; @@ -148,7 +154,10 @@ void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber, // set the QoS const char tos = (char) iQosNumber; // Quality of Service - setsockopt ( UdpSocket, IPPROTO_IP, IP_TOS, &tos, sizeof ( tos ) ); + if ( setsockopt ( UdpSocket, IPPROTO_IP, IP_TOS, &tos, sizeof ( tos ) ) == -1 ) + { + throw CGenErr ( "setsockopt for IP_TOS failed", "Network Error" ); + } // preinitialize socket in address (only the port number is missing) UdpSocketAddr.sa4.sin_family = AF_INET; From a64794138f54c8abca8baa0152ed2a704a0c9ee7 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sun, 8 Feb 2026 22:23:36 +0000 Subject: [PATCH 3/8] Correct data size for IPV6_V6ONLY --- src/socket.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/socket.cpp b/src/socket.cpp index aa452fffbd..81d252ffbe 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -114,7 +114,7 @@ void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber, // The IPV6_V6ONLY socket option must be false in order for the socket to listen on both protocols. // On Linux it's false by default on most (all?) distros, but on Windows it is true by default - const uint8_t no = 0; + const int no = 0; if ( setsockopt ( UdpSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*) &no, sizeof ( no ) ) == -1 ) { throw CGenErr ( "setsockopt for IPV6_V6ONLY failed", "Network Error" ); From 6de1d023b1bc62d8a6f418e596b243f598958db6 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sun, 8 Feb 2026 22:48:29 +0000 Subject: [PATCH 4/8] Also need setsockopt() for IPv4 on a dual-proto socket --- src/socket.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/socket.cpp b/src/socket.cpp index 81d252ffbe..02d1d5d8cb 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -127,6 +127,12 @@ void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber, throw CGenErr ( "setsockopt for IPV6_TCLASS failed", "Network Error" ); } + // set the QoS for IPv4 as well, as this is a dual-protocol socket + if ( setsockopt ( UdpSocket, IPPROTO_IP, IP_TOS, (const char*) &tos, sizeof ( tos ) ) == -1 ) + { + throw CGenErr ( "setsockopt for IP_TOS failed", "Network Error" ); + } + UdpSocketAddr.sa6.sin6_family = AF_INET6; UdpSocketAddr.sa6.sin6_addr = in6addr_any; UdpSocketAddrLen = sizeof ( UdpSocketAddr.sa6 ); @@ -153,8 +159,8 @@ void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber, } // set the QoS - const char tos = (char) iQosNumber; // Quality of Service - if ( setsockopt ( UdpSocket, IPPROTO_IP, IP_TOS, &tos, sizeof ( tos ) ) == -1 ) + const int tos = (int) iQosNumber; // Quality of Service + if ( setsockopt ( UdpSocket, IPPROTO_IP, IP_TOS, (const char*) &tos, sizeof ( tos ) ) == -1 ) { throw CGenErr ( "setsockopt for IP_TOS failed", "Network Error" ); } From b5a38db56b4064c5f38c43bffb55e8db0ebd800a Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 9 Feb 2026 18:06:08 +0000 Subject: [PATCH 5/8] Remove socket options that do not work on Windows and Apple --- src/socket.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/socket.cpp b/src/socket.cpp index 02d1d5d8cb..94323c390b 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -122,16 +122,20 @@ void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber, // set the QoS const int tos = (int) iQosNumber; // Quality of Service +#if !defined( Q_OS_WIN ) if ( setsockopt ( UdpSocket, IPPROTO_IPV6, IPV6_TCLASS, (const char*) &tos, sizeof ( tos ) ) == -1 ) { throw CGenErr ( "setsockopt for IPV6_TCLASS failed", "Network Error" ); } +#endif +#if !defined( Q_OS_DARWIN ) // set the QoS for IPv4 as well, as this is a dual-protocol socket if ( setsockopt ( UdpSocket, IPPROTO_IP, IP_TOS, (const char*) &tos, sizeof ( tos ) ) == -1 ) { throw CGenErr ( "setsockopt for IP_TOS failed", "Network Error" ); } +#endif UdpSocketAddr.sa6.sin6_family = AF_INET6; UdpSocketAddr.sa6.sin6_addr = in6addr_any; From 22a4293449f937238f97646e5c7b5b1180587bc6 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sat, 14 Feb 2026 15:04:42 +0000 Subject: [PATCH 6/8] Add macOS support for TOS on IPv4 over IPv6 --- src/socket.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/socket.cpp b/src/socket.cpp index 94323c390b..cd485f60a5 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -270,6 +270,50 @@ CSocket::~CSocket() #endif } +#if defined( Q_OS_DARWIN ) +// sendto_ipv4_with_tos - helper function for macOS to set TOS when sending IPv4 over IPv6 socket +static ssize_t sendto_ipv4_with_tos ( int fd, const void* buf, size_t len, int flags, const struct sockaddr* dest, socklen_t destlen, int tos ) +{ + // For a description of 'struct cmsghdr' and the 'CMSG_xxx' macros, see 'man 3 cmsg' on a Linux machine. + // The macOS man pages are less descriptive, but the API is the same, being based on the BSD socket interface. + + // The cmsg buffer is only set up once (tos doesn't change) so can be static + static union + { + unsigned char cbuf[CMSG_SPACE ( sizeof ( int ) )]; + struct cmsghdr h; + } u; + static socklen_t clen = 0; + + if ( clen == 0 ) + { + // set up the cmsg buffer + memset ( u.cbuf, 0, sizeof ( u.cbuf ) ); + + u.h.cmsg_level = IPPROTO_IP; + u.h.cmsg_type = IP_TOS; + u.h.cmsg_len = CMSG_LEN ( sizeof ( int ) ); + memcpy ( CMSG_DATA ( &u.h ), &tos, sizeof ( int ) ); + clen = (socklen_t) u.h.cmsg_len; + } + + struct iovec iov; + iov.iov_base = const_cast ( buf ); + iov.iov_len = len; + + struct msghdr msg; + + msg.msg_name = const_cast ( dest ); + msg.msg_namelen = destlen; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = (void*) u.cbuf; + msg.msg_controllen = clen; + + return sendmsg ( fd, &msg, flags ); +} +#endif + void CSocket::SendPacket ( const CVector& vecbySendBuf, const CHostAddress& HostAddr ) { int status = 0; @@ -309,12 +353,23 @@ void CSocket::SendPacket ( const CVector& vecbySendBuf, const CHostAddr addr[2] = htonl ( 0xFFFF ); addr[3] = htonl ( HostAddr.InetAddr.toIPv4Address() ); +#if defined( Q_OS_DARWIN ) + // In macOS we need to set TOS explicitly when sending IPv4 over IPv6 socket + status = sendto_ipv4_with_tos ( UdpSocket, + (const char*) &( (CVector) vecbySendBuf )[0], + iVecSizeOut, + 0, + &UdpSocketAddr.sa, + sizeof ( UdpSocketAddr.sa6 ), + (int) iQosNumber ); +#else status = sendto ( UdpSocket, (const char*) &( (CVector) vecbySendBuf )[0], iVecSizeOut, 0, &UdpSocketAddr.sa, sizeof ( UdpSocketAddr.sa6 ) ); +#endif } else { From b6ea05a60059065f5e7f9b17e3e50b9b0773bbef Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sun, 15 Feb 2026 14:34:53 +0000 Subject: [PATCH 7/8] Apply suggestions from code review Make error messages more descriptive Co-authored-by: Peter L Jones --- src/socket.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/socket.cpp b/src/socket.cpp index cd485f60a5..a83f22796f 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -117,7 +117,7 @@ void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber, const int no = 0; if ( setsockopt ( UdpSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*) &no, sizeof ( no ) ) == -1 ) { - throw CGenErr ( "setsockopt for IPV6_V6ONLY failed", "Network Error" ); + throw CGenErr ( "request to support IPv4 over IPv6 failed", "Network Error" ); } // set the QoS @@ -125,7 +125,7 @@ void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber, #if !defined( Q_OS_WIN ) if ( setsockopt ( UdpSocket, IPPROTO_IPV6, IPV6_TCLASS, (const char*) &tos, sizeof ( tos ) ) == -1 ) { - throw CGenErr ( "setsockopt for IPV6_TCLASS failed", "Network Error" ); + throw CGenErr ( "request to set ToS for IPv6 failed", "Network Error" ); } #endif @@ -133,7 +133,7 @@ void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber, // set the QoS for IPv4 as well, as this is a dual-protocol socket if ( setsockopt ( UdpSocket, IPPROTO_IP, IP_TOS, (const char*) &tos, sizeof ( tos ) ) == -1 ) { - throw CGenErr ( "setsockopt for IP_TOS failed", "Network Error" ); + throw CGenErr ( "request to set ToS for IPv4 over IPv6 failed", "Network Error" ); } #endif @@ -166,7 +166,7 @@ void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber, const int tos = (int) iQosNumber; // Quality of Service if ( setsockopt ( UdpSocket, IPPROTO_IP, IP_TOS, (const char*) &tos, sizeof ( tos ) ) == -1 ) { - throw CGenErr ( "setsockopt for IP_TOS failed", "Network Error" ); + throw CGenErr ( "request to set ToS for IPv4 failed", "Network Error" ); } // preinitialize socket in address (only the port number is missing) From f0ca488ca445951f006a1bb712cbb210450bde57 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sun, 15 Feb 2026 17:41:36 +0000 Subject: [PATCH 8/8] Clarify QoS is not available on Windows --- src/main.cpp | 4 ++++ src/socket.cpp | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 8e36f80e23..cebbcc8ccc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -222,8 +222,12 @@ int main ( int argc, char** argv ) // Quality of Service -------------------------------------------------- if ( GetNumericArgument ( argc, argv, i, "-Q", "--qos", 0, 255, rDbleArgument ) ) { +#if defined( Q_OS_WIN ) + qWarning() << "QoS is currently not available under Windows - ignoring"; +#else iQosNumber = static_cast ( rDbleArgument ); qInfo() << qUtf8Printable ( QString ( "- selected QoS value: %1" ).arg ( iQosNumber ) ); +#endif CommandLineOptions << "--qos"; continue; } diff --git a/src/socket.cpp b/src/socket.cpp index a83f22796f..f67f37c578 100644 --- a/src/socket.cpp +++ b/src/socket.cpp @@ -129,7 +129,7 @@ void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber, } #endif -#if !defined( Q_OS_DARWIN ) +#if !defined( Q_OS_DARWIN ) && !defined( Q_OS_WIN ) // set the QoS for IPv4 as well, as this is a dual-protocol socket if ( setsockopt ( UdpSocket, IPPROTO_IP, IP_TOS, (const char*) &tos, sizeof ( tos ) ) == -1 ) { @@ -162,12 +162,14 @@ void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber, throw CGenErr ( "IPv4 requested but not available on this system.", "Network Error" ); } +#if !defined( Q_OS_WIN ) // set the QoS const int tos = (int) iQosNumber; // Quality of Service if ( setsockopt ( UdpSocket, IPPROTO_IP, IP_TOS, (const char*) &tos, sizeof ( tos ) ) == -1 ) { throw CGenErr ( "request to set ToS for IPv4 failed", "Network Error" ); } +#endif // preinitialize socket in address (only the port number is missing) UdpSocketAddr.sa4.sin_family = AF_INET;