@@ -114,12 +114,28 @@ void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber,
114114
115115 // The IPV6_V6ONLY socket option must be false in order for the socket to listen on both protocols.
116116 // On Linux it's false by default on most (all?) distros, but on Windows it is true by default
117- const uint8_t no = 0 ;
118- setsockopt ( UdpSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char *) &no, sizeof ( no ) );
117+ const int no = 0 ;
118+ if ( setsockopt ( UdpSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char *) &no, sizeof ( no ) ) == -1 )
119+ {
120+ throw CGenErr ( " request to support IPv4 over IPv6 failed" , " Network Error" );
121+ }
119122
120123 // set the QoS
121- const char tos = (char ) iQosNumber; // Quality of Service
122- setsockopt ( UdpSocket, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof ( tos ) );
124+ const int tos = (int ) iQosNumber; // Quality of Service
125+ #if !defined( Q_OS_WIN )
126+ if ( setsockopt ( UdpSocket, IPPROTO_IPV6, IPV6_TCLASS, (const char *) &tos, sizeof ( tos ) ) == -1 )
127+ {
128+ throw CGenErr ( " request to set ToS for IPv6 failed" , " Network Error" );
129+ }
130+ #endif
131+
132+ #if !defined( Q_OS_DARWIN ) && !defined( Q_OS_WIN )
133+ // set the QoS for IPv4 as well, as this is a dual-protocol socket
134+ if ( setsockopt ( UdpSocket, IPPROTO_IP, IP_TOS, (const char *) &tos, sizeof ( tos ) ) == -1 )
135+ {
136+ throw CGenErr ( " request to set ToS for IPv4 over IPv6 failed" , " Network Error" );
137+ }
138+ #endif
123139
124140 UdpSocketAddr.sa6 .sin6_family = AF_INET6;
125141 UdpSocketAddr.sa6 .sin6_addr = in6addr_any;
@@ -146,9 +162,14 @@ void CSocket::Init ( const quint16 iNewPortNumber, const quint16 iNewQosNumber,
146162 throw CGenErr ( " IPv4 requested but not available on this system." , " Network Error" );
147163 }
148164
165+ #if !defined( Q_OS_WIN )
149166 // set the QoS
150- const char tos = (char ) iQosNumber; // Quality of Service
151- setsockopt ( UdpSocket, IPPROTO_IP, IP_TOS, &tos, sizeof ( tos ) );
167+ const int tos = (int ) iQosNumber; // Quality of Service
168+ if ( setsockopt ( UdpSocket, IPPROTO_IP, IP_TOS, (const char *) &tos, sizeof ( tos ) ) == -1 )
169+ {
170+ throw CGenErr ( " request to set ToS for IPv4 failed" , " Network Error" );
171+ }
172+ #endif
152173
153174 // preinitialize socket in address (only the port number is missing)
154175 UdpSocketAddr.sa4 .sin_family = AF_INET;
@@ -251,6 +272,50 @@ CSocket::~CSocket()
251272#endif
252273}
253274
275+ #if defined( Q_OS_DARWIN )
276+ // sendto_ipv4_with_tos - helper function for macOS to set TOS when sending IPv4 over IPv6 socket
277+ 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 )
278+ {
279+ // For a description of 'struct cmsghdr' and the 'CMSG_xxx' macros, see 'man 3 cmsg' on a Linux machine.
280+ // The macOS man pages are less descriptive, but the API is the same, being based on the BSD socket interface.
281+
282+ // The cmsg buffer is only set up once (tos doesn't change) so can be static
283+ static union
284+ {
285+ unsigned char cbuf[CMSG_SPACE ( sizeof ( int ) )];
286+ struct cmsghdr h;
287+ } u;
288+ static socklen_t clen = 0 ;
289+
290+ if ( clen == 0 )
291+ {
292+ // set up the cmsg buffer
293+ memset ( u.cbuf , 0 , sizeof ( u.cbuf ) );
294+
295+ u.h .cmsg_level = IPPROTO_IP;
296+ u.h .cmsg_type = IP_TOS;
297+ u.h .cmsg_len = CMSG_LEN ( sizeof ( int ) );
298+ memcpy ( CMSG_DATA ( &u.h ), &tos, sizeof ( int ) );
299+ clen = (socklen_t ) u.h .cmsg_len ;
300+ }
301+
302+ struct iovec iov;
303+ iov.iov_base = const_cast <void *> ( buf );
304+ iov.iov_len = len;
305+
306+ struct msghdr msg;
307+
308+ msg.msg_name = const_cast <sockaddr*> ( dest );
309+ msg.msg_namelen = destlen;
310+ msg.msg_iov = &iov;
311+ msg.msg_iovlen = 1 ;
312+ msg.msg_control = (void *) u.cbuf ;
313+ msg.msg_controllen = clen;
314+
315+ return sendmsg ( fd, &msg, flags );
316+ }
317+ #endif
318+
254319void CSocket::SendPacket ( const CVector<uint8_t >& vecbySendBuf, const CHostAddress& HostAddr )
255320{
256321 int status = 0 ;
@@ -290,12 +355,23 @@ void CSocket::SendPacket ( const CVector<uint8_t>& vecbySendBuf, const CHostAddr
290355 addr[2 ] = htonl ( 0xFFFF );
291356 addr[3 ] = htonl ( HostAddr.InetAddr .toIPv4Address () );
292357
358+ #if defined( Q_OS_DARWIN )
359+ // In macOS we need to set TOS explicitly when sending IPv4 over IPv6 socket
360+ status = sendto_ipv4_with_tos ( UdpSocket,
361+ (const char *) &( (CVector<uint8_t >) vecbySendBuf )[0 ],
362+ iVecSizeOut,
363+ 0 ,
364+ &UdpSocketAddr.sa ,
365+ sizeof ( UdpSocketAddr.sa6 ),
366+ (int ) iQosNumber );
367+ #else
293368 status = sendto ( UdpSocket,
294369 (const char *) &( (CVector<uint8_t >) vecbySendBuf )[0 ],
295370 iVecSizeOut,
296371 0 ,
297372 &UdpSocketAddr.sa ,
298373 sizeof ( UdpSocketAddr.sa6 ) );
374+ #endif
299375 }
300376 else
301377 {
0 commit comments