/* * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "udp_socket_windows.h" // Disable deprication warning from traffic.h #pragma warning(disable : 4995) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #include #include #include // Don't change include order for these header files. #include #include #include #include "traffic_control_windows.h" #include "udp_socket_manager_wrapper.h" namespace webrtc { typedef struct _QOS_DESTADDR { QOS_OBJECT_HDR ObjectHdr; const struct sockaddr* SocketAddress; ULONG SocketAddressLength; } QOS_DESTADDR, *LPQOS_DESTADDR; typedef const QOS_DESTADDR* LPCQOS_DESTADDR; #define QOS_GENERAL_ID_BASE 2000 #define QOS_OBJECT_DESTADDR (0x00000004 + QOS_GENERAL_ID_BASE) #define MAX_PACKET_SIZE 2048 class UDPPacket { public: UDPPacket() { _length = 0; } WebRtc_Word32 Set(const WebRtc_Word8* buf, WebRtc_Word32 length) { if(length > MAX_PACKET_SIZE) return 0; _length = length; memcpy(_buffer,buf,length); return length; } WebRtc_Word32 Set(const WebRtc_Word8* buf, WebRtc_Word32 length, const SocketAddress* addr) { if(length > MAX_PACKET_SIZE) { return 0; } _length = length; memcpy(&_remoteAddr,addr,sizeof(SocketAddress)); memcpy(_buffer,buf,length); return length; } SocketAddress _remoteAddr; WebRtc_Word8 _buffer[MAX_PACKET_SIZE]; WebRtc_Word32 _length; }; UdpSocketWindows::UdpSocketWindows(const WebRtc_Word32 id, UdpSocketManager* mgr, bool ipV6Enable) : _id(id), _qos(true) { _wantsIncoming = false; _error = 0; _mgr = mgr; _addedToMgr = false; _obj = NULL; _incomingCb = NULL; _socket = INVALID_SOCKET; _terminate=false; _clientHandle = INVALID_HANDLE_VALUE; _flowHandle = INVALID_HANDLE_VALUE; _filterHandle = INVALID_HANDLE_VALUE; WEBRTC_TRACE(kTraceMemory, kTraceTransport, _id, "UdpSocketWindows::UdpSocketWindows()"); _gtc = NULL; // Check if QoS is supported. WSAPROTOCOL_INFO pProtocolInfo; DWORD dwBufLen = 0; BOOL bProtocolFound = FALSE; WSAPROTOCOL_INFO* lpProtocolBuf = NULL; // Set dwBufLen to the size needed to retreive all the requested information // from WSAEnumProtocols. WebRtc_Word32 nRet = WSAEnumProtocols(NULL, lpProtocolBuf, &dwBufLen); lpProtocolBuf = (WSAPROTOCOL_INFO*)malloc(dwBufLen); nRet = WSAEnumProtocols(NULL, lpProtocolBuf, &dwBufLen); WebRtc_Word32 iProtocol; if (ipV6Enable) { iProtocol = AF_INET6; } else { iProtocol = AF_INET; } for (WebRtc_Word32 i = 0; i < nRet; i++) { if (iProtocol == lpProtocolBuf[i].iAddressFamily && IPPROTO_UDP == lpProtocolBuf[i].iProtocol) { if ((XP1_QOS_SUPPORTED == (XP1_QOS_SUPPORTED & lpProtocolBuf[i].dwServiceFlags1))) { pProtocolInfo = lpProtocolBuf[i]; bProtocolFound = TRUE; break; } } } if(!bProtocolFound) { _socket = INVALID_SOCKET; _qos = false; free(lpProtocolBuf); _error = SOCKET_ERROR_NO_QOS; }else { _socket = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,&pProtocolInfo, 0, WSA_FLAG_OVERLAPPED); free(lpProtocolBuf); if (_socket != INVALID_SOCKET) { return; }else { _qos = false; _error = SOCKET_ERROR_NO_QOS; } } // QoS not supported. if(ipV6Enable) { _socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); }else { _socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); } // Non-blocking mode. WebRtc_Word32 iMode = 1; ioctlsocket(_socket, FIONBIO, (u_long FAR*) &iMode); } UdpSocketWindows::~UdpSocketWindows() { WEBRTC_TRACE(kTraceMemory, kTraceTransport, _id, "UdpSocketWindows::~UdpSocketWindows()"); if (_gtc) { TrafficControlWindows::Release(_gtc); } } WebRtc_Word32 UdpSocketWindows::ChangeUniqueId(const WebRtc_Word32 id) { _id = id; if (_gtc) { _gtc->ChangeUniqueId(id); } return 0; } bool UdpSocketWindows::ValidHandle() { return GetFd() != INVALID_SOCKET; } bool UdpSocketWindows::SetCallback(CallbackObj obj, IncomingSocketCallback cb) { _obj = obj; _incomingCb = cb; if (_mgr->AddSocket(this)) { _addedToMgr = true; return true; } return false; } bool UdpSocketWindows::SetSockopt(WebRtc_Word32 level, WebRtc_Word32 optname, const WebRtc_Word8* optval, WebRtc_Word32 optlen) { if(0 == setsockopt(_socket, level, optname, optval, optlen)) { return true; } _error = WSAGetLastError(); return false; } bool UdpSocketWindows::Bind(const SocketAddress& name) { const struct sockaddr* socketName = reinterpret_cast(&name); if (0 == bind(_socket, socketName, sizeof(SocketAddress))) { _localAddr = name; return true; } _error = WSAGetLastError(); return false; } WebRtc_Word32 UdpSocketWindows::SendTo(const WebRtc_Word8* buf, WebRtc_Word32 len, const SocketAddress& to) { // Don't try to send this packet if there are older packets queued up. if(!_notSentPackets.Empty()) { UDPPacket* packet = new UDPPacket(); packet->Set(buf, len, &to); if(!_notSentPackets.Empty()) { _notSentPackets.PushBack(packet); return len; }else { // No old packets queued up. Free to try to send. delete packet; } } WebRtc_Word32 retVal; retVal = sendto(_socket, buf, len, 0, reinterpret_cast(&to), sizeof(SocketAddress)); if(retVal == SOCKET_ERROR) { _error = WSAGetLastError(); if (_error == WSAEWOULDBLOCK) { UDPPacket* packet = new UDPPacket(); packet->Set(buf,len, &to); _notSentPackets.PushBack(packet); return len; } } return retVal; } void UdpSocketWindows::HasIncoming() { WebRtc_Word8 buf[MAX_PACKET_SIZE]; SocketAddress from; int fromlen = sizeof(from); WebRtc_Word32 retval = recvfrom(_socket,buf, sizeof(buf), 0, reinterpret_cast(&from), &fromlen); switch(retval) { case 0: // The connection has been gracefully closed. break; case SOCKET_ERROR: _error = WSAGetLastError(); break; default: if(_wantsIncoming && _incomingCb) _incomingCb(_obj,buf, retval, &from); break; } } void UdpSocketWindows::CleanUp() { WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, "UdpSocketWindows::CleanUp()"); _wantsIncoming = false; if(_clientHandle != INVALID_HANDLE_VALUE) { assert(_filterHandle != INVALID_HANDLE_VALUE); assert(_flowHandle != INVALID_HANDLE_VALUE); if (_gtc) { _gtc->TcDeleteFilter(_filterHandle); _gtc->TcDeleteFlow(_flowHandle); _gtc->TcDeregisterClient(_clientHandle); } _clientHandle = INVALID_HANDLE_VALUE; _filterHandle = INVALID_HANDLE_VALUE; _flowHandle = INVALID_HANDLE_VALUE; } while(!_notSentPackets.Empty()) { UDPPacket* packet = (UDPPacket*)_notSentPackets.First()->GetItem(); if(!packet) { break; } delete packet; _notSentPackets.PopFront(); } if (_socket != INVALID_SOCKET) { if (closesocket(_socket) == SOCKET_ERROR) { WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "closesocket() => error = %d", WSAGetLastError()); } WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, "WinSock::closesocket() done"); if(_addedToMgr) { WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, "calling UdpSocketManager::RemoveSocket()"); _mgr->RemoveSocket(this); } } } void UdpSocketWindows::SetWritable() { // Try to send packets that have been queued up. while(!_notSentPackets.Empty()) { UDPPacket* packet = (UDPPacket*)_notSentPackets.First()->GetItem(); if(!packet) { break; } if(sendto( _socket,packet->_buffer, packet->_length, 0, reinterpret_cast( &(packet->_remoteAddr)), sizeof(SocketAddress)) == SOCKET_ERROR) { _error = WSAGetLastError(); if (_error == WSAEWOULDBLOCK) { return; } } else { delete packet; _notSentPackets.PopFront(); } } } bool UdpSocketWindows::SetQos(WebRtc_Word32 serviceType, WebRtc_Word32 tokenRate, WebRtc_Word32 bucketSize, WebRtc_Word32 peekBandwith, WebRtc_Word32 minPolicedSize, WebRtc_Word32 maxSduSize, const SocketAddress &stRemName, WebRtc_Word32 overrideDSCP) { if(_qos == false) { WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "UdpSocket2Windows::SetQos(), socket not capable of QOS"); return false; } QOS Qos; WebRtc_Word32 result; DWORD BytesRet; if(overrideDSCP != 0) { FLOWSPEC f; WebRtc_Word32 err = CreateFlowSpec(serviceType, tokenRate, bucketSize, peekBandwith, minPolicedSize, maxSduSize, &f); if(err == -1) { return false; } return SetTOSByte(overrideDSCP, &f, &f) == 0; } memset(&Qos, QOS_NOT_SPECIFIED, sizeof(QOS)); Qos.SendingFlowspec.ServiceType = serviceType; Qos.SendingFlowspec.TokenRate = tokenRate; Qos.SendingFlowspec.TokenBucketSize = bucketSize; Qos.SendingFlowspec.PeakBandwidth = peekBandwith; Qos.SendingFlowspec.DelayVariation = QOS_NOT_SPECIFIED; Qos.SendingFlowspec.Latency = QOS_NOT_SPECIFIED; Qos.SendingFlowspec.MinimumPolicedSize = minPolicedSize; Qos.SendingFlowspec.MaxSduSize = maxSduSize; // Only ServiceType is needed for receiving. Qos.ReceivingFlowspec.ServiceType = serviceType; Qos.ReceivingFlowspec.TokenRate = QOS_NOT_SPECIFIED; Qos.ReceivingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED; Qos.ReceivingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED; Qos.ReceivingFlowspec.Latency = QOS_NOT_SPECIFIED; Qos.ReceivingFlowspec.DelayVariation = QOS_NOT_SPECIFIED; Qos.ReceivingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED; Qos.ReceivingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED; Qos.ProviderSpecific.len = 0; Qos.ProviderSpecific.buf = NULL; WebRtc_Word8* p = (WebRtc_Word8*)malloc(sizeof(QOS_DESTADDR) + sizeof(QOS_DS_CLASS)); QOS_DESTADDR* QosDestaddr = (QOS_DESTADDR*)p; ZeroMemory((WebRtc_Word8 *)QosDestaddr, sizeof(QOS_DESTADDR)); QosDestaddr->ObjectHdr.ObjectType = QOS_OBJECT_DESTADDR; QosDestaddr->ObjectHdr.ObjectLength = sizeof(QOS_DESTADDR); QosDestaddr->SocketAddress = (SOCKADDR*)&stRemName; QosDestaddr->SocketAddressLength = sizeof(SocketAddress); Qos.ProviderSpecific.len = QosDestaddr->ObjectHdr.ObjectLength; Qos.ProviderSpecific.buf = (WebRtc_Word8*)p; // Socket must be bound for this call to be successfull. If socket is not // bound WSAGetLastError() will return 10022. result = WSAIoctl(GetFd(),SIO_SET_QOS, &Qos,sizeof(QOS),NULL, 0, &BytesRet, NULL,NULL); if (result == SOCKET_ERROR) { _error = WSAGetLastError(); free(p); return false; } free(p); return true; } WebRtc_Word32 UdpSocketWindows::SetTOS(WebRtc_Word32 serviceType) { WebRtc_Word32 res = SetTOSByte(serviceType, NULL, NULL); if (res == -1) { OSVERSIONINFO OsVersion; OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx (&OsVersion); if ((OsVersion.dwMajorVersion == 4)) { return -1; } } return res; } WebRtc_Word32 UdpSocketWindows::CreateFlowSpec(WebRtc_Word32 serviceType, WebRtc_Word32 tokenRate, WebRtc_Word32 bucketSize, WebRtc_Word32 peekBandwith, WebRtc_Word32 minPolicedSize, WebRtc_Word32 maxSduSize, FLOWSPEC *f) { if(!f) { return -1; } f->ServiceType = serviceType; f->TokenRate = tokenRate; f->TokenBucketSize = bucketSize; f->PeakBandwidth = peekBandwith; f->DelayVariation = QOS_NOT_SPECIFIED; f->Latency = QOS_NOT_SPECIFIED; f->MinimumPolicedSize = minPolicedSize; f->MaxSduSize = maxSduSize; return 0; } WebRtc_Word32 UdpSocketWindows::SetTOSByte(WebRtc_Word32 serviceType, FLOWSPEC* send, FLOWSPEC* recv) { if(_socket == INVALID_SOCKET) { return -1; } if (!_gtc) { _gtc = TrafficControlWindows::GetInstance(_id); } if (!_gtc) { return -1; } TCI_CLIENT_FUNC_LIST QoSFunctions; QoSFunctions.ClAddFlowCompleteHandler = NULL; QoSFunctions.ClDeleteFlowCompleteHandler = NULL; QoSFunctions.ClModifyFlowCompleteHandler = NULL; QoSFunctions.ClNotifyHandler = (TCI_NOTIFY_HANDLER)MyClNotifyHandler; // Register the client with Traffic control interface. HANDLE ClientHandle; ULONG result = _gtc->TcRegisterClient(CURRENT_TCI_VERSION, NULL, &QoSFunctions,&ClientHandle); if(result != NO_ERROR) { WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "TcRegisterClient returned %d", result); return result; } // Find traffic control-enabled network interfaces. ULONG BufferSize = 0; result = _gtc->TcEnumerateInterfaces(ClientHandle, &BufferSize, NULL); if(result != NO_ERROR && result != ERROR_INSUFFICIENT_BUFFER) { WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "Error enumerating interfaces, %d", result); _gtc->TcDeregisterClient(ClientHandle); return result; } if(result != ERROR_INSUFFICIENT_BUFFER) { // Empty buffer contains all control-enabled network interfaces. I.e. // ToS is not enabled. WEBRTC_TRACE( kTraceError, kTraceTransport, _id, "Error enumerating interfaces: passed in 0 and received\ NO_ERROR when expecting INSUFFICIENT_BUFFER, %d"); _gtc->TcDeregisterClient(ClientHandle); return -1; } PTC_IFC_DESCRIPTOR pInterfaceBuffer = (PTC_IFC_DESCRIPTOR)malloc(BufferSize); if(pInterfaceBuffer == NULL) { WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "Out ot memory failure"); _gtc->TcDeregisterClient(ClientHandle); return ERROR_NOT_ENOUGH_MEMORY; } result = _gtc->TcEnumerateInterfaces(ClientHandle, &BufferSize, pInterfaceBuffer); if(result != NO_ERROR) { WEBRTC_TRACE( kTraceError, kTraceTransport, _id, "Critical: error enumerating interfaces when passing in correct\ buffer size: %d", result); _gtc->TcDeregisterClient(ClientHandle); free(pInterfaceBuffer); return result; } PTC_IFC_DESCRIPTOR oneinterface; HANDLE ifcHandle, iFilterHandle, iflowHandle; bool addrFound = false; ULONG filterSourceAddress = ULONG_MAX; const struct sockaddr_in* name; name = reinterpret_cast(&_localAddr); // Find the interface corresponding to the local address. for(oneinterface = pInterfaceBuffer; oneinterface != (PTC_IFC_DESCRIPTOR) (((WebRtc_Word8*)pInterfaceBuffer) + BufferSize); oneinterface = (PTC_IFC_DESCRIPTOR) ((WebRtc_Word8*)oneinterface + oneinterface->Length)) { WebRtc_Word8 interfaceName[500]; WideCharToMultiByte(CP_ACP, 0, oneinterface->pInterfaceName, -1, interfaceName, sizeof(interfaceName), 0, 0); PNETWORK_ADDRESS_LIST addresses = &(oneinterface->AddressListDesc.AddressList); for(LONG i = 0; i < addresses->AddressCount; i++) { // Only look at TCP/IP addresses. if(addresses->Address[i].AddressType != NDIS_PROTOCOL_ID_TCP_IP) { continue; } NETWORK_ADDRESS_IP* pIpAddr = (NETWORK_ADDRESS_IP*)&(addresses->Address[i].Address); WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, "Examining Interface %s", interfaceName); if(pIpAddr->in_addr == name->sin_addr.S_un.S_addr) { filterSourceAddress = pIpAddr->in_addr; WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, "Found ip addr: %s", inet_ntoa(name->sin_addr)); addrFound = true; } } if(!addrFound) { continue; } else { break; } } if(!addrFound) { WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "IP Address not found"); _gtc->TcDeregisterClient(ClientHandle); free(pInterfaceBuffer); return -1; } result = _gtc->TcOpenInterfaceW(oneinterface->pInterfaceName, ClientHandle, NULL, &ifcHandle); if(result != NO_ERROR) { WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "Error opening interface: %d", result); _gtc->TcDeregisterClient(ClientHandle); free(pInterfaceBuffer); return result; } FLOWSPEC defaultSend, defaultRecv; if(send == NULL) { defaultSend.DelayVariation = QOS_NOT_SPECIFIED; defaultSend.Latency = QOS_NOT_SPECIFIED; defaultSend.MaxSduSize = QOS_NOT_SPECIFIED; defaultSend.MinimumPolicedSize = QOS_NOT_SPECIFIED; defaultSend.PeakBandwidth = QOS_NOT_SPECIFIED; defaultSend.ServiceType = SERVICETYPE_BESTEFFORT; defaultSend.TokenBucketSize = QOS_NOT_SPECIFIED; defaultSend.TokenRate = 10000; } else { defaultSend = *send; } if(recv == NULL) { defaultRecv = defaultSend; defaultRecv.ServiceType = SERVICETYPE_CONTROLLEDLOAD; } else { defaultRecv = *recv; } PTC_GEN_FLOW flow = (PTC_GEN_FLOW)malloc(sizeof(TC_GEN_FLOW) + sizeof(QOS_DS_CLASS)); flow->ReceivingFlowspec = defaultRecv; flow->SendingFlowspec = defaultSend; QOS_DS_CLASS dsClass; ZeroMemory((WebRtc_Word8*)&dsClass, sizeof(QOS_DS_CLASS)); dsClass.DSField = serviceType; dsClass.ObjectHdr.ObjectType = QOS_OBJECT_DS_CLASS; dsClass.ObjectHdr.ObjectLength = sizeof(dsClass); memcpy(flow->TcObjects, (void*)&dsClass, sizeof(QOS_DS_CLASS)); flow->TcObjectsLength = sizeof(dsClass); result = _gtc->TcAddFlow(ifcHandle, NULL, 0, flow, &iflowHandle); if(result != NO_ERROR) { WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "Error adding flow: %d", result); _gtc->TcCloseInterface(ifcHandle); _gtc->TcDeregisterClient(ClientHandle); free(pInterfaceBuffer); return -1; } free(flow); IP_PATTERN filterPattern, mask; ZeroMemory((WebRtc_Word8*)&filterPattern, sizeof(IP_PATTERN)); ZeroMemory((WebRtc_Word8*)&mask, sizeof(IP_PATTERN)); filterPattern.ProtocolId = IPPROTO_UDP; // "name" fields are in network order. filterPattern.S_un.S_un_ports.s_srcport = name->sin_port; filterPattern.SrcAddr = filterSourceAddress; // Unsigned max of a type corresponds to a bitmask with all bits set to 1. // I.e. the filter should allow all ProtocolIds, any source port and any // IP address. mask.ProtocolId = UCHAR_MAX; mask.S_un.S_un_ports.s_srcport = USHRT_MAX; mask.SrcAddr = ULONG_MAX; TC_GEN_FILTER filter; filter.AddressType = NDIS_PROTOCOL_ID_TCP_IP; filter.Mask = (LPVOID)&mask; filter.Pattern = (LPVOID)&filterPattern; filter.PatternSize = sizeof(IP_PATTERN); if(_filterHandle != INVALID_HANDLE_VALUE) { _gtc->TcDeleteFilter(_filterHandle); } result = _gtc->TcAddFilter(iflowHandle, &filter, &iFilterHandle); if(result != NO_ERROR) { WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "Error adding filter: %d", result); _gtc->TcDeleteFlow(iflowHandle); _gtc->TcCloseInterface(ifcHandle); _gtc->TcDeregisterClient(ClientHandle); free(pInterfaceBuffer); return result; } _flowHandle = iflowHandle; _filterHandle = iFilterHandle; _clientHandle = ClientHandle; _gtc->TcCloseInterface(ifcHandle); free(pInterfaceBuffer); WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, "Successfully created flow and filter."); return 0; } } // namespace webrtc