diff --git a/win32.c b/win32.c index dcc3922..ef76a09 100644 --- a/win32.c +++ b/win32.c @@ -8,6 +8,7 @@ #include "enet/enet.h" #include #include +#include #ifndef HAS_QOS_FLOWID typedef UINT32 QOS_FLOWID; #endif @@ -19,14 +20,6 @@ typedef UINT32 *PQOS_FLOWID; #define QOS_NON_ADAPTIVE_FLOW 0x00000002 #endif -// This is missing from MinGW headers -#ifndef IN6ADDR_V4MAPPEDPREFIX_INIT -#define IN6ADDR_V4MAPPEDPREFIX_INIT { \ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ - 0x00, 0x00, 0xff, 0xff, \ -} -#endif - static enet_uint32 timeBase = 0; #if !(defined(WINAPI_FAMILY) && WINAPI_FAMILY == WINAPI_FAMILY_APP) @@ -38,6 +31,7 @@ static enet_uint32 timeBase = 0; static HANDLE qosHandle = INVALID_HANDLE_VALUE; static QOS_FLOWID qosFlowId; static BOOL qosAddedFlow; +static BOOL enableEcn; static HMODULE QwaveLibraryHandle; @@ -93,6 +87,7 @@ enet_deinitialize (void) #ifdef HAS_QWAVE qosAddedFlow = FALSE; qosFlowId = 0; + enableEcn = FALSE; if (qosHandle != INVALID_HANDLE_VALUE) { @@ -330,6 +325,9 @@ enet_socket_set_option (ENetSocket socket, ENetSocketOption option, int value) case ENET_SOCKOPT_QOS: { + // Enable ECN marking on Windows 10 if QOS is enabled + enableEcn = value != 0 && IsWindows10OrGreater(); + #ifdef HAS_QWAVE if (value) { @@ -440,14 +438,14 @@ enet_socket_send (ENetSocket socket, DWORD sentLength; WSAMSG msg = { 0 }; char controlBufData[1024]; + PWSACMSGHDR chdr = NULL; + #ifdef HAS_QWAVE if (!qosAddedFlow && qosHandle != INVALID_HANDLE_VALUE) { - static const char V4_MAPPED_V6_PREFIX[] = IN6ADDR_V4MAPPEDPREFIX_INIT; - BOOL isV4MappedV6Addr = peerAddress->address.ss_family == AF_INET6 && - !memcmp(&((PSOCKADDR_IN6)&peerAddress->address)->sin6_addr, V4_MAPPED_V6_PREFIX, sizeof(V4_MAPPED_V6_PREFIX)); + IN6_IS_ADDR_V4MAPPED(&((PSOCKADDR_IN6)&peerAddress->address)->sin6_addr); // qWAVE doesn't properly support IPv4-mapped IPv6 addresses, nor does it // correctly support IPv4 addresses on a dual-stack socket (despite MSDN's @@ -478,8 +476,8 @@ enet_socket_send (ENetSocket socket, } #endif - msg.name = peerAddress != NULL ? (struct sockaddr *) & peerAddress -> address : NULL; - msg.namelen = peerAddress != NULL ? peerAddress -> addressLength : 0; + msg.name = (struct sockaddr *) & peerAddress -> address; + msg.namelen = peerAddress -> addressLength; msg.lpBuffers = (LPWSABUF) buffers; msg.dwBufferCount = (DWORD) bufferCount; @@ -494,9 +492,14 @@ enet_socket_send (ENetSocket socket, pktInfo.ipi_ifindex = 0; // Unspecified msg.Control.buf = controlBufData; - msg.Control.len = WSA_CMSG_SPACE(sizeof(pktInfo)); + msg.Control.len += WSA_CMSG_SPACE(sizeof(pktInfo)); + if (chdr == NULL) { + chdr = WSA_CMSG_FIRSTHDR(&msg); + } + else { + chdr = WSA_CMSG_NXTHDR(&msg, chdr); + } - PWSACMSGHDR chdr = WSA_CMSG_FIRSTHDR(&msg); chdr->cmsg_level = IPPROTO_IP; chdr->cmsg_type = IP_PKTINFO; chdr->cmsg_len = WSA_CMSG_LEN(sizeof(pktInfo)); @@ -509,9 +512,14 @@ enet_socket_send (ENetSocket socket, pktInfo.ipi6_ifindex = 0; // Unspecified msg.Control.buf = controlBufData; - msg.Control.len = WSA_CMSG_SPACE(sizeof(pktInfo)); + msg.Control.len += WSA_CMSG_SPACE(sizeof(pktInfo)); + if (chdr == NULL) { + chdr = WSA_CMSG_FIRSTHDR(&msg); + } + else { + chdr = WSA_CMSG_NXTHDR(&msg, chdr); + } - PWSACMSGHDR chdr = WSA_CMSG_FIRSTHDR(&msg); chdr->cmsg_level = IPPROTO_IPV6; chdr->cmsg_type = IPV6_PKTINFO; chdr->cmsg_len = WSA_CMSG_LEN(sizeof(pktInfo)); @@ -519,6 +527,35 @@ enet_socket_send (ENetSocket socket, } } + // This is a bit of a hack because it's not really per-socket or + // per-destination, but it is fine for our current usage of ENet + // in Moonlight and Sunshine where only a single socket is used. + if (enableEcn) { + BOOL isV4MappedV6Addr = + peerAddress->address.ss_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&((PSOCKADDR_IN6)&peerAddress->address)->sin6_addr); + + msg.Control.buf = controlBufData; + msg.Control.len += WSA_CMSG_SPACE(sizeof(INT)); + if (chdr == NULL) { + chdr = WSA_CMSG_FIRSTHDR(&msg); + } + else { + chdr = WSA_CMSG_NXTHDR(&msg, chdr); + } + + if (peerAddress->address.ss_family == AF_INET || isV4MappedV6Addr) { + chdr->cmsg_level = IPPROTO_IP; + chdr->cmsg_type = IP_ECN; + } + else { + chdr->cmsg_level = IPPROTO_IPV6; + chdr->cmsg_type = IPV6_ECN; + } + chdr->cmsg_len = WSA_CMSG_LEN(sizeof(INT)); + *(PINT)WSA_CMSG_DATA(chdr) = 0x01; // ECT(1) (L4S) + } + if (WSASendMsg (socket, & msg, 0,