diff --git a/webrtc.gyp b/webrtc.gyp index 0cff614697..46e87bbfea 100644 --- a/webrtc.gyp +++ b/webrtc.gyp @@ -31,6 +31,7 @@ 'dependencies': [ 'webrtc/test/metrics.gyp:*', 'webrtc/test/test.gyp:*', + 'webrtc/test/channel_transport.gyp:*', 'webrtc/tools/tools.gyp:*', 'tools/e2e_quality/e2e_quality.gyp:*', ], diff --git a/webrtc/test/channel_transport.gyp b/webrtc/test/channel_transport.gyp new file mode 100644 index 0000000000..10808db583 --- /dev/null +++ b/webrtc/test/channel_transport.gyp @@ -0,0 +1,67 @@ +# Copyright (c) 2013 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. + +{ + 'includes': [ + '../build/common.gypi', + ], + 'targets': [ + { + 'target_name': 'channel_transport', + 'type': 'static_library', + 'dependencies': [ + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', + ], + 'sources': [ + # PLATFORM INDEPENDENT SOURCE FILES + 'channel_transport/channel_transport.cc', + 'channel_transport/include/channel_transport.h', + 'channel_transport/udp_transport.h', + 'channel_transport/udp_transport_impl.cc', + 'channel_transport/udp_socket_wrapper.cc', + 'channel_transport/udp_socket_manager_wrapper.cc', + 'channel_transport/udp_transport_impl.h', + 'channel_transport/udp_socket_wrapper.h', + 'channel_transport/udp_socket_manager_wrapper.h', + # PLATFORM SPECIFIC SOURCE FILES - Will be filtered below + # Posix (Linux/Mac) + 'channel_transport/udp_socket_posix.cc', + 'channel_transport/udp_socket_posix.h', + 'channel_transport/udp_socket_manager_posix.cc', + 'channel_transport/udp_socket_manager_posix.h', + # win + 'channel_transport/udp_socket2_manager_win.cc', + 'channel_transport/udp_socket2_manager_win.h', + 'channel_transport/udp_socket2_win.cc', + 'channel_transport/udp_socket2_win.h', + 'channel_transport/traffic_control_win.cc', + 'channel_transport/traffic_control_win.h', + ], # source + }, + { + 'target_name': 'channel_transport_unittests', + 'type': 'executable', + 'dependencies': [ + 'channel_transport', + '<(DEPTH)/testing/gtest.gyp:gtest', + '<(DEPTH)/testing/gmock.gyp:gmock', + '<(webrtc_root)/test/test.gyp:test_support_main', + ], + 'sources': [ + 'channel_transport/udp_transport_unittest.cc', + 'channel_transport/udp_socket_manager_unittest.cc', + 'channel_transport/udp_socket_wrapper_unittest.cc', + ], + # Disable warnings to enable Win64 build, issue 1323. + 'msvs_disabled_warnings': [ + 4267, # size_t to int truncation. + ], + }, # channel_transport_unittests + ], # targets +} diff --git a/webrtc/test/channel_transport/channel_transport.cc b/webrtc/test/channel_transport/channel_transport.cc new file mode 100644 index 0000000000..877a6012fc --- /dev/null +++ b/webrtc/test/channel_transport/channel_transport.cc @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2013 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 "webrtc/test/channel_transport/include/channel_transport.h" + +#include + +#include "gtest/gtest.h" +#include "webrtc/test/channel_transport/udp_transport.h" +#include "webrtc/video_engine/include/vie_network.h" +#include "webrtc/voice_engine/include/voe_network.h" +#include "webrtc/video_engine/vie_defines.h" + +namespace webrtc { +namespace test { + +VoiceChannelTransport::VoiceChannelTransport(VoENetwork* voe_network, + int channel) + : channel_(channel), + voe_network_(voe_network) { + WebRtc_UWord8 socket_threads = 1; + socket_transport_ = UdpTransport::Create(channel, socket_threads); + EXPECT_EQ(0, voe_network_->RegisterExternalTransport(channel, + *socket_transport_)); +} + +VoiceChannelTransport::~VoiceChannelTransport() { + voe_network_->DeRegisterExternalTransport(channel_); + UdpTransport::Destroy(socket_transport_); +} + +void VoiceChannelTransport::IncomingRTPPacket( + const WebRtc_Word8* incoming_rtp_packet, + const WebRtc_Word32 packet_length, + const char* /*from_ip*/, + const WebRtc_UWord16 /*from_port*/) { + voe_network_->ReceivedRTPPacket(channel_, incoming_rtp_packet, packet_length); +} + +void VoiceChannelTransport::IncomingRTCPPacket( + const WebRtc_Word8* incoming_rtcp_packet, + const WebRtc_Word32 packet_length, + const char* /*from_ip*/, + const WebRtc_UWord16 /*from_port*/) { + voe_network_->ReceivedRTCPPacket(channel_, incoming_rtcp_packet, + packet_length); +} + +int VoiceChannelTransport::SetLocalReceiver(WebRtc_UWord16 rtp_port) { + return socket_transport_->InitializeReceiveSockets(this, rtp_port); +} + +int VoiceChannelTransport::SetSendDestination(const char* ip_address, + WebRtc_UWord16 rtp_port) { + return socket_transport_->InitializeSendSockets(ip_address, rtp_port); +} + + +VideoChannelTransport::VideoChannelTransport(ViENetwork* vie_network, + int channel) + : channel_(channel), + vie_network_(vie_network) { + WebRtc_UWord8 socket_threads = 1; + socket_transport_ = UdpTransport::Create(channel, socket_threads); + EXPECT_EQ(0, vie_network_->RegisterSendTransport(channel, + *socket_transport_)); +} + +VideoChannelTransport::~VideoChannelTransport() { + vie_network_->DeregisterSendTransport(channel_); + UdpTransport::Destroy(socket_transport_); +} + +void VideoChannelTransport::IncomingRTPPacket( + const WebRtc_Word8* incoming_rtp_packet, + const WebRtc_Word32 packet_length, + const char* /*from_ip*/, + const WebRtc_UWord16 /*from_port*/) { + vie_network_->ReceivedRTPPacket(channel_, incoming_rtp_packet, packet_length); +} + +void VideoChannelTransport::IncomingRTCPPacket( + const WebRtc_Word8* incoming_rtcp_packet, + const WebRtc_Word32 packet_length, + const char* /*from_ip*/, + const WebRtc_UWord16 /*from_port*/) { + vie_network_->ReceivedRTCPPacket(channel_, incoming_rtcp_packet, + packet_length); +} + +int VideoChannelTransport::SetLocalReceiver(WebRtc_UWord16 rtp_port) { + int return_value = socket_transport_->InitializeReceiveSockets(this, + rtp_port); + if (return_value == 0) { + return socket_transport_->StartReceiving(kViENumReceiveSocketBuffers); + } + return return_value; +} + +int VideoChannelTransport::SetSendDestination(const char* ip_address, + WebRtc_UWord16 rtp_port) { + return socket_transport_->InitializeSendSockets(ip_address, rtp_port); +} + +} // namespace test +} // namespace webrtc diff --git a/webrtc/test/channel_transport/include/channel_transport.h b/webrtc/test/channel_transport/include/channel_transport.h new file mode 100644 index 0000000000..ba7182948f --- /dev/null +++ b/webrtc/test/channel_transport/include/channel_transport.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2013 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. + */ + +#ifndef WEBRTC_TEST_CHANNEL_TRANSPORT_INCLUDE_CHANNEL_TRANSPORT_H_ +#define WEBRTC_TEST_CHANNEL_TRANSPORT_INCLUDE_CHANNEL_TRANSPORT_H_ + +#include "webrtc/test/channel_transport/udp_transport.h" + +namespace webrtc { + +class ViENetwork; +class VoENetwork; + +namespace test { + +// Helper class for VoiceEngine tests. +class VoiceChannelTransport : public UdpTransportData { + public: + VoiceChannelTransport(VoENetwork* voe_network, int channel); + + virtual ~VoiceChannelTransport(); + + // Start implementation of UdpTransportData. + void IncomingRTPPacket(const WebRtc_Word8* incoming_rtp_packet, + const WebRtc_Word32 packet_length, + const char* /*from_ip*/, + const WebRtc_UWord16 /*from_port*/); + + void IncomingRTCPPacket(const WebRtc_Word8* incoming_rtcp_packet, + const WebRtc_Word32 packet_length, + const char* /*from_ip*/, + const WebRtc_UWord16 /*from_port*/); + // End implementation of UdpTransportData. + + // Specifies the ports to receive RTP packets on. + int SetLocalReceiver(WebRtc_UWord16 rtp_port); + + // Specifies the destination port and IP address for a specified channel. + int SetSendDestination(const char* ip_address, WebRtc_UWord16 rtp_port); + + private: + int channel_; + VoENetwork* voe_network_; + UdpTransport* socket_transport_; +}; + +// Helper class for VideoEngine tests. +class VideoChannelTransport : public UdpTransportData { + public: + VideoChannelTransport(ViENetwork* vie_network, int channel); + + virtual ~VideoChannelTransport(); + + // Start implementation of UdpTransportData. + void IncomingRTPPacket(const WebRtc_Word8* incoming_rtp_packet, + const WebRtc_Word32 packet_length, + const char* /*from_ip*/, + const WebRtc_UWord16 /*from_port*/); + + void IncomingRTCPPacket(const WebRtc_Word8* incoming_rtcp_packet, + const WebRtc_Word32 packet_length, + const char* /*from_ip*/, + const WebRtc_UWord16 /*from_port*/); + // End implementation of UdpTransportData. + + // Specifies the ports to receive RTP packets on. + int SetLocalReceiver(WebRtc_UWord16 rtp_port); + + // Specifies the destination port and IP address for a specified channel. + int SetSendDestination(const char* ip_address, WebRtc_UWord16 rtp_port); + + private: + int channel_; + ViENetwork* vie_network_; + UdpTransport* socket_transport_; +}; + +} // namespace test +} // namespace webrtc + +#endif // WEBRTC_TEST_CHANNEL_TRANSPORT_INCLUDE_CHANNEL_TRANSPORT_H_ + diff --git a/webrtc/test/channel_transport/traffic_control_win.cc b/webrtc/test/channel_transport/traffic_control_win.cc new file mode 100644 index 0000000000..1833586251 --- /dev/null +++ b/webrtc/test/channel_transport/traffic_control_win.cc @@ -0,0 +1,257 @@ +/* + * 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 "webrtc/test/channel_transport/traffic_control_win.h" + +#include + +#include "webrtc/system_wrappers/interface/trace.h" + +namespace webrtc { +namespace test { + +TrafficControlWindows* TrafficControlWindows::instance = NULL; +WebRtc_UWord32 TrafficControlWindows::refCounter = 0; + +TrafficControlWindows::TrafficControlWindows(const WebRtc_Word32 id) : _id(id) +{ +} + +TrafficControlWindows* TrafficControlWindows::GetInstance( + const WebRtc_Word32 id) +{ + if(instance != NULL) + { + WEBRTC_TRACE( + kTraceDebug, + kTraceTransport, + id, + "TrafficControlWindows - Returning already created object"); + refCounter++; + return instance; + } + + WEBRTC_TRACE(kTraceMemory, kTraceTransport, id, + "TrafficControlWindows - Creating new object"); + instance = new TrafficControlWindows(id); + if(instance == NULL) + { + WEBRTC_TRACE(kTraceMemory, kTraceTransport, id, + "TrafficControlWindows - Error allocating memory"); + return NULL; + } + + instance->tcRegister = NULL; + instance->tcDeregister = NULL; + + instance->tcEnumerate = NULL; + instance->tcOpenInterface = NULL; + instance->tcCloseInterface = NULL; + + instance->tcAddFlow = NULL; + instance->tcDeleteFlow = NULL; + + instance->tcAddFilter = NULL; + instance->tcDeleteFilter = NULL; + + HMODULE trafficLib = LoadLibrary(TEXT("traffic.dll")); + if(trafficLib == NULL) + { + WEBRTC_TRACE( + kTraceWarning, + kTraceTransport, + id, + "TrafficControlWindows - No QOS support, LoadLibrary returned NULL,\ + last error: %d\n", + GetLastError()); + delete instance; + instance = NULL; + return NULL; + } + + instance->tcRegister = (registerFn)GetProcAddress(trafficLib, + "TcRegisterClient"); + instance->tcDeregister = (deregisterFn)GetProcAddress(trafficLib, + "TcDeregisterClient"); + instance->tcEnumerate = (enumerateFn)GetProcAddress( + trafficLib, + "TcEnumerateInterfaces"); + instance->tcOpenInterface = (openInterfaceFn)GetProcAddress( + trafficLib, + "TcOpenInterfaceW"); + instance->tcCloseInterface = (closeInterfaceFn)GetProcAddress( + trafficLib, + "TcCloseInterface"); + instance->tcAddFlow = (flowAddFn)GetProcAddress(trafficLib, + "TcAddFlow"); + instance->tcDeleteFlow = (flowDeleteFn)GetProcAddress(trafficLib, + "TcDeleteFlow"); + + instance->tcAddFilter = (filterAddFn)GetProcAddress(trafficLib, + "TcAddFilter"); + instance->tcDeleteFilter = (filterDeleteFn)GetProcAddress(trafficLib, + "TcDeleteFilter"); + + if(instance->tcRegister == NULL || + instance->tcDeregister == NULL || + instance->tcEnumerate == NULL || + instance->tcOpenInterface == NULL || + instance->tcCloseInterface == NULL || + instance->tcAddFlow == NULL || + instance->tcAddFilter == NULL || + instance->tcDeleteFlow == NULL || + instance->tcDeleteFilter == NULL) + { + delete instance; + instance = NULL; + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + id, + "TrafficControlWindows - Could not find function pointer for\ + traffic control functions"); + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + id, + "Tcregister : %x, tcDeregister: %x, tcEnumerate: %x,\ + tcOpenInterface: %x, tcCloseInterface: %x, tcAddFlow: %x, tcAddFilter: %x,\ + tcDeleteFlow: %x, tcDeleteFilter: %x", + instance->tcRegister, + instance->tcDeregister, + instance->tcEnumerate, + instance->tcOpenInterface, + instance->tcCloseInterface, + instance->tcAddFlow, + instance->tcAddFilter, + instance->tcDeleteFlow, + instance->tcDeleteFilter ); + return NULL; + } + refCounter++; + return instance; +} + +void TrafficControlWindows::Release(TrafficControlWindows* gtc) +{ + if (0 == refCounter) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, -1, + "TrafficControlWindows - Cannot release, refCounter is 0"); + return; + } + if (NULL == gtc) + { + WEBRTC_TRACE(kTraceDebug, kTraceTransport, -1, + "TrafficControlWindows - Not releasing, gtc is NULL"); + return; + } + + WEBRTC_TRACE(kTraceDebug, kTraceTransport, gtc->_id, + "TrafficControlWindows - Releasing object"); + refCounter--; + if ((0 == refCounter) && instance) + { + WEBRTC_TRACE(kTraceMemory, kTraceTransport, gtc->_id, + "TrafficControlWindows - Deleting object"); + delete instance; + instance = NULL; + } +} +WebRtc_Word32 TrafficControlWindows::ChangeUniqueId(const WebRtc_Word32 id) +{ + _id = id; + return 0; +} + +ULONG TrafficControlWindows::TcRegisterClient( + ULONG TciVersion, + HANDLE ClRegCtx, + PTCI_CLIENT_FUNC_LIST ClientHandlerList, + PHANDLE pClientHandle) +{ + assert(tcRegister != NULL); + + return tcRegister(TciVersion, ClRegCtx, ClientHandlerList, pClientHandle); +} + +ULONG TrafficControlWindows::TcDeregisterClient(HANDLE clientHandle) +{ + assert(tcDeregister != NULL); + + return tcDeregister(clientHandle); +} + + +ULONG TrafficControlWindows::TcEnumerateInterfaces( + HANDLE ClientHandle, + PULONG pBufferSize, + PTC_IFC_DESCRIPTOR interfaceBuffer) +{ + assert(tcEnumerate != NULL); + + return tcEnumerate(ClientHandle, pBufferSize, interfaceBuffer); +} + + +ULONG TrafficControlWindows::TcOpenInterfaceW(LPWSTR pInterfaceName, + HANDLE ClientHandle, + HANDLE ClIfcCtx, + PHANDLE pIfcHandle) +{ + assert(tcOpenInterface != NULL); + + return tcOpenInterface(pInterfaceName, ClientHandle, ClIfcCtx, pIfcHandle); + +} + +ULONG TrafficControlWindows::TcCloseInterface(HANDLE IfcHandle) +{ + assert(tcCloseInterface != NULL); + + return tcCloseInterface(IfcHandle); +} + +ULONG TrafficControlWindows::TcAddFlow(HANDLE IfcHandle, HANDLE ClFlowCtx, + ULONG Flags, PTC_GEN_FLOW pGenericFlow, + PHANDLE pFlowHandle) +{ + assert(tcAddFlow != NULL); + return tcAddFlow(IfcHandle, ClFlowCtx, Flags, pGenericFlow, pFlowHandle); +} + +ULONG TrafficControlWindows::TcAddFilter(HANDLE FlowHandle, + PTC_GEN_FILTER pGenericFilter, + PHANDLE pFilterHandle) +{ + assert(tcAddFilter != NULL); + return tcAddFilter(FlowHandle, pGenericFilter, pFilterHandle); +} + +ULONG TrafficControlWindows::TcDeleteFlow(HANDLE FlowHandle) +{ + assert(tcDeleteFlow != NULL); + return tcDeleteFlow(FlowHandle); + +} + +ULONG TrafficControlWindows::TcDeleteFilter(HANDLE FilterHandle) +{ + assert(tcDeleteFilter != NULL); + return tcDeleteFilter(FilterHandle); +} + +void MyClNotifyHandler(HANDLE ClRegCtx, HANDLE ClIfcCtx, ULONG Event, + HANDLE SubCode, ULONG BufSize, PVOID Buffer) +{ +} + +} // namespace test +} // namespace webrtc diff --git a/webrtc/test/channel_transport/traffic_control_win.h b/webrtc/test/channel_transport/traffic_control_win.h new file mode 100644 index 0000000000..7286ed5238 --- /dev/null +++ b/webrtc/test/channel_transport/traffic_control_win.h @@ -0,0 +1,102 @@ +/* + * 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. + */ + +#ifndef WEBRTC_TEST_CHANNEL_TRANSPORT_TRAFFIC_CONTROL_WINDOWS_H_ +#define WEBRTC_TEST_CHANNEL_TRANSPORT_TRAFFIC_CONTROL_WINDOWS_H_ + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +// Disable deprication warning from traffic.h +#pragma warning(disable : 4995) + +#include +#include +#include +#include + +#include "webrtc/system_wrappers/interface/trace.h" + +namespace webrtc { +namespace test { +void MyClNotifyHandler(HANDLE ClRegCtx, HANDLE ClIfcCtx, ULONG Event, + HANDLE SubCode, ULONG BufSize, PVOID Buffer); + + +typedef ULONG (WINAPI *registerFn)(ULONG, HANDLE, PTCI_CLIENT_FUNC_LIST, + PHANDLE); +typedef ULONG (WINAPI *deregisterFn)(HANDLE); +typedef ULONG (WINAPI *enumerateFn)(HANDLE, PULONG, PTC_IFC_DESCRIPTOR); +typedef ULONG (WINAPI *openInterfaceFn)(LPWSTR, HANDLE, HANDLE, PHANDLE); +typedef ULONG (WINAPI *closeInterfaceFn)(HANDLE); +typedef ULONG (WINAPI *flowAddFn)(HANDLE, HANDLE, ULONG, PTC_GEN_FLOW, PHANDLE); +typedef ULONG (WINAPI *filterAddFn)(HANDLE, PTC_GEN_FILTER, PHANDLE); +typedef ULONG (WINAPI *flowDeleteFn)(HANDLE); +typedef ULONG (WINAPI *filterDeleteFn)(HANDLE); + +class TrafficControlWindows +{ + public: + // Factory method. Constructor disabled. + static TrafficControlWindows* GetInstance(const WebRtc_Word32 id); + static void Release(TrafficControlWindows* gtc); + + WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); + + ULONG TcRegisterClient(ULONG TciVersion, HANDLE ClRegCtx, + PTCI_CLIENT_FUNC_LIST ClientHandlerList, + PHANDLE pClientHandle); + + ULONG TcDeregisterClient(HANDLE clientHandle); + + ULONG TcEnumerateInterfaces(HANDLE ClientHandle, PULONG pBufferSize, + PTC_IFC_DESCRIPTOR interfaceBuffer); + + ULONG TcOpenInterfaceW(LPWSTR pInterfaceName, HANDLE ClientHandle, + HANDLE ClIfcCtx, PHANDLE pIfcHandle); + + ULONG TcCloseInterface(HANDLE IfcHandle); + + ULONG TcAddFlow(HANDLE IfcHandle, HANDLE ClFlowCtx, ULONG Flags, + PTC_GEN_FLOW pGenericFlow, PHANDLE pFlowHandle); + + ULONG TcAddFilter(HANDLE FlowHandle, PTC_GEN_FILTER pGenericFilter, + PHANDLE pFilterHandle); + + ULONG TcDeleteFlow(HANDLE FlowHandle); + ULONG TcDeleteFilter(HANDLE FilterHandle); +private: + TrafficControlWindows(const WebRtc_Word32 id); + WebRtc_Word32 _id; + TCI_CLIENT_FUNC_LIST QoSFunctions; + + static TrafficControlWindows* instance; + + registerFn tcRegister; + deregisterFn tcDeregister; + + enumerateFn tcEnumerate; + openInterfaceFn tcOpenInterface; + closeInterfaceFn tcCloseInterface; + + flowAddFn tcAddFlow; + flowDeleteFn tcDeleteFlow; + + filterAddFn tcAddFilter; + filterDeleteFn tcDeleteFilter; + + static WebRtc_UWord32 refCounter; +}; + +} // namespace test +} // namespace webrtc + +#endif // WEBRTC_TEST_CHANNEL_TRANSPORT_TRAFFIC_CONTROL_WINDOWS_H_ diff --git a/webrtc/test/channel_transport/udp_socket2_manager_win.cc b/webrtc/test/channel_transport/udp_socket2_manager_win.cc new file mode 100644 index 0000000000..7d490814d3 --- /dev/null +++ b/webrtc/test/channel_transport/udp_socket2_manager_win.cc @@ -0,0 +1,661 @@ +/* + * Copyright (c) 2012 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 "webrtc/test/channel_transport/udp_socket2_manager_win.h" + +#include +#include + +#include "webrtc/system_wrappers/interface/aligned_malloc.h" +#include "webrtc/test/channel_transport/udp_socket2_win.h" + +namespace webrtc { +namespace test { + +WebRtc_UWord32 UdpSocket2ManagerWindows::_numOfActiveManagers = 0; +bool UdpSocket2ManagerWindows::_wsaInit = false; + +UdpSocket2ManagerWindows::UdpSocket2ManagerWindows() + : UdpSocketManager(), + _id(-1), + _stopped(false), + _init(false), + _pCrit(CriticalSectionWrapper::CreateCriticalSection()), + _ioCompletionHandle(NULL), + _numActiveSockets(0), + _event(EventWrapper::Create()) +{ + _managerNumber = _numOfActiveManagers++; + + if(_numOfActiveManagers == 1) + { + WORD wVersionRequested = MAKEWORD(2, 2); + WSADATA wsaData; + _wsaInit = WSAStartup(wVersionRequested, &wsaData) == 0; + // TODO (hellner): seems safer to use RAII for this. E.g. what happens + // if a UdpSocket2ManagerWindows() created and destroyed + // without being initialized. + } +} + +UdpSocket2ManagerWindows::~UdpSocket2ManagerWindows() +{ + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, + "UdpSocket2ManagerWindows(%d)::~UdpSocket2ManagerWindows()", + _managerNumber); + + if(_init) + { + _pCrit->Enter(); + if(_numActiveSockets) + { + _pCrit->Leave(); + _event->Wait(INFINITE); + } + else + { + _pCrit->Leave(); + } + StopWorkerThreads(); + + // All threads are stopped. Safe to delete them. + ListItem* pItem = NULL; + while((pItem = _workerThreadsList.First()) != NULL) + { + delete static_cast(pItem->GetItem()); + _workerThreadsList.PopFront(); + } + + _ioContextPool.Free(); + + _numOfActiveManagers--; + if(_ioCompletionHandle) + { + CloseHandle(_ioCompletionHandle); + } + if (_numOfActiveManagers == 0) + { + if(_wsaInit) + { + WSACleanup(); + } + } + } + if(_pCrit) + { + delete _pCrit; + } + if(_event) + { + delete _event; + } +} + +bool UdpSocket2ManagerWindows::Init(WebRtc_Word32 id, + WebRtc_UWord8& numOfWorkThreads) { + CriticalSectionScoped cs(_pCrit); + if ((_id != -1) || (_numOfWorkThreads != 0)) { + assert(_id != -1); + assert(_numOfWorkThreads != 0); + return false; + } + _id = id; + _numOfWorkThreads = numOfWorkThreads; + return true; +} + +WebRtc_Word32 UdpSocket2ManagerWindows::ChangeUniqueId(const WebRtc_Word32 id) +{ + _id = id; + return 0; +} + +bool UdpSocket2ManagerWindows::Start() +{ + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, + "UdpSocket2ManagerWindows(%d)::Start()",_managerNumber); + if(!_init) + { + StartWorkerThreads(); + } + + if(!_init) + { + return false; + } + _pCrit->Enter(); + // Start worker threads. + _stopped = false; + WebRtc_Word32 error = 0; + ListItem* pItem = _workerThreadsList.First(); + UdpSocket2WorkerWindows* pWorker; + while(pItem != NULL && !error) + { + pWorker = (UdpSocket2WorkerWindows*)pItem->GetItem(); + if(!pWorker->Start()) + error = 1; + pItem = _workerThreadsList.Next(pItem); + } + if(error) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpSocket2ManagerWindows(%d)::Start() error starting worker\ + threads", + _managerNumber); + _pCrit->Leave(); + return false; + } + _pCrit->Leave(); + return true; +} + +bool UdpSocket2ManagerWindows::StartWorkerThreads() +{ + if(!_init) + { + _pCrit->Enter(); + + _ioCompletionHandle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, + 0, 0); + if(_ioCompletionHandle == NULL) + { + WebRtc_Word32 error = GetLastError(); + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpSocket2ManagerWindows(%d)::StartWorkerThreads()" + "_ioCompletioHandle == NULL: error:%d", + _managerNumber,error); + _pCrit->Leave(); + return false; + } + + // Create worker threads. + WebRtc_UWord32 i = 0; + bool error = false; + while(i < _numOfWorkThreads && !error) + { + UdpSocket2WorkerWindows* pWorker = + new UdpSocket2WorkerWindows(_ioCompletionHandle); + if(pWorker->Init() != 0) + { + error = true; + delete pWorker; + break; + } + _workerThreadsList.PushFront(pWorker); + i++; + } + if(error) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpSocket2ManagerWindows(%d)::StartWorkerThreads() error " + "creating work threads", + _managerNumber); + // Delete worker threads. + ListItem* pItem = NULL; + while((pItem = _workerThreadsList.First()) != NULL) + { + delete static_cast(pItem->GetItem()); + _workerThreadsList.PopFront(); + } + _pCrit->Leave(); + return false; + } + if(_ioContextPool.Init()) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpSocket2ManagerWindows(%d)::StartWorkerThreads() error " + "initiating _ioContextPool", + _managerNumber); + _pCrit->Leave(); + return false; + } + _init = true; + WEBRTC_TRACE( + kTraceDebug, + kTraceTransport, + _id, + "UdpSocket2ManagerWindows::StartWorkerThreads %d number of work " + "threads created and initialized", + _numOfWorkThreads); + _pCrit->Leave(); + } + return true; +} + +bool UdpSocket2ManagerWindows::Stop() +{ + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, + "UdpSocket2ManagerWindows(%d)::Stop()",_managerNumber); + + if(!_init) + { + return false; + } + _pCrit->Enter(); + _stopped = true; + if(_numActiveSockets) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpSocket2ManagerWindows(%d)::Stop() there is still active\ + sockets", + _managerNumber); + _pCrit->Leave(); + return false; + } + // No active sockets. Stop all worker threads. + bool result = StopWorkerThreads(); + _pCrit->Leave(); + return result; +} + +bool UdpSocket2ManagerWindows::StopWorkerThreads() +{ + WebRtc_Word32 error = 0; + WEBRTC_TRACE( + kTraceDebug, + kTraceTransport, + _id, + "UdpSocket2ManagerWindows(%d)::StopWorkerThreads() Worker\ + threadsStoped, numActicve Sockets=%d", + _managerNumber, + _numActiveSockets); + UdpSocket2WorkerWindows* pWorker; + ListItem* pItem = _workerThreadsList.First(); + + // Set worker threads to not alive so that they will stop calling + // UdpSocket2WorkerWindows::Run(). + while(pItem != NULL) + { + pWorker = (UdpSocket2WorkerWindows*)pItem->GetItem(); + pWorker->SetNotAlive(); + pItem = _workerThreadsList.Next(pItem); + } + // Release all threads waiting for GetQueuedCompletionStatus(..). + if(_ioCompletionHandle) + { + WebRtc_UWord32 i = 0; + for(i = 0; i < _workerThreadsList.GetSize(); i++) + { + PostQueuedCompletionStatus(_ioCompletionHandle, 0 ,0 , NULL); + } + } + pItem = _workerThreadsList.First(); + + while(pItem != NULL) + { + pWorker = (UdpSocket2WorkerWindows*)pItem->GetItem(); + if(pWorker->Stop() == false) + { + error = -1; + WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1, + "failed to stop worker thread"); + } + pItem = _workerThreadsList.Next(pItem); + } + + if(error) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpSocket2ManagerWindows(%d)::StopWorkerThreads() error stopping\ + worker threads", + _managerNumber); + return false; + } + return true; +} + +bool UdpSocket2ManagerWindows::AddSocketPrv(UdpSocket2Windows* s) +{ + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, + "UdpSocket2ManagerWindows(%d)::AddSocketPrv()",_managerNumber); + if(!_init) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpSocket2ManagerWindows(%d)::AddSocketPrv() manager not\ + initialized", + _managerNumber); + return false; + } + _pCrit->Enter(); + if(s == NULL) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpSocket2ManagerWindows(%d)::AddSocketPrv() socket == NULL", + _managerNumber); + _pCrit->Leave(); + return false; + } + if(s->GetFd() == NULL || s->GetFd() == INVALID_SOCKET) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpSocket2ManagerWindows(%d)::AddSocketPrv() socket->GetFd() ==\ + %d", + _managerNumber, + (WebRtc_Word32)s->GetFd()); + _pCrit->Leave(); + return false; + + } + _ioCompletionHandle = CreateIoCompletionPort((HANDLE)s->GetFd(), + _ioCompletionHandle, + (ULONG_PTR)(s), 0); + if(_ioCompletionHandle == NULL) + { + WebRtc_Word32 error = GetLastError(); + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpSocket2ManagerWindows(%d)::AddSocketPrv() Error adding to IO\ + completion: %d", + _managerNumber, + error); + _pCrit->Leave(); + return false; + } + _numActiveSockets++; + _pCrit->Leave(); + return true; +} +bool UdpSocket2ManagerWindows::RemoveSocketPrv(UdpSocket2Windows* s) +{ + if(!_init) + { + return false; + } + _pCrit->Enter(); + _numActiveSockets--; + if(_numActiveSockets == 0) + { + _event->Set(); + } + _pCrit->Leave(); + return true; +} + +PerIoContext* UdpSocket2ManagerWindows::PopIoContext() +{ + if(!_init) + { + return NULL; + } + + PerIoContext* pIoC = NULL; + if(!_stopped) + { + pIoC = _ioContextPool.PopIoContext(); + }else + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpSocket2ManagerWindows(%d)::PopIoContext() Manager Not started", + _managerNumber); + } + return pIoC; +} + +WebRtc_Word32 UdpSocket2ManagerWindows::PushIoContext(PerIoContext* pIoContext) +{ + return _ioContextPool.PushIoContext(pIoContext); +} + +IoContextPool::IoContextPool() + : _pListHead(NULL), + _init(false), + _size(0), + _inUse(0) +{ +} + +IoContextPool::~IoContextPool() +{ + Free(); + assert(_size.Value() == 0); + AlignedFree(_pListHead); +} + +WebRtc_Word32 IoContextPool::Init(WebRtc_UWord32 /*increaseSize*/) +{ + if(_init) + { + return 0; + } + + _pListHead = (PSLIST_HEADER)AlignedMalloc(sizeof(SLIST_HEADER), + MEMORY_ALLOCATION_ALIGNMENT); + if(_pListHead == NULL) + { + return -1; + } + InitializeSListHead(_pListHead); + _init = true; + return 0; +} + +PerIoContext* IoContextPool::PopIoContext() +{ + if(!_init) + { + return NULL; + } + + PSLIST_ENTRY pListEntry = InterlockedPopEntrySList(_pListHead); + if(pListEntry == NULL) + { + IoContextPoolItem* item = (IoContextPoolItem*) + AlignedMalloc( + sizeof(IoContextPoolItem), + MEMORY_ALLOCATION_ALIGNMENT); + if(item == NULL) + { + return NULL; + } + memset(&item->payload.ioContext,0,sizeof(PerIoContext)); + item->payload.base = item; + pListEntry = &(item->itemEntry); + ++_size; + } + ++_inUse; + return &((IoContextPoolItem*)pListEntry)->payload.ioContext; +} + +WebRtc_Word32 IoContextPool::PushIoContext(PerIoContext* pIoContext) +{ + // TODO (hellner): Overlapped IO should be completed at this point. Perhaps + // add an assert? + const bool overlappedIOCompleted = HasOverlappedIoCompleted( + (LPOVERLAPPED)pIoContext); + + IoContextPoolItem* item = ((IoContextPoolItemPayload*)pIoContext)->base; + + const WebRtc_Word32 usedItems = --_inUse; + const WebRtc_Word32 totalItems = _size.Value(); + const WebRtc_Word32 freeItems = totalItems - usedItems; + if(freeItems < 0) + { + assert(false); + AlignedFree(item); + return -1; + } + if((freeItems >= totalItems>>1) && + overlappedIOCompleted) + { + AlignedFree(item); + --_size; + return 0; + } + InterlockedPushEntrySList(_pListHead, &(item->itemEntry)); + return 0; +} + +WebRtc_Word32 IoContextPool::Free() +{ + if(!_init) + { + return 0; + } + + WebRtc_Word32 itemsFreed = 0; + PSLIST_ENTRY pListEntry = InterlockedPopEntrySList(_pListHead); + while(pListEntry != NULL) + { + IoContextPoolItem* item = ((IoContextPoolItem*)pListEntry); + AlignedFree(item); + --_size; + itemsFreed++; + pListEntry = InterlockedPopEntrySList(_pListHead); + } + return itemsFreed; +} + +WebRtc_Word32 UdpSocket2WorkerWindows::_numOfWorkers = 0; + +UdpSocket2WorkerWindows::UdpSocket2WorkerWindows(HANDLE ioCompletionHandle) + : _ioCompletionHandle(ioCompletionHandle), + _pThread(NULL), + _init(false) +{ + _workerNumber = _numOfWorkers++; + WEBRTC_TRACE(kTraceMemory, kTraceTransport, -1, + "UdpSocket2WorkerWindows created"); +} + +UdpSocket2WorkerWindows::~UdpSocket2WorkerWindows() +{ + if(_pThread) + { + delete _pThread; + } + WEBRTC_TRACE(kTraceMemory, kTraceTransport, -1, + "UdpSocket2WorkerWindows deleted"); +} + +bool UdpSocket2WorkerWindows::Start() +{ + unsigned int id = 0; + WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, -1, + "Start UdpSocket2WorkerWindows"); + return _pThread->Start(id); +} + +bool UdpSocket2WorkerWindows::Stop() +{ + WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, -1, + "Stop UdpSocket2WorkerWindows"); + return _pThread->Stop(); +} + +void UdpSocket2WorkerWindows::SetNotAlive() +{ + WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, -1, + "SetNotAlive UdpSocket2WorkerWindows"); + _pThread->SetNotAlive(); +} + +WebRtc_Word32 UdpSocket2WorkerWindows::Init() +{ + if(!_init) + { + const char* threadName = "UdpSocket2ManagerWindows_thread"; + _pThread = ThreadWrapper::CreateThread(Run, this, kRealtimePriority, + threadName); + if(_pThread == NULL) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + -1, + "UdpSocket2WorkerWindows(%d)::Init(), error creating thread!", + _workerNumber); + return -1; + } + _init = true; + } + return 0; +} + +bool UdpSocket2WorkerWindows::Run(ThreadObj obj) +{ + UdpSocket2WorkerWindows* pWorker = + static_cast(obj); + return pWorker->Process(); +} + +// Process should always return true. Stopping the worker threads is done in +// the UdpSocket2ManagerWindows::StopWorkerThreads() function. +bool UdpSocket2WorkerWindows::Process() +{ + WebRtc_Word32 success = 0; + DWORD ioSize = 0; + UdpSocket2Windows* pSocket = NULL; + PerIoContext* pIOContext = 0; + OVERLAPPED* pOverlapped = 0; + success = GetQueuedCompletionStatus(_ioCompletionHandle, + &ioSize, + (ULONG_PTR*)&pSocket, &pOverlapped, 200); + + WebRtc_UWord32 error = 0; + if(!success) + { + error = GetLastError(); + if(error == WAIT_TIMEOUT) + { + return true; + } + // This may happen if e.g. PostQueuedCompletionStatus() has been called. + // The IO context still needs to be reclaimed or re-used which is done + // in UdpSocket2Windows::IOCompleted(..). + } + if(pSocket == NULL) + { + WEBRTC_TRACE( + kTraceDebug, + kTraceTransport, + -1, + "UdpSocket2WorkerWindows(%d)::Process(), pSocket == 0, end thread", + _workerNumber); + return true; + } + pIOContext = (PerIoContext*)pOverlapped; + pSocket->IOCompleted(pIOContext,ioSize,error); + return true; +} + +} // namespace test +} // namespace webrtc diff --git a/webrtc/test/channel_transport/udp_socket2_manager_win.h b/webrtc/test/channel_transport/udp_socket2_manager_win.h new file mode 100644 index 0000000000..d77c792d37 --- /dev/null +++ b/webrtc/test/channel_transport/udp_socket2_manager_win.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2012 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. + */ + +#ifndef WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET2_MANAGER_WINDOWS_H_ +#define WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET2_MANAGER_WINDOWS_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include + +#include "webrtc/system_wrappers/interface/atomic32.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/event_wrapper.h" +#include "webrtc/system_wrappers/interface/list_wrapper.h" +#include "webrtc/system_wrappers/interface/thread_wrapper.h" +#include "webrtc/test/channel_transport/udp_socket2_win.h" +#include "webrtc/test/channel_transport/udp_socket_manager_wrapper.h" + +#define MAX_IO_BUFF_SIZE 1600 + +namespace webrtc { +namespace test { + +enum IO_OPERATION { + OP_READ, + OP_WRITE +}; + +class UdpSocket2Windows; + +// Struct used for all socket I/O operations. +struct PerIoContext { + WSAOVERLAPPED overlapped; + char buffer[MAX_IO_BUFF_SIZE]; + WSABUF wsabuf; + int nTotalBytes; + int nSentBytes; + int bytes; + IO_OPERATION ioOperation; + SocketAddress from; + int fromLen; + // Should be set to true if the I/O context was passed to the system by + // a thread not controlled by the socket implementation. + bool ioInitiatedByThreadWrapper; + // TODO (hellner): Not used. Delete it. + PerIoContext* pNextFree; +}; + +struct IoContextPoolItem; +struct IoContextPoolItemPayload +{ + PerIoContext ioContext; + IoContextPoolItem* base; +}; + +struct IoContextPoolItem +{ + // Atomic single linked list entry header. + SLIST_ENTRY itemEntry; + // Atomic single linked list payload + IoContextPoolItemPayload payload; +}; + +class IoContextPool +{ +public: + IoContextPool(); + virtual ~IoContextPool(); + virtual WebRtc_Word32 Init(WebRtc_UWord32 increaseSize = 128); + // Re-use an old unused IO context or create a new one. + virtual PerIoContext* PopIoContext(); + virtual WebRtc_Word32 PushIoContext(PerIoContext* pIoContext); + virtual inline WebRtc_Word32 GetSize(WebRtc_UWord32* inUse = 0) + {return _size.Value();} + virtual WebRtc_Word32 Free(); +private: + // Sample code for use of msfts single linked atomic list can be found here: + // http://msdn.microsoft.com/en-us/library/ms686962(VS.85).aspx + + // Atomic single linked list head. + PSLIST_HEADER _pListHead; + + bool _init; + Atomic32 _size; + Atomic32 _inUse; +}; + + +class UdpSocket2ManagerWindows : public UdpSocketManager +{ +public: + UdpSocket2ManagerWindows(); + virtual ~UdpSocket2ManagerWindows(); + + virtual bool Init(WebRtc_Word32 id, WebRtc_UWord8& numOfWorkThreads); + virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); + + virtual bool Start(); + virtual bool Stop(); + + virtual inline bool AddSocket(UdpSocketWrapper* s) + {if(s) return AddSocketPrv(reinterpret_cast(s)); + return false;} + virtual bool RemoveSocket(UdpSocketWrapper* s) + {if(s) return RemoveSocketPrv(reinterpret_cast(s)); + return false;} + + PerIoContext* PopIoContext(void); + WebRtc_Word32 PushIoContext(PerIoContext* pIoContext); + +private: + bool StopWorkerThreads(); + bool StartWorkerThreads(); + bool AddSocketPrv(UdpSocket2Windows* s); + bool RemoveSocketPrv(UdpSocket2Windows* s); + + static WebRtc_UWord32 _numOfActiveManagers; + static bool _wsaInit; + + WebRtc_Word32 _id; + CriticalSectionWrapper* _pCrit; + WebRtc_Word32 _managerNumber; + volatile bool _stopped; + bool _init; + WebRtc_Word32 _numActiveSockets; + ListWrapper _workerThreadsList; + EventWrapper* _event; + + HANDLE _ioCompletionHandle; + IoContextPool _ioContextPool; +}; + +class UdpSocket2WorkerWindows +{ +public: + UdpSocket2WorkerWindows(HANDLE ioCompletionHandle); + virtual ~UdpSocket2WorkerWindows(); + + virtual bool Start(); + virtual bool Stop(); + virtual WebRtc_Word32 Init(); + virtual void SetNotAlive(); +protected: + static bool Run(ThreadObj obj); + bool Process(); +private: + HANDLE _ioCompletionHandle; + ThreadWrapper*_pThread; + static WebRtc_Word32 _numOfWorkers; + WebRtc_Word32 _workerNumber; + volatile bool _stop; + bool _init; +}; + +} // namespace test +} // namespace webrtc + +#endif // WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET2_MANAGER_WINDOWS_H_ diff --git a/webrtc/test/channel_transport/udp_socket2_win.cc b/webrtc/test/channel_transport/udp_socket2_win.cc new file mode 100644 index 0000000000..03a823e6a2 --- /dev/null +++ b/webrtc/test/channel_transport/udp_socket2_win.cc @@ -0,0 +1,1390 @@ +/* + * Copyright (c) 2012 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 "webrtc/test/channel_transport/udp_socket2_win.h" + +#include +#include +#include + +#include "webrtc/test/channel_transport/traffic_control_win.h" +#include "webrtc/test/channel_transport/udp_socket2_manager_win.h" + +#pragma warning(disable : 4311) + +namespace webrtc { +namespace test { + +typedef struct _QOS_DESTADDR +{ + QOS_OBJECT_HDR ObjectHdr; + const struct sockaddr* SocketAddress; + ULONG SocketAddressLength; +} QOS_DESTADDR, *LPQOS_DESTADDR; + +typedef const QOS_DESTADDR* LPCQOS_DESTADDR; + +// TODO (patrikw): seems to be defined in ws2ipdef.h as 3. How come it's +// redefined here (as a different value)? +#define IP_TOS 8 + +#define QOS_GENERAL_ID_BASE 2000 +#define QOS_OBJECT_DESTADDR (0x00000004 + QOS_GENERAL_ID_BASE) + +UdpSocket2Windows::UdpSocket2Windows(const WebRtc_Word32 id, + UdpSocketManager* mgr, bool ipV6Enable, + bool disableGQOS) + : _id(id), + _qos(true), + _iProtocol(0), + _outstandingCalls(0), + _outstandingCallComplete(0), + _terminate(false), + _addedToMgr(false), + _safeTodelete(false), + _outstandingCallsDisabled(false), + _clientHandle(NULL), + _flowHandle(NULL), + _filterHandle(NULL), + _flow(NULL), + _gtc(NULL), + _pcp(-2), + _receiveBuffers(0) +{ + WEBRTC_TRACE(kTraceMemory, kTraceTransport, _id, + "UdpSocket2Windows::UdpSocket2Windows()"); + + _wantsIncoming = false; + _mgr = static_cast(mgr); + + _obj = NULL; + _incomingCb = NULL; + _socket = INVALID_SOCKET; + _pCrit = CriticalSectionWrapper::CreateCriticalSection(); + _ptrCbRWLock = RWLockWrapper::CreateRWLock(); + _ptrDestRWLock = RWLockWrapper::CreateRWLock(); + _ptrSocketRWLock = RWLockWrapper::CreateRWLock(); + _ptrDeleteCrit = CriticalSectionWrapper::CreateCriticalSection(); + _ptrDeleteCond = ConditionVariableWrapper::CreateConditionVariable(); + + // Check if QoS is supported. + BOOL bProtocolFound = FALSE; + WSAPROTOCOL_INFO *lpProtocolBuf = NULL; + WSAPROTOCOL_INFO pProtocolInfo; + + if(!disableGQOS) + { + DWORD dwBufLen = 0; + // 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); + + if (ipV6Enable) + { + _iProtocol=AF_INET6; + } else { + _iProtocol=AF_INET; + } + + for (WebRtc_Word32 i=0; iTcDeleteFilter(_filterHandle); + } + if(_flowHandle) + { + _gtc->TcDeleteFlow(_flowHandle); + } + TrafficControlWindows::Release( _gtc); + } +} + +WebRtc_Word32 UdpSocket2Windows::ChangeUniqueId(const WebRtc_Word32 id) +{ + _id = id; + if (_gtc) + { + _gtc->ChangeUniqueId(id); + } + return 0; +} + +bool UdpSocket2Windows::ValidHandle() +{ + return GetFd() != INVALID_SOCKET; +} + +bool UdpSocket2Windows::SetCallback(CallbackObj obj, IncomingSocketCallback cb) +{ + _ptrCbRWLock->AcquireLockExclusive(); + _obj = obj; + _incomingCb = cb; + _ptrCbRWLock->ReleaseLockExclusive(); + + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, + "UdpSocket2Windows(%d)::SetCallback ",(WebRtc_Word32)this); + if(_addedToMgr) + { + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, + "UdpSocket2Windows(%d)::SetCallback alreadey added", + (WebRtc_Word32) this); + return false; + + } + if (_mgr->AddSocket(this)) + { + WEBRTC_TRACE( + kTraceDebug, kTraceTransport, _id, + "UdpSocket2Windows(%d)::SetCallback socket added to manager", + (WebRtc_Word32)this); + _addedToMgr = true; + return true; + } + + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, + "UdpSocket2Windows(%d)::SetCallback error adding me to mgr", + (WebRtc_Word32) this); + return false; +} + +bool UdpSocket2Windows::SetSockopt(WebRtc_Word32 level, WebRtc_Word32 optname, + const WebRtc_Word8* optval, + WebRtc_Word32 optlen) +{ + bool returnValue = true; + if(!AquireSocket()) + { + return false; + } + if(0 != setsockopt(_socket, level, optname, + reinterpret_cast(optval), optlen )) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "UdpSocket2Windows::SetSockopt(), WSAerror:%d", + WSAGetLastError()); + returnValue = false; + } + ReleaseSocket(); + return returnValue; +} + +bool UdpSocket2Windows::StartReceiving(WebRtc_UWord32 receiveBuffers) +{ + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, + "UdpSocket2Windows(%d)::StartReceiving(%d)", + (WebRtc_Word32)this, receiveBuffers); + + _wantsIncoming = true; + + WebRtc_Word32 numberOfReceiveBuffersToCreate = + receiveBuffers - _receiveBuffers.Value(); + numberOfReceiveBuffersToCreate = (numberOfReceiveBuffersToCreate < 0) ? + 0 : numberOfReceiveBuffersToCreate; + + WebRtc_Word32 error = 0; + for(WebRtc_Word32 i = 0; + i < numberOfReceiveBuffersToCreate; + i++) + { + if(PostRecv()) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "UdpSocket2Windows::StartReceiving() i=%d", i); + error = -1; + break; + } + ++_receiveBuffers; + } + if(error == -1) + { + return false; + } + + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, + "Socket receiving using:%d number of buffers", + _receiveBuffers.Value()); + return true; +} + +bool UdpSocket2Windows::StopReceiving() +{ + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, + "UdpSocket2Windows::StopReceiving()"); + _wantsIncoming = false; + return true; +} + +bool UdpSocket2Windows::Bind(const SocketAddress& name) +{ + const struct sockaddr* addr = + reinterpret_cast(&name); + bool returnValue = true; + if(!AquireSocket()) + { + return false; + } + if (0 != bind(_socket, addr, sizeof(SocketAddress))) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "UdpSocket2Windows::Bind() WSAerror: %d", + WSAGetLastError()); + returnValue = false; + } + ReleaseSocket(); + return returnValue; +} + +WebRtc_Word32 UdpSocket2Windows::SendTo(const WebRtc_Word8* buf, + WebRtc_Word32 len, + const SocketAddress& to) +{ + WebRtc_Word32 retVal = 0; + WebRtc_Word32 error = 0; + if(len < 0) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "UdpSocket2Windows(%d)::SendTo(), len= %d < 0", + (WebRtc_Word32)this, len); + return -1; + } + + PerIoContext* pIoContext = _mgr->PopIoContext(); + if(pIoContext == 0) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "UdpSocket2Windows(%d)::SendTo(), pIoContext==0", + (WebRtc_Word32) this); + return -1; + } + // sizeof(pIoContext->buffer) is smaller than the highest number that + // can be represented by a WebRtc_Word32. + if(len >= (WebRtc_Word32) sizeof(pIoContext->buffer)) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpSocket2Windows(%d)::SendTo(), len= %d > buffer_size = %d", + (WebRtc_Word32) this, + len,sizeof(pIoContext->buffer)); + len = sizeof(pIoContext->buffer); + } + + memcpy(pIoContext->buffer,buf,len); + pIoContext->wsabuf.buf = pIoContext->buffer; + pIoContext->wsabuf.len = len; + pIoContext->fromLen=sizeof(SocketAddress); + pIoContext->ioOperation = OP_WRITE; + pIoContext->nTotalBytes = len; + pIoContext->nSentBytes=0; + + DWORD numOfbytesSent = 0; + const struct sockaddr* addr = reinterpret_cast(&to); + + if(!AquireSocket()) + { + _mgr->PushIoContext(pIoContext); + return -1; + } + // Assume that the WSASendTo call will be successfull to make sure that + // _outstandingCalls is positive. Roll back if WSASendTo failed. + if(!NewOutstandingCall()) + { + _mgr->PushIoContext(pIoContext); + ReleaseSocket(); + return -1; + } + retVal = WSASendTo(_socket, &pIoContext->wsabuf, 1, &numOfbytesSent, + 0, addr, sizeof(SocketAddress), + &(pIoContext->overlapped), 0); + ReleaseSocket(); + + if( retVal == SOCKET_ERROR ) + { + error = WSAGetLastError(); + if(error != ERROR_IO_PENDING) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "UdpSocket2Windows::SendTo() WSAerror: %d",error); + } + } + if(retVal == 0 || (retVal == SOCKET_ERROR && error == ERROR_IO_PENDING)) + { + return len; + } + if((error = _mgr->PushIoContext(pIoContext))) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpSocket2Windows(%d)::SendTo(), error:%d pushing ioContext", + (WebRtc_Word32)this, error); + } + + // Roll back. + OutstandingCallCompleted(); + return -1; +} + +void UdpSocket2Windows::IOCompleted(PerIoContext* pIOContext, + WebRtc_UWord32 ioSize, WebRtc_UWord32 error) +{ + if(pIOContext == NULL || error == ERROR_OPERATION_ABORTED) + { + if ((pIOContext != NULL) && + !pIOContext->ioInitiatedByThreadWrapper && + (error == ERROR_OPERATION_ABORTED) && + (pIOContext->ioOperation == OP_READ) && + _outstandingCallsDisabled) + { + // !pIOContext->initiatedIOByThreadWrapper indicate that the I/O + // was not initiated by a ThreadWrapper thread. + // This may happen if the thread that initiated receiving (e.g. + // by calling StartListen())) is deleted before any packets have + // been received. + // In this case there is no packet in the PerIoContext. Re-use it + // to post a new PostRecv(..). + // Note 1: the PerIoContext will henceforth be posted by a thread + // that is controlled by the socket implementation. + // Note 2: This is more likely to happen to RTCP packets as + // they are less frequent than RTP packets. + // Note 3: _outstandingCallsDisabled being false indicates + // that the socket isn't being shut down. + // Note 4: This should only happen buffers set to receive packets + // (OP_READ). + } else { + if(pIOContext == NULL) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpSocket2Windows::IOCompleted(%d,%d,%d), %d", + (WebRtc_Word32)pIOContext, + ioSize, + error, + pIOContext ? (WebRtc_Word32)pIOContext->ioOperation : -1); + } else { + WEBRTC_TRACE( + kTraceDebug, + kTraceTransport, + _id, + "UdpSocket2Windows::IOCompleted() Operation aborted"); + } + if(pIOContext) + { + WebRtc_Word32 remainingReceiveBuffers = --_receiveBuffers; + if(remainingReceiveBuffers < 0) + { + assert(false); + } + WebRtc_Word32 err = 0; + if((err = _mgr->PushIoContext(pIOContext))) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpSocket2Windows::IOCompleted(), err = %d, when\ + pushing ioContext after error", + err); + } + } + OutstandingCallCompleted(); + return; + } + } // if (pIOContext == NULL || error == ERROR_OPERATION_ABORTED) + + if(pIOContext->ioOperation == OP_WRITE) + { + _mgr->PushIoContext(pIOContext); + } + else if(pIOContext->ioOperation == OP_READ) + { + if(!error && ioSize != 0) + { + _ptrCbRWLock->AcquireLockShared(); + if(_wantsIncoming && _incomingCb) + { + _incomingCb(_obj, + reinterpret_cast( + pIOContext->wsabuf.buf), + ioSize, + &pIOContext->from); + } + _ptrCbRWLock->ReleaseLockShared(); + } + WebRtc_Word32 err = PostRecv(pIOContext); + if(err == 0) + { + // The PerIoContext was posted by a thread controlled by the socket + // implementation. + pIOContext->ioInitiatedByThreadWrapper = true; + } + OutstandingCallCompleted(); + return; + } else { + // Unknown operation. Should not happen. Return pIOContext to avoid + // memory leak. + assert(false); + _mgr->PushIoContext(pIOContext); + } + OutstandingCallCompleted(); + // Don't touch any members after OutstandingCallCompleted() since the socket + // may be deleted at this point. +} + +WebRtc_Word32 UdpSocket2Windows::PostRecv() +{ + PerIoContext* pIoContext=_mgr->PopIoContext(); + if(pIoContext == 0) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "UdpSocket2Windows(%d)::PostRecv(), pIoContext == 0", + (WebRtc_Word32)this); + return -1; + } + // This function may have been called by thread not controlled by the socket + // implementation. + pIoContext->ioInitiatedByThreadWrapper = false; + return PostRecv(pIoContext); +} + +WebRtc_Word32 UdpSocket2Windows::PostRecv(PerIoContext* pIoContext) +{ + if(pIoContext==0) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "UdpSocket2Windows(%d)::PostRecv(?), pIoContext==0", + (WebRtc_Word32)this); + return -1; + } + + DWORD numOfRecivedBytes = 0; + DWORD flags = 0; + pIoContext->wsabuf.buf = pIoContext->buffer; + pIoContext->wsabuf.len = sizeof(pIoContext->buffer); + pIoContext->fromLen = sizeof(SocketAddress); + pIoContext->ioOperation = OP_READ; + WebRtc_Word32 rxError = 0; + WebRtc_Word32 nRet = 0; + WebRtc_Word32 postingSucessfull = false; + + if(!AquireSocket()) + { + _mgr->PushIoContext(pIoContext); + return -1; + } + + // Assume that the WSARecvFrom() call will be successfull to make sure that + // _outstandingCalls is positive. Roll back if WSARecvFrom() failed. + if(!NewOutstandingCall()) + { + _mgr->PushIoContext(pIoContext); + ReleaseSocket(); + return -1; + } + for(WebRtc_Word32 tries = 0; tries < 10; tries++) + { + nRet = WSARecvFrom( + _socket, + &(pIoContext->wsabuf), + 1, + &numOfRecivedBytes, + &flags, + reinterpret_cast(&(pIoContext->from)), + &(pIoContext->fromLen), + &(pIoContext->overlapped), + 0); + + if( nRet == SOCKET_ERROR) + { + rxError = WSAGetLastError(); + if(rxError != ERROR_IO_PENDING) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpSocket2Windows(%d)::PostRecv(?), WSAerror:%d when\ + posting new recieve,trie:%d", + (WebRtc_Word32)this, + rxError, + tries); + // Tell the OS that this is a good place to context switch if + // it wants to. + Sleep(0); + } + } + if((rxError == ERROR_IO_PENDING) || (nRet == 0)) + { + postingSucessfull = true; + break; + } + } + ReleaseSocket(); + + if(postingSucessfull) + { + return 0; + } + WebRtc_Word32 remainingReceiveBuffers = --_receiveBuffers; + if(remainingReceiveBuffers < 0) + { + assert(false); + } + WebRtc_Word32 error = 0; + if((error = _mgr->PushIoContext(pIoContext))) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpSocket2Windows(%d)::PostRecv(?), error:%d when PushIoContext", + (WebRtc_Word32)this, + error); + } + // Roll back. + OutstandingCallCompleted(); + return -1; +} + +void UdpSocket2Windows::CloseBlocking() +{ + LINGER lingerStruct; + + lingerStruct.l_onoff = 1; + lingerStruct.l_linger = 0; + if(AquireSocket()) + { + setsockopt(_socket, SOL_SOCKET, SO_LINGER, + reinterpret_cast(&lingerStruct), + sizeof(lingerStruct)); + ReleaseSocket(); + } + + _wantsIncoming = false; + // Reclaims the socket and prevents it from being used again. + InvalidateSocket(); + DisableNewOutstandingCalls(); + WaitForOutstandingCalls(); + delete this; +} + +bool UdpSocket2Windows::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; + } + if(overrideDSCP != 0) + { + FLOWSPEC f; + WebRtc_Word32 err = CreateFlowSpec(serviceType, tokenRate, bucketSize, + peekBandwith, minPolicedSize, + maxSduSize, &f); + if(err == -1) + { + return false; + } + + SocketAddress socketName; + struct sockaddr_in* name = + reinterpret_cast(&socketName); + int nameLength = sizeof(SocketAddress); + if(AquireSocket()) + { + getsockname(_socket, (struct sockaddr*)name, &nameLength); + ReleaseSocket(); + } + + if(serviceType == 0) + { + // Disable TOS byte setting. + return SetTrafficControl(0, -1, name, &f, &f) == 0; + } + return SetTrafficControl(overrideDSCP, -1, name, &f, &f) == 0; + } + + QOS Qos; + DWORD BytesRet; + QOS_DESTADDR QosDestaddr; + + memset (&Qos, QOS_NOT_SPECIFIED, sizeof(QOS)); + + Qos.SendingFlowspec.ServiceType = serviceType; + Qos.SendingFlowspec.TokenRate = tokenRate; + Qos.SendingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED; + Qos.SendingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED; + Qos.SendingFlowspec.DelayVariation = QOS_NOT_SPECIFIED; + Qos.SendingFlowspec.Latency = QOS_NOT_SPECIFIED; + Qos.SendingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED; + Qos.SendingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED; + + // 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; + + ZeroMemory((WebRtc_Word8 *)&QosDestaddr, sizeof(QosDestaddr)); + + OSVERSIONINFOEX osvie; + osvie.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + GetVersionEx((LPOSVERSIONINFO)&osvie); + +// Operating system Version number dwMajorVersion dwMinorVersion +// Windows 7 6.1 6 1 +// Windows Server 2008 R2 6.1 6 1 +// Windows Server 2008 6.0 6 0 +// Windows Vista 6.0 6 0 +// Windows Server 2003 R2 5.2 5 2 +// Windows Server 2003 5.2 5 2 +// Windows XP 5.1 5 1 +// Windows 2000 5.0 5 0 + + // SERVICE_NO_QOS_SIGNALING and QOS_DESTADDR should not be used if version + // is 6.0 or greater. + if(osvie.dwMajorVersion >= 6) + { + Qos.SendingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED; + Qos.ReceivingFlowspec.ServiceType = serviceType; + + } else { + Qos.SendingFlowspec.MinimumPolicedSize = + QOS_NOT_SPECIFIED | SERVICE_NO_QOS_SIGNALING; + Qos.ReceivingFlowspec.ServiceType = + serviceType | SERVICE_NO_QOS_SIGNALING; + + QosDestaddr.ObjectHdr.ObjectType = QOS_OBJECT_DESTADDR; + QosDestaddr.ObjectHdr.ObjectLength = sizeof(QosDestaddr); + QosDestaddr.SocketAddress = (SOCKADDR *)&stRemName; + if (AF_INET6 == _iProtocol) + { + QosDestaddr.SocketAddressLength = sizeof(SocketAddressInVersion6); + } else { + QosDestaddr.SocketAddressLength = sizeof(SocketAddressIn); + } + + Qos.ProviderSpecific.len = QosDestaddr.ObjectHdr.ObjectLength; + Qos.ProviderSpecific.buf = (char*)&QosDestaddr; + } + + if(!AquireSocket()) { + return false; + } + // To set QoS with SIO_SET_QOS the socket must be locally bound first + // or the call will fail with error code 10022. + WebRtc_Word32 result = WSAIoctl(GetFd(), SIO_SET_QOS, &Qos, sizeof(QOS), + NULL, 0, &BytesRet, NULL,NULL); + ReleaseSocket(); + if (result == SOCKET_ERROR) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "UdpSocket2Windows::SetQos() WSAerror : %d", + WSAGetLastError()); + return false; + } + return true; +} + +WebRtc_Word32 UdpSocket2Windows::SetTOS(WebRtc_Word32 serviceType) +{ + SocketAddress socketName; + + struct sockaddr_in* name = + reinterpret_cast(&socketName); + int nameLength = sizeof(SocketAddress); + if(AquireSocket()) + { + getsockname(_socket, (struct sockaddr*)name, &nameLength); + ReleaseSocket(); + } + + WebRtc_Word32 res = SetTrafficControl(serviceType, -1, name); + if (res == -1) + { + OSVERSIONINFO OsVersion; + OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx (&OsVersion); + + if ((OsVersion.dwMajorVersion == 4)) // NT 4.0 + { + if(SetSockopt(IPPROTO_IP,IP_TOS , + (WebRtc_Word8*)&serviceType, 4) != 0) + { + return -1; + } + } + } + return res; +} + +WebRtc_Word32 UdpSocket2Windows::SetPCP(WebRtc_Word32 pcp) +{ + SocketAddress socketName; + struct sockaddr_in* name = + reinterpret_cast(&socketName); + int nameLength = sizeof(SocketAddress); + if(AquireSocket()) + { + getsockname(_socket, (struct sockaddr*)name, &nameLength); + ReleaseSocket(); + } + return SetTrafficControl(-1, pcp, name); +} + +WebRtc_Word32 UdpSocket2Windows::SetTrafficControl( + WebRtc_Word32 dscp, + WebRtc_Word32 pcp, + const struct sockaddr_in* name, + FLOWSPEC* send, FLOWSPEC* recv) +{ + if (pcp == _pcp) + { + // No change. + pcp = -1; + } + if ((-1 == pcp) && (-1 == dscp)) + { + return 0; + } + if (!_gtc) + { + _gtc = TrafficControlWindows::GetInstance(_id); + } + if (!_gtc) + { + return -1; + } + if(_filterHandle) + { + _gtc->TcDeleteFilter(_filterHandle); + _filterHandle = NULL; + } + if(_flowHandle) + { + _gtc->TcDeleteFlow(_flowHandle); + _flowHandle = NULL; + } + if(_clientHandle) + { + _gtc->TcDeregisterClient(_clientHandle); + _clientHandle = NULL; + } + if ((0 == dscp) && (-2 == _pcp) && (-1 == pcp)) + { + // TODO (pwestin): why is this not done before deleting old filter and + // flow? This scenario should probably be documented in + // the function declaration. + return 0; + } + + 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) + { + // This is likely caused by the application not being run as + // administrator. + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "TcRegisterClient returned %d", result); + return result; + } + + // Find traffic control-enabled network interfaces that matches this + // socket's IP address. + ULONG BufferSize = 0; + result = _gtc->TcEnumerateInterfaces(ClientHandle, &BufferSize, NULL); + + if(result != NO_ERROR && result != ERROR_INSUFFICIENT_BUFFER) + { + _gtc->TcDeregisterClient(ClientHandle); + return result; + } + + if(result != ERROR_INSUFFICIENT_BUFFER) + { + // Empty buffer contains all control-enabled network interfaces. I.e. + // QoS is not enabled. + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "QOS faild since QOS is not installed on the interface"); + + _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; + + // 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)) + { + + char 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); + struct in_addr in; + in.S_un.S_addr = pIpAddr->in_addr; + if(pIpAddr->in_addr == name->sin_addr.S_un.S_addr) + { + filterSourceAddress = pIpAddr->in_addr; + addrFound = true; + } + } + if(!addrFound) + { + continue; + } else + { + break; + } + } + if(!addrFound) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "QOS faild since address is 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; + } + + // Create flow if one doesn't exist. + if (!_flow) + { + bool addPCP = ((pcp >= 0) || ((-1 == pcp) && (_pcp >= 0))); + int allocSize = sizeof(TC_GEN_FLOW) + sizeof(QOS_DS_CLASS) + + (addPCP ? sizeof(QOS_TRAFFIC_CLASS) : 0); + _flow = (PTC_GEN_FLOW)malloc(allocSize); + + _flow->SendingFlowspec.DelayVariation = QOS_NOT_SPECIFIED; + _flow->SendingFlowspec.Latency = QOS_NOT_SPECIFIED; + _flow->SendingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED; + _flow->SendingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED; + _flow->SendingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED; + _flow->SendingFlowspec.ServiceType = SERVICETYPE_BESTEFFORT; + _flow->SendingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED; + _flow->SendingFlowspec.TokenRate = QOS_NOT_SPECIFIED; + + _flow->ReceivingFlowspec.DelayVariation = QOS_NOT_SPECIFIED; + _flow->ReceivingFlowspec.Latency = QOS_NOT_SPECIFIED; + _flow->ReceivingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED; + _flow->ReceivingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED; + _flow->ReceivingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED; + _flow->ReceivingFlowspec.ServiceType = SERVICETYPE_BESTEFFORT; + _flow->ReceivingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED; + _flow->ReceivingFlowspec.TokenRate = QOS_NOT_SPECIFIED; + + QOS_DS_CLASS* dsClass = (QOS_DS_CLASS*)_flow->TcObjects; + dsClass->DSField = 0; + dsClass->ObjectHdr.ObjectType = QOS_OBJECT_DS_CLASS; + dsClass->ObjectHdr.ObjectLength = sizeof(QOS_DS_CLASS); + + if (addPCP) + { + QOS_TRAFFIC_CLASS* trafficClass = (QOS_TRAFFIC_CLASS*)(dsClass + 1); + trafficClass->TrafficClass = 0; + trafficClass->ObjectHdr.ObjectType = QOS_OBJECT_TRAFFIC_CLASS; + trafficClass->ObjectHdr.ObjectLength = sizeof(QOS_TRAFFIC_CLASS); + } + + _flow->TcObjectsLength = sizeof(QOS_DS_CLASS) + + (addPCP ? sizeof(QOS_TRAFFIC_CLASS) : 0); + } else if (-1 != pcp) { + // Reallocate memory since pcp has changed. + PTC_GEN_FLOW oldFlow = _flow; + bool addPCP = (pcp >= 0); + int allocSize = sizeof(TC_GEN_FLOW) + sizeof(QOS_DS_CLASS) + + (addPCP ? sizeof(QOS_TRAFFIC_CLASS) : 0); + _flow = (PTC_GEN_FLOW)malloc(allocSize); + + // Copy old flow. + _flow->ReceivingFlowspec = oldFlow->ReceivingFlowspec; + _flow->SendingFlowspec = oldFlow->SendingFlowspec; + // The DS info is always the first object. + QOS_DS_CLASS* dsClass = (QOS_DS_CLASS*)_flow->TcObjects; + QOS_DS_CLASS* oldDsClass = (QOS_DS_CLASS*)oldFlow->TcObjects; + dsClass->DSField = oldDsClass->DSField; + dsClass->ObjectHdr.ObjectType = oldDsClass->ObjectHdr.ObjectType; + dsClass->ObjectHdr.ObjectLength = oldDsClass->ObjectHdr.ObjectLength; + + if (addPCP) + { + QOS_TRAFFIC_CLASS* trafficClass = (QOS_TRAFFIC_CLASS*)(dsClass + 1); + trafficClass->TrafficClass = 0; + trafficClass->ObjectHdr.ObjectType = QOS_OBJECT_TRAFFIC_CLASS; + trafficClass->ObjectHdr.ObjectLength = sizeof(QOS_TRAFFIC_CLASS); + } + + _flow->TcObjectsLength = sizeof(QOS_DS_CLASS) + + (addPCP ? sizeof(QOS_TRAFFIC_CLASS) : 0); + free(oldFlow); + } + + // Setup send and receive flow and DS object. + if (dscp >= 0) + { + if (!send || (0 == dscp)) + { + _flow->SendingFlowspec.DelayVariation = QOS_NOT_SPECIFIED; + _flow->SendingFlowspec.Latency = QOS_NOT_SPECIFIED; + _flow->SendingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED; + _flow->SendingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED; + _flow->SendingFlowspec.PeakBandwidth = + (0 == dscp ? QOS_NOT_SPECIFIED : POSITIVE_INFINITY_RATE); + _flow->SendingFlowspec.ServiceType = SERVICETYPE_BESTEFFORT; + _flow->SendingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED; + // 128000 * 10 is 10mbit/s. + _flow->SendingFlowspec.TokenRate = + (0 == dscp ? QOS_NOT_SPECIFIED : 128000 * 10); + } + else + { + _flow->SendingFlowspec.DelayVariation = send->DelayVariation; + _flow->SendingFlowspec.Latency = send->Latency; + _flow->SendingFlowspec.MaxSduSize = send->MaxSduSize; + _flow->SendingFlowspec.MinimumPolicedSize = + send->MinimumPolicedSize; + _flow->SendingFlowspec.PeakBandwidth = send->PeakBandwidth; + _flow->SendingFlowspec.PeakBandwidth = POSITIVE_INFINITY_RATE; + _flow->SendingFlowspec.ServiceType = send->ServiceType; + _flow->SendingFlowspec.TokenBucketSize = send->TokenBucketSize; + _flow->SendingFlowspec.TokenRate = send->TokenRate; + } + + if (!recv || (0 == dscp)) + { + _flow->ReceivingFlowspec.DelayVariation = + _flow->SendingFlowspec.DelayVariation; + _flow->ReceivingFlowspec.Latency = _flow->SendingFlowspec.Latency; + _flow->ReceivingFlowspec.MaxSduSize = + _flow->SendingFlowspec.MaxSduSize; + _flow->ReceivingFlowspec.MinimumPolicedSize = + _flow->SendingFlowspec.MinimumPolicedSize; + _flow->ReceivingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED; + _flow->ReceivingFlowspec.ServiceType = + 0 == dscp ? SERVICETYPE_BESTEFFORT : SERVICETYPE_CONTROLLEDLOAD; + _flow->ReceivingFlowspec.TokenBucketSize = + _flow->SendingFlowspec.TokenBucketSize; + _flow->ReceivingFlowspec.TokenRate = + _flow->SendingFlowspec.TokenRate; + } else { + _flow->ReceivingFlowspec.DelayVariation = recv->DelayVariation; + _flow->ReceivingFlowspec.Latency = recv->Latency; + _flow->ReceivingFlowspec.MaxSduSize = recv->MaxSduSize; + _flow->ReceivingFlowspec.MinimumPolicedSize = + recv->MinimumPolicedSize; + _flow->ReceivingFlowspec.PeakBandwidth = recv->PeakBandwidth; + _flow->ReceivingFlowspec.ServiceType = recv->ServiceType; + _flow->ReceivingFlowspec.TokenBucketSize = recv->TokenBucketSize; + _flow->ReceivingFlowspec.TokenRate = QOS_NOT_SPECIFIED; + } + + // Setup DS (for DSCP value). + // DS is always the first object. + QOS_DS_CLASS* dsClass = (QOS_DS_CLASS*)_flow->TcObjects; + dsClass->DSField = dscp; + } + + // Setup PCP (802.1p priority in 802.1Q/VLAN tagging) + if (pcp >= 0) + { + // DS is always first object. + QOS_DS_CLASS* dsClass = (QOS_DS_CLASS*)_flow->TcObjects; + QOS_TRAFFIC_CLASS* trafficClass = (QOS_TRAFFIC_CLASS*)(dsClass + 1); + trafficClass->TrafficClass = pcp; + } + + result = _gtc->TcAddFlow(ifcHandle, NULL, 0, _flow, &iflowHandle); + if(result != NO_ERROR) + { + _gtc->TcCloseInterface(ifcHandle); + _gtc->TcDeregisterClient(ClientHandle); + free(pInterfaceBuffer); + return -1; + } + + IP_PATTERN filterPattern, mask; + + ZeroMemory((WebRtc_Word8*)&filterPattern, sizeof(IP_PATTERN)); + ZeroMemory((WebRtc_Word8*)&mask, sizeof(IP_PATTERN)); + + filterPattern.ProtocolId = IPPROTO_UDP; + // "name" fields already 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); + + result = _gtc->TcAddFilter(iflowHandle, &filter, &iFilterHandle); + if(result != NO_ERROR) + { + _gtc->TcDeleteFlow(iflowHandle); + _gtc->TcCloseInterface(ifcHandle); + _gtc->TcDeregisterClient(ClientHandle); + free(pInterfaceBuffer); + return result; + } + + _flowHandle = iflowHandle; + _filterHandle = iFilterHandle; + _clientHandle = ClientHandle; + if (-1 != pcp) + { + _pcp = pcp; + } + + _gtc->TcCloseInterface(ifcHandle); + free(pInterfaceBuffer); + + return 0; +} + +WebRtc_Word32 UdpSocket2Windows::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 = QOS_NOT_SPECIFIED; + f->PeakBandwidth = QOS_NOT_SPECIFIED; + f->DelayVariation = QOS_NOT_SPECIFIED; + f->Latency = QOS_NOT_SPECIFIED; + f->MaxSduSize = QOS_NOT_SPECIFIED; + f->MinimumPolicedSize = QOS_NOT_SPECIFIED; + return 0; +} + +bool UdpSocket2Windows::NewOutstandingCall() +{ + assert(!_outstandingCallsDisabled); + + ++_outstandingCalls; + return true; +} + +void UdpSocket2Windows::OutstandingCallCompleted() +{ + _ptrDestRWLock->AcquireLockShared(); + ++_outstandingCallComplete; + if((--_outstandingCalls == 0) && _outstandingCallsDisabled) + { + // When there are no outstanding calls and new outstanding calls are + // disabled it is time to terminate. + _terminate = true; + } + _ptrDestRWLock->ReleaseLockShared(); + + if((--_outstandingCallComplete == 0) && + (_terminate)) + { + // Only one thread will enter here. The thread with the last outstanding + // call. + CriticalSectionScoped cs(_ptrDeleteCrit); + _safeTodelete = true; + _ptrDeleteCond->Wake(); + } +} + +void UdpSocket2Windows::DisableNewOutstandingCalls() +{ + _ptrDestRWLock->AcquireLockExclusive(); + if(_outstandingCallsDisabled) + { + // Outstandning calls are already disabled. + _ptrDestRWLock->ReleaseLockExclusive(); + return; + } + _outstandingCallsDisabled = true; + const bool noOutstandingCalls = (_outstandingCalls.Value() == 0); + _ptrDestRWLock->ReleaseLockExclusive(); + + RemoveSocketFromManager(); + + if(noOutstandingCalls) + { + CriticalSectionScoped cs(_ptrDeleteCrit); + _safeTodelete = true; + _ptrDeleteCond->Wake(); + } +} + +void UdpSocket2Windows::WaitForOutstandingCalls() +{ + CriticalSectionScoped cs(_ptrDeleteCrit); + while(!_safeTodelete) + { + _ptrDeleteCond->SleepCS(*_ptrDeleteCrit); + } +} + +void UdpSocket2Windows::RemoveSocketFromManager() +{ + // New outstanding calls should be disabled at this point. + assert(_outstandingCallsDisabled); + + if(_addedToMgr) + { + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, + "calling UdpSocketManager::RemoveSocket()"); + if(_mgr->RemoveSocket(this)) + { + _addedToMgr=false; + } + } +} + +bool UdpSocket2Windows::AquireSocket() +{ + _ptrSocketRWLock->AcquireLockShared(); + const bool returnValue = _socket != INVALID_SOCKET; + if(!returnValue) + { + _ptrSocketRWLock->ReleaseLockShared(); + } + return returnValue; +} + +void UdpSocket2Windows::ReleaseSocket() +{ + _ptrSocketRWLock->ReleaseLockShared(); +} + +bool UdpSocket2Windows::InvalidateSocket() +{ + _ptrSocketRWLock->AcquireLockExclusive(); + if(_socket == INVALID_SOCKET) + { + _ptrSocketRWLock->ReleaseLockExclusive(); + return true; + } + // Give the socket back to the system. All socket calls will fail from now + // on. + if(closesocket(_socket) == SOCKET_ERROR) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "UdpSocket2Windows(%d)::InvalidateSocket() WSAerror: %d", + (WebRtc_Word32)this, WSAGetLastError()); + } + _socket = INVALID_SOCKET; + _ptrSocketRWLock->ReleaseLockExclusive(); + return true; +} + +} // namespace test +} // namespace webrtc diff --git a/webrtc/test/channel_transport/udp_socket2_win.h b/webrtc/test/channel_transport/udp_socket2_win.h new file mode 100644 index 0000000000..624db9b3bf --- /dev/null +++ b/webrtc/test/channel_transport/udp_socket2_win.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2012 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. + */ + +#ifndef WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET2_WINDOWS_H_ +#define WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET2_WINDOWS_H_ + +// Disable deprication warning from traffic.h +#pragma warning(disable : 4995) + +// Don't change include order for these header files. +#include +#include +#include + +#include "webrtc/system_wrappers/interface/atomic32.h" +#include "webrtc/system_wrappers/interface/condition_variable_wrapper.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/event_wrapper.h" +#include "webrtc/system_wrappers/interface/list_wrapper.h" +#include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" +#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/test/channel_transport/udp_socket_wrapper.h" +#include "webrtc/test/channel_transport/udp_socket2_manager_win.h" + +namespace webrtc { +namespace test { + +class UdpSocket2ManagerWindows; +class TrafficControlWindows; +struct PerIoContext; + +class UdpSocket2Windows : public UdpSocketWrapper +{ +public: + UdpSocket2Windows(const WebRtc_Word32 id, UdpSocketManager* mgr, + bool ipV6Enable = false, bool disableGQOS = false); + virtual ~UdpSocket2Windows(); + + virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); + + virtual bool ValidHandle(); + + virtual bool SetCallback(CallbackObj, IncomingSocketCallback); + + virtual bool Bind(const SocketAddress& name); + virtual bool SetSockopt(WebRtc_Word32 level, WebRtc_Word32 optname, + const WebRtc_Word8* optval, WebRtc_Word32 optlen); + + virtual bool StartReceiving(const WebRtc_UWord32 receiveBuffers); + virtual inline bool StartReceiving() {return StartReceiving(8);} + virtual bool StopReceiving(); + + virtual WebRtc_Word32 SendTo(const WebRtc_Word8* buf, WebRtc_Word32 len, + const SocketAddress& to); + + virtual void CloseBlocking(); + + virtual SOCKET GetFd() { return _socket;} + virtual bool 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 = 0); + + virtual WebRtc_Word32 SetTOS(const WebRtc_Word32 serviceType); + virtual WebRtc_Word32 SetPCP(const WebRtc_Word32 pcp); + + virtual WebRtc_UWord32 ReceiveBuffers(){return _receiveBuffers.Value();} + +protected: + void IOCompleted(PerIoContext* pIOContext, WebRtc_UWord32 ioSize, + WebRtc_UWord32 error); + + WebRtc_Word32 PostRecv(); + // Use pIoContext to post a new WSARecvFrom(..). + WebRtc_Word32 PostRecv(PerIoContext* pIoContext); + +private: + friend class UdpSocket2WorkerWindows; + + // Set traffic control (TC) flow adding it the interface that matches this + // sockets address. + // A filter is created and added to the flow. + // The flow consists of: + // (1) QoS send and receive information (flow specifications). + // (2) A DS object (for specifying exact DSCP value). + // (3) Possibly a traffic object (for specifying exact 802.1p priority (PCP) + // value). + // + // dscp values: + // -1 don't change the current dscp value. + // 0 don't add any flow to TC, unless pcp is specified. + // 1-63 Add a flow to TC with the specified dscp value. + // pcp values: + // -2 Don't add pcp info to the flow, (3) will not be added. + // -1 Don't change the current value. + // 0-7 Add pcp info to the flow with the specified value, + // (3) will be added. + // + // If both dscp and pcp are -1 no flow will be created or added to TC. + // If dscp is 0 and pcp is 0-7 (1), (2) and (3) will be created. + // Note: input parameter values are assumed to be in valid range, checks + // must be done by caller. + WebRtc_Word32 SetTrafficControl(WebRtc_Word32 dscp, WebRtc_Word32 pcp, + const struct sockaddr_in* name, + FLOWSPEC* send = NULL, + FLOWSPEC* recv = NULL); + WebRtc_Word32 CreateFlowSpec(WebRtc_Word32 serviceType, + WebRtc_Word32 tokenRate, + WebRtc_Word32 bucketSize, + WebRtc_Word32 peekBandwith, + WebRtc_Word32 minPolicedSize, + WebRtc_Word32 maxSduSize, FLOWSPEC *f); + + WebRtc_Word32 _id; + RWLockWrapper* _ptrCbRWLock; + IncomingSocketCallback _incomingCb; + CallbackObj _obj; + bool _qos; + + SocketAddress _remoteAddr; + SOCKET _socket; + WebRtc_Word32 _iProtocol; + UdpSocket2ManagerWindows* _mgr; + + CriticalSectionWrapper* _pCrit; + Atomic32 _outstandingCalls; + Atomic32 _outstandingCallComplete; + volatile bool _terminate; + volatile bool _addedToMgr; + + CriticalSectionWrapper* _ptrDeleteCrit; + ConditionVariableWrapper* _ptrDeleteCond; + bool _safeTodelete; + + RWLockWrapper* _ptrDestRWLock; + bool _outstandingCallsDisabled; + bool NewOutstandingCall(); + void OutstandingCallCompleted(); + void DisableNewOutstandingCalls(); + void WaitForOutstandingCalls(); + + void RemoveSocketFromManager(); + + // RWLockWrapper is used as a reference counter for the socket. Write lock + // is used for creating and deleting socket. Read lock is used for + // accessing the socket. + RWLockWrapper* _ptrSocketRWLock; + bool AquireSocket(); + void ReleaseSocket(); + bool InvalidateSocket(); + + // Traffic control handles and structure pointers. + HANDLE _clientHandle; + HANDLE _flowHandle; + HANDLE _filterHandle; + PTC_GEN_FLOW _flow; + // TrafficControlWindows implements TOS and PCP. + TrafficControlWindows* _gtc; + // Holds the current pcp value. Can be -2 or 0 - 7. + int _pcp; + + Atomic32 _receiveBuffers; +}; + +} // namespace test +} // namespace webrtc + +#endif // WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET2_WINDOWS_H_ diff --git a/webrtc/test/channel_transport/udp_socket_manager_posix.cc b/webrtc/test/channel_transport/udp_socket_manager_posix.cc new file mode 100644 index 0000000000..874164164e --- /dev/null +++ b/webrtc/test/channel_transport/udp_socket_manager_posix.cc @@ -0,0 +1,431 @@ +/* + * 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 "webrtc/test/channel_transport/udp_socket_manager_posix.h" + +#include +#include +#include +#include +#include +#include + +#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/test/channel_transport/udp_socket_posix.h" + +namespace webrtc { +namespace test { + +UdpSocketManagerPosix::UdpSocketManagerPosix() + : UdpSocketManager(), + _id(-1), + _critSect(CriticalSectionWrapper::CreateCriticalSection()), + _numberOfSocketMgr(-1), + _incSocketMgrNextTime(0), + _nextSocketMgrToAssign(0), + _socketMgr() +{ +} + +bool UdpSocketManagerPosix::Init(WebRtc_Word32 id, + WebRtc_UWord8& numOfWorkThreads) { + CriticalSectionScoped cs(_critSect); + if ((_id != -1) || (_numOfWorkThreads != 0)) { + assert(_id != -1); + assert(_numOfWorkThreads != 0); + return false; + } + + _id = id; + _numberOfSocketMgr = numOfWorkThreads; + _numOfWorkThreads = numOfWorkThreads; + + if(MAX_NUMBER_OF_SOCKET_MANAGERS_LINUX < _numberOfSocketMgr) + { + _numberOfSocketMgr = MAX_NUMBER_OF_SOCKET_MANAGERS_LINUX; + } + for(int i = 0;i < _numberOfSocketMgr; i++) + { + _socketMgr[i] = new UdpSocketManagerPosixImpl(); + } + return true; +} + + +UdpSocketManagerPosix::~UdpSocketManagerPosix() +{ + Stop(); + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, + "UdpSocketManagerPosix(%d)::UdpSocketManagerPosix()", + _numberOfSocketMgr); + + for(int i = 0;i < _numberOfSocketMgr; i++) + { + delete _socketMgr[i]; + } + delete _critSect; +} + +WebRtc_Word32 UdpSocketManagerPosix::ChangeUniqueId(const WebRtc_Word32 id) +{ + _id = id; + return 0; +} + +bool UdpSocketManagerPosix::Start() +{ + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, + "UdpSocketManagerPosix(%d)::Start()", + _numberOfSocketMgr); + + _critSect->Enter(); + bool retVal = true; + for(int i = 0;i < _numberOfSocketMgr && retVal; i++) + { + retVal = _socketMgr[i]->Start(); + } + if(!retVal) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpSocketManagerPosix(%d)::Start() error starting socket managers", + _numberOfSocketMgr); + } + _critSect->Leave(); + return retVal; +} + +bool UdpSocketManagerPosix::Stop() +{ + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, + "UdpSocketManagerPosix(%d)::Stop()",_numberOfSocketMgr); + + _critSect->Enter(); + bool retVal = true; + for(int i = 0; i < _numberOfSocketMgr && retVal; i++) + { + retVal = _socketMgr[i]->Stop(); + } + if(!retVal) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpSocketManagerPosix(%d)::Stop() there are still active socket " + "managers", + _numberOfSocketMgr); + } + _critSect->Leave(); + return retVal; +} + +bool UdpSocketManagerPosix::AddSocket(UdpSocketWrapper* s) +{ + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, + "UdpSocketManagerPosix(%d)::AddSocket()",_numberOfSocketMgr); + + _critSect->Enter(); + bool retVal = _socketMgr[_nextSocketMgrToAssign]->AddSocket(s); + if(!retVal) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpSocketManagerPosix(%d)::AddSocket() failed to add socket to\ + manager", + _numberOfSocketMgr); + } + + // Distribute sockets on UdpSocketManagerPosixImpls in a round-robin + // fashion. + if(_incSocketMgrNextTime == 0) + { + _incSocketMgrNextTime++; + } else { + _incSocketMgrNextTime = 0; + _nextSocketMgrToAssign++; + if(_nextSocketMgrToAssign >= _numberOfSocketMgr) + { + _nextSocketMgrToAssign = 0; + } + } + _critSect->Leave(); + return retVal; +} + +bool UdpSocketManagerPosix::RemoveSocket(UdpSocketWrapper* s) +{ + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, + "UdpSocketManagerPosix(%d)::RemoveSocket()", + _numberOfSocketMgr); + + _critSect->Enter(); + bool retVal = false; + for(int i = 0;i < _numberOfSocketMgr && (retVal == false); i++) + { + retVal = _socketMgr[i]->RemoveSocket(s); + } + if(!retVal) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpSocketManagerPosix(%d)::RemoveSocket() failed to remove socket\ + from manager", + _numberOfSocketMgr); + } + _critSect->Leave(); + return retVal; +} + + +UdpSocketManagerPosixImpl::UdpSocketManagerPosixImpl() +{ + _critSectList = CriticalSectionWrapper::CreateCriticalSection(); + _thread = ThreadWrapper::CreateThread(UdpSocketManagerPosixImpl::Run, this, + kRealtimePriority, + "UdpSocketManagerPosixImplThread"); + FD_ZERO(&_readFds); + WEBRTC_TRACE(kTraceMemory, kTraceTransport, -1, + "UdpSocketManagerPosix created"); +} + +UdpSocketManagerPosixImpl::~UdpSocketManagerPosixImpl() +{ + if(_thread != NULL) + { + delete _thread; + } + + if (_critSectList != NULL) + { + UpdateSocketMap(); + + _critSectList->Enter(); + + MapItem* item = _socketMap.First(); + while(item) + { + UdpSocketPosix* s = static_cast(item->GetItem()); + _socketMap.Erase(item); + item = _socketMap.First(); + delete s; + } + _critSectList->Leave(); + + delete _critSectList; + } + + WEBRTC_TRACE(kTraceMemory, kTraceTransport, -1, + "UdpSocketManagerPosix deleted"); +} + +bool UdpSocketManagerPosixImpl::Start() +{ + unsigned int id = 0; + if (_thread == NULL) + { + return false; + } + + WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, -1, + "Start UdpSocketManagerPosix"); + return _thread->Start(id); +} + +bool UdpSocketManagerPosixImpl::Stop() +{ + if (_thread == NULL) + { + return true; + } + + WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, -1, + "Stop UdpSocketManagerPosix"); + return _thread->Stop(); +} + +bool UdpSocketManagerPosixImpl::Process() +{ + bool doSelect = false; + // Timeout = 1 second. + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 10000; + MapItem* it; + + FD_ZERO(&_readFds); + + UpdateSocketMap(); + + unsigned int maxFd = 0; + for (it = _socketMap.First(); it != NULL; it=_socketMap.Next(it)) + { + doSelect = true; + maxFd = maxFd > it->GetUnsignedId() ? maxFd : it->GetUnsignedId(); + FD_SET(it->GetUnsignedId(), &_readFds); + } + + int num = 0; + if (doSelect) + { + num = select(maxFd+1, &_readFds, NULL, NULL, &timeout); + + if (num == SOCKET_ERROR) + { + // Timeout = 10 ms. + timespec t; + t.tv_sec = 0; + t.tv_nsec = 10000*1000; + nanosleep(&t, NULL); + return true; + } + }else + { + // Timeout = 10 ms. + timespec t; + t.tv_sec = 0; + t.tv_nsec = 10000*1000; + nanosleep(&t, NULL); + return true; + } + + for (it = _socketMap.First(); it != NULL && num > 0; + it = _socketMap.Next(it)) + { + UdpSocketPosix* s = static_cast(it->GetItem()); + if (FD_ISSET(it->GetUnsignedId(), &_readFds)) + { + s->HasIncoming(); + num--; + } + } + return true; +} + +bool UdpSocketManagerPosixImpl::Run(ThreadObj obj) +{ + UdpSocketManagerPosixImpl* mgr = + static_cast(obj); + return mgr->Process(); +} + +bool UdpSocketManagerPosixImpl::AddSocket(UdpSocketWrapper* s) +{ + UdpSocketPosix* sl = static_cast(s); + if(sl->GetFd() == INVALID_SOCKET || !(sl->GetFd() < FD_SETSIZE)) + { + return false; + } + _critSectList->Enter(); + _addList.PushBack(s); + _critSectList->Leave(); + return true; +} + +bool UdpSocketManagerPosixImpl::RemoveSocket(UdpSocketWrapper* s) +{ + // Put in remove list if this is the correct UdpSocketManagerPosixImpl. + _critSectList->Enter(); + + // If the socket is in the add list it's safe to remove and delete it. + ListItem* addListItem = _addList.First(); + while(addListItem) + { + UdpSocketPosix* addSocket = (UdpSocketPosix*)addListItem->GetItem(); + unsigned int addFD = addSocket->GetFd(); + unsigned int removeFD = static_cast(s)->GetFd(); + if(removeFD == addFD) + { + _removeList.PushBack(removeFD); + _critSectList->Leave(); + return true; + } + addListItem = _addList.Next(addListItem); + } + + // Checking the socket map is safe since all Erase and Insert calls to this + // map are also protected by _critSectList. + if(_socketMap.Find(static_cast(s)->GetFd()) != NULL) + { + _removeList.PushBack(static_cast(s)->GetFd()); + _critSectList->Leave(); + return true; + } + _critSectList->Leave(); + return false; +} + +void UdpSocketManagerPosixImpl::UpdateSocketMap() +{ + // Remove items in remove list. + _critSectList->Enter(); + while(!_removeList.Empty()) + { + UdpSocketPosix* deleteSocket = NULL; + unsigned int removeFD = _removeList.First()->GetUnsignedItem(); + + // If the socket is in the add list it hasn't been added to the socket + // map yet. Just remove the socket from the add list. + ListItem* addListItem = _addList.First(); + while(addListItem) + { + UdpSocketPosix* addSocket = (UdpSocketPosix*)addListItem->GetItem(); + unsigned int addFD = addSocket->GetFd(); + if(removeFD == addFD) + { + deleteSocket = addSocket; + _addList.Erase(addListItem); + break; + } + addListItem = _addList.Next(addListItem); + } + + // Find and remove socket from _socketMap. + MapItem* it = _socketMap.Find(removeFD); + if(it != NULL) + { + UdpSocketPosix* socket = + static_cast(it->GetItem()); + if(socket) + { + deleteSocket = socket; + } + _socketMap.Erase(it); + } + if(deleteSocket) + { + deleteSocket->ReadyForDeletion(); + delete deleteSocket; + } + _removeList.PopFront(); + } + + // Add sockets from add list. + while(!_addList.Empty()) + { + UdpSocketPosix* s = + static_cast(_addList.First()->GetItem()); + if(s) + { + _socketMap.Insert(s->GetFd(), s); + } + _addList.PopFront(); + } + _critSectList->Leave(); +} + +} // namespace test +} // namespace webrtc diff --git a/webrtc/test/channel_transport/udp_socket_manager_posix.h b/webrtc/test/channel_transport/udp_socket_manager_posix.h new file mode 100644 index 0000000000..e6df98e302 --- /dev/null +++ b/webrtc/test/channel_transport/udp_socket_manager_posix.h @@ -0,0 +1,89 @@ +/* + * 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. + */ + +#ifndef WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET_MANAGER_POSIX_H_ +#define WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET_MANAGER_POSIX_H_ + +#include +#include + +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/list_wrapper.h" +#include "webrtc/system_wrappers/interface/map_wrapper.h" +#include "webrtc/system_wrappers/interface/thread_wrapper.h" +#include "webrtc/test/channel_transport/udp_socket_manager_wrapper.h" +#include "webrtc/test/channel_transport/udp_socket_wrapper.h" + +namespace webrtc { + +class ConditionVariableWrapper; + +namespace test { + +class UdpSocketManagerPosixImpl; +#define MAX_NUMBER_OF_SOCKET_MANAGERS_LINUX 8 + +class UdpSocketManagerPosix : public UdpSocketManager +{ +public: + UdpSocketManagerPosix(); + virtual ~UdpSocketManagerPosix(); + + virtual bool Init(WebRtc_Word32 id, + WebRtc_UWord8& numOfWorkThreads); + + virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); + + virtual bool Start(); + virtual bool Stop(); + + virtual bool AddSocket(UdpSocketWrapper* s); + virtual bool RemoveSocket(UdpSocketWrapper* s); +private: + WebRtc_Word32 _id; + CriticalSectionWrapper* _critSect; + WebRtc_UWord8 _numberOfSocketMgr; + WebRtc_UWord8 _incSocketMgrNextTime; + WebRtc_UWord8 _nextSocketMgrToAssign; + UdpSocketManagerPosixImpl* _socketMgr[MAX_NUMBER_OF_SOCKET_MANAGERS_LINUX]; +}; + +class UdpSocketManagerPosixImpl +{ +public: + UdpSocketManagerPosixImpl(); + virtual ~UdpSocketManagerPosixImpl(); + + virtual bool Start(); + virtual bool Stop(); + + virtual bool AddSocket(UdpSocketWrapper* s); + virtual bool RemoveSocket(UdpSocketWrapper* s); + +protected: + static bool Run(ThreadObj obj); + bool Process(); + void UpdateSocketMap(); + +private: + ThreadWrapper* _thread; + CriticalSectionWrapper* _critSectList; + + fd_set _readFds; + + MapWrapper _socketMap; + ListWrapper _addList; + ListWrapper _removeList; +}; + +} // namespace test +} // namespace webrtc + +#endif // WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET_MANAGER_POSIX_H_ diff --git a/webrtc/test/channel_transport/udp_socket_manager_unittest.cc b/webrtc/test/channel_transport/udp_socket_manager_unittest.cc new file mode 100644 index 0000000000..8045dfe4c3 --- /dev/null +++ b/webrtc/test/channel_transport/udp_socket_manager_unittest.cc @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2012 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. + */ + +// Tests for the UdpSocketManager interface. +// Note: This tests UdpSocketManager together with UdpSocketWrapper, +// due to the way the code is full of static-casts to the platform dependent +// subtypes. +// It also uses the static UdpSocketManager object. +// The most important property of these tests is that they do not leak memory. + +#include "gtest/gtest.h" +#include "webrtc/test/channel_transport/udp_socket_wrapper.h" +#include "webrtc/test/channel_transport/udp_socket_manager_wrapper.h" +#include "webrtc/system_wrappers/interface/trace.h" + +namespace webrtc { +namespace test { + +TEST(UdpSocketManager, CreateCallsInitAndDoesNotLeakMemory) { + WebRtc_Word32 id = 42; + WebRtc_UWord8 threads = 1; + UdpSocketManager* mgr = UdpSocketManager::Create(id, threads); + // Create is supposed to have called init on the object. + EXPECT_FALSE(mgr->Init(id, threads)) + << "Init should return false since Create is supposed to call it."; + UdpSocketManager::Return(); +} + +// Creates a socket and adds it to the socket manager, and then removes it +// before destroying the socket manager. +TEST(UdpSocketManager, AddAndRemoveSocketDoesNotLeakMemory) { + WebRtc_Word32 id = 42; + WebRtc_UWord8 threads = 1; + UdpSocketManager* mgr = UdpSocketManager::Create(id, threads); + UdpSocketWrapper* socket = + UdpSocketWrapper::CreateSocket(id, + mgr, + NULL, // CallbackObj + NULL, // IncomingSocketCallback + false, // ipV6Enable + false); // disableGQOS + // The constructor will do AddSocket on the manager. + // RemoveSocket indirectly calls Delete. + EXPECT_EQ(true, mgr->RemoveSocket(socket)); + UdpSocketManager::Return(); +} + +// Creates a socket and add it to the socket manager, but does not remove it +// before destroying the socket manager. +// On Posix, this destroys the socket. +// On Winsock2 Windows, it enters an infinite wait for all the sockets +// to go away. +TEST(UdpSocketManager, UnremovedSocketsGetCollectedAtManagerDeletion) { +#if defined(_WIN32) + // It's hard to test an infinite wait, so we don't. +#else + WebRtc_Word32 id = 42; + WebRtc_UWord8 threads = 1; + UdpSocketManager* mgr = UdpSocketManager::Create(id, threads); + UdpSocketWrapper* unused_socket = UdpSocketWrapper::CreateSocket( + id, + mgr, + NULL, // CallbackObj + NULL, // IncomingSocketCallback + false, // ipV6Enable + false); // disableGQOS + // The constructor will do AddSocket on the manager. + // Call a member funtion to work around "set but not used" compliation + // error on ChromeOS ARM. + unused_socket->SetEventToNull(); + unused_socket = NULL; + UdpSocketManager::Return(); +#endif +} + +} // namespace test +} // namespace webrtc diff --git a/webrtc/test/channel_transport/udp_socket_manager_wrapper.cc b/webrtc/test/channel_transport/udp_socket_manager_wrapper.cc new file mode 100644 index 0000000000..e26e649a28 --- /dev/null +++ b/webrtc/test/channel_transport/udp_socket_manager_wrapper.cc @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2012 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 "webrtc/test/channel_transport/udp_socket_manager_wrapper.h" + +#include + +#ifdef _WIN32 +#include "webrtc/system_wrappers/interface/fix_interlocked_exchange_pointer_win.h" +#include "webrtc/test/channel_transport/udp_socket2_manager_win.h" +#else +#include "webrtc/test/channel_transport/udp_socket_manager_posix.h" +#endif + +namespace webrtc { +namespace test { + +UdpSocketManager* UdpSocketManager::CreateInstance() +{ +#if defined(_WIN32) + return static_cast(new UdpSocket2ManagerWindows()); +#else + return new UdpSocketManagerPosix(); +#endif +} + +UdpSocketManager* UdpSocketManager::StaticInstance( + CountOperation count_operation, + const WebRtc_Word32 id, + WebRtc_UWord8& numOfWorkThreads) +{ + UdpSocketManager* impl = + GetStaticInstance(count_operation); + if (count_operation == kAddRef && impl != NULL) { + if (impl->Init(id, numOfWorkThreads)) { + impl->Start(); + } + } + return impl; +} + +UdpSocketManager* UdpSocketManager::Create(const WebRtc_Word32 id, + WebRtc_UWord8& numOfWorkThreads) +{ + return UdpSocketManager::StaticInstance(kAddRef, id, numOfWorkThreads); +} + +void UdpSocketManager::Return() +{ + WebRtc_UWord8 numOfWorkThreads = 0; + UdpSocketManager::StaticInstance(kRelease, -1, + numOfWorkThreads); +} + +UdpSocketManager::UdpSocketManager() : _numOfWorkThreads(0) +{ +} + +WebRtc_UWord8 UdpSocketManager::WorkThreads() const +{ + return _numOfWorkThreads; +} + +} // namespace test +} // namespace webrtc diff --git a/webrtc/test/channel_transport/udp_socket_manager_wrapper.h b/webrtc/test/channel_transport/udp_socket_manager_wrapper.h new file mode 100644 index 0000000000..2cad1cbb9e --- /dev/null +++ b/webrtc/test/channel_transport/udp_socket_manager_wrapper.h @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#ifndef WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET_MANAGER_WRAPPER_H_ +#define WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET_MANAGER_WRAPPER_H_ + +#include "webrtc/system_wrappers/interface/static_instance.h" +#include "webrtc/typedefs.h" + +namespace webrtc { +namespace test { + +class UdpSocketWrapper; + +class UdpSocketManager +{ +public: + static UdpSocketManager* Create(const WebRtc_Word32 id, + WebRtc_UWord8& numOfWorkThreads); + static void Return(); + + // Initializes the socket manager. Returns true if the manager wasn't + // already initialized. + virtual bool Init(WebRtc_Word32 id, + WebRtc_UWord8& numOfWorkThreads) = 0; + + virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id) = 0; + + // Start listening to sockets that have been registered via the + // AddSocket(..) API. + virtual bool Start() = 0; + // Stop listening to sockets. + virtual bool Stop() = 0; + + virtual WebRtc_UWord8 WorkThreads() const; + + // Register a socket with the socket manager. + virtual bool AddSocket(UdpSocketWrapper* s) = 0; + // Unregister a socket from the manager. + virtual bool RemoveSocket(UdpSocketWrapper* s) = 0; + +protected: + UdpSocketManager(); + virtual ~UdpSocketManager() {} + + WebRtc_UWord8 _numOfWorkThreads; + + // Factory method. + static UdpSocketManager* CreateInstance(); + +private: + // Friend function to allow the UDP destructor to be accessed from the + // instance template. + friend UdpSocketManager* webrtc::GetStaticInstance( + CountOperation count_operation); + + static UdpSocketManager* StaticInstance( + CountOperation count_operation, + const WebRtc_Word32 id, + WebRtc_UWord8& numOfWorkThreads); +}; + +} // namespace test +} // namespace webrtc + +#endif // WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET_MANAGER_WRAPPER_H_ diff --git a/webrtc/test/channel_transport/udp_socket_posix.cc b/webrtc/test/channel_transport/udp_socket_posix.cc new file mode 100644 index 0000000000..016526c3b8 --- /dev/null +++ b/webrtc/test/channel_transport/udp_socket_posix.cc @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2012 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 "webrtc/test/channel_transport/udp_socket_posix.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/test/channel_transport/udp_socket_manager_wrapper.h" +#include "webrtc/test/channel_transport/udp_socket_wrapper.h" + +namespace webrtc { +namespace test { +UdpSocketPosix::UdpSocketPosix(const WebRtc_Word32 id, UdpSocketManager* mgr, + bool ipV6Enable) +{ + WEBRTC_TRACE(kTraceMemory, kTraceTransport, id, + "UdpSocketPosix::UdpSocketPosix()"); + + _wantsIncoming = false; + _error = 0; + _mgr = mgr; + + _id = id; + _obj = NULL; + _incomingCb = NULL; + _readyForDeletionCond = ConditionVariableWrapper::CreateConditionVariable(); + _closeBlockingCompletedCond = + ConditionVariableWrapper::CreateConditionVariable(); + _cs = CriticalSectionWrapper::CreateCriticalSection(); + _readyForDeletion = false; + _closeBlockingActive = false; + _closeBlockingCompleted= false; + if(ipV6Enable) + { + _socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + } + else { + _socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + } + + // Set socket to nonblocking mode. + int enable_non_blocking = 1; + if(ioctl(_socket, FIONBIO, &enable_non_blocking) == -1) + { + WEBRTC_TRACE(kTraceWarning, kTraceTransport, id, + "Failed to make socket nonblocking"); + } + // Enable close on fork for file descriptor so that it will not block until + // forked process terminates. + if(fcntl(_socket, F_SETFD, FD_CLOEXEC) == -1) + { + WEBRTC_TRACE(kTraceWarning, kTraceTransport, id, + "Failed to set FD_CLOEXEC for socket"); + } +} + +UdpSocketPosix::~UdpSocketPosix() +{ + if(_socket != INVALID_SOCKET) + { + close(_socket); + _socket = INVALID_SOCKET; + } + if(_readyForDeletionCond) + { + delete _readyForDeletionCond; + } + + if(_closeBlockingCompletedCond) + { + delete _closeBlockingCompletedCond; + } + + if(_cs) + { + delete _cs; + } +} + +WebRtc_Word32 UdpSocketPosix::ChangeUniqueId(const WebRtc_Word32 id) +{ + _id = id; + return 0; +} + +bool UdpSocketPosix::SetCallback(CallbackObj obj, IncomingSocketCallback cb) +{ + _obj = obj; + _incomingCb = cb; + + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, + "UdpSocketPosix(%p)::SetCallback", this); + + if (_mgr->AddSocket(this)) + { + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, + "UdpSocketPosix(%p)::SetCallback socket added to manager", + this); + return true; // socket is now ready for action + } + + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, + "UdpSocketPosix(%p)::SetCallback error adding me to mgr", + this); + return false; +} + +bool UdpSocketPosix::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 = errno; + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "UdpSocketPosix::SetSockopt(), error:%d", _error); + return false; +} + +WebRtc_Word32 UdpSocketPosix::SetTOS(WebRtc_Word32 serviceType) +{ + if (SetSockopt(IPPROTO_IP, IP_TOS ,(WebRtc_Word8*)&serviceType ,4) != 0) + { + return -1; + } + return 0; +} + +bool UdpSocketPosix::Bind(const SocketAddress& name) +{ + int size = sizeof(sockaddr); + if (0 == bind(_socket, reinterpret_cast(&name),size)) + { + return true; + } + _error = errno; + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "UdpSocketPosix::Bind() error: %d",_error); + return false; +} + +WebRtc_Word32 UdpSocketPosix::SendTo(const WebRtc_Word8* buf, WebRtc_Word32 len, + const SocketAddress& to) +{ + int size = sizeof(sockaddr); + int retVal = sendto(_socket,buf, len, 0, + reinterpret_cast(&to), size); + if(retVal == SOCKET_ERROR) + { + _error = errno; + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "UdpSocketPosix::SendTo() error: %d", _error); + } + + return retVal; +} + +bool UdpSocketPosix::ValidHandle() +{ + return _socket != INVALID_SOCKET; +} + +void UdpSocketPosix::HasIncoming() +{ + // replace 2048 with a mcro define and figure out + // where 2048 comes from + WebRtc_Word8 buf[2048]; + int retval; + SocketAddress from; +#if defined(WEBRTC_MAC) + sockaddr sockaddrfrom; + memset(&from, 0, sizeof(from)); + memset(&sockaddrfrom, 0, sizeof(sockaddrfrom)); + socklen_t fromlen = sizeof(sockaddrfrom); +#else + memset(&from, 0, sizeof(from)); + socklen_t fromlen = sizeof(from); +#endif + +#if defined(WEBRTC_MAC) + retval = recvfrom(_socket,buf, sizeof(buf), 0, + reinterpret_cast(&sockaddrfrom), &fromlen); + memcpy(&from, &sockaddrfrom, fromlen); + from._sockaddr_storage.sin_family = sockaddrfrom.sa_family; +#else + retval = recvfrom(_socket,buf, sizeof(buf), 0, + reinterpret_cast(&from), &fromlen); +#endif + + switch(retval) + { + case 0: + // The peer has performed an orderly shutdown. + break; + case SOCKET_ERROR: + break; + default: + if (_wantsIncoming && _incomingCb) + { + _incomingCb(_obj, buf, retval, &from); + } + break; + } +} + +void UdpSocketPosix::CloseBlocking() +{ + _cs->Enter(); + _closeBlockingActive = true; + if(!CleanUp()) + { + _closeBlockingActive = false; + _cs->Leave(); + return; + } + + while(!_readyForDeletion) + { + _readyForDeletionCond->SleepCS(*_cs); + } + _closeBlockingCompleted = true; + _closeBlockingCompletedCond->Wake(); + _cs->Leave(); +} + +void UdpSocketPosix::ReadyForDeletion() +{ + _cs->Enter(); + if(!_closeBlockingActive) + { + _cs->Leave(); + return; + } + close(_socket); + _socket = INVALID_SOCKET; + _readyForDeletion = true; + _readyForDeletionCond->Wake(); + while(!_closeBlockingCompleted) + { + _closeBlockingCompletedCond->SleepCS(*_cs); + } + _cs->Leave(); +} + +bool UdpSocketPosix::CleanUp() +{ + _wantsIncoming = false; + + if (_socket == INVALID_SOCKET) + { + return false; + } + + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, + "calling UdpSocketManager::RemoveSocket()..."); + _mgr->RemoveSocket(this); + // After this, the socket should may be or will be as deleted. Return + // immediately. + return true; +} + +} // namespace test +} // namespace webrtc diff --git a/webrtc/test/channel_transport/udp_socket_posix.h b/webrtc/test/channel_transport/udp_socket_posix.h new file mode 100644 index 0000000000..b77d663227 --- /dev/null +++ b/webrtc/test/channel_transport/udp_socket_posix.h @@ -0,0 +1,95 @@ +/* + * 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. + */ + +#ifndef WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET_POSIX_H_ +#define WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET_POSIX_H_ + +#include +#include +#include +#include + +#include "webrtc/system_wrappers/interface/condition_variable_wrapper.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/test/channel_transport/udp_socket_wrapper.h" + +namespace webrtc { +namespace test { + +#define SOCKET_ERROR -1 + +class UdpSocketPosix : public UdpSocketWrapper +{ +public: + UdpSocketPosix(const WebRtc_Word32 id, UdpSocketManager* mgr, + bool ipV6Enable = false); + + virtual ~UdpSocketPosix(); + + virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id); + + virtual bool SetCallback(CallbackObj obj, IncomingSocketCallback cb); + + virtual bool Bind(const SocketAddress& name); + + virtual bool SetSockopt(WebRtc_Word32 level, WebRtc_Word32 optname, + const WebRtc_Word8* optval, WebRtc_Word32 optlen); + + virtual WebRtc_Word32 SetTOS(const WebRtc_Word32 serviceType); + + virtual WebRtc_Word32 SendTo(const WebRtc_Word8* buf, WebRtc_Word32 len, + const SocketAddress& to); + + // Deletes socket in addition to closing it. + // TODO (hellner): make destructor protected. + virtual void CloseBlocking(); + + virtual SOCKET GetFd() {return _socket;} + virtual WebRtc_Word32 GetError() {return _error;} + + virtual bool ValidHandle(); + + virtual bool 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*/) {return false;} + + bool CleanUp(); + void HasIncoming(); + bool WantsIncoming() {return _wantsIncoming;} + void ReadyForDeletion(); +private: + friend class UdpSocketManagerPosix; + + WebRtc_Word32 _id; + IncomingSocketCallback _incomingCb; + CallbackObj _obj; + WebRtc_Word32 _error; + + SOCKET _socket; + UdpSocketManager* _mgr; + ConditionVariableWrapper* _closeBlockingCompletedCond; + ConditionVariableWrapper* _readyForDeletionCond; + + bool _closeBlockingActive; + bool _closeBlockingCompleted; + bool _readyForDeletion; + + CriticalSectionWrapper* _cs; +}; + +} // namespace test +} // namespace webrtc + +#endif // WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET_POSIX_H_ diff --git a/webrtc/test/channel_transport/udp_socket_wrapper.cc b/webrtc/test/channel_transport/udp_socket_wrapper.cc new file mode 100644 index 0000000000..aae43d4aa9 --- /dev/null +++ b/webrtc/test/channel_transport/udp_socket_wrapper.cc @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2012 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 "webrtc/test/channel_transport/udp_socket_wrapper.h" + +#include +#include + +#include "webrtc/system_wrappers/interface/event_wrapper.h" +#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/test/channel_transport/udp_socket_manager_wrapper.h" + +#if defined(_WIN32) + #include "webrtc/test/channel_transport/udp_socket2_win.h" +#else + #include "webrtc/test/channel_transport/udp_socket_posix.h" +#endif + + +namespace webrtc { +namespace test { + +bool UdpSocketWrapper::_initiated = false; + +// Temporary Android hack. The value 1024 is taken from +// /build/platforms/android-1.5/arch-arm/usr/include/linux/posix_types.h +// TODO (tomasl): can we remove this now? +#ifndef FD_SETSIZE +#define FD_SETSIZE 1024 +#endif + +UdpSocketWrapper::UdpSocketWrapper() + : _wantsIncoming(false), + _deleteEvent(NULL) +{ +} + +UdpSocketWrapper::~UdpSocketWrapper() +{ + if(_deleteEvent) + { + _deleteEvent->Set(); + _deleteEvent = NULL; + } +} + +void UdpSocketWrapper::SetEventToNull() +{ + if (_deleteEvent) + { + _deleteEvent = NULL; + } +} + +UdpSocketWrapper* UdpSocketWrapper::CreateSocket(const WebRtc_Word32 id, + UdpSocketManager* mgr, + CallbackObj obj, + IncomingSocketCallback cb, + bool ipV6Enable, + bool disableGQOS) + +{ + WEBRTC_TRACE(kTraceMemory, kTraceTransport, id, + "UdpSocketWrapper::CreateSocket"); + + UdpSocketWrapper* s = 0; + +#ifdef _WIN32 + if (!_initiated) + { + WSADATA wsaData; + WORD wVersionRequested = MAKEWORD( 2, 2 ); + WebRtc_Word32 err = WSAStartup( wVersionRequested, &wsaData); + if (err != 0) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + id, + "UdpSocketWrapper::CreateSocket failed to initialize sockets\ + WSAStartup error:%d", + err); + return NULL; + } + + _initiated = true; + } + + s = new UdpSocket2Windows(id, mgr, ipV6Enable, disableGQOS); + +#else + if (!_initiated) + { + _initiated = true; + } + s = new UdpSocketPosix(id, mgr, ipV6Enable); + if (s) + { + UdpSocketPosix* sl = static_cast(s); + if (sl->GetFd() != INVALID_SOCKET && sl->GetFd() < FD_SETSIZE) + { + // ok + } else + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + id, + "UdpSocketWrapper::CreateSocket failed to initialize socket"); + delete s; + s = NULL; + } + } +#endif + if (s) + { + s->_deleteEvent = NULL; + if (!s->SetCallback(obj, cb)) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + id, + "UdpSocketWrapper::CreateSocket failed to ser callback"); + return(NULL); + } + } + return s; +} + +bool UdpSocketWrapper::StartReceiving() +{ + _wantsIncoming = true; + return true; +} + +bool UdpSocketWrapper::StopReceiving() +{ + _wantsIncoming = false; + return true; +} + +} // namespace test +} // namespace webrtc diff --git a/webrtc/test/channel_transport/udp_socket_wrapper.h b/webrtc/test/channel_transport/udp_socket_wrapper.h new file mode 100644 index 0000000000..39880b7edb --- /dev/null +++ b/webrtc/test/channel_transport/udp_socket_wrapper.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2012 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. + */ + +#ifndef WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET_WRAPPER_H_ +#define WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET_WRAPPER_H_ + +#include "webrtc/test/channel_transport/udp_transport.h" + +namespace webrtc { + +class EventWrapper; + +namespace test { + +class UdpSocketManager; + +#define SOCKET_ERROR_NO_QOS -1000 + +#ifndef _WIN32 +typedef int SOCKET; +#endif + +#ifndef INVALID_SOCKET +#define INVALID_SOCKET (SOCKET)(~0) + +#ifndef AF_INET +#define AF_INET 2 +#endif + +#endif + +typedef void* CallbackObj; +typedef void(*IncomingSocketCallback)(CallbackObj obj, const WebRtc_Word8* buf, + WebRtc_Word32 len, + const SocketAddress* from); + +class UdpSocketWrapper +{ +public: + static UdpSocketWrapper* CreateSocket(const WebRtc_Word32 id, + UdpSocketManager* mgr, + CallbackObj obj, + IncomingSocketCallback cb, + bool ipV6Enable = false, + bool disableGQOS = false); + + // Set the unique identifier of this class to id. + virtual WebRtc_Word32 ChangeUniqueId(const WebRtc_Word32 id) = 0; + + // Register cb for receiving callbacks when there are incoming packets. + // Register obj so that it will be passed in calls to cb. + virtual bool SetCallback(CallbackObj obj, IncomingSocketCallback cb) = 0; + + // Socket to local address specified by name. + virtual bool Bind(const SocketAddress& name) = 0; + + // Start receiving UDP data. + virtual bool StartReceiving(); + virtual inline bool StartReceiving(const WebRtc_UWord32 /*receiveBuffers*/) + {return StartReceiving();} + // Stop receiving UDP data. + virtual bool StopReceiving(); + + virtual bool ValidHandle() = 0; + + // Set socket options. + virtual bool SetSockopt(WebRtc_Word32 level, WebRtc_Word32 optname, + const WebRtc_Word8* optval, + WebRtc_Word32 optlen) = 0; + + // Set TOS for outgoing packets. + virtual WebRtc_Word32 SetTOS(const WebRtc_Word32 serviceType) = 0; + + // Set 802.1Q PCP field (802.1p) for outgoing VLAN traffic. + virtual WebRtc_Word32 SetPCP(const WebRtc_Word32 /*pcp*/) {return -1;} + + // Send buf of length len to the address specified by to. + virtual WebRtc_Word32 SendTo(const WebRtc_Word8* buf, WebRtc_Word32 len, + const SocketAddress& to) = 0; + + virtual void SetEventToNull(); + + // Close socket and don't return until completed. + virtual void CloseBlocking() {} + + // tokenRate is in bit/s. peakBandwidt is in byte/s + virtual bool 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 = 0) = 0; + + virtual WebRtc_UWord32 ReceiveBuffers() {return 0;}; + +protected: + // Creating the socket is done via CreateSocket(). + UdpSocketWrapper(); + // Destroying the socket is done via CloseBlocking(). + virtual ~UdpSocketWrapper(); + + bool _wantsIncoming; + EventWrapper* _deleteEvent; + +private: + static bool _initiated; +}; + +} // namespac test +} // namespace webrtc + +#endif // WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_SOCKET_WRAPPER_H_ diff --git a/webrtc/test/channel_transport/udp_socket_wrapper_unittest.cc b/webrtc/test/channel_transport/udp_socket_wrapper_unittest.cc new file mode 100644 index 0000000000..759a222ecc --- /dev/null +++ b/webrtc/test/channel_transport/udp_socket_wrapper_unittest.cc @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2012 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. + */ + +// Tests for the UdpSocketWrapper interface. +// This will test the UdpSocket implementations on various platforms. +// Note that this test is using a real SocketManager, which starts up +// an extra worker thread, making the testing more complex than it +// should be. +// This is because on Posix, the CloseBlocking function waits for the +// ReadyForDeletion function to be called, which has to be called after +// CloseBlocking, and thus has to be called from another thread. +// The manager is the one actually doing the deleting. +// This is done differently in the Winsock2 code, but that code +// will also hang if the destructor is called directly. + +#include "gtest/gtest.h" +#include "gmock/gmock.h" +#include "webrtc/test/channel_transport/udp_socket_wrapper.h" +#include "webrtc/test/channel_transport/udp_socket_manager_wrapper.h" + +using ::testing::_; +using ::testing::Return; + +namespace webrtc { +namespace test { + +class MockSocketManager : public UdpSocketManager { + public: + MockSocketManager() {} + // Access to protected destructor. + void Destroy() { + delete this; + } + MOCK_METHOD2(Init, bool(WebRtc_Word32, WebRtc_UWord8&)); + MOCK_METHOD1(ChangeUniqueId, WebRtc_Word32(const WebRtc_Word32)); + MOCK_METHOD0(Start, bool()); + MOCK_METHOD0(Stop, bool()); + MOCK_METHOD1(AddSocket, bool(UdpSocketWrapper*)); + MOCK_METHOD1(RemoveSocket, bool(UdpSocketWrapper*)); +}; + +// Creates a socket using the static constructor method and verifies that +// it's added to the socket manager. +TEST(UdpSocketWrapper, CreateSocket) { + WebRtc_Word32 id = 42; + // We can't test deletion of sockets without a socket manager. + WebRtc_UWord8 threads = 1; + UdpSocketManager* mgr = UdpSocketManager::Create(id, threads); + UdpSocketWrapper* socket = + UdpSocketWrapper::CreateSocket(id, + mgr, + NULL, // CallbackObj + NULL, // IncomingSocketCallback + false, // ipV6Enable + false); // disableGQOS + socket->CloseBlocking(); + UdpSocketManager::Return(); +} + +} // namespace test +} // namespace webrtc diff --git a/webrtc/test/channel_transport/udp_transport.h b/webrtc/test/channel_transport/udp_transport.h new file mode 100644 index 0000000000..75859c9a0a --- /dev/null +++ b/webrtc/test/channel_transport/udp_transport.h @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2012 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. + */ + +#ifndef WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_TRANSPORT_H_ +#define WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_TRANSPORT_H_ + +#include "webrtc/common_types.h" +#include "webrtc/typedefs.h" + +/* + * WARNING + * This code is not use in production/testing and might have security issues + * for example: http://code.google.com/p/webrtc/issues/detail?id=1028 + * + */ + +#define SS_MAXSIZE 128 +#define SS_ALIGNSIZE (sizeof (WebRtc_UWord64)) +#define SS_PAD1SIZE (SS_ALIGNSIZE - sizeof(WebRtc_Word16)) +#define SS_PAD2SIZE (SS_MAXSIZE - (sizeof(WebRtc_Word16) + SS_PAD1SIZE +\ + SS_ALIGNSIZE)) + +// BSD requires use of HAVE_STRUCT_SOCKADDR_SA_LEN +namespace webrtc { +namespace test { + +struct SocketAddressIn { + // sin_family should be either AF_INET (IPv4) or AF_INET6 (IPv6) +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + WebRtc_Word8 sin_length; + WebRtc_Word8 sin_family; +#else + WebRtc_Word16 sin_family; +#endif + WebRtc_UWord16 sin_port; + WebRtc_UWord32 sin_addr; + WebRtc_Word8 sin_zero[8]; +}; + +struct Version6InAddress { + union { + WebRtc_UWord8 _s6_u8[16]; + WebRtc_UWord32 _s6_u32[4]; + WebRtc_UWord64 _s6_u64[2]; + } Version6AddressUnion; +}; + +struct SocketAddressInVersion6 { + // sin_family should be either AF_INET (IPv4) or AF_INET6 (IPv6) +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + WebRtc_Word8 sin_length; + WebRtc_Word8 sin_family; +#else + WebRtc_Word16 sin_family; +#endif + // Transport layer port number. + WebRtc_UWord16 sin6_port; + // IPv6 traffic class and flow info or ip4 address. + WebRtc_UWord32 sin6_flowinfo; + // IPv6 address + struct Version6InAddress sin6_addr; + // Set of interfaces for a scope. + WebRtc_UWord32 sin6_scope_id; +}; + +struct SocketAddressStorage { + // sin_family should be either AF_INET (IPv4) or AF_INET6 (IPv6) +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + WebRtc_Word8 sin_length; + WebRtc_Word8 sin_family; +#else + WebRtc_Word16 sin_family; +#endif + WebRtc_Word8 __ss_pad1[SS_PAD1SIZE]; + WebRtc_UWord64 __ss_align; + WebRtc_Word8 __ss_pad2[SS_PAD2SIZE]; +}; + +struct SocketAddress { + union { + struct SocketAddressIn _sockaddr_in; + struct SocketAddressInVersion6 _sockaddr_in6; + struct SocketAddressStorage _sockaddr_storage; + }; +}; + +// Callback class that receives packets from UdpTransport. +class UdpTransportData { + public: + virtual ~UdpTransportData() {}; + + virtual void IncomingRTPPacket(const WebRtc_Word8* incomingRtpPacket, + const WebRtc_Word32 rtpPacketLength, + const char* fromIP, + const WebRtc_UWord16 fromPort) = 0; + + virtual void IncomingRTCPPacket(const WebRtc_Word8* incomingRtcpPacket, + const WebRtc_Word32 rtcpPacketLength, + const char* fromIP, + const WebRtc_UWord16 fromPort) = 0; +}; + +class UdpTransport : public Transport { + public: + enum + { + kIpAddressVersion6Length = 64, + kIpAddressVersion4Length = 16 + }; + enum ErrorCode + { + kNoSocketError = 0, + kFailedToBindPort = 1, + kIpAddressInvalid = 2, + kAddressInvalid = 3, + kSocketInvalid = 4, + kPortInvalid = 5, + kTosInvalid = 6, + kMulticastAddressInvalid = 7, + kQosError = 8, + kSocketAlreadyInitialized = 9, + kIpVersion6Error = 10, + FILTER_ERROR = 11, + kStartReceiveError = 12, + kStopReceiveError = 13, + kCannotFindLocalIp = 14, + kTosError = 16, + kNotInitialized = 17, + kPcpError = 18 + }; + + // Factory method. Constructor disabled. + static UdpTransport* Create(const WebRtc_Word32 id, + WebRtc_UWord8& numSocketThreads); + static void Destroy(UdpTransport* module); + + // Prepares the class for sending RTP packets to ipAddr:rtpPort and RTCP + // packets to ipAddr:rtpPort+1 if rtcpPort is zero. Otherwise to + // ipAddr:rtcpPort. + virtual WebRtc_Word32 InitializeSendSockets( + const char* ipAddr, + const WebRtc_UWord16 rtpPort, + const WebRtc_UWord16 rtcpPort = 0) = 0; + + // Register packetCallback for receiving incoming packets. Set the local + // RTP port to rtpPort. Bind local IP address to ipAddr. If ipAddr is NULL + // bind to local IP ANY. Set the local rtcp port to rtcpPort or rtpPort + 1 + // if rtcpPort is 0. + virtual WebRtc_Word32 InitializeReceiveSockets( + UdpTransportData* const packetCallback, + const WebRtc_UWord16 rtpPort, + const char* ipAddr = NULL, + const char* multicastIpAddr = NULL, + const WebRtc_UWord16 rtcpPort = 0) = 0; + + // Set local RTP port to rtpPort and RTCP port to rtcpPort or rtpPort + 1 if + // rtcpPort is 0. These ports will be used for sending instead of the local + // ports set by InitializeReceiveSockets(..). + virtual WebRtc_Word32 InitializeSourcePorts( + const WebRtc_UWord16 rtpPort, + const WebRtc_UWord16 rtcpPort = 0) = 0; + + // Retrieve local ports used for sending if other than the ports specified + // by InitializeReceiveSockets(..). rtpPort is set to the RTP port. + // rtcpPort is set to the RTCP port. + virtual WebRtc_Word32 SourcePorts(WebRtc_UWord16& rtpPort, + WebRtc_UWord16& rtcpPort) const = 0; + + // Set ipAddr to the IP address that is currently being listened on. rtpPort + // to the RTP port listened to. rtcpPort to the RTCP port listened on. + // multicastIpAddr to the multicast IP address group joined (the address + // is NULL terminated). + virtual WebRtc_Word32 ReceiveSocketInformation( + char ipAddr[kIpAddressVersion6Length], + WebRtc_UWord16& rtpPort, + WebRtc_UWord16& rtcpPort, + char multicastIpAddr[kIpAddressVersion6Length]) const = 0; + + // Set ipAddr to the IP address being sent from. rtpPort to the local RTP + // port used for sending and rtcpPort to the local RTCP port used for + // sending. + virtual WebRtc_Word32 SendSocketInformation( + char ipAddr[kIpAddressVersion6Length], + WebRtc_UWord16& rtpPort, + WebRtc_UWord16& rtcpPort) const = 0; + + // Put the IP address, RTP port and RTCP port from the last received packet + // into ipAddr, rtpPort and rtcpPort respectively. + virtual WebRtc_Word32 RemoteSocketInformation( + char ipAddr[kIpAddressVersion6Length], + WebRtc_UWord16& rtpPort, + WebRtc_UWord16& rtcpPort) const = 0; + + // Enable/disable quality of service if QoS is true or false respectively. + // Set the type of service to serviceType, max bitrate in kbit/s to + // maxBitrate and override DSCP if overrideDSCP is not 0. + // Note: Must be called both InitializeSendSockets() and + // InitializeReceiveSockets() has been called. + virtual WebRtc_Word32 SetQoS(const bool QoS, + const WebRtc_Word32 serviceType, + const WebRtc_UWord32 maxBitrate = 0, + const WebRtc_Word32 overrideDSCP = 0, + const bool audio = false) = 0; + + // Set QoS to true if quality of service has been turned on. If QoS is true, + // also set serviceType to type of service and overrideDSCP to override + // DSCP. + virtual WebRtc_Word32 QoS(bool& QoS, + WebRtc_Word32& serviceType, + WebRtc_Word32& overrideDSCP) const = 0; + + // Set type of service. + virtual WebRtc_Word32 SetToS(const WebRtc_Word32 DSCP, + const bool useSetSockOpt = false) = 0; + + // Get type of service configuration. + virtual WebRtc_Word32 ToS(WebRtc_Word32& DSCP, + bool& useSetSockOpt) const = 0; + + // Set Priority Code Point (IEEE 802.1Q) + // Note: for Linux this function will set the priority for the socket, + // which then can be mapped to a PCP value with vconfig. + virtual WebRtc_Word32 SetPCP(const WebRtc_Word32 PCP) = 0; + + // Get Priority Code Point + virtual WebRtc_Word32 PCP(WebRtc_Word32& PCP) const = 0; + + // Enable IPv6. + // Note: this API must be called before any call to + // InitializeReceiveSockets() or InitializeSendSockets(). It is not + // possible to go back to IPv4 (default) after this call. + virtual WebRtc_Word32 EnableIpV6() = 0; + + // Return true if IPv6 has been enabled. + virtual bool IpV6Enabled() const = 0; + + // Only allow packets received from filterIPAddress to be processed. + // Note: must be called after EnableIPv6(), if IPv6 is used. + virtual WebRtc_Word32 SetFilterIP( + const char filterIPAddress[kIpAddressVersion6Length]) = 0; + + // Write the filter IP address (if any) to filterIPAddress. + virtual WebRtc_Word32 FilterIP( + char filterIPAddress[kIpAddressVersion6Length]) const = 0; + + // Only allow RTP packets from rtpFilterPort and RTCP packets from + // rtcpFilterPort be processed. + // Note: must be called after EnableIPv6(), if IPv6 is used. + virtual WebRtc_Word32 SetFilterPorts( + const WebRtc_UWord16 rtpFilterPort, + const WebRtc_UWord16 rtcpFilterPort) = 0; + + // Set rtpFilterPort to the filter RTP port and rtcpFilterPort to the + // filter RTCP port (if filtering based on port is enabled). + virtual WebRtc_Word32 FilterPorts(WebRtc_UWord16& rtpFilterPort, + WebRtc_UWord16& rtcpFilterPort) const = 0; + + // Set the number of buffers that the socket implementation may use for + // receiving packets to numberOfSocketBuffers. I.e. the number of packets + // that can be received in parallell. + // Note: this API only has effect on Windows. + virtual WebRtc_Word32 StartReceiving( + const WebRtc_UWord32 numberOfSocketBuffers) = 0; + + // Stop receive incoming packets. + virtual WebRtc_Word32 StopReceiving() = 0; + + // Return true incoming packets are received. + virtual bool Receiving() const = 0; + + // Return true if send sockets have been initialized. + virtual bool SendSocketsInitialized() const = 0; + + // Return true if local ports for sending has been set. + virtual bool SourcePortsInitialized() const = 0; + + // Return true if receive sockets have been initialized. + virtual bool ReceiveSocketsInitialized() const = 0; + + // Send data with size length to ip:portnr. The same port as the set + // with InitializeSendSockets(..) is used if portnr is 0. The same IP + // address as set with InitializeSendSockets(..) is used if ip is NULL. + // If isRTCP is true the port used will be the RTCP port. + virtual WebRtc_Word32 SendRaw(const WebRtc_Word8* data, + WebRtc_UWord32 length, + WebRtc_Word32 isRTCP, + WebRtc_UWord16 portnr = 0, + const char* ip = NULL) = 0; + + // Send RTP data with size length to the address specified by to. + virtual WebRtc_Word32 SendRTPPacketTo(const WebRtc_Word8* data, + WebRtc_UWord32 length, + const SocketAddress& to) = 0; + + + // Send RTCP data with size length to the address specified by to. + virtual WebRtc_Word32 SendRTCPPacketTo(const WebRtc_Word8* data, + WebRtc_UWord32 length, + const SocketAddress& to) = 0; + + // Send RTP data with size length to ip:rtpPort where ip is the ip set by + // the InitializeSendSockets(..) call. + virtual WebRtc_Word32 SendRTPPacketTo(const WebRtc_Word8* data, + WebRtc_UWord32 length, + WebRtc_UWord16 rtpPort) = 0; + + + // Send RTCP data with size length to ip:rtcpPort where ip is the ip set by + // the InitializeSendSockets(..) call. + virtual WebRtc_Word32 SendRTCPPacketTo(const WebRtc_Word8* data, + WebRtc_UWord32 length, + WebRtc_UWord16 rtcpPort) = 0; + + // Set the IP address to which packets are sent to ipaddr. + virtual WebRtc_Word32 SetSendIP( + const char ipaddr[kIpAddressVersion6Length]) = 0; + + // Set the send RTP and RTCP port to rtpPort and rtcpPort respectively. + virtual WebRtc_Word32 SetSendPorts(const WebRtc_UWord16 rtpPort, + const WebRtc_UWord16 rtcpPort = 0) = 0; + + // Retreive the last registered error code. + virtual ErrorCode LastError() const = 0; + + // Put the local IPv4 address in localIP. + // Note: this API is for IPv4 only. + static WebRtc_Word32 LocalHostAddress(WebRtc_UWord32& localIP); + + // Put the local IP6 address in localIP. + // Note: this API is for IPv6 only. + static WebRtc_Word32 LocalHostAddressIPV6(char localIP[16]); + + // Return a copy of hostOrder (host order) in network order. + static WebRtc_UWord16 Htons(WebRtc_UWord16 hostOrder); + + // Return a copy of hostOrder (host order) in network order. + static WebRtc_UWord32 Htonl(WebRtc_UWord32 hostOrder); + + // Return IPv4 address in ip as 32 bit integer. + static WebRtc_UWord32 InetAddrIPV4(const char* ip); + + // Convert the character string src into a network address structure in + // the af address family and put it in dst. + // Note: same functionality as inet_pton(..) + static WebRtc_Word32 InetPresentationToNumeric(WebRtc_Word32 af, + const char* src, + void* dst); + + // Set ip and sourcePort according to address. As input parameter ipSize + // is the length of ip. As output parameter it's the number of characters + // written to ip (not counting the '\0' character). + // Note: this API is only implemented on Windows and Linux. + static WebRtc_Word32 IPAddress(const SocketAddress& address, + char* ip, + WebRtc_UWord32& ipSize, + WebRtc_UWord16& sourcePort); + + // Set ip and sourcePort according to address. As input parameter ipSize + // is the length of ip. As output parameter it's the number of characters + // written to ip (not counting the '\0' character). + // Note: this API is only implemented on Windows and Linux. + // Additional note: this API caches the address of the last call to it. If + // address is likley to be the same for multiple calls it may be beneficial + // to call this API instead of IPAddress(). + virtual WebRtc_Word32 IPAddressCached(const SocketAddress& address, + char* ip, + WebRtc_UWord32& ipSize, + WebRtc_UWord16& sourcePort) = 0; + + // Return true if ipaddr is a valid IP address. + // If ipV6 is false ipaddr is interpreted as an IPv4 address otherwise it + // is interptreted as IPv6. + static bool IsIpAddressValid(const char* ipaddr, const bool ipV6); +}; + +} // namespace test +} // namespace webrtc + +#endif // WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_TRANSPORT_H_ diff --git a/webrtc/test/channel_transport/udp_transport_impl.cc b/webrtc/test/channel_transport/udp_transport_impl.cc new file mode 100644 index 0000000000..3c0419a7a1 --- /dev/null +++ b/webrtc/test/channel_transport/udp_transport_impl.cc @@ -0,0 +1,2999 @@ +/* + * Copyright (c) 2012 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 "webrtc/test/channel_transport/udp_transport_impl.h" + +#include +#include +#include +#include + +#if defined(_WIN32) +#include +#include +#elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef WEBRTC_IOS +#include +#endif +#endif // defined(WEBRTC_LINUX) || defined(WEBRTC_MAC) + +#if defined(WEBRTC_MAC) +#include +#include +#endif +#if defined(WEBRTC_LINUX) +#include +#include +#endif + +#include "webrtc/common_types.h" +#include "webrtc/typedefs.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" +#include "webrtc/system_wrappers/interface/rw_lock_wrapper.h" +#include "webrtc/system_wrappers/interface/trace.h" +#include "webrtc/test/channel_transport/udp_socket_manager_wrapper.h" + +#if defined(WEBRTC_LINUX) || defined(WEBRTC_MAC) +#define GetLastError() errno + +#define IFRSIZE ((int)(size * sizeof (struct ifreq))) + +#define NLMSG_OK_NO_WARNING(nlh,len) \ + ((len) >= (int)sizeof(struct nlmsghdr) && \ + (int)(nlh)->nlmsg_len >= (int)sizeof(struct nlmsghdr) && \ + (int)(nlh)->nlmsg_len <= (len)) + +#endif // defined(WEBRTC_LINUX) || defined(WEBRTC_MAC) + +namespace webrtc { +namespace test { + +class SocketFactory : public UdpTransportImpl::SocketFactoryInterface { + public: + UdpSocketWrapper* CreateSocket(const WebRtc_Word32 id, + UdpSocketManager* mgr, + CallbackObj obj, + IncomingSocketCallback cb, + bool ipV6Enable, + bool disableGQOS) { + return UdpSocketWrapper::CreateSocket(id, mgr, obj, cb, ipV6Enable, + disableGQOS); + } +}; + +// Creates an UdpTransport using the definition of SocketFactory above, +// and passes (creating if needed) a pointer to the static singleton +// UdpSocketManager. +UdpTransport* UdpTransport::Create(const WebRtc_Word32 id, + WebRtc_UWord8& numSocketThreads) +{ + return new UdpTransportImpl(id, + new SocketFactory(), + UdpSocketManager::Create(id, numSocketThreads)); +} + +// Deletes the UdpTransport and decrements the refcount of the +// static singleton UdpSocketManager, possibly destroying it. +// Should only be used on UdpTransports that are created using Create. +void UdpTransport::Destroy(UdpTransport* module) +{ + if(module) + { + delete module; + UdpSocketManager::Return(); + } +} + +UdpTransportImpl::UdpTransportImpl(const WebRtc_Word32 id, + SocketFactoryInterface* maker, + UdpSocketManager* socket_manager) + : _id(id), + _socket_creator(maker), + _crit(CriticalSectionWrapper::CreateCriticalSection()), + _critFilter(CriticalSectionWrapper::CreateCriticalSection()), + _critPacketCallback(CriticalSectionWrapper::CreateCriticalSection()), + _mgr(socket_manager), + _lastError(kNoSocketError), + _destPort(0), + _destPortRTCP(0), + _localPort(0), + _localPortRTCP(0), + _srcPort(0), + _srcPortRTCP(0), + _fromPort(0), + _fromPortRTCP(0), + _fromIP(), + _destIP(), + _localIP(), + _localMulticastIP(), + _ptrRtpSocket(NULL), + _ptrRtcpSocket(NULL), + _ptrSendRtpSocket(NULL), + _ptrSendRtcpSocket(NULL), + _remoteRTPAddr(), + _remoteRTCPAddr(), + _localRTPAddr(), + _localRTCPAddr(), + _tos(0), + _receiving(false), + _useSetSockOpt(false), + _qos(false), + _pcp(0), + _ipV6Enabled(false), + _serviceType(0), + _overrideDSCP(0), + _maxBitrate(0), + _cachLock(RWLockWrapper::CreateRWLock()), + _previousAddress(), + _previousIP(), + _previousIPSize(0), + _previousSourcePort(0), + _filterIPAddress(), + _rtpFilterPort(0), + _rtcpFilterPort(0), + _packetCallback(0) +{ + memset(&_remoteRTPAddr, 0, sizeof(_remoteRTPAddr)); + memset(&_remoteRTCPAddr, 0, sizeof(_remoteRTCPAddr)); + memset(&_localRTPAddr, 0, sizeof(_localRTPAddr)); + memset(&_localRTCPAddr, 0, sizeof(_localRTCPAddr)); + + memset(_fromIP, 0, sizeof(_fromIP)); + memset(_destIP, 0, sizeof(_destIP)); + memset(_localIP, 0, sizeof(_localIP)); + memset(_localMulticastIP, 0, sizeof(_localMulticastIP)); + + memset(&_filterIPAddress, 0, sizeof(_filterIPAddress)); + + WEBRTC_TRACE(kTraceMemory, kTraceTransport, id, "%s created", __FUNCTION__); +} + +UdpTransportImpl::~UdpTransportImpl() +{ + CloseSendSockets(); + CloseReceiveSockets(); + delete _crit; + delete _critFilter; + delete _critPacketCallback; + delete _cachLock; + delete _socket_creator; + + WEBRTC_TRACE(kTraceMemory, kTraceTransport, _id, "%s deleted", + __FUNCTION__); +} + +UdpTransport::ErrorCode UdpTransportImpl::LastError() const +{ + return _lastError; +} + +bool SameAddress(const SocketAddress& address1, const SocketAddress& address2) +{ + return (memcmp(&address1,&address2,sizeof(address1)) == 0); +} + +void UdpTransportImpl::GetCachedAddress(char* ip, + WebRtc_UWord32& ipSize, + WebRtc_UWord16& sourcePort) +{ + const WebRtc_UWord32 originalIPSize = ipSize; + // If the incoming string is too small, fill it as much as there is room + // for. Make sure that there is room for the '\0' character. + ipSize = (ipSize - 1 < _previousIPSize) ? ipSize - 1 : _previousIPSize; + memcpy(ip,_previousIP,sizeof(WebRtc_Word8)*(ipSize + 1)); + ip[originalIPSize - 1] = '\0'; + sourcePort = _previousSourcePort; +} + +WebRtc_Word32 UdpTransportImpl::IPAddressCached(const SocketAddress& address, + char* ip, + WebRtc_UWord32& ipSize, + WebRtc_UWord16& sourcePort) +{ + { + ReadLockScoped rl(*_cachLock); + // Check if the old address can be re-used (is the same). + if(SameAddress(address,_previousAddress)) + { + GetCachedAddress(ip,ipSize,sourcePort); + return 0; + } + } + // Get the new address and store it. + WriteLockScoped wl(*_cachLock); + ipSize = kIpAddressVersion6Length; + if(IPAddress(address,_previousIP,ipSize,_previousSourcePort) != 0) + { + return -1; + } + _previousIPSize = ipSize; + memcpy(&_previousAddress, &address, sizeof(address)); + // Address has been cached at this point. + GetCachedAddress(ip,ipSize,sourcePort); + return 0; +} + +WebRtc_Word32 UdpTransportImpl::InitializeReceiveSockets( + UdpTransportData* const packetCallback, + const WebRtc_UWord16 portnr, + const char* ip, + const char* multicastIpAddr, + const WebRtc_UWord16 rtcpPort) +{ + { + CriticalSectionScoped cs(_critPacketCallback); + _packetCallback = packetCallback; + + if(packetCallback == NULL) + { + WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, _id, + "Closing down receive sockets"); + return 0; + } + } + + CriticalSectionScoped cs(_crit); + CloseReceiveSockets(); + + if(portnr == 0) + { + // TODO (hellner): why not just fail here? + if(_destPort == 0) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "InitializeReceiveSockets port 0 not allowed"); + _lastError = kPortInvalid; + return -1; + } + _localPort = _destPort; + } else { + _localPort = portnr; + } + if(rtcpPort) + { + _localPortRTCP = rtcpPort; + }else { + _localPortRTCP = _localPort + 1; + WEBRTC_TRACE( + kTraceStateInfo, + kTraceTransport, + _id, + "InitializeReceiveSockets RTCP port not configured using RTP\ + port+1=%d", + _localPortRTCP); + } + + if(ip) + { + if(IsIpAddressValid(ip,IpV6Enabled())) + { + strncpy(_localIP, ip,kIpAddressVersion6Length); + } else + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "InitializeReceiveSockets invalid IP address"); + _lastError = kIpAddressInvalid; + return -1; + } + }else + { + // Don't bind to a specific IP address. + if(! IpV6Enabled()) + { + strncpy(_localIP, "0.0.0.0",16); + } else + { + strncpy(_localIP, "0000:0000:0000:0000:0000:0000:0000:0000", + kIpAddressVersion6Length); + } + } + if(multicastIpAddr && !IpV6Enabled()) + { + if(IsIpAddressValid(multicastIpAddr,IpV6Enabled())) + { + strncpy(_localMulticastIP, multicastIpAddr, + kIpAddressVersion6Length); + } else + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "InitializeReceiveSockets invalid IP address"); + _lastError = kIpAddressInvalid; + return -1; + } + } + if(_mgr == NULL) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "InitializeReceiveSockets no socket manager"); + return -1; + } + + _useSetSockOpt=false; + _tos=0; + _pcp=0; + + _ptrRtpSocket = _socket_creator->CreateSocket(_id, _mgr, this, + IncomingRTPCallback, + IpV6Enabled(), false); + + _ptrRtcpSocket = _socket_creator->CreateSocket(_id, _mgr, this, + IncomingRTCPCallback, + IpV6Enabled(), false); + + ErrorCode retVal = BindLocalRTPSocket(); + if(retVal != kNoSocketError) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "InitializeReceiveSockets faild to bind RTP socket"); + _lastError = retVal; + CloseReceiveSockets(); + return -1; + } + retVal = BindLocalRTCPSocket(); + if(retVal != kNoSocketError) + { + _lastError = retVal; + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "InitializeReceiveSockets faild to bind RTCP socket"); + CloseReceiveSockets(); + return -1; + } + return 0; +} + +WebRtc_Word32 UdpTransportImpl::ReceiveSocketInformation( + char ipAddr[kIpAddressVersion6Length], + WebRtc_UWord16& rtpPort, + WebRtc_UWord16& rtcpPort, + char multicastIpAddr[kIpAddressVersion6Length]) const +{ + CriticalSectionScoped cs(_crit); + rtpPort = _localPort; + rtcpPort = _localPortRTCP; + if (ipAddr) + { + strncpy(ipAddr, _localIP, IpV6Enabled() ? + UdpTransport::kIpAddressVersion6Length : + UdpTransport::kIpAddressVersion4Length); + } + if (multicastIpAddr) + { + strncpy(multicastIpAddr, _localMulticastIP, IpV6Enabled() ? + UdpTransport::kIpAddressVersion6Length : + UdpTransport::kIpAddressVersion4Length); + } + return 0; +} + +WebRtc_Word32 UdpTransportImpl::SendSocketInformation( + char ipAddr[kIpAddressVersion6Length], + WebRtc_UWord16& rtpPort, + WebRtc_UWord16& rtcpPort) const +{ + CriticalSectionScoped cs(_crit); + rtpPort = _destPort; + rtcpPort = _destPortRTCP; + strncpy(ipAddr, _destIP, IpV6Enabled() ? + UdpTransport::kIpAddressVersion6Length : + UdpTransport::kIpAddressVersion4Length); + return 0; +} + +WebRtc_Word32 UdpTransportImpl::RemoteSocketInformation( + char ipAddr[kIpAddressVersion6Length], + WebRtc_UWord16& rtpPort, + WebRtc_UWord16& rtcpPort) const +{ + CriticalSectionScoped cs(_crit); + rtpPort = _fromPort; + rtcpPort = _fromPortRTCP; + if(ipAddr) + { + strncpy(ipAddr, _fromIP, IpV6Enabled() ? + kIpAddressVersion6Length : + kIpAddressVersion4Length); + } + return 0; +} + +WebRtc_Word32 UdpTransportImpl::FilterPorts( + WebRtc_UWord16& rtpFilterPort, + WebRtc_UWord16& rtcpFilterPort) const +{ + CriticalSectionScoped cs(_critFilter); + rtpFilterPort = _rtpFilterPort; + rtcpFilterPort = _rtcpFilterPort; + return 0; +} + +WebRtc_Word32 UdpTransportImpl::SetQoS(bool QoS, WebRtc_Word32 serviceType, + WebRtc_UWord32 maxBitrate, + WebRtc_Word32 overrideDSCP, bool audio) +{ + if(QoS) + { + return EnableQoS(serviceType, audio, maxBitrate, overrideDSCP); + }else + { + return DisableQoS(); + } +} + +WebRtc_Word32 UdpTransportImpl::EnableQoS(WebRtc_Word32 serviceType, + bool audio, WebRtc_UWord32 maxBitrate, + WebRtc_Word32 overrideDSCP) +{ + if (_ipV6Enabled) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "QOS is enabled but will be ignored since IPv6 is enabled"); + _lastError = kQosError; + return -1; + } + if (_tos) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "TOS already enabled, can't use TOS and QoS at the same time"); + _lastError = kQosError; + return -1; + } + if (_pcp) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "PCP already enabled, can't use PCP and QoS at the same time"); + _lastError = kQosError; + return -1; + } + if(_destPort == 0) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "QOS is enabled but not started since we have not yet configured\ + the send destination"); + return -1; + } + if(_qos) + { + if(_overrideDSCP == 0 && overrideDSCP != 0) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "QOS is already enabled and overrideDSCP differs, not allowed"); + return -1; + } + } + CriticalSectionScoped cs(_crit); + + UdpSocketWrapper* rtpSock = _ptrSendRtpSocket ? + _ptrSendRtpSocket : + _ptrRtpSocket; + if (!rtpSock || !rtpSock->ValidHandle()) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "QOS is enabled but not started since we have not yet created the\ + RTP socket"); + return -1; + } + UdpSocketWrapper* rtcpSock = _ptrSendRtcpSocket ? + _ptrSendRtcpSocket : + _ptrRtcpSocket; + if (!rtcpSock || !rtcpSock->ValidHandle()) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "QOS is enabled but not started since we have not yet created the\ + RTCP socket"); + return -1; + } + + // Minimum packet size in bytes for which the requested quality of service + // will be provided. The smallest RTP header is 12 byte. + const WebRtc_Word32 min_policed_size = 12; + // Max SDU, maximum packet size permitted or used in the traffic flow, in + // bytes. + const WebRtc_Word32 max_sdu_size = 1500; + + // Enable QoS for RTP sockets. + if(maxBitrate) + { + // Note: 1 kbit is 125 bytes. + // Token Rate is typically set to the average bit rate from peak to + // peak. + // Bucket size is normally set to the largest average frame size. + if(audio) + { + WEBRTC_TRACE(kTraceStateInfo, + kTraceTransport, + _id, + "Enable QOS for audio with max bitrate:%d", + maxBitrate); + + const WebRtc_Word32 token_rate = maxBitrate*125; + // The largest audio packets are 60ms frames. This is a fraction + // more than 16 packets/second. These 16 frames are sent, at max, + // at a bitrate of maxBitrate*125 -> 1 frame is maxBitrate*125/16 ~ + // maxBitrate * 8. + const WebRtc_Word32 bucket_size = maxBitrate * 8; + const WebRtc_Word32 peek_bandwith = maxBitrate * 125; + if (!rtpSock->SetQos(serviceType, token_rate, bucket_size, + peek_bandwith, min_policed_size, + max_sdu_size, _remoteRTPAddr, overrideDSCP)) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "QOS failed on the RTP socket"); + _lastError = kQosError; + return -1; + } + }else + { + WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, _id, + "Enable QOS for video with max bitrate:%d", + maxBitrate); + + // Allow for a token rate that is twice that of the maximum bitrate + // (in bytes). + const WebRtc_Word32 token_rate = maxBitrate*250; + // largest average frame size (key frame size). Assuming that a + // keyframe is 25% of the bitrate during the second its sent + // Assume that a key frame is 25% of the bitrate the second that it + // is sent. The largest frame size is then maxBitrate* 125 * 0.25 ~ + // 31. + const WebRtc_Word32 bucket_size = maxBitrate*31; + const WebRtc_Word32 peek_bandwith = maxBitrate*125; + if (!rtpSock->SetQos(serviceType, token_rate, bucket_size, + peek_bandwith, min_policed_size, max_sdu_size, + _remoteRTPAddr, overrideDSCP)) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "QOS failed on the RTP socket"); + _lastError = kQosError; + return -1; + } + } + } else if(audio) + { + // No max bitrate set. Audio. + WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, _id, + "Enable QOS for audio with default max bitrate"); + + // Let max bitrate be 240kbit/s. + const WebRtc_Word32 token_rate = 30000; + const WebRtc_Word32 bucket_size = 2000; + const WebRtc_Word32 peek_bandwith = 30000; + if (!rtpSock->SetQos(serviceType, token_rate, bucket_size, + peek_bandwith, min_policed_size, max_sdu_size, + _remoteRTPAddr, overrideDSCP)) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "QOS failed on the RTP socket"); + _lastError = kQosError; + return -1; + } + }else + { + // No max bitrate set. Video. + WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, _id, + "Enable QOS for video with default max bitrate"); + + // Let max bitrate be 10mbit/s. + const WebRtc_Word32 token_rate = 128000*10; + const WebRtc_Word32 bucket_size = 32000; + const WebRtc_Word32 peek_bandwith = 256000; + if (!rtpSock->SetQos(serviceType, token_rate, bucket_size, + peek_bandwith, min_policed_size, max_sdu_size, + _remoteRTPAddr, overrideDSCP)) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "QOS failed on the RTP socket"); + _lastError = kQosError; + return -1; + } + } + + // Enable QoS for RTCP sockets. + // TODO (hellner): shouldn't RTCP be based on 5% of the maximum bandwidth? + if(audio) + { + const WebRtc_Word32 token_rate = 200; + const WebRtc_Word32 bucket_size = 200; + const WebRtc_Word32 peek_bandwith = 400; + if (!rtcpSock->SetQos(serviceType, token_rate, bucket_size, + peek_bandwith, min_policed_size, max_sdu_size, + _remoteRTCPAddr, overrideDSCP)) + { + WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id, + "QOS failed on the RTCP socket"); + _lastError = kQosError; + } + }else + { + const WebRtc_Word32 token_rate = 5000; + const WebRtc_Word32 bucket_size = 100; + const WebRtc_Word32 peek_bandwith = 10000; + if (!rtcpSock->SetQos(serviceType, token_rate, bucket_size, + peek_bandwith, min_policed_size, max_sdu_size, + _remoteRTCPAddr, _overrideDSCP)) + { + WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id, + "QOS failed on the RTCP socket"); + _lastError = kQosError; + } + } + _qos = true; + _serviceType = serviceType; + _maxBitrate = maxBitrate; + _overrideDSCP = overrideDSCP; + return 0; +} + +WebRtc_Word32 UdpTransportImpl::DisableQoS() +{ + if(_qos == false) + { + return 0; + } + CriticalSectionScoped cs(_crit); + + UdpSocketWrapper* rtpSock = (_ptrSendRtpSocket ? + _ptrSendRtpSocket : _ptrRtpSocket); + if (!rtpSock || !rtpSock->ValidHandle()) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "QOS is enabled but not started since we have not yet created the\ + RTP socket"); + return -1; + } + UdpSocketWrapper* rtcpSock = (_ptrSendRtcpSocket ? + _ptrSendRtcpSocket : _ptrRtcpSocket); + if (!rtcpSock || !rtcpSock->ValidHandle()) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "QOS is enabled but not started since we have not yet created the\ + RTCP socket"); + return -1; + } + + const WebRtc_Word32 service_type = 0; // = SERVICETYPE_NOTRAFFIC + const WebRtc_Word32 not_specified = -1; + if (!rtpSock->SetQos(service_type, not_specified, not_specified, + not_specified, not_specified, not_specified, + _remoteRTPAddr, _overrideDSCP)) + { + _lastError = kQosError; + return -1; + } + if (!rtcpSock->SetQos(service_type, not_specified, not_specified, + not_specified, not_specified, not_specified, + _remoteRTCPAddr,_overrideDSCP)) + { + _lastError = kQosError; + } + _qos = false; + return 0; +} + +WebRtc_Word32 UdpTransportImpl::QoS(bool& QoS, WebRtc_Word32& serviceType, + WebRtc_Word32& overrideDSCP) const +{ + CriticalSectionScoped cs(_crit); + QoS = _qos; + serviceType = _serviceType; + overrideDSCP = _overrideDSCP; + return 0; +} + +WebRtc_Word32 UdpTransportImpl::SetToS(WebRtc_Word32 DSCP, bool useSetSockOpt) +{ + if (_qos) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "QoS already enabled"); + _lastError = kQosError; + return -1; + } + if (DSCP < 0 || DSCP > 63) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "Invalid DSCP"); + _lastError = kTosInvalid; + return -1; + } + if(_tos) + { + if(useSetSockOpt != _useSetSockOpt) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "Can't switch SetSockOpt method without disabling TOS first"); + _lastError = kTosInvalid; + return -1; + } + } + CriticalSectionScoped cs(_crit); + UdpSocketWrapper* rtpSock = NULL; + UdpSocketWrapper* rtcpSock = NULL; + if(_ptrSendRtpSocket) + { + rtpSock = _ptrSendRtpSocket; + }else + { + rtpSock = _ptrRtpSocket; + } + if (rtpSock == NULL) + { + _lastError = kSocketInvalid; + return -1; + } + if(!rtpSock->ValidHandle()) + { + _lastError = kSocketInvalid; + return -1; + } + if(_ptrSendRtcpSocket) + { + rtcpSock = _ptrSendRtcpSocket; + }else + { + rtcpSock = _ptrRtcpSocket; + } + if (rtcpSock == NULL) + { + _lastError = kSocketInvalid; + return -1; + } + if(!rtcpSock->ValidHandle()) + { + _lastError = kSocketInvalid; + return -1; + } + + if (useSetSockOpt) + { +#ifdef _WIN32 + OSVERSIONINFO OsVersion; + OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&OsVersion); + // Disable QoS before setting ToS on Windows XP. This is done by closing + // and re-opening the sockets. + // TODO (hellner): why not just fail here and force the user to + // re-initialize sockets? Doing this may trick the user + // into thinking that the sockets are in a state which + // they aren't. + if (OsVersion.dwMajorVersion == 5 && + OsVersion.dwMinorVersion == 1) + { + if(!_useSetSockOpt) + { + if(_ptrSendRtpSocket) + { + CloseSendSockets(); + _ptrSendRtpSocket = + _socket_creator->CreateSocket(_id, _mgr, NULL, + NULL, IpV6Enabled(), + true); + _ptrSendRtcpSocket = + _socket_creator->CreateSocket(_id, _mgr, NULL, + NULL, IpV6Enabled(), + true); + rtpSock=_ptrSendRtpSocket; + rtcpSock=_ptrSendRtcpSocket; + ErrorCode retVal = BindRTPSendSocket(); + if(retVal != kNoSocketError) + { + _lastError = retVal; + return -1; + } + retVal = BindRTCPSendSocket(); + if(retVal != kNoSocketError) + { + _lastError = retVal; + return -1; + } + } + else + { + bool receiving=_receiving; + WebRtc_UWord32 noOfReceiveBuffers = 0; + if(receiving) + { + noOfReceiveBuffers=_ptrRtpSocket->ReceiveBuffers(); + if(StopReceiving()!=0) + { + return -1; + } + } + CloseReceiveSockets(); + _ptrRtpSocket = _socket_creator->CreateSocket( + _id, _mgr, this, IncomingRTPCallback, IpV6Enabled(), + true); + _ptrRtcpSocket = _socket_creator->CreateSocket( + _id, _mgr, this, IncomingRTCPCallback, IpV6Enabled(), + true); + rtpSock=_ptrRtpSocket; + rtcpSock=_ptrRtcpSocket; + ErrorCode retVal = BindLocalRTPSocket(); + if(retVal != kNoSocketError) + { + _lastError = retVal; + return -1; + } + retVal = BindLocalRTCPSocket(); + if(retVal != kNoSocketError) + { + _lastError = retVal; + return -1; + } + if(receiving) + { + if(StartReceiving(noOfReceiveBuffers) != + kNoSocketError) + { + return -1; + } + } + } + } + } +#endif // #ifdef _WIN32 + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, + "Setting TOS using SetSockopt"); + WebRtc_Word32 TOSShifted = DSCP << 2; + if (!rtpSock->SetSockopt(IPPROTO_IP, IP_TOS, + (WebRtc_Word8*) &TOSShifted, 4)) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "Could not SetSockopt tos value on RTP socket"); + _lastError = kTosInvalid; + return -1; + } + if (!rtcpSock->SetSockopt(IPPROTO_IP, IP_TOS, + (WebRtc_Word8*) &TOSShifted, 4)) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "Could not sSetSockopt tos value on RTCP socket"); + _lastError = kTosInvalid; + return -1; + } + } else + { + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, + "Setting TOS NOT using SetSockopt"); + if (rtpSock->SetTOS(DSCP) != 0) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "Could not set tos value on RTP socket"); + _lastError = kTosError; + return -1; + } + if (rtcpSock->SetTOS(DSCP) != 0) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "Could not set tos value on RTCP socket"); + _lastError = kTosError; + return -1; + } + } + _useSetSockOpt = useSetSockOpt; + _tos = DSCP; + return 0; +} + +WebRtc_Word32 UdpTransportImpl::ToS(WebRtc_Word32& DSCP, + bool& useSetSockOpt) const +{ + CriticalSectionScoped cs(_crit); + DSCP = _tos; + useSetSockOpt = _useSetSockOpt; + return 0; +} + +WebRtc_Word32 UdpTransportImpl::SetPCP(WebRtc_Word32 PCP) +{ + + if (_qos) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "QoS already enabled"); + _lastError = kQosError; + return -1; + } + if ((PCP < 0) || (PCP > 7)) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "Invalid PCP"); + _lastError = kPcpError; + return -1; + } + + CriticalSectionScoped cs(_crit); + UdpSocketWrapper* rtpSock = NULL; + UdpSocketWrapper* rtcpSock = NULL; + if(_ptrSendRtpSocket) + { + rtpSock = _ptrSendRtpSocket; + }else + { + rtpSock = _ptrRtpSocket; + } + if (rtpSock == NULL) + { + _lastError = kSocketInvalid; + return -1; + } + if(!rtpSock->ValidHandle()) + { + _lastError = kSocketInvalid; + return -1; + } + if(_ptrSendRtcpSocket) + { + rtcpSock = _ptrSendRtcpSocket; + }else + { + rtcpSock = _ptrRtcpSocket; + } + if (rtcpSock == NULL) + { + _lastError = kSocketInvalid; + return -1; + } + if(!rtcpSock->ValidHandle()) + { + _lastError = kSocketInvalid; + return -1; + } + +#if defined(_WIN32) + if (rtpSock->SetPCP(PCP) != 0) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "Could not set PCP value on RTP socket"); + _lastError = kPcpError; + return -1; + } + if (rtcpSock->SetPCP(PCP) != 0) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "Could not set PCP value on RTCP socket"); + _lastError = kPcpError; + return -1; + } + +#elif defined(WEBRTC_LINUX) + if (!rtpSock->SetSockopt(SOL_SOCKET, SO_PRIORITY, (WebRtc_Word8*) &PCP, + sizeof(PCP))) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "Could not SetSockopt PCP value on RTP socket"); + _lastError = kPcpError; + return -1; + } + if (!rtcpSock->SetSockopt(SOL_SOCKET, SO_PRIORITY, (WebRtc_Word8*) &PCP, + sizeof(PCP))) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "Could not SetSockopt PCP value on RTCP socket"); + _lastError = kPcpError; + return -1; + } +#else + // Not supported on other platforms (WEBRTC_MAC) + _lastError = kPcpError; + return -1; +#endif + _pcp = PCP; + return 0; +} + +WebRtc_Word32 UdpTransportImpl::PCP(WebRtc_Word32& PCP) const +{ + CriticalSectionScoped cs(_crit); + PCP = _pcp; + return 0; +} + +bool UdpTransportImpl::SetSockOptUsed() +{ + return _useSetSockOpt; +} + +WebRtc_Word32 UdpTransportImpl::EnableIpV6() { + + CriticalSectionScoped cs(_crit); + const bool initialized = (_ptrSendRtpSocket || _ptrRtpSocket); + + if (_ipV6Enabled) { + return 0; + } + if (initialized) { + _lastError = kIpVersion6Error; + return -1; + } + _ipV6Enabled = true; + return 0; +} + +WebRtc_Word32 UdpTransportImpl::FilterIP( + char filterIPAddress[kIpAddressVersion6Length]) const +{ + + if(filterIPAddress == NULL) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "FilterIP: Invalid argument"); + return -1; + } + if(_filterIPAddress._sockaddr_storage.sin_family == 0) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "No Filter configured"); + return -1; + } + CriticalSectionScoped cs(_critFilter); + WebRtc_UWord32 ipSize = kIpAddressVersion6Length; + WebRtc_UWord16 sourcePort; + return IPAddress(_filterIPAddress, filterIPAddress, ipSize, sourcePort); +} + +WebRtc_Word32 UdpTransportImpl::SetFilterIP( + const char filterIPAddress[kIpAddressVersion6Length]) +{ + if(filterIPAddress == NULL) + { + memset(&_filterIPAddress, 0, sizeof(_filterIPAddress)); + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, "Filter IP reset"); + return 0; + } + CriticalSectionScoped cs(_critFilter); + if (_ipV6Enabled) + { + _filterIPAddress._sockaddr_storage.sin_family = AF_INET6; + + if (InetPresentationToNumeric( + AF_INET6, + filterIPAddress, + &_filterIPAddress._sockaddr_in6.sin6_addr) < 0) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "Failed to set\ + filter IP for IPv6"); + _lastError = FILTER_ERROR; + return -1; + } + } + else + { + _filterIPAddress._sockaddr_storage.sin_family = AF_INET; + + if(InetPresentationToNumeric( + AF_INET, + filterIPAddress, + &_filterIPAddress._sockaddr_in.sin_addr) < 0) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "Failed to set filter IP for IPv4"); + _lastError = FILTER_ERROR; + return -1; + } + } + WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, "Filter IP set"); + return 0; +} + +WebRtc_Word32 UdpTransportImpl::SetFilterPorts(WebRtc_UWord16 rtpFilterPort, + WebRtc_UWord16 rtcpFilterPort) +{ + CriticalSectionScoped cs(_critFilter); + _rtpFilterPort = rtpFilterPort; + _rtcpFilterPort = rtcpFilterPort; + return 0; +} + +bool UdpTransportImpl::SendSocketsInitialized() const +{ + CriticalSectionScoped cs(_crit); + if(_ptrSendRtpSocket) + { + return true; + } + if(_destPort !=0) + { + return true; + } + return false; +} + +bool UdpTransportImpl::ReceiveSocketsInitialized() const +{ + if(_ptrRtpSocket) + { + return true; + } + return false; +} + +bool UdpTransportImpl::SourcePortsInitialized() const +{ + if(_ptrSendRtpSocket) + { + return true; + } + return false; +} + +bool UdpTransportImpl::IpV6Enabled() const +{ + WEBRTC_TRACE(kTraceStream, kTraceTransport, _id, "%s", __FUNCTION__); + return _ipV6Enabled; +} + +void UdpTransportImpl::BuildRemoteRTPAddr() +{ + if(_ipV6Enabled) + { +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + _remoteRTPAddr.sin_length = 0; + _remoteRTPAddr.sin_family = PF_INET6; +#else + _remoteRTPAddr._sockaddr_storage.sin_family = PF_INET6; +#endif + + _remoteRTPAddr._sockaddr_in6.sin6_flowinfo=0; + _remoteRTPAddr._sockaddr_in6.sin6_scope_id=0; + _remoteRTPAddr._sockaddr_in6.sin6_port = Htons(_destPort); + InetPresentationToNumeric(AF_INET6,_destIP, + &_remoteRTPAddr._sockaddr_in6.sin6_addr); + } else + { +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + _remoteRTPAddr.sin_length = 0; + _remoteRTPAddr.sin_family = PF_INET; +#else + _remoteRTPAddr._sockaddr_storage.sin_family = PF_INET; +#endif + _remoteRTPAddr._sockaddr_in.sin_port = Htons(_destPort); + _remoteRTPAddr._sockaddr_in.sin_addr = InetAddrIPV4(_destIP); + } +} + +void UdpTransportImpl::BuildRemoteRTCPAddr() +{ + if(_ipV6Enabled) + { +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + _remoteRTCPAddr.sin_length = 0; + _remoteRTCPAddr.sin_family = PF_INET6; +#else + _remoteRTCPAddr._sockaddr_storage.sin_family = PF_INET6; +#endif + + _remoteRTCPAddr._sockaddr_in6.sin6_flowinfo=0; + _remoteRTCPAddr._sockaddr_in6.sin6_scope_id=0; + _remoteRTCPAddr._sockaddr_in6.sin6_port = Htons(_destPortRTCP); + InetPresentationToNumeric(AF_INET6,_destIP, + &_remoteRTCPAddr._sockaddr_in6.sin6_addr); + + } else + { +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + _remoteRTCPAddr.sin_length = 0; + _remoteRTCPAddr.sin_family = PF_INET; +#else + _remoteRTCPAddr._sockaddr_storage.sin_family = PF_INET; +#endif + _remoteRTCPAddr._sockaddr_in.sin_port = Htons(_destPortRTCP); + _remoteRTCPAddr._sockaddr_in.sin_addr= InetAddrIPV4(_destIP); + } +} + +UdpTransportImpl::ErrorCode UdpTransportImpl::BindRTPSendSocket() +{ + if(!_ptrSendRtpSocket) + { + return kSocketInvalid; + } + if(!_ptrSendRtpSocket->ValidHandle()) + { + return kIpAddressInvalid; + } + if(_ipV6Enabled) + { +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + _localRTPAddr.sin_length = 0; + _localRTPAddr.sin_family = PF_INET6; +#else + _localRTPAddr._sockaddr_storage.sin_family = PF_INET6; +#endif + _localRTPAddr._sockaddr_in6.sin6_flowinfo=0; + _localRTPAddr._sockaddr_in6.sin6_scope_id=0; + _localRTPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[0] = + 0; // = INADDR_ANY + _localRTPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[1] = + 0; + _localRTPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[2] = + 0; + _localRTPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[3] = + 0; + _localRTPAddr._sockaddr_in6.sin6_port = Htons(_srcPort); + if(_ptrSendRtpSocket->Bind(_localRTPAddr) == false) + { + WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id, + "Failed to bind to port:%d ", _srcPort); + return kFailedToBindPort; + } + + } else { +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + _localRTPAddr.sin_length = 0; + _localRTPAddr.sin_family = PF_INET; +#else + _localRTPAddr._sockaddr_storage.sin_family = PF_INET; +#endif + _localRTPAddr._sockaddr_in.sin_addr = 0; + _localRTPAddr._sockaddr_in.sin_port = Htons(_srcPort); + if(_ptrSendRtpSocket->Bind(_localRTPAddr) == false) + { + WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id, + "Failed to bind to port:%d ", _srcPort); + return kFailedToBindPort; + } + } + return kNoSocketError; +} + +UdpTransportImpl::ErrorCode UdpTransportImpl::BindRTCPSendSocket() +{ + if(!_ptrSendRtcpSocket) + { + return kSocketInvalid; + } + + if(_ipV6Enabled) + { +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + _localRTCPAddr.sin_length = 0; + _localRTCPAddr.sin_family = PF_INET6; +#else + _localRTCPAddr._sockaddr_storage.sin_family = PF_INET6; +#endif + _localRTCPAddr._sockaddr_in6.sin6_flowinfo=0; + _localRTCPAddr._sockaddr_in6.sin6_scope_id=0; + _localRTCPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[0] = + 0; // = INADDR_ANY + _localRTCPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[1] = + 0; + _localRTCPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[2] = + 0; + _localRTCPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[3] = + 0; + _localRTCPAddr._sockaddr_in6.sin6_port = Htons(_srcPortRTCP); + if(_ptrSendRtcpSocket->Bind(_localRTCPAddr) == false) + { + WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id, + "Failed to bind to port:%d ", _srcPortRTCP); + return kFailedToBindPort; + } + } else { +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + _localRTCPAddr.sin_length = 0; + _localRTCPAddr.sin_family = PF_INET; +#else + _localRTCPAddr._sockaddr_storage.sin_family = PF_INET; +#endif + _localRTCPAddr._sockaddr_in.sin_addr= 0; + _localRTCPAddr._sockaddr_in.sin_port = Htons(_srcPortRTCP); + if(_ptrSendRtcpSocket->Bind(_localRTCPAddr) == false) + { + WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id, + "Failed to bind to port:%d ", _srcPortRTCP); + return kFailedToBindPort; + } + } + return kNoSocketError; +} + +UdpTransportImpl::ErrorCode UdpTransportImpl::BindLocalRTPSocket() +{ + if(!_ptrRtpSocket) + { + return kSocketInvalid; + } + if(!IpV6Enabled()) + { + SocketAddress recAddr; + memset(&recAddr, 0, sizeof(SocketAddress)); + recAddr._sockaddr_storage.sin_family = AF_INET; +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + recAddr.sin_length = 0; + recAddr.sin_family = PF_INET; +#else + recAddr._sockaddr_storage.sin_family = PF_INET; +#endif + recAddr._sockaddr_in.sin_addr = InetAddrIPV4(_localIP); + recAddr._sockaddr_in.sin_port = Htons(_localPort); + + if (!_ptrRtpSocket->Bind(recAddr)) + { + WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id, + "Failed to bind to port:%d ", _localPort); + return kFailedToBindPort; + } + } + else + { + SocketAddress stLclName; +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + stLclName.sin_lenght = 0; + stLclName.sin_family = PF_INET6; +#else + stLclName._sockaddr_storage.sin_family = PF_INET6; +#endif + InetPresentationToNumeric(AF_INET6,_localIP, + &stLclName._sockaddr_in6.sin6_addr); + stLclName._sockaddr_in6.sin6_port = Htons(_localPort); + stLclName._sockaddr_in6.sin6_flowinfo = 0; + stLclName._sockaddr_in6.sin6_scope_id = 0; + + if (!_ptrRtpSocket->Bind(stLclName)) + { + WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id, + "Failed to bind to port:%d ", _localPort); + return kFailedToBindPort; + } + } + + if(_localMulticastIP[0] != 0) + { + // Join the multicast group from which to receive datagrams. + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = InetAddrIPV4(_localMulticastIP); + mreq.imr_interface.s_addr = INADDR_ANY; + + if (!_ptrRtpSocket->SetSockopt(IPPROTO_IP,IP_ADD_MEMBERSHIP, + (WebRtc_Word8*)&mreq,sizeof (mreq))) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "setsockopt() for multicast failed, not closing socket"); + }else + { + WEBRTC_TRACE(kTraceInfo, kTraceTransport, _id, + "multicast group successfully joined"); + } + } + return kNoSocketError; +} + +UdpTransportImpl::ErrorCode UdpTransportImpl::BindLocalRTCPSocket() +{ + if(!_ptrRtcpSocket) + { + return kSocketInvalid; + } + if(! IpV6Enabled()) + { + SocketAddress recAddr; + memset(&recAddr, 0, sizeof(SocketAddress)); +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + recAddr.sin_length = 0; + recAddr.sin_family = AF_INET; +#else + recAddr._sockaddr_storage.sin_family = AF_INET; +#endif + recAddr._sockaddr_in.sin_addr = InetAddrIPV4(_localIP); + recAddr._sockaddr_in.sin_port = Htons(_localPortRTCP); + + if (!_ptrRtcpSocket->Bind(recAddr)) + { + WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id, + "Failed to bind to port:%d ", _localPortRTCP); + return kFailedToBindPort; + } + } + else + { + SocketAddress stLclName; +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + stLclName.sin_length = 0; + stLclName.sin_family = PF_INET6; +#else + stLclName._sockaddr_storage.sin_family = PF_INET6; +#endif + stLclName._sockaddr_in6.sin6_flowinfo = 0; + stLclName._sockaddr_in6.sin6_scope_id = 0; + stLclName._sockaddr_in6.sin6_port = Htons(_localPortRTCP); + + InetPresentationToNumeric(AF_INET6,_localIP, + &stLclName._sockaddr_in6.sin6_addr); + if (!_ptrRtcpSocket->Bind(stLclName)) + { + WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id, + "Failed to bind to port:%d ", _localPortRTCP); + return kFailedToBindPort; + } + } + if(_localMulticastIP[0] != 0) + { + // Join the multicast group from which to receive datagrams. + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = InetAddrIPV4(_localMulticastIP); + mreq.imr_interface.s_addr = INADDR_ANY; + + if (!_ptrRtcpSocket->SetSockopt(IPPROTO_IP,IP_ADD_MEMBERSHIP, + (WebRtc_Word8*)&mreq,sizeof (mreq))) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "setsockopt() for multicast failed, not closing socket"); + }else + { + WEBRTC_TRACE(kTraceInfo, kTraceTransport, _id, + "multicast group successfully joined"); + } + } + return kNoSocketError; +} + +WebRtc_Word32 UdpTransportImpl::InitializeSourcePorts(WebRtc_UWord16 rtpPort, + WebRtc_UWord16 rtcpPort) +{ + + if(rtpPort == 0) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "InitializeSourcePorts port 0 not allowed"); + _lastError = kPortInvalid; + return -1; + } + + CriticalSectionScoped cs(_crit); + + CloseSendSockets(); + + if(_mgr == NULL) + { + return -1; + } + + _srcPort = rtpPort; + if(rtcpPort == 0) + { + _srcPortRTCP = rtpPort+1; + } else + { + _srcPortRTCP = rtcpPort; + } + _useSetSockOpt =false; + _tos=0; + _pcp=0; + + _ptrSendRtpSocket = _socket_creator->CreateSocket(_id, _mgr, NULL, NULL, + IpV6Enabled(), false); + _ptrSendRtcpSocket = _socket_creator->CreateSocket(_id, _mgr, NULL, NULL, + IpV6Enabled(), false); + + ErrorCode retVal = BindRTPSendSocket(); + if(retVal != kNoSocketError) + { + _lastError = retVal; + return -1; + } + retVal = BindRTCPSendSocket(); + if(retVal != kNoSocketError) + { + _lastError = retVal; + return -1; + } + return 0; +} + +WebRtc_Word32 UdpTransportImpl::SourcePorts(WebRtc_UWord16& rtpPort, + WebRtc_UWord16& rtcpPort) const +{ + CriticalSectionScoped cs(_crit); + + rtpPort = (_srcPort != 0) ? _srcPort : _localPort; + rtcpPort = (_srcPortRTCP != 0) ? _srcPortRTCP : _localPortRTCP; + return 0; +} + + +#ifdef _WIN32 +WebRtc_Word32 UdpTransportImpl::StartReceiving( + WebRtc_UWord32 numberOfSocketBuffers) +#else +WebRtc_Word32 UdpTransportImpl::StartReceiving( + WebRtc_UWord32 /*numberOfSocketBuffers*/) +#endif +{ + CriticalSectionScoped cs(_crit); + if(_receiving) + { + return 0; + } + if(_ptrRtpSocket) + { +#ifdef _WIN32 + if(!_ptrRtpSocket->StartReceiving(numberOfSocketBuffers)) +#else + if(!_ptrRtpSocket->StartReceiving()) +#endif + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "Failed to start receive on RTP socket"); + _lastError = kStartReceiveError; + return -1; + } + } + if(_ptrRtcpSocket) + { + if(!_ptrRtcpSocket->StartReceiving()) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "Failed to start receive on RTCP socket"); + _lastError = kStartReceiveError; + return -1; + } + } + if( _ptrRtpSocket == NULL && + _ptrRtcpSocket == NULL) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "Failed to StartReceiving, no socket initialized"); + _lastError = kStartReceiveError; + return -1; + } + _receiving = true; + return 0; +} + +bool UdpTransportImpl::Receiving() const +{ + return _receiving; +} + +WebRtc_Word32 UdpTransportImpl::StopReceiving() +{ + + CriticalSectionScoped cs(_crit); + + _receiving = false; + + if (_ptrRtpSocket) + { + if (!_ptrRtpSocket->StopReceiving()) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "Failed to stop receiving on RTP socket"); + _lastError = kStopReceiveError; + return -1; + } + } + if (_ptrRtcpSocket) + { + if (!_ptrRtcpSocket->StopReceiving()) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "Failed to stop receiving on RTCP socket"); + _lastError = kStopReceiveError; + return -1; + } + } + return 0; +} + +WebRtc_Word32 UdpTransportImpl::InitializeSendSockets( + const char* ipaddr, + const WebRtc_UWord16 rtpPort, + const WebRtc_UWord16 rtcpPort) +{ + { + CriticalSectionScoped cs(_crit); + _destPort = rtpPort; + if(rtcpPort == 0) + { + _destPortRTCP = _destPort+1; + } else + { + _destPortRTCP = rtcpPort; + } + + if(ipaddr == NULL) + { + if (!IsIpAddressValid(_destIP, IpV6Enabled())) + { + _destPort = 0; + _destPortRTCP = 0; + _lastError = kIpAddressInvalid; + return -1; + } + } else + { + if (IsIpAddressValid(ipaddr, IpV6Enabled())) + { + strncpy( + _destIP, + ipaddr, + IpV6Enabled() ? kIpAddressVersion6Length : + kIpAddressVersion4Length); + } else { + _destPort = 0; + _destPortRTCP = 0; + _lastError = kIpAddressInvalid; + return -1; + } + } + BuildRemoteRTPAddr(); + BuildRemoteRTCPAddr(); + } + + if (_ipV6Enabled) + { + if (_qos) + { + WEBRTC_TRACE( + kTraceWarning, + kTraceTransport, + _id, + "QOS is enabled but will be ignored since IPv6 is enabled"); + } + }else + { + // TODO (grunell): Multicast support is experimantal. + + // Put the first digit of the remote address in val. + WebRtc_Word32 val = ntohl(_remoteRTPAddr._sockaddr_in.sin_addr)>> 24; + + if((val > 223) && (val < 240)) + { + // Multicast address. + CriticalSectionScoped cs(_crit); + + UdpSocketWrapper* rtpSock = (_ptrSendRtpSocket ? + _ptrSendRtpSocket : _ptrRtpSocket); + if (!rtpSock || !rtpSock->ValidHandle()) + { + _lastError = kSocketInvalid; + return -1; + } + UdpSocketWrapper* rtcpSock = (_ptrSendRtcpSocket ? + _ptrSendRtcpSocket : _ptrRtcpSocket); + if (!rtcpSock || !rtcpSock->ValidHandle()) + { + _lastError = kSocketInvalid; + return -1; + } + + // Set Time To Live to same region + WebRtc_Word32 iOptVal = 64; + if (!rtpSock->SetSockopt(IPPROTO_IP, IP_MULTICAST_TTL, + (WebRtc_Word8*)&iOptVal, + sizeof (WebRtc_Word32))) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "setsockopt for multicast error on RTP socket"); + _ptrRtpSocket->CloseBlocking(); + _ptrRtpSocket = NULL; + _lastError = kMulticastAddressInvalid; + return -1; + } + if (!rtcpSock->SetSockopt(IPPROTO_IP, IP_MULTICAST_TTL, + (WebRtc_Word8*)&iOptVal, + sizeof (WebRtc_Word32))) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "setsockopt for multicast error on RTCP socket"); + _ptrRtpSocket->CloseBlocking(); + _ptrRtpSocket = NULL; + _lastError = kMulticastAddressInvalid; + return -1; + } + } + } + return 0; +} + +void UdpTransportImpl::BuildSockaddrIn(WebRtc_UWord16 portnr, + const char* ip, + SocketAddress& remoteAddr) const +{ + if(_ipV6Enabled) + { +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + remoteAddr.sin_length = 0; + remoteAddr.sin_family = PF_INET6; +#else + remoteAddr._sockaddr_storage.sin_family = PF_INET6; +#endif + remoteAddr._sockaddr_in6.sin6_port = Htons(portnr); + InetPresentationToNumeric(AF_INET6, ip, + &remoteAddr._sockaddr_in6.sin6_addr); + remoteAddr._sockaddr_in6.sin6_flowinfo=0; + remoteAddr._sockaddr_in6.sin6_scope_id=0; + } else + { +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + remoteAddr.sin_length = 0; + remoteAddr.sin_family = PF_INET; +#else + remoteAddr._sockaddr_storage.sin_family = PF_INET; +#endif + remoteAddr._sockaddr_in.sin_port = Htons(portnr); + remoteAddr._sockaddr_in.sin_addr= InetAddrIPV4( + const_cast(ip)); + } +} + +WebRtc_Word32 UdpTransportImpl::SendRaw(const WebRtc_Word8 *data, + WebRtc_UWord32 length, + WebRtc_Word32 isRTCP, + WebRtc_UWord16 portnr, + const char* ip) +{ + CriticalSectionScoped cs(_crit); + if(isRTCP) + { + UdpSocketWrapper* rtcpSock = NULL; + if(_ptrSendRtcpSocket) + { + rtcpSock = _ptrSendRtcpSocket; + } else if(_ptrRtcpSocket) + { + rtcpSock = _ptrRtcpSocket; + } else + { + return -1; + } + if(portnr == 0 && ip == NULL) + { + return rtcpSock->SendTo(data,length,_remoteRTCPAddr); + + } else if(portnr != 0 && ip != NULL) + { + SocketAddress remoteAddr; + BuildSockaddrIn(portnr, ip, remoteAddr); + return rtcpSock->SendTo(data,length,remoteAddr); + } else if(ip != NULL) + { + SocketAddress remoteAddr; + BuildSockaddrIn(_destPortRTCP, ip, remoteAddr); + return rtcpSock->SendTo(data,length,remoteAddr); + } else + { + SocketAddress remoteAddr; + BuildSockaddrIn(portnr, _destIP, remoteAddr); + return rtcpSock->SendTo(data,length,remoteAddr); + } + } else { + UdpSocketWrapper* rtpSock = NULL; + if(_ptrSendRtpSocket) + { + rtpSock = _ptrSendRtpSocket; + + } else if(_ptrRtpSocket) + { + rtpSock = _ptrRtpSocket; + } else + { + return -1; + } + if(portnr == 0 && ip == NULL) + { + return rtpSock->SendTo(data,length,_remoteRTPAddr); + + } else if(portnr != 0 && ip != NULL) + { + SocketAddress remoteAddr; + BuildSockaddrIn(portnr, ip, remoteAddr); + return rtpSock->SendTo(data,length,remoteAddr); + } else if(ip != NULL) + { + SocketAddress remoteAddr; + BuildSockaddrIn(_destPort, ip, remoteAddr); + return rtpSock->SendTo(data,length,remoteAddr); + } else + { + SocketAddress remoteAddr; + BuildSockaddrIn(portnr, _destIP, remoteAddr); + return rtpSock->SendTo(data,length,remoteAddr); + } + } +} + +WebRtc_Word32 UdpTransportImpl::SendRTPPacketTo(const WebRtc_Word8* data, + WebRtc_UWord32 length, + const SocketAddress& to) +{ + CriticalSectionScoped cs(_crit); + if(_ptrSendRtpSocket) + { + return _ptrSendRtpSocket->SendTo(data,length,to); + + } else if(_ptrRtpSocket) + { + return _ptrRtpSocket->SendTo(data,length,to); + } + return -1; +} + +WebRtc_Word32 UdpTransportImpl::SendRTCPPacketTo(const WebRtc_Word8* data, + WebRtc_UWord32 length, + const SocketAddress& to) +{ + + CriticalSectionScoped cs(_crit); + + if(_ptrSendRtcpSocket) + { + return _ptrSendRtcpSocket->SendTo(data,length,to); + + } else if(_ptrRtcpSocket) + { + return _ptrRtcpSocket->SendTo(data,length,to); + } + return -1; +} + +WebRtc_Word32 UdpTransportImpl::SendRTPPacketTo(const WebRtc_Word8* data, + WebRtc_UWord32 length, + const WebRtc_UWord16 rtpPort) +{ + CriticalSectionScoped cs(_crit); + // Use the current SocketAdress but update it with rtpPort. + SocketAddress to; + memcpy(&to, &_remoteRTPAddr, sizeof(SocketAddress)); + + if(_ipV6Enabled) + { + to._sockaddr_in6.sin6_port = Htons(rtpPort); + } else + { + to._sockaddr_in.sin_port = Htons(rtpPort); + } + + if(_ptrSendRtpSocket) + { + return _ptrSendRtpSocket->SendTo(data,length,to); + + } else if(_ptrRtpSocket) + { + return _ptrRtpSocket->SendTo(data,length,to); + } + return -1; +} + +WebRtc_Word32 UdpTransportImpl::SendRTCPPacketTo(const WebRtc_Word8* data, + WebRtc_UWord32 length, + const WebRtc_UWord16 rtcpPort) +{ + CriticalSectionScoped cs(_crit); + + // Use the current SocketAdress but update it with rtcpPort. + SocketAddress to; + memcpy(&to, &_remoteRTCPAddr, sizeof(SocketAddress)); + + if(_ipV6Enabled) + { + to._sockaddr_in6.sin6_port = Htons(rtcpPort); + } else + { + to._sockaddr_in.sin_port = Htons(rtcpPort); + } + + if(_ptrSendRtcpSocket) + { + return _ptrSendRtcpSocket->SendTo(data,length,to); + + } else if(_ptrRtcpSocket) + { + return _ptrRtcpSocket->SendTo(data,length,to); + } + return -1; +} + +int UdpTransportImpl::SendPacket(int /*channel*/, const void* data, int length) +{ + WEBRTC_TRACE(kTraceStream, kTraceTransport, _id, "%s", __FUNCTION__); + + CriticalSectionScoped cs(_crit); + + if(_destIP[0] == 0) + { + return -1; + } + if(_destPort == 0) + { + return -1; + } + + // Create socket if it hasn't been set up already. + // TODO (hellner): why not fail here instead. Sockets not being initialized + // indicates that there is a problem somewhere. + if( _ptrSendRtpSocket == NULL && + _ptrRtpSocket == NULL) + { + WEBRTC_TRACE( + kTraceStateInfo, + kTraceTransport, + _id, + "Creating RTP socket since no receive or source socket is\ + configured"); + + _ptrRtpSocket = _socket_creator->CreateSocket(_id, _mgr, this, + IncomingRTPCallback, + IpV6Enabled(), false); + + // Don't bind to a specific IP address. + if(! IpV6Enabled()) + { + strncpy(_localIP, "0.0.0.0",16); + } else + { + strncpy(_localIP, "0000:0000:0000:0000:0000:0000:0000:0000", + kIpAddressVersion6Length); + } + _localPort = _destPort; + + ErrorCode retVal = BindLocalRTPSocket(); + if(retVal != kNoSocketError) + { + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "SendPacket() failed to bind RTP socket"); + _lastError = retVal; + CloseReceiveSockets(); + return -1; + } + } + + if(_ptrSendRtpSocket) + { + return _ptrSendRtpSocket->SendTo((const WebRtc_Word8*)data, length, + _remoteRTPAddr); + + } else if(_ptrRtpSocket) + { + return _ptrRtpSocket->SendTo((const WebRtc_Word8*)data, length, + _remoteRTPAddr); + } + return -1; +} + +int UdpTransportImpl::SendRTCPPacket(int /*channel*/, const void* data, + int length) +{ + + CriticalSectionScoped cs(_crit); + if(_destIP[0] == 0) + { + return -1; + } + if(_destPortRTCP == 0) + { + return -1; + } + + // Create socket if it hasn't been set up already. + // TODO (hellner): why not fail here instead. Sockets not being initialized + // indicates that there is a problem somewhere. + if( _ptrSendRtcpSocket == NULL && + _ptrRtcpSocket == NULL) + { + WEBRTC_TRACE( + kTraceStateInfo, + kTraceTransport, + _id, + "Creating RTCP socket since no receive or source socket is\ + configured"); + + _ptrRtcpSocket = _socket_creator->CreateSocket(_id, _mgr, this, + IncomingRTCPCallback, + IpV6Enabled(), false); + + // Don't bind to a specific IP address. + if(! IpV6Enabled()) + { + strncpy(_localIP, "0.0.0.0",16); + } else + { + strncpy(_localIP, "0000:0000:0000:0000:0000:0000:0000:0000", + kIpAddressVersion6Length); + } + _localPortRTCP = _destPortRTCP; + + ErrorCode retVal = BindLocalRTCPSocket(); + if(retVal != kNoSocketError) + { + _lastError = retVal; + WEBRTC_TRACE(kTraceError, kTraceTransport, _id, + "SendRTCPPacket() failed to bind RTCP socket"); + CloseReceiveSockets(); + return -1; + } + } + + if(_ptrSendRtcpSocket) + { + return _ptrSendRtcpSocket->SendTo((const WebRtc_Word8*)data, length, + _remoteRTCPAddr); + } else if(_ptrRtcpSocket) + { + return _ptrRtcpSocket->SendTo((const WebRtc_Word8*)data, length, + _remoteRTCPAddr); + } + return -1; +} + +WebRtc_Word32 UdpTransportImpl::SetSendIP(const char* ipaddr) +{ + if(!IsIpAddressValid(ipaddr,IpV6Enabled())) + { + return kIpAddressInvalid; + } + CriticalSectionScoped cs(_crit); + strncpy(_destIP, ipaddr,kIpAddressVersion6Length); + BuildRemoteRTPAddr(); + BuildRemoteRTCPAddr(); + return 0; +} + +WebRtc_Word32 UdpTransportImpl::SetSendPorts(WebRtc_UWord16 rtpPort, + WebRtc_UWord16 rtcpPort) +{ + CriticalSectionScoped cs(_crit); + _destPort = rtpPort; + if(rtcpPort == 0) + { + _destPortRTCP = _destPort+1; + } else + { + _destPortRTCP = rtcpPort; + } + BuildRemoteRTPAddr(); + BuildRemoteRTCPAddr(); + return 0; +} + +void UdpTransportImpl::IncomingRTPCallback(CallbackObj obj, + const WebRtc_Word8* rtpPacket, + WebRtc_Word32 rtpPacketLength, + const SocketAddress* from) +{ + if (rtpPacket && rtpPacketLength > 0) + { + UdpTransportImpl* socketTransport = (UdpTransportImpl*) obj; + socketTransport->IncomingRTPFunction(rtpPacket, rtpPacketLength, from); + } +} + +void UdpTransportImpl::IncomingRTCPCallback(CallbackObj obj, + const WebRtc_Word8* rtcpPacket, + WebRtc_Word32 rtcpPacketLength, + const SocketAddress* from) +{ + if (rtcpPacket && rtcpPacketLength > 0) + { + UdpTransportImpl* socketTransport = (UdpTransportImpl*) obj; + socketTransport->IncomingRTCPFunction(rtcpPacket, rtcpPacketLength, + from); + } +} + +void UdpTransportImpl::IncomingRTPFunction(const WebRtc_Word8* rtpPacket, + WebRtc_Word32 rtpPacketLength, + const SocketAddress* fromSocket) +{ + char ipAddress[kIpAddressVersion6Length]; + WebRtc_UWord32 ipAddressLength = kIpAddressVersion6Length; + WebRtc_UWord16 portNr = 0; + + { + CriticalSectionScoped cs(_critFilter); + if (FilterIPAddress(fromSocket) == false) + { + // Packet should be filtered out. Drop it. + WEBRTC_TRACE(kTraceStream, kTraceTransport, _id, + "Incoming RTP packet blocked by IP filter"); + return; + } + + if (IPAddressCached(*fromSocket, ipAddress, ipAddressLength, portNr) < + 0) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpTransportImpl::IncomingRTPFunction - Cannot get sender\ + information"); + }else + { + // Make sure ipAddress is null terminated. + ipAddress[kIpAddressVersion6Length - 1] = 0; + strncpy(_fromIP, ipAddress, kIpAddressVersion6Length - 1); + } + + // Filter based on port. + if (_rtpFilterPort != 0 && + _rtpFilterPort != portNr) + { + // Drop packet. + memset(_fromIP, 0, sizeof(_fromIP)); + WEBRTC_TRACE( + kTraceStream, + kTraceTransport, + _id, + "Incoming RTP packet blocked by filter incoming from port:%d\ + allowed port:%d", + portNr, + _rtpFilterPort); + return; + } + _fromPort = portNr; + } + + CriticalSectionScoped cs(_critPacketCallback); + if (_packetCallback) + { + WEBRTC_TRACE(kTraceStream, kTraceTransport, _id, + "Incoming RTP packet from ip:%s port:%d", ipAddress, portNr); + _packetCallback->IncomingRTPPacket(rtpPacket, rtpPacketLength, + ipAddress, portNr); + } +} + +void UdpTransportImpl::IncomingRTCPFunction(const WebRtc_Word8* rtcpPacket, + WebRtc_Word32 rtcpPacketLength, + const SocketAddress* fromSocket) +{ + char ipAddress[kIpAddressVersion6Length]; + WebRtc_UWord32 ipAddressLength = kIpAddressVersion6Length; + WebRtc_UWord16 portNr = 0; + + { + CriticalSectionScoped cs(_critFilter); + if (FilterIPAddress(fromSocket) == false) + { + // Packet should be filtered out. Drop it. + WEBRTC_TRACE(kTraceStream, kTraceTransport, _id, + "Incoming RTCP packet blocked by IP filter"); + return; + } + if (IPAddress(*fromSocket, ipAddress, ipAddressLength, portNr) < 0) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpTransportImpl::IncomingRTCPFunction - Cannot get sender\ + information"); + }else { + // Make sure ipAddress is null terminated. + ipAddress[kIpAddressVersion6Length - 1] = 0; + strncpy(_fromIP, ipAddress, kIpAddressVersion6Length - 1); + } + + // Filter based on port. + if (_rtcpFilterPort != 0 && + _rtcpFilterPort != portNr) + { + // Drop packet. + WEBRTC_TRACE( + kTraceStream, + kTraceTransport, + _id, + "Incoming RTCP packet blocked by filter incoming from port:%d\ + allowed port:%d", + portNr, + _rtpFilterPort); + return; + } + _fromPortRTCP = portNr; + } + + CriticalSectionScoped cs(_critPacketCallback); + if (_packetCallback) + { + WEBRTC_TRACE(kTraceStream, kTraceTransport, _id, + "Incoming RTCP packet from ip:%s port:%d", ipAddress, + portNr); + _packetCallback->IncomingRTCPPacket(rtcpPacket, rtcpPacketLength, + ipAddress, portNr); + } +} + +bool UdpTransportImpl::FilterIPAddress(const SocketAddress* fromAddress) +{ + if(fromAddress->_sockaddr_storage.sin_family == AF_INET) + { + if (_filterIPAddress._sockaddr_storage.sin_family == AF_INET) + { + // IP is stored in sin_addr. + if (_filterIPAddress._sockaddr_in.sin_addr != 0 && + (_filterIPAddress._sockaddr_in.sin_addr != + fromAddress->_sockaddr_in.sin_addr)) + { + return false; + } + } + } + else if(fromAddress->_sockaddr_storage.sin_family == AF_INET6) + { + if (_filterIPAddress._sockaddr_storage.sin_family == AF_INET6) + { + // IP is stored in sin_6addr. + for (WebRtc_Word32 i = 0; i < 4; i++) + { + if (_filterIPAddress._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[i] != 0 && + _filterIPAddress._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[i] != fromAddress->_sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[i]) + { + return false; + } + } + } + } + else + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + _id, + "UdpTransportImpl::FilterIPAddress() unknown address family"); + return false; + } + return true; +} + +void UdpTransportImpl::CloseReceiveSockets() +{ + if(_ptrRtpSocket) + { + _ptrRtpSocket->CloseBlocking(); + _ptrRtpSocket = NULL; + } + if(_ptrRtcpSocket) + { + _ptrRtcpSocket->CloseBlocking(); + _ptrRtcpSocket = NULL; + } + _receiving = false; +} + +void UdpTransportImpl::CloseSendSockets() +{ + if(_ptrSendRtpSocket) + { + _ptrSendRtpSocket->CloseBlocking(); + _ptrSendRtpSocket = 0; + } + if(_ptrSendRtcpSocket) + { + _ptrSendRtcpSocket->CloseBlocking(); + _ptrSendRtcpSocket = 0; + } +} + +WebRtc_UWord16 UdpTransport::Htons(const WebRtc_UWord16 port) +{ + return htons(port); +} + +WebRtc_UWord32 UdpTransport::Htonl(const WebRtc_UWord32 a) +{ + return htonl(a); +} + +WebRtc_UWord32 UdpTransport::InetAddrIPV4(const char* ip) +{ + return ::inet_addr(ip); +} + +WebRtc_Word32 UdpTransport::InetPresentationToNumeric(WebRtc_Word32 af, + const char* src, + void* dst) +{ +#if defined(WEBRTC_LINUX) || defined(WEBRTC_MAC) + const WebRtc_Word32 result = inet_pton(af, src, dst); + return result > 0 ? 0 : -1; + +#elif defined(_WIN32) + SocketAddress temp; + int length=sizeof(SocketAddress); + + if(af == AF_INET) + { + WebRtc_Word32 result = WSAStringToAddressA( + (const LPSTR)src, + af, + 0, + reinterpret_cast(&temp), + &length); + if(result != 0) + { + return -1; + } + memcpy(dst,&(temp._sockaddr_in.sin_addr), + sizeof(temp._sockaddr_in.sin_addr)); + return 0; + } + else if(af == AF_INET6) + { + WebRtc_Word32 result = WSAStringToAddressA( + (const LPSTR)src, + af, + 0, + reinterpret_cast(&temp), + &length); + if(result !=0) + { + return -1; + } + memcpy(dst,&(temp._sockaddr_in6.sin6_addr), + sizeof(temp._sockaddr_in6.sin6_addr)); + return 0; + + }else + { + return -1; + } +#else + return -1; +#endif +} + +WebRtc_Word32 UdpTransport::LocalHostAddressIPV6(char n_localIP[16]) +{ + +#if defined(_WIN32) + struct addrinfo *result = NULL; + struct addrinfo *ptr = NULL; + struct addrinfo hints; + + ZeroMemory(&hints, sizeof(hints)); + hints.ai_family = AF_INET6; + + char szHostName[256] = ""; + if(::gethostname(szHostName, sizeof(szHostName) - 1)) + { + WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1, "gethostname failed"); + return -1; + } + + DWORD dwRetval = getaddrinfo(szHostName, NULL, &hints, &result); + if ( dwRetval != 0 ) + { + WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1, + "getaddrinfo failed, error:%d", dwRetval); + return -1; + } + for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) + { + switch (ptr->ai_family) + { + case AF_INET6: + { + for(int i = 0; i< 16; i++) + { + n_localIP[i] = (*(SocketAddress*)ptr->ai_addr). + _sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u8[i]; + } + bool islocalIP = true; + + for(int n = 0; n< 15; n++) + { + if(n_localIP[n] != 0) + { + islocalIP = false; + break; + } + } + + if(islocalIP && n_localIP[15] != 1) + { + islocalIP = false; + } + + if(islocalIP && ptr->ai_next) + { + continue; + } + if(n_localIP[0] == 0xfe && + n_localIP[1] == 0x80 && ptr->ai_next) + { + continue; + } + freeaddrinfo(result); + } + return 0; + default: + break; + }; + } + freeaddrinfo(result); + WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1, + "getaddrinfo failed to find address"); + return -1; + +#elif defined(WEBRTC_MAC) + struct ifaddrs* ptrIfAddrs = NULL; + struct ifaddrs* ptrIfAddrsStart = NULL; + + getifaddrs(&ptrIfAddrsStart); + ptrIfAddrs = ptrIfAddrsStart; + while(ptrIfAddrs) + { + if(ptrIfAddrs->ifa_addr->sa_family == AF_INET6) + { + const struct sockaddr_in6* sock_in6 = + reinterpret_cast(ptrIfAddrs->ifa_addr); + const struct in6_addr* sin6_addr = &sock_in6->sin6_addr; + + if (IN6_IS_ADDR_LOOPBACK(sin6_addr) || + IN6_IS_ADDR_LINKLOCAL(sin6_addr)) { + ptrIfAddrs = ptrIfAddrs->ifa_next; + continue; + } + memcpy(n_localIP, sin6_addr->s6_addr, sizeof(sin6_addr->s6_addr)); + freeifaddrs(ptrIfAddrsStart); + return 0; + } + ptrIfAddrs = ptrIfAddrs->ifa_next; + } + freeifaddrs(ptrIfAddrsStart); + return -1; +#elif defined(WEBRTC_ANDROID) + return -1; +#else // WEBRTC_LINUX + struct + { + struct nlmsghdr n; + struct ifaddrmsg r; + } req; + + struct rtattr* rta = NULL; + int status; + char buf[16384]; // = 16 * 1024 (16 kB) + struct nlmsghdr* nlmp; + struct ifaddrmsg* rtmp; + struct rtattr* rtatp; + int rtattrlen; + struct in6_addr* in6p; + + int fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (fd == -1) + { + return -1; + } + + // RTM_GETADDR is used to fetch the ip address from the kernel interface + // table. Populate the msg structure (req) the size of the message buffer + // is specified to netlinkmessage header, and flags values are set as + // NLM_F_ROOT | NLM_F_REQUEST. + // The request flag must be set for all messages requesting the data from + // kernel. The root flag is used to notify the kernel to return the full + // tabel. Another flag (not used) is NLM_F_MATCH. This is used to get only + // specified entries in the table. At the time of writing this program this + // flag is not implemented in kernel + + memset(&req, 0, sizeof(req)); + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT; + req.n.nlmsg_type = RTM_GETADDR; + req.r.ifa_family = AF_INET6; + + // Fill up all the attributes for the rtnetlink header. + // The lenght is very important. 16 signifies the ipv6 address. + rta = (struct rtattr*)(((char*)&req) + NLMSG_ALIGN(req.n.nlmsg_len)); + rta->rta_len = RTA_LENGTH(16); + + status = send(fd, &req, req.n.nlmsg_len, 0); + if (status < 0) + { + close(fd); + return -1; + } + status = recv(fd, buf, sizeof(buf), 0); + if (status < 0) + { + close(fd); + return -1; + } + if(status == 0) + { + close(fd); + return -1; + } + close(fd); + + // The message is stored in buff. Parse the message to get the requested + // data. + { + nlmp = (struct nlmsghdr*)buf; + int len = nlmp->nlmsg_len; + int req_len = len - sizeof(*nlmp); + + if (req_len < 0 || len > status) + { + return -1; + } + if (!NLMSG_OK_NO_WARNING(nlmp, status)) + { + return -1; + } + rtmp = (struct ifaddrmsg*)NLMSG_DATA(nlmp); + rtatp = (struct rtattr*)IFA_RTA(rtmp); + + rtattrlen = IFA_PAYLOAD(nlmp); + + for (; RTA_OK(rtatp, rtattrlen); rtatp = RTA_NEXT(rtatp, rtattrlen)) + { + + // Here we hit the fist chunk of the message. Time to validate the + // type. For more info on the different types see + // "man(7) rtnetlink" The table below is taken from man pages. + // Attributes + // rta_type value type description + // ------------------------------------------------------------- + // IFA_UNSPEC - unspecified. + // IFA_ADDRESS raw protocol address interface address + // IFA_LOCAL raw protocol address local address + // IFA_LABEL asciiz string name of the interface + // IFA_BROADCAST raw protocol address broadcast address. + // IFA_ANYCAST raw protocol address anycast address + // IFA_CACHEINFO struct ifa_cacheinfo Address information. + + if(rtatp->rta_type == IFA_ADDRESS) + { + bool islocalIP = true; + in6p = (struct in6_addr*)RTA_DATA(rtatp); + for(int n = 0; n< 15; n++) + { + if(in6p->s6_addr[n] != 0) + { + islocalIP = false; + break; + } + } + if(islocalIP && in6p->s6_addr[15] != 1) + { + islocalIP = false; + } + if(!islocalIP) + { + for(int i = 0; i< 16; i++) + { + n_localIP[i] = in6p->s6_addr[i]; + } + if(n_localIP[0] == static_cast (0xfe) + && n_localIP[1] == static_cast(0x80) ) + { + // Auto configured IP. + continue; + } + break; + } + } + } + } + return 0; +#endif +} + +WebRtc_Word32 UdpTransport::LocalHostAddress(WebRtc_UWord32& localIP) +{ + #if defined(_WIN32) + hostent* localHost; + localHost = gethostbyname( "" ); + if(localHost) + { + if(localHost->h_addrtype != AF_INET) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + -1, + "LocalHostAddress can only get local IP for IP Version 4"); + return -1; + } + localIP= Htonl( + (*(struct in_addr *)localHost->h_addr_list[0]).S_un.S_addr); + return 0; + } + else + { + WebRtc_Word32 error = WSAGetLastError(); + WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1, + "gethostbyname failed, error:%d", error); + return -1; + } +#elif (defined(WEBRTC_MAC)) + char localname[255]; + if (gethostname(localname, 255) != -1) + { + hostent* localHost; + localHost = gethostbyname(localname); + if(localHost) + { + if(localHost->h_addrtype != AF_INET) + { + WEBRTC_TRACE( + kTraceError, + kTraceTransport, + -1, + "LocalHostAddress can only get local IP for IP Version 4"); + return -1; + } + localIP = Htonl((*(struct in_addr*)*localHost->h_addr_list).s_addr); + return 0; + } + } + WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1, "gethostname failed"); + return -1; +#else // WEBRTC_LINUX + int sockfd, size = 1; + struct ifreq* ifr; + struct ifconf ifc; + + if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) + { + return -1; + } + ifc.ifc_len = IFRSIZE; + ifc.ifc_req = NULL; + do + { + ++size; + // Buffer size needed is unknown. Try increasing it until no overflow + // occurs. + if (NULL == (ifc.ifc_req = (ifreq*)realloc(ifc.ifc_req, IFRSIZE))) { + fprintf(stderr, "Out of memory.\n"); + exit(EXIT_FAILURE); + } + ifc.ifc_len = IFRSIZE; + if (ioctl(sockfd, SIOCGIFCONF, &ifc)) + { + free(ifc.ifc_req); + close(sockfd); + return -1; + } + } while (IFRSIZE <= ifc.ifc_len); + + ifr = ifc.ifc_req; + for (;(char *) ifr < (char *) ifc.ifc_req + ifc.ifc_len; ++ifr) + { + if (ifr->ifr_addr.sa_data == (ifr+1)->ifr_addr.sa_data) + { + continue; // duplicate, skip it + } + if (ioctl(sockfd, SIOCGIFFLAGS, ifr)) + { + continue; // failed to get flags, skip it + } + if(strncmp(ifr->ifr_name, "lo",3) == 0) + { + continue; + }else + { + struct sockaddr* saddr = &(ifr->ifr_addr); + SocketAddress* socket_addess = reinterpret_cast( + saddr); + localIP = Htonl(socket_addess->_sockaddr_in.sin_addr); + close(sockfd); + free(ifc.ifc_req); + return 0; + } + } + free(ifc.ifc_req); + close(sockfd); + return -1; +#endif +} + +WebRtc_Word32 UdpTransport::IPAddress(const SocketAddress& address, + char* ip, + WebRtc_UWord32& ipSize, + WebRtc_UWord16& sourcePort) +{ + #if defined(_WIN32) + DWORD dwIPSize = ipSize; + WebRtc_Word32 returnvalue = WSAAddressToStringA((LPSOCKADDR)(&address), + sizeof(SocketAddress), + NULL, + ip, + &dwIPSize); + if(returnvalue == -1) + { + return -1; + } + + WebRtc_UWord16 source_port = 0; + if(address._sockaddr_storage.sin_family == AF_INET) + { + // Parse IP assuming format "a.b.c.d:port". + char* ipEnd = strchr(ip,':'); + if(ipEnd != NULL) + { + *ipEnd = '\0'; + } + ipSize = (WebRtc_Word32)strlen(ip); + if(ipSize == 0) + { + return -1; + } + source_port = address._sockaddr_in.sin_port; + } + else + { + // Parse IP assuming format "[address]:port". + char* ipEnd = strchr(ip,']'); + if(ipEnd != NULL) + { + // Calculate length + WebRtc_Word32 adrSize = WebRtc_Word32(ipEnd - ip) - 1; + memmove(ip, &ip[1], adrSize); // Remove '[' + *(ipEnd - 1) = '\0'; + } + ipSize = (WebRtc_Word32)strlen(ip); + if(ipSize == 0) + { + return -1; + } + + source_port = address._sockaddr_in6.sin6_port; + } + // Convert port number to network byte order. + sourcePort = htons(source_port); + return 0; + + #elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC) + WebRtc_Word32 ipFamily = address._sockaddr_storage.sin_family; + const void* ptrNumericIP = NULL; + + if(ipFamily == AF_INET) + { + ptrNumericIP = &(address._sockaddr_in.sin_addr); + } + else if(ipFamily == AF_INET6) + { + ptrNumericIP = &(address._sockaddr_in6.sin6_addr); + } + else + { + return -1; + } + if(inet_ntop(ipFamily, ptrNumericIP, ip, ipSize) == NULL) + { + return -1; + } + WebRtc_UWord16 source_port; + if(ipFamily == AF_INET) + { + source_port = address._sockaddr_in.sin_port; + } else + { + source_port = address._sockaddr_in6.sin6_port; + } + // Convert port number to network byte order. + sourcePort = htons(source_port); + return 0; + #else + return -1; + #endif +} + +bool UdpTransport::IsIpAddressValid(const char* ipadr, const bool ipV6) +{ + if(ipV6) + { + WebRtc_Word32 len = (WebRtc_Word32)strlen(ipadr); + if( len>39 || len == 0) + { + return false; + } + + WebRtc_Word32 i; + WebRtc_Word32 colonPos[7] = {0,0,0,0,0,0,0}; + WebRtc_Word32 lastColonPos = -2; + WebRtc_Word32 nColons = 0; + WebRtc_Word32 nDubbleColons = 0; + WebRtc_Word32 nDots = 0; + WebRtc_Word32 error = 0; + char c; + for(i = 0; i < len ; i++) + { + c=ipadr[i]; + if(isxdigit(c)) + ; + else if(c == ':') + { + if(nColons < 7) + colonPos[nColons] = i; + if((i-lastColonPos)==1) + nDubbleColons++; + lastColonPos=i; + if(nDots != 0) + { + error = 1; + } + nColons++; + } + else if(c == '.') + { + nDots++; + } + else + { + error = 1; + } + + } + if(error) + { + return false; + } + if(nDubbleColons > 1) + { + return false; + } + if(nColons > 7 || nColons < 2) + { + return false; + } + if(!(nDots == 3 || nDots == 0)) + { + return false; + } + lastColonPos = -1; + WebRtc_Word32 charsBeforeColon = 0; + for(i = 0; i < nColons; i++) + { + charsBeforeColon=colonPos[i]-lastColonPos-1; + if(charsBeforeColon > 4) + { + return false; + } + lastColonPos=colonPos[i]; + } + WebRtc_Word32 lengthAfterLastColon = len - lastColonPos - 1; + if(nDots == 0) + { + if(lengthAfterLastColon > 4) + return false; + } + if(nDots == 3 && lengthAfterLastColon > 0) + { + return IsIpAddressValid((ipadr+lastColonPos+1),false); + } + + } + else + { + WebRtc_Word32 len = (WebRtc_Word32)strlen(ipadr); + if((len>15)||(len==0)) + { + return false; + } + + // IPv4 should be [0-255].[0-255].[0-255].[0-255] + WebRtc_Word32 i; + WebRtc_Word32 nDots = 0; + WebRtc_Word32 iDotPos[4] = {0,0,0,0}; + + for (i = 0; (i < len) && (nDots < 4); i++) + { + if (ipadr[i] == (char)'.') + { + // Store index of dots and count number of dots. + iDotPos[nDots++] = i; + } + } + + bool allUnder256 = false; + // TODO (hellner): while loop seems to be abused here to get + // label like functionality. Fix later to avoid introducing bugs now. + + // Check that all numbers are smaller than 256. + do + { + if (nDots != 3 ) + { + break; + } + + if (iDotPos[0] <= 3) + { + char nr[4]; + memset(nr,0,4); + strncpy(nr,&ipadr[0],iDotPos[0]); + WebRtc_Word32 num = atoi(nr); + if (num > 255) + { + break; + } + } else { + break; + } + + if (iDotPos[1] - iDotPos[0] <= 4) + { + char nr[4]; + memset(nr,0,4); + strncpy(nr,&ipadr[iDotPos[0]+1], iDotPos[1] - iDotPos[0] - 1); + WebRtc_Word32 num = atoi(nr); + if (num > 255) + break; + } else { + break; + } + + if (iDotPos[2] - iDotPos[1] <= 4) + { + char nr[4]; + memset(nr,0,4); + strncpy(nr,&ipadr[iDotPos[1]+1], iDotPos[1] - iDotPos[0] - 1); + WebRtc_Word32 num = atoi(nr); + if (num > 255) + break; + + memset(nr,0,4); + strncpy(nr,&ipadr[iDotPos[2]+1], len - iDotPos[2] -1); + num = atoi(nr); + if (num > 255) + break; + else + allUnder256 = true; + } else + break; + } while(false); + + if (nDots != 3 || !allUnder256) + { + return false; + } + } + return true; +} + +} // namespace test +} // namespace webrtc diff --git a/webrtc/test/channel_transport/udp_transport_impl.h b/webrtc/test/channel_transport/udp_transport_impl.h new file mode 100644 index 0000000000..644b716509 --- /dev/null +++ b/webrtc/test/channel_transport/udp_transport_impl.h @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2012 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. + */ + +#ifndef WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_TRANSPORT_IMPL_H_ +#define WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_TRANSPORT_IMPL_H_ + +#include "webrtc/test/channel_transport/udp_transport.h" +#include "webrtc/test/channel_transport/udp_socket_wrapper.h" + +namespace webrtc { + +class CriticalSectionWrapper; +class RWLockWrapper; + +namespace test { + +class UdpSocketManager; + +class UdpTransportImpl : public UdpTransport +{ +public: + // A factory that returns a wrapped UDP socket or equivalent. + class SocketFactoryInterface { + public: + virtual ~SocketFactoryInterface() {} + virtual UdpSocketWrapper* CreateSocket(const WebRtc_Word32 id, + UdpSocketManager* mgr, + CallbackObj obj, + IncomingSocketCallback cb, + bool ipV6Enable, + bool disableGQOS) = 0; + }; + + // Constructor, only called by UdpTransport::Create and tests. + // The constructor takes ownership of the "maker". + // The constructor does not take ownership of socket_manager. + UdpTransportImpl(const WebRtc_Word32 id, + SocketFactoryInterface* maker, + UdpSocketManager* socket_manager); + virtual ~UdpTransportImpl(); + + // UdpTransport functions + virtual WebRtc_Word32 InitializeSendSockets( + const char* ipAddr, + const WebRtc_UWord16 rtpPort, + const WebRtc_UWord16 rtcpPort = 0); + virtual WebRtc_Word32 InitializeReceiveSockets( + UdpTransportData* const packetCallback, + const WebRtc_UWord16 rtpPort, + const char* ipAddr = NULL, + const char* multicastIpAddr = NULL, + const WebRtc_UWord16 rtcpPort = 0); + virtual WebRtc_Word32 InitializeSourcePorts( + const WebRtc_UWord16 rtpPort, + const WebRtc_UWord16 rtcpPort = 0); + virtual WebRtc_Word32 SourcePorts(WebRtc_UWord16& rtpPort, + WebRtc_UWord16& rtcpPort) const; + virtual WebRtc_Word32 ReceiveSocketInformation( + char ipAddr[kIpAddressVersion6Length], + WebRtc_UWord16& rtpPort, + WebRtc_UWord16& rtcpPort, + char multicastIpAddr[kIpAddressVersion6Length]) const; + virtual WebRtc_Word32 SendSocketInformation( + char ipAddr[kIpAddressVersion6Length], + WebRtc_UWord16& rtpPort, + WebRtc_UWord16& rtcpPort) const; + virtual WebRtc_Word32 RemoteSocketInformation( + char ipAddr[kIpAddressVersion6Length], + WebRtc_UWord16& rtpPort, + WebRtc_UWord16& rtcpPort) const; + virtual WebRtc_Word32 SetQoS(const bool QoS, + const WebRtc_Word32 serviceType, + const WebRtc_UWord32 maxBitrate = 0, + const WebRtc_Word32 overrideDSCP = 0, + const bool audio = false); + virtual WebRtc_Word32 QoS(bool& QoS, WebRtc_Word32& serviceType, + WebRtc_Word32& overrideDSCP) const; + virtual WebRtc_Word32 SetToS(const WebRtc_Word32 DSCP, + const bool useSetSockOpt = false); + virtual WebRtc_Word32 ToS(WebRtc_Word32& DSCP, + bool& useSetSockOpt) const; + virtual WebRtc_Word32 SetPCP(const WebRtc_Word32 PCP); + virtual WebRtc_Word32 PCP(WebRtc_Word32& PCP) const; + virtual WebRtc_Word32 EnableIpV6(); + virtual bool IpV6Enabled() const; + virtual WebRtc_Word32 SetFilterIP( + const char filterIPAddress[kIpAddressVersion6Length]); + virtual WebRtc_Word32 FilterIP( + char filterIPAddress[kIpAddressVersion6Length]) const; + virtual WebRtc_Word32 SetFilterPorts(const WebRtc_UWord16 rtpFilterPort, + const WebRtc_UWord16 rtcpFilterPort); + virtual WebRtc_Word32 FilterPorts(WebRtc_UWord16& rtpFilterPort, + WebRtc_UWord16& rtcpFilterPort) const; + virtual WebRtc_Word32 StartReceiving( + const WebRtc_UWord32 numberOfSocketBuffers); + virtual WebRtc_Word32 StopReceiving(); + virtual bool Receiving() const; + virtual bool SendSocketsInitialized() const; + virtual bool SourcePortsInitialized() const; + virtual bool ReceiveSocketsInitialized() const; + virtual WebRtc_Word32 SendRaw(const WebRtc_Word8* data, + WebRtc_UWord32 length, WebRtc_Word32 isRTCP, + WebRtc_UWord16 portnr = 0, + const char* ip = NULL); + virtual WebRtc_Word32 SendRTPPacketTo(const WebRtc_Word8 *data, + WebRtc_UWord32 length, + const SocketAddress& to); + virtual WebRtc_Word32 SendRTCPPacketTo(const WebRtc_Word8 *data, + WebRtc_UWord32 length, + const SocketAddress& to); + virtual WebRtc_Word32 SendRTPPacketTo(const WebRtc_Word8 *data, + WebRtc_UWord32 length, + WebRtc_UWord16 rtpPort); + virtual WebRtc_Word32 SendRTCPPacketTo(const WebRtc_Word8 *data, + WebRtc_UWord32 length, + WebRtc_UWord16 rtcpPort); + // Transport functions + virtual int SendPacket(int channel, const void* data, int length); + virtual int SendRTCPPacket(int channel, const void* data, int length); + + // UdpTransport functions continue. + virtual WebRtc_Word32 SetSendIP(const char* ipaddr); + virtual WebRtc_Word32 SetSendPorts(const WebRtc_UWord16 rtpPort, + const WebRtc_UWord16 rtcpPort = 0); + + virtual ErrorCode LastError() const; + + virtual WebRtc_Word32 IPAddressCached(const SocketAddress& address, + char* ip, + WebRtc_UWord32& ipSize, + WebRtc_UWord16& sourcePort); + + WebRtc_Word32 Id() const {return _id;} +protected: + // IncomingSocketCallback signature functions for receiving callbacks from + // UdpSocketWrapper. + static void IncomingRTPCallback(CallbackObj obj, + const WebRtc_Word8* rtpPacket, + WebRtc_Word32 rtpPacketLength, + const SocketAddress* from); + static void IncomingRTCPCallback(CallbackObj obj, + const WebRtc_Word8* rtcpPacket, + WebRtc_Word32 rtcpPacketLength, + const SocketAddress* from); + + void CloseSendSockets(); + void CloseReceiveSockets(); + + // Update _remoteRTPAddr according to _destPort and _destIP + void BuildRemoteRTPAddr(); + // Update _remoteRTCPAddr according to _destPortRTCP and _destIP + void BuildRemoteRTCPAddr(); + + void BuildSockaddrIn(WebRtc_UWord16 portnr, const char* ip, + SocketAddress& remoteAddr) const; + + ErrorCode BindLocalRTPSocket(); + ErrorCode BindLocalRTCPSocket(); + + ErrorCode BindRTPSendSocket(); + ErrorCode BindRTCPSendSocket(); + + void IncomingRTPFunction(const WebRtc_Word8* rtpPacket, + WebRtc_Word32 rtpPacketLength, + const SocketAddress* from); + void IncomingRTCPFunction(const WebRtc_Word8* rtcpPacket, + WebRtc_Word32 rtcpPacketLength, + const SocketAddress* from); + + bool FilterIPAddress(const SocketAddress* fromAddress); + + bool SetSockOptUsed(); + + WebRtc_Word32 EnableQoS(WebRtc_Word32 serviceType, bool audio, + WebRtc_UWord32 maxBitrate, + WebRtc_Word32 overrideDSCP); + + WebRtc_Word32 DisableQoS(); + +private: + void GetCachedAddress(char* ip, WebRtc_UWord32& ipSize, + WebRtc_UWord16& sourcePort); + + WebRtc_Word32 _id; + SocketFactoryInterface* _socket_creator; + // Protects the sockets from being re-configured while receiving packets. + CriticalSectionWrapper* _crit; + CriticalSectionWrapper* _critFilter; + // _packetCallback's critical section. + CriticalSectionWrapper* _critPacketCallback; + UdpSocketManager* _mgr; + ErrorCode _lastError; + + // Remote RTP and RTCP ports. + WebRtc_UWord16 _destPort; + WebRtc_UWord16 _destPortRTCP; + + // Local RTP and RTCP ports. + WebRtc_UWord16 _localPort; + WebRtc_UWord16 _localPortRTCP; + + // Local port number when the local port for receiving and local port number + // for sending are not the same. + WebRtc_UWord16 _srcPort; + WebRtc_UWord16 _srcPortRTCP; + + // Remote port from which last received packet was sent. + WebRtc_UWord16 _fromPort; + WebRtc_UWord16 _fromPortRTCP; + + char _fromIP[kIpAddressVersion6Length]; + char _destIP[kIpAddressVersion6Length]; + char _localIP[kIpAddressVersion6Length]; + char _localMulticastIP[kIpAddressVersion6Length]; + + UdpSocketWrapper* _ptrRtpSocket; + UdpSocketWrapper* _ptrRtcpSocket; + + // Local port when the local port for receiving and local port for sending + // are not the same. + UdpSocketWrapper* _ptrSendRtpSocket; + UdpSocketWrapper* _ptrSendRtcpSocket; + + SocketAddress _remoteRTPAddr; + SocketAddress _remoteRTCPAddr; + + SocketAddress _localRTPAddr; + SocketAddress _localRTCPAddr; + + WebRtc_Word32 _tos; + bool _receiving; + bool _useSetSockOpt; + bool _qos; + WebRtc_Word32 _pcp; + bool _ipV6Enabled; + WebRtc_Word32 _serviceType; + WebRtc_Word32 _overrideDSCP; + WebRtc_UWord32 _maxBitrate; + + // Cache used by GetCachedAddress(..). + RWLockWrapper* _cachLock; + SocketAddress _previousAddress; + char _previousIP[kIpAddressVersion6Length]; + WebRtc_UWord32 _previousIPSize; + WebRtc_UWord16 _previousSourcePort; + + SocketAddress _filterIPAddress; + WebRtc_UWord16 _rtpFilterPort; + WebRtc_UWord16 _rtcpFilterPort; + + UdpTransportData* _packetCallback; +}; + +} // namespace test +} // namespace webrtc + +#endif // WEBRTC_TEST_CHANNEL_TRANSPORT_UDP_TRANSPORT_IMPL_H_ diff --git a/webrtc/test/channel_transport/udp_transport_unittest.cc b/webrtc/test/channel_transport/udp_transport_unittest.cc new file mode 100644 index 0000000000..7408e4b813 --- /dev/null +++ b/webrtc/test/channel_transport/udp_transport_unittest.cc @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2012 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 + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "webrtc/test/channel_transport/udp_transport.h" +// We include the implementation header file to get at the dependency-injecting +// constructor. +#include "webrtc/test/channel_transport/udp_transport_impl.h" +// We must mock the socket manager, for which we need its definition. +#include "webrtc/test/channel_transport/udp_socket_manager_wrapper.h" + +using ::testing::_; +using ::testing::Return; + +namespace webrtc { +namespace test { + +class MockUdpSocketWrapper : public UdpSocketWrapper { + public: + // The following methods have to be mocked because they are pure. + MOCK_METHOD1(ChangeUniqueId, WebRtc_Word32(WebRtc_Word32)); + MOCK_METHOD2(SetCallback, bool(CallbackObj, IncomingSocketCallback)); + MOCK_METHOD1(Bind, bool(const SocketAddress&)); + MOCK_METHOD0(ValidHandle, bool()); + MOCK_METHOD4(SetSockopt, bool(WebRtc_Word32, WebRtc_Word32, + const WebRtc_Word8*, + WebRtc_Word32)); + MOCK_METHOD1(SetTOS, WebRtc_Word32(WebRtc_Word32)); + MOCK_METHOD3(SendTo, WebRtc_Word32(const WebRtc_Word8*, WebRtc_Word32, + const SocketAddress&)); + MOCK_METHOD8(SetQos, bool(WebRtc_Word32, WebRtc_Word32, + WebRtc_Word32, WebRtc_Word32, + WebRtc_Word32, WebRtc_Word32, + const SocketAddress &, + WebRtc_Word32)); +}; + +class MockUdpSocketManager : public UdpSocketManager { + public: + // Access to protected destructor. + void Destroy() { + delete this; + } + MOCK_METHOD2(Init, bool(WebRtc_Word32, WebRtc_UWord8&)); + MOCK_METHOD1(ChangeUniqueId, WebRtc_Word32(const WebRtc_Word32)); + MOCK_METHOD0(Start, bool()); + MOCK_METHOD0(Stop, bool()); + MOCK_METHOD1(AddSocket, bool(UdpSocketWrapper*)); + MOCK_METHOD1(RemoveSocket, bool(UdpSocketWrapper*)); +}; + +class MockSocketFactory : + public UdpTransportImpl::SocketFactoryInterface { + public: + MockSocketFactory(std::vector* socket_counter) + : socket_counter_(socket_counter) { + } + UdpSocketWrapper* CreateSocket(const WebRtc_Word32 id, + UdpSocketManager* mgr, + CallbackObj obj, + IncomingSocketCallback cb, + bool ipV6Enable, + bool disableGQOS) { + MockUdpSocketWrapper* socket = new MockUdpSocketWrapper(); + // We instrument the socket with calls that are expected, but do + // not matter for any specific test, in order to avoid warning messages. + EXPECT_CALL(*socket, ValidHandle()).WillRepeatedly(Return(true)); + EXPECT_CALL(*socket, Bind(_)).WillOnce(Return(true)); + socket_counter_->push_back(socket); + return socket; + } + std::vector* socket_counter_; +}; + +class UDPTransportTest : public ::testing::Test { + public: + UDPTransportTest() + : sockets_created_(0) { + } + + ~UDPTransportTest() { + // In production, sockets register themselves at creation time with + // an UdpSocketManager, and the UdpSocketManager is responsible for + // deleting them. In this test, we just delete them after the test. + while (!sockets_created_.empty()) { + delete sockets_created_.back(); + sockets_created_.pop_back(); + } + } + + int NumSocketsCreated() { + return sockets_created_.size(); + } + + std::vector* sockets_created() { + return &sockets_created_; + } +private: + std::vector sockets_created_; +}; + +TEST_F(UDPTransportTest, CreateTransport) { + WebRtc_Word32 id = 0; + WebRtc_UWord8 threads = 1; + UdpTransport* transport = UdpTransport::Create(id, threads); + UdpTransport::Destroy(transport); +} + +// This test verifies that the mock_socket is not called from the constructor. +TEST_F(UDPTransportTest, ConstructorDoesNotCreateSocket) { + WebRtc_Word32 id = 0; + UdpTransportImpl::SocketFactoryInterface* null_maker = NULL; + UdpSocketManager* null_manager = NULL; + UdpTransport* transport = new UdpTransportImpl(id, + null_maker, + null_manager); + delete transport; +} + +TEST_F(UDPTransportTest, InitializeSourcePorts) { + WebRtc_Word32 id = 0; + UdpTransportImpl::SocketFactoryInterface* mock_maker + = new MockSocketFactory(sockets_created()); + MockUdpSocketManager* mock_manager = new MockUdpSocketManager(); + UdpTransport* transport = new UdpTransportImpl(id, + mock_maker, + mock_manager); + EXPECT_EQ(0, transport->InitializeSourcePorts(4711, 4712)); + EXPECT_EQ(2, NumSocketsCreated()); + + delete transport; + mock_manager->Destroy(); +} + +} // namespace test +} // namespace webrtc