Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 80 additions & 6 deletions src/socket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,28 @@ 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 ) );
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" );
Copy link
Collaborator

@pljones pljones Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this say "to clear IPV6_V6ONLY" - otherwise people might get confused as to what went wrong.

Also, something "less technical", similar to the line 112 error -- maybe "request to clear block on IPv4 over IPv6 failed"?

}

// 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
#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" );
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
throw CGenErr ( "setsockopt for IPV6_TCLASS failed", "Network Error" );
throw CGenErr ( "request to set ToS for IPv6 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" );
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
throw CGenErr ( "setsockopt for IP_TOS failed", "Network Error" );
throw CGenErr ( "request to set ToS for IPv4 over IPv6 failed", "Network Error" );

}
#endif

UdpSocketAddr.sa6.sin6_family = AF_INET6;
UdpSocketAddr.sa6.sin6_addr = in6addr_any;
Expand Down Expand Up @@ -147,8 +163,11 @@ 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 ) );
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" );
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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)
UdpSocketAddr.sa4.sin_family = AF_INET;
Expand Down Expand Up @@ -251,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<void*> ( buf );
iov.iov_len = len;

struct msghdr msg;

msg.msg_name = const_cast<sockaddr*> ( 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<uint8_t>& vecbySendBuf, const CHostAddress& HostAddr )
{
int status = 0;
Expand Down Expand Up @@ -290,12 +353,23 @@ void CSocket::SendPacket ( const CVector<uint8_t>& 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<uint8_t>) vecbySendBuf )[0],
iVecSizeOut,
0,
&UdpSocketAddr.sa,
sizeof ( UdpSocketAddr.sa6 ),
(int) iQosNumber );
#else
status = sendto ( UdpSocket,
(const char*) &( (CVector<uint8_t>) vecbySendBuf )[0],
iVecSizeOut,
0,
&UdpSocketAddr.sa,
sizeof ( UdpSocketAddr.sa6 ) );
#endif
}
else
{
Expand Down