From cd8d78b3662bb53a70c6771fc8803d3f55a94d7d Mon Sep 17 00:00:00 2001 From: Grishka Date: Tue, 6 Jun 2017 04:44:16 +0300 Subject: [PATCH] Added TCP fallback Support for Windows Phone Silverlight Various fixes --- NetworkSocket.cpp | 271 ++++++++++++++++------------ NetworkSocket.h | 18 ++ VoIPController.cpp | 91 ++++++++-- VoIPController.h | 6 +- libtgvoip.WP81.vcxproj | 26 +-- logging.h | 2 +- os/linux/AudioInputPulse.cpp | 2 +- os/linux/AudioInputPulse.h | 2 +- os/linux/AudioOutputPulse.cpp | 3 +- os/linux/AudioOutputPulse.h | 2 +- os/posix/NetworkSocketPosix.cpp | 238 +++++++++++++++++++++--- os/posix/NetworkSocketPosix.h | 20 +- os/windows/AudioInputWASAPI.h | 2 + os/windows/AudioOutputWASAPI.h | 2 + os/windows/NetworkSocketWinsock.cpp | 236 +++++++++++++++++++----- os/windows/NetworkSocketWinsock.h | 11 ++ os/windows/WindowsSandboxUtils.cpp | 19 +- os/windows/WindowsSandboxUtils.h | 4 + 18 files changed, 728 insertions(+), 227 deletions(-) diff --git a/NetworkSocket.cpp b/NetworkSocket.cpp index 53c313e8d5..5eaa0131a9 100644 --- a/NetworkSocket.cpp +++ b/NetworkSocket.cpp @@ -4,6 +4,7 @@ #include "NetworkSocket.h" #include +#include #include #include #if defined(_WIN32) @@ -13,130 +14,162 @@ #endif #include "logging.h" #include "VoIPServerConfig.h" +#include "VoIPController.h" #define MIN_UDP_PORT 16384 #define MAX_UDP_PORT 32768 -namespace tgvoip { +using namespace tgvoip; -#pragma mark - NetworkSocket +NetworkSocket::NetworkSocket(){ + ipv6Timeout=ServerConfig::GetSharedInstance()->GetDouble("nat64_fallback_timeout", 3); + failed=false; +} - NetworkSocket::NetworkSocket(){ - ipv6Timeout=ServerConfig::GetSharedInstance()->GetDouble("nat64_fallback_timeout", 3); - failed=false; - } - - NetworkSocket::~NetworkSocket(){ - - } - - std::string NetworkSocket::GetLocalInterfaceInfo(IPv4Address *inet4addr, IPv6Address *inet6addr){ - std::string r="not implemented"; - return r; - } - - uint16_t NetworkSocket::GenerateLocalPort(){ - return (uint16_t) ((rand()%(MAX_UDP_PORT-MIN_UDP_PORT))+MIN_UDP_PORT); - } - - void NetworkSocket::SetMaxPriority(){ - } - - bool NetworkSocket::IsFailed(){ - return failed; - } - - NetworkSocket *NetworkSocket::Create(){ -#ifndef _WIN32 - return new NetworkSocketPosix(); -#else - return new NetworkSocketWinsock(); -#endif - } - -#pragma mark - NetworkAddress - - bool NetworkAddress::operator==(const NetworkAddress &other){ - IPv4Address* self4=dynamic_cast(this); - IPv4Address* other4=dynamic_cast((NetworkAddress*)&other); - if(self4 && other4){ - return self4->GetAddress()==other4->GetAddress(); - } - IPv6Address* self6=dynamic_cast(this); - IPv6Address* other6=dynamic_cast((NetworkAddress*)&other); - if(self6 && other6){ - return memcmp(self6->GetAddress(), other6->GetAddress(), 16)==0; - } - return false; - } - - bool NetworkAddress::operator!=(const NetworkAddress &other){ - return !(*this == other); - } - -#pragma mark - IPv4Address - - IPv4Address::IPv4Address(std::string addr){ -#ifndef _WIN32 - this->address=NetworkSocketPosix::StringToV4Address(addr); -#else - this->address=NetworkSocketWinsock::StringToV4Address(addr); -#endif - } - - IPv4Address::IPv4Address(uint32_t addr){ - this->address=addr; - } - - - std::string IPv4Address::ToString(){ -#ifndef _WIN32 - return NetworkSocketPosix::V4AddressToString(address); -#else - return NetworkSocketWinsock::V4AddressToString(address); -#endif - } - - /*sockaddr &IPv4Address::ToSockAddr(uint16_t port){ - sockaddr_in sa; - sa.sin_family=AF_INET; - sa.sin_addr=addr; - sa.sin_port=port; - return *((sockaddr *) &sa); - }*/ - - uint32_t IPv4Address::GetAddress(){ - return address; - } - -#pragma mark - IPv6Address - - IPv6Address::IPv6Address(std::string addr){ -#ifndef _WIN32 - NetworkSocketPosix::StringToV6Address(addr, this->address); -#else - NetworkSocketWinsock::StringToV6Address(addr, this->address); -#endif - } - - IPv6Address::IPv6Address(uint8_t addr[16]){ - memcpy(address, addr, 16); - } - - std::string IPv6Address::ToString(){ - return ""; - } - - /*sockaddr &IPv6Address::ToSockAddr(uint16_t port){ - sockaddr_in6 sa; - sa.sin6_family=AF_INET6; - sa.sin6_addr=addr; - sa.sin6_port=port; - return *((sockaddr *) &sa); - }*/ - - const uint8_t *IPv6Address::GetAddress(){ - return address; - } +NetworkSocket::~NetworkSocket(){ } + +std::string NetworkSocket::GetLocalInterfaceInfo(IPv4Address *inet4addr, IPv6Address *inet6addr){ + std::string r="not implemented"; + return r; +} + +uint16_t NetworkSocket::GenerateLocalPort(){ + return (uint16_t) ((rand()%(MAX_UDP_PORT-MIN_UDP_PORT))+MIN_UDP_PORT); +} + +void NetworkSocket::SetMaxPriority(){ +} + +bool NetworkSocket::IsFailed(){ + return failed; +} + +NetworkSocket *NetworkSocket::Create(){ +#ifndef _WIN32 + return new NetworkSocketPosix(); +#else + return new NetworkSocketWinsock(); +#endif +} + +void NetworkSocket::GenerateTCPO2States(unsigned char* buffer, TCPO2State* recvState, TCPO2State* sendState){ + memset(recvState, 0, sizeof(TCPO2State)); + memset(sendState, 0, sizeof(TCPO2State)); + unsigned char nonce[64]; + uint32_t *first = reinterpret_cast(nonce), *second = first + 1; + uint32_t first1 = 0x44414548U, first2 = 0x54534f50U, first3 = 0x20544547U, first4 = 0x20544547U, first5 = 0xeeeeeeeeU; + uint32_t second1 = 0; + do { + VoIPController::crypto.rand_bytes(nonce, sizeof(nonce)); + } while (*first == first1 || *first == first2 || *first == first3 || *first == first4 || *first == first5 || *second == second1 || *reinterpret_cast(nonce) == 0xef); + + // prepare encryption key/iv + memcpy(sendState->key, nonce + 8, 32); + memcpy(sendState->iv, nonce + 8 + 32, 16); + + // prepare decryption key/iv + char reversed[48]; + memcpy(reversed, nonce + 8, sizeof(reversed)); + std::reverse(reversed, reversed + sizeof(reversed)); + memcpy(recvState->key, reversed, 32); + memcpy(recvState->iv, reversed + 32, 16); + + // write protocol identifier + *reinterpret_cast(nonce + 56) = 0xefefefefU; + memcpy(buffer, nonce, 56); + EncryptForTCPO2(nonce, sizeof(nonce), sendState); + memcpy(buffer+56, nonce+56, 8); +} + +void NetworkSocket::EncryptForTCPO2(unsigned char *buffer, size_t len, TCPO2State *state){ + VoIPController::crypto.aes_ctr_encrypt(buffer, len, state->key, state->iv, state->ecount, &state->num); +} + +bool NetworkAddress::operator==(const NetworkAddress &other){ + IPv4Address* self4=dynamic_cast(this); + IPv4Address* other4=dynamic_cast((NetworkAddress*)&other); + if(self4 && other4){ + return self4->GetAddress()==other4->GetAddress(); + } + IPv6Address* self6=dynamic_cast(this); + IPv6Address* other6=dynamic_cast((NetworkAddress*)&other); + if(self6 && other6){ + return memcmp(self6->GetAddress(), other6->GetAddress(), 16)==0; + } + return false; +} + +bool NetworkAddress::operator!=(const NetworkAddress &other){ + return !(*this == other); +} + +IPv4Address::IPv4Address(std::string addr){ +#ifndef _WIN32 + this->address=NetworkSocketPosix::StringToV4Address(addr); +#else + this->address=NetworkSocketWinsock::StringToV4Address(addr); +#endif +} + +IPv4Address::IPv4Address(uint32_t addr){ + this->address=addr; +} + +IPv4Address::IPv4Address(){ + this->address=0; +} + + +std::string IPv4Address::ToString(){ +#ifndef _WIN32 + return NetworkSocketPosix::V4AddressToString(address); +#else + return NetworkSocketWinsock::V4AddressToString(address); +#endif +} + +/*sockaddr &IPv4Address::ToSockAddr(uint16_t port){ + sockaddr_in sa; + sa.sin_family=AF_INET; + sa.sin_addr=addr; + sa.sin_port=port; + return *((sockaddr *) &sa); +}*/ + +uint32_t IPv4Address::GetAddress(){ + return address; +} + +IPv6Address::IPv6Address(std::string addr){ +#ifndef _WIN32 + NetworkSocketPosix::StringToV6Address(addr, this->address); +#else + NetworkSocketWinsock::StringToV6Address(addr, this->address); +#endif +} + +IPv6Address::IPv6Address(uint8_t addr[16]){ + memcpy(address, addr, 16); +} + +IPv6Address::IPv6Address(){ + memset(address, 0, 16); +} + +std::string IPv6Address::ToString(){ + return ""; +} + +/*sockaddr &IPv6Address::ToSockAddr(uint16_t port){ + sockaddr_in6 sa; + sa.sin6_family=AF_INET6; + sa.sin6_addr=addr; + sa.sin6_port=port; + return *((sockaddr *) &sa); +}*/ + +const uint8_t *IPv6Address::GetAddress(){ + return address; +} diff --git a/NetworkSocket.h b/NetworkSocket.h index c9680bbb4b..d311727810 100644 --- a/NetworkSocket.h +++ b/NetworkSocket.h @@ -9,6 +9,19 @@ #include namespace tgvoip { + + enum NetworkProtocol{ + PROTO_UDP=0, + PROTO_TCP + }; + + struct TCPO2State{ + unsigned char key[32]; + unsigned char iv[16]; + unsigned char ecount[16]; + uint32_t num; + }; + class NetworkAddress{ public: virtual std::string ToString()=0; @@ -21,6 +34,7 @@ namespace tgvoip { public: IPv4Address(std::string addr); IPv4Address(uint32_t addr); + IPv4Address(); virtual std::string ToString(); //virtual sockaddr& ToSockAddr(uint16_t port); uint32_t GetAddress(); @@ -33,6 +47,7 @@ namespace tgvoip { public: IPv6Address(std::string addr); IPv6Address(uint8_t addr[16]); + IPv6Address(); virtual std::string ToString(); //virtual sockaddr& ToSockAddr(uint16_t port); const uint8_t* GetAddress(); @@ -45,6 +60,7 @@ namespace tgvoip { size_t length; NetworkAddress* address; uint16_t port; + NetworkProtocol protocol; }; typedef struct NetworkPacket NetworkPacket; @@ -66,6 +82,8 @@ namespace tgvoip { protected: virtual uint16_t GenerateLocalPort(); virtual void SetMaxPriority(); + static void GenerateTCPO2States(unsigned char* buffer, TCPO2State* recvState, TCPO2State* sendState); + static void EncryptForTCPO2(unsigned char* buffer, size_t len, TCPO2State* state); double ipv6Timeout; unsigned char nat64Prefix[12]; bool failed; diff --git a/VoIPController.cpp b/VoIPController.cpp index 83425b1fbb..c5a0ff4a98 100644 --- a/VoIPController.cpp +++ b/VoIPController.cpp @@ -71,12 +71,19 @@ void tgvoip_openssl_sha256(uint8_t* msg, size_t len, uint8_t* output){ SHA256(msg, len, output); } +void tgvoip_openssl_aes_ctr_encrypt(uint8_t* inout, size_t length, uint8_t* key, uint8_t* iv, uint8_t* ecount, uint32_t* num){ + AES_KEY akey; + AES_set_encrypt_key(key, 32*8, &akey); + AES_ctr128_encrypt(inout, inout, length, &akey, iv, ecount, num); +} + voip_crypto_functions_t VoIPController::crypto={ tgvoip_openssl_rand_bytes, tgvoip_openssl_sha1, tgvoip_openssl_sha256, tgvoip_openssl_aes_ige_encrypt, - tgvoip_openssl_aes_ige_decrypt + tgvoip_openssl_aes_ige_decrypt, + tgvoip_openssl_aes_ctr_encrypt }; #else @@ -146,6 +153,9 @@ VoIPController::VoIPController() : activeNetItfName(""), currentAudioInput("defa receivedInitAck=false; peerPreferredRelay=NULL; statsDump=NULL; + useTCP=false; + didAddTcpRelays=false; + enableTcpAt=0; socket=NetworkSocket::Create(); @@ -287,8 +297,14 @@ void VoIPController::SetRemoteEndpoints(std::vector endpoints, bool al size_t i; lock_mutex(endpointsMutex); this->endpoints.clear(); + didAddTcpRelays=false; + useTCP=true; for(std::vector::iterator itrtr=endpoints.begin();itrtr!=endpoints.end();++itrtr){ this->endpoints.push_back(new Endpoint(*itrtr)); + if(itrtr->type==EP_TYPE_TCP_RELAY) + didAddTcpRelays=true; + if(itrtr->type==EP_TYPE_UDP_RELAY) + useTCP=false; } unlock_mutex(endpointsMutex); currentEndpoint=this->endpoints[0]; @@ -316,7 +332,7 @@ void* VoIPController::StartTickThread(void* controller){ void VoIPController::Start(){ int res; LOGW("Starting voip controller"); - int32_t cfgFrameSize=ServerConfig::GetSharedInstance()->GetInt("audio_frame_size", 60); + int32_t cfgFrameSize=60; //ServerConfig::GetSharedInstance()->GetInt("audio_frame_size", 60); if(cfgFrameSize==20 || cfgFrameSize==40 || cfgFrameSize==60) outgoingStreams[0]->frameDuration=(uint16_t) cfgFrameSize; socket->Open(); @@ -402,6 +418,7 @@ void VoIPController::HandleAudioInput(unsigned char *data, size_t len){ void VoIPController::Connect(){ assert(state!=STATE_WAIT_INIT_ACK); connectionInitTime=GetCurrentTime(); + enableTcpAt=connectionInitTime+5; SendInit(); } @@ -537,6 +554,8 @@ void VoIPController::SendInit(){ out->WriteByte(0); // video codecs count lock_mutex(endpointsMutex); for(std::vector::iterator itr=endpoints.begin();itr!=endpoints.end();++itr){ + if((*itr)->type==EP_TYPE_TCP_RELAY && !useTCP) + continue; SendPacket(out->GetBuffer(), out->GetLength(), *itr); } unlock_mutex(endpointsMutex); @@ -574,8 +593,10 @@ void VoIPController::RunRecvThread(){ lock_mutex(endpointsMutex); for(std::vector::iterator itrtr=endpoints.begin();itrtr!=endpoints.end();++itrtr){ if((*itrtr)->address==*src4 && (*itrtr)->port==packet.port){ - srcEndpoint=*itrtr; - break; + if(((*itrtr)->type!=EP_TYPE_TCP_RELAY && packet.protocol==PROTO_UDP) || ((*itrtr)->type==EP_TYPE_TCP_RELAY && packet.protocol==PROTO_TCP)){ + srcEndpoint=*itrtr; + break; + } } } unlock_mutex(endpointsMutex); @@ -595,7 +616,7 @@ void VoIPController::RunRecvThread(){ stats.bytesRecvdWifi+=(uint64_t)len; BufferInputStream in(buffer, (size_t)len); try{ - if(memcmp(buffer, srcEndpoint->type==EP_TYPE_UDP_RELAY ? srcEndpoint->peerTag : callID, 16)!=0){ + if(memcmp(buffer, srcEndpoint->type==EP_TYPE_UDP_RELAY || srcEndpoint->type==EP_TYPE_TCP_RELAY ? srcEndpoint->peerTag : callID, 16)!=0){ LOGW("Received packet has wrong peerTag"); continue; @@ -859,7 +880,7 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA if(!receivedInit){ receivedInit=true; currentEndpoint=srcEndpoint; - if(srcEndpoint->type==EP_TYPE_UDP_RELAY) + if(srcEndpoint->type==EP_TYPE_UDP_RELAY || (useTCP && srcEndpoint->type==EP_TYPE_TCP_RELAY)) preferredRelay=srcEndpoint; LogDebugInfo(); } @@ -1131,7 +1152,7 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA endpoints.push_back(new Endpoint(0, peerPort, v4addr, v6addr, EP_TYPE_UDP_P2P_LAN, peerTag)); unlock_mutex(endpointsMutex); } - if(type==PKT_NETWORK_CHANGED){ + if(type==PKT_NETWORK_CHANGED && currentEndpoint->type!=EP_TYPE_UDP_RELAY && currentEndpoint->type!=EP_TYPE_TCP_RELAY){ currentEndpoint=preferredRelay; if(allowP2p) SendPublicEndpointsRequest(); @@ -1211,6 +1232,7 @@ void VoIPController::RunTickThread(){ Sleep(100); #endif tickCount++; + double time=GetCurrentTime(); if(tickCount%5==0 && state==STATE_ESTABLISHED){ memmove(&rttHistory[1], rttHistory, 31*sizeof(double)); rttHistory[0]=GetAverageRTT(); @@ -1244,6 +1266,26 @@ void VoIPController::RunTickThread(){ conctl->Tick(); + if(!useTCP && ((state==STATE_WAIT_INIT_ACK && tickCount>=50) || (state==STATE_ESTABLISHED && time-lastRecvPacketTime>=5.0))){ + useTCP=true; + if(!didAddTcpRelays){ + std::vector relays; + for(std::vector::iterator itr=endpoints.begin(); itr!=endpoints.end(); ++itr){ + if((*itr)->type!=EP_TYPE_UDP_RELAY) + continue; + Endpoint *tcpRelay=new Endpoint(**itr); + tcpRelay->type=EP_TYPE_TCP_RELAY; + tcpRelay->averageRTT=0; + tcpRelay->lastPingSeq=0; + tcpRelay->lastPingTime=0; + memset(tcpRelay->rtts, 0, sizeof(tcpRelay->rtts)); + relays.push_back(tcpRelay); + } + endpoints.insert(endpoints.end(), relays.begin(), relays.end()); + didAddTcpRelays=true; + } + } + if(state==STATE_ESTABLISHED){ if((audioInput && !audioInput->IsInitialized()) || (audioOutput && !audioOutput->IsInitialized())){ LOGE("Audio I/O failed"); @@ -1372,6 +1414,8 @@ void VoIPController::RunTickThread(){ double minPing=preferredRelay->averageRTT; for(std::vector::iterator e=endpoints.begin();e!=endpoints.end();++e){ Endpoint* endpoint=*e; + if(endpoint->type==EP_TYPE_TCP_RELAY && !useTCP) + continue; if(GetCurrentTime()-endpoint->lastPingTime>=10){ LOGV("Sending ping to %s", endpoint->address.ToString().c_str()); BufferOutputStream pkt(32); @@ -1380,9 +1424,10 @@ void VoIPController::RunTickThread(){ endpoint->lastPingSeq=seq; SendPacket(pkt.GetBuffer(), pkt.GetLength(), endpoint); } - if(endpoint->type==EP_TYPE_UDP_RELAY){ - if(endpoint->averageRTT>0 && endpoint->averageRTTaverageRTT; + if(endpoint->type==EP_TYPE_UDP_RELAY || (useTCP && endpoint->type==EP_TYPE_TCP_RELAY)){ + double k=endpoint->type==EP_TYPE_UDP_RELAY ? 1 : 2; + if(endpoint->averageRTT>0 && endpoint->averageRTT*kaverageRTT*k; minPingRelay=endpoint; } } @@ -1390,7 +1435,7 @@ void VoIPController::RunTickThread(){ if(minPingRelay!=preferredRelay){ preferredRelay=minPingRelay; LOGV("set preferred relay to %s", preferredRelay->address.ToString().c_str()); - if(currentEndpoint->type==EP_TYPE_UDP_RELAY) + if(currentEndpoint->type==EP_TYPE_UDP_RELAY || currentEndpoint->type==EP_TYPE_TCP_RELAY) currentEndpoint=preferredRelay; LogDebugInfo(); /*BufferOutputStream pkt(32); @@ -1427,7 +1472,7 @@ void VoIPController::RunTickThread(){ if(state==STATE_ESTABLISHED){ if(GetCurrentTime()-lastRecvPacketTime>=config.recv_timeout){ - if(currentEndpoint && currentEndpoint->type!=EP_TYPE_UDP_RELAY){ + if(currentEndpoint && currentEndpoint->type!=EP_TYPE_UDP_RELAY && currentEndpoint->type!=EP_TYPE_TCP_RELAY){ LOGW("Packet receive timeout, switching to relay"); currentEndpoint=preferredRelay; for(std::vector::iterator itrtr=endpoints.begin();itrtr!=endpoints.end();++itrtr){ @@ -1498,11 +1543,13 @@ Endpoint& VoIPController::GetRemoteEndpoint(){ void VoIPController::SendPacket(unsigned char *data, size_t len, Endpoint* ep){ if(stopping) return; + if(ep->type==EP_TYPE_TCP_RELAY && !useTCP) + return; //dst.sin_addr=ep->address; //dst.sin_port=htons(ep->port); //dst.sin_family=AF_INET; BufferOutputStream out(len+128); - if(ep->type==EP_TYPE_UDP_RELAY) + if(ep->type==EP_TYPE_UDP_RELAY || ep->type==EP_TYPE_TCP_RELAY) out.WriteBytes((unsigned char*)ep->peerTag, 16); else out.WriteBytes(callID, 16); @@ -1537,6 +1584,7 @@ void VoIPController::SendPacket(unsigned char *data, size_t len, Endpoint* ep){ pkt.port=ep->port; pkt.length=out.GetLength(); pkt.data=out.GetBuffer(); + pkt.protocol=ep->type==EP_TYPE_TCP_RELAY ? PROTO_TCP : PROTO_UDP; socket->Send(&pkt); } @@ -1554,13 +1602,21 @@ void VoIPController::SetNetworkType(int type){ if(isFirstChange) return; if(currentEndpoint && currentEndpoint->type!=EP_TYPE_UDP_RELAY){ - currentEndpoint=preferredRelay; + if(preferredRelay->type==EP_TYPE_UDP_RELAY) + currentEndpoint=preferredRelay; for(std::vector::iterator itr=endpoints.begin();itr!=endpoints.end();){ Endpoint* endpoint=*itr; - if(endpoint->type==EP_TYPE_UDP_P2P_INET){ + if(endpoint->type==EP_TYPE_UDP_RELAY && useTCP){ + useTCP=false; + if(preferredRelay->type==EP_TYPE_TCP_RELAY){ + preferredRelay=endpoint; + currentEndpoint=endpoint; + } + } + //if(endpoint->type==EP_TYPE_UDP_P2P_INET){ endpoint->averageRTT=0; memset(endpoint->rtts, 0, sizeof(endpoint->rtts)); - } + //} if(endpoint->type==EP_TYPE_UDP_P2P_LAN){ delete endpoint; itr=endpoints.erase(itr); @@ -1987,6 +2043,9 @@ void VoIPController::LogDebugInfo(){ case EP_TYPE_UDP_P2P_LAN: typeStr="udp_p2p_lan"; break; + case EP_TYPE_TCP_RELAY: + typeStr="tcp_relay"; + break; } snprintf(buffer, 1024, "{\"address\":\"%s\",\"port\":%u,\"type\":\"%s\",\"rtt\":%u%s%s}", e->address.ToString().c_str(), e->port, typeStr, (unsigned int)round(e->averageRTT*1000), currentEndpoint==&*e ? ",\"in_use\":true" : "", preferredRelay==&*e ? ",\"preferred\":true" : ""); json+=buffer; diff --git a/VoIPController.h b/VoIPController.h index 10a6d6ebed..05174117e7 100644 --- a/VoIPController.h +++ b/VoIPController.h @@ -29,7 +29,7 @@ #include "CongestionControl.h" #include "NetworkSocket.h" -#define LIBTGVOIP_VERSION "0.4.1" +#define LIBTGVOIP_VERSION "0.4.2" #define PKT_INIT 1 #define PKT_INIT_ACK 2 @@ -180,6 +180,7 @@ struct voip_crypto_functions_t{ void (*sha256)(uint8_t* msg, size_t length, uint8_t* output); void (*aes_ige_encrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv); void (*aes_ige_decrypt)(uint8_t* in, uint8_t* out, size_t length, uint8_t* key, uint8_t* iv); + void (*aes_ctr_encrypt)(uint8_t* inout, size_t length, uint8_t* key, uint8_t* iv, uint8_t* ecount, uint32_t* num); }; typedef struct voip_crypto_functions_t voip_crypto_functions_t; @@ -362,6 +363,9 @@ private: FILE* statsDump; std::string currentAudioInput; std::string currentAudioOutput; + bool useTCP; + bool didAddTcpRelays; + double enableTcpAt; /*** server config values ***/ uint32_t maxAudioBitrate; diff --git a/libtgvoip.WP81.vcxproj b/libtgvoip.WP81.vcxproj index a74ad3d5ff..cf5f90e7ab 100644 --- a/libtgvoip.WP81.vcxproj +++ b/libtgvoip.WP81.vcxproj @@ -26,31 +26,31 @@ en-US 12.0 true - Windows Phone + Windows Phone Silverlight 8.1 DynamicLibrary true - v120_wp81 + v120 DynamicLibrary false true - v120_wp81 + v120 DynamicLibrary true - v120_wp81 + v120 DynamicLibrary false true - v120_wp81 + v120 @@ -78,7 +78,7 @@ NotUsing - _WINRT_DLL;_CRT_SECURE_NO_WARNINGS;NOMINMAX;WEBRTC_APM_DEBUG_DUMP=0;TGVOIP_USE_CUSTOM_CRYPTO;%(PreprocessorDefinitions) + _WINRT_DLL;TGVOIP_WP_SILVERLIGHT;_CRT_SECURE_NO_WARNINGS;NOMINMAX;WEBRTC_APM_DEBUG_DUMP=0;TGVOIP_USE_CUSTOM_CRYPTO;%(PreprocessorDefinitions) pch.h $(IntDir)pch.pch /bigobj %(AdditionalOptions) @@ -88,13 +88,13 @@ Console false - ws2_32.lib;mmdevapi.lib;../TelegramClient/$(Platform)/$(Configuration)/TelegramClient.Opus/TelegramClient.Opus.lib;%(AdditionalDependencies) + ws2_32.lib;phoneaudioses.lib;../TelegramClient/$(Platform)/$(Configuration)/TelegramClient.Opus/TelegramClient.Opus.lib;%(AdditionalDependencies) NotUsing - _WINRT_DLL;NDEBUG;_CRT_SECURE_NO_WARNINGS;NOMINMAX;%(PreprocessorDefinitions) + _WINRT_DLL;TGVOIP_WP_SILVERLIGHT;NDEBUG;_CRT_SECURE_NO_WARNINGS;NOMINMAX;%(PreprocessorDefinitions) pch.h $(IntDir)pch.pch /bigobj %(AdditionalOptions) @@ -104,13 +104,13 @@ Console false - ws2_32.lib;mmdevapi.lib;../TelegramClient/$(Platform)/$(Configuration)/TelegramClient.Opus/TelegramClient.Opus.lib;%(AdditionalDependencies) + ws2_32.lib;phoneaudioses.lib;../TelegramClient/$(Platform)/$(Configuration)/TelegramClient.Opus/TelegramClient.Opus.lib;%(AdditionalDependencies) NotUsing - _WINRT_DLL;_CRT_SECURE_NO_WARNINGS;NOMINMAX;WEBRTC_APM_DEBUG_DUMP=0;TGVOIP_USE_CUSTOM_CRYPTO;%(PreprocessorDefinitions) + _WINRT_DLL;TGVOIP_WP_SILVERLIGHT;_CRT_SECURE_NO_WARNINGS;NOMINMAX;WEBRTC_APM_DEBUG_DUMP=0;TGVOIP_USE_CUSTOM_CRYPTO;%(PreprocessorDefinitions) pch.h $(IntDir)pch.pch /bigobj %(AdditionalOptions) @@ -120,13 +120,13 @@ Console false - ws2_32.lib;mmdevapi.lib;../TelegramClient/$(Platform)/$(Configuration)/TelegramClient.Opus/TelegramClient.Opus.lib;%(AdditionalDependencies) + ws2_32.lib;phoneaudioses.lib;../TelegramClient/$(Platform)/$(Configuration)/TelegramClient.Opus/TelegramClient.Opus.lib;%(AdditionalDependencies) NotUsing - _WINRT_DLL;NDEBUG;_CRT_SECURE_NO_WARNINGS;NOMINMAX;WEBRTC_APM_DEBUG_DUMP=0;TGVOIP_USE_CUSTOM_CRYPTO;%(PreprocessorDefinitions) + _WINRT_DLL;TGVOIP_WP_SILVERLIGHT;NDEBUG;_CRT_SECURE_NO_WARNINGS;NOMINMAX;WEBRTC_APM_DEBUG_DUMP=0;TGVOIP_USE_CUSTOM_CRYPTO;%(PreprocessorDefinitions) pch.h $(IntDir)pch.pch /bigobj %(AdditionalOptions) @@ -136,7 +136,7 @@ Console false - ws2_32.lib;mmdevapi.lib;../TelegramClient/$(Platform)/$(Configuration)/TelegramClient.Opus/TelegramClient.Opus.lib;%(AdditionalDependencies) + ws2_32.lib;phoneaudioses.lib;../TelegramClient/$(Platform)/$(Configuration)/TelegramClient.Opus/TelegramClient.Opus.lib;%(AdditionalDependencies) diff --git a/logging.h b/logging.h index 8923d173b4..f850db3cd7 100644 --- a/logging.h +++ b/logging.h @@ -16,7 +16,7 @@ void tgvoip_log_file_printf(char level, const char* msg, ...); void tgvoip_log_file_write_header(); -#if !defined(snprintf) && defined(_WIN32) +#if !defined(snprintf) && defined(_WIN32) && defined(__cplusplus_winrt) #define snprintf _snprintf #endif diff --git a/os/linux/AudioInputPulse.cpp b/os/linux/AudioInputPulse.cpp index 01952cfd70..cdd31aa046 100644 --- a/os/linux/AudioInputPulse.cpp +++ b/os/linux/AudioInputPulse.cpp @@ -169,7 +169,7 @@ void AudioInputPulse::SetCurrentDevice(std::string devID){ pa_buffer_attr bufferAttr={ .maxlength=960*6, - .tlength=960*4, + .tlength=960*6, .prebuf=0, .minreq=960*2 }; diff --git a/os/linux/AudioInputPulse.h b/os/linux/AudioInputPulse.h index e37fdc6da3..3dd55ce5e2 100644 --- a/os/linux/AudioInputPulse.h +++ b/os/linux/AudioInputPulse.h @@ -46,7 +46,7 @@ private: bool isConnected; bool didStart; bool isLocked; - unsigned char remainingData[10240]; + unsigned char remainingData[960*8*2]; size_t remainingDataSize; }; diff --git a/os/linux/AudioOutputPulse.cpp b/os/linux/AudioOutputPulse.cpp index cd9c67c879..facab10b84 100644 --- a/os/linux/AudioOutputPulse.cpp +++ b/os/linux/AudioOutputPulse.cpp @@ -171,7 +171,7 @@ void AudioOutputPulse::SetCurrentDevice(std::string devID){ pa_buffer_attr bufferAttr={ .maxlength=960*6, - .tlength=960*4, + .tlength=960*6, .prebuf=0, .minreq=960*2 }; @@ -283,6 +283,7 @@ void AudioOutputPulse::StreamWriteCallback(pa_stream *stream, size_t requestedBy if(remainingDataSize+960*2>=sizeof(remainingData)){ LOGE("Can't provide %d bytes of audio data at a time", (int)bytesToFill); failed=true; + pa_threaded_mainloop_unlock(mainloop); return; } InvokeCallback(remainingData+remainingDataSize, 960*2); diff --git a/os/linux/AudioOutputPulse.h b/os/linux/AudioOutputPulse.h index bff80087ab..e2b7838d8d 100644 --- a/os/linux/AudioOutputPulse.h +++ b/os/linux/AudioOutputPulse.h @@ -44,7 +44,7 @@ private: bool isConnected; bool didStart; bool isLocked; - unsigned char remainingData[10240]; + unsigned char remainingData[960*8*2]; size_t remainingDataSize; }; diff --git a/os/posix/NetworkSocketPosix.cpp b/os/posix/NetworkSocketPosix.cpp index 2f91af480e..2716d83c1b 100644 --- a/os/posix/NetworkSocketPosix.cpp +++ b/os/posix/NetworkSocketPosix.cpp @@ -1,5 +1,7 @@ // -// Created by Grishka on 10.04.17. +// libtgvoip is free and unencumbered public domain software. +// For more information, see http://unlicense.org or the UNLICENSE file +// you should have received with this source code distribution. // #include "NetworkSocketPosix.h" @@ -10,29 +12,42 @@ #include #include #include +#include #include "../../logging.h" #include "../../VoIPController.h" +#include "../../BufferInputStream.h" +#include "../../BufferOutputStream.h" using namespace tgvoip; + NetworkSocketPosix::NetworkSocketPosix() : lastRecvdV4(0), lastRecvdV6("::0"){ needUpdateNat64Prefix=true; nat64Present=false; switchToV6at=0; isV4Available=false; + useTCP=false; + closing=false; + + int p[2]; + int pipeRes=pipe(p); + assert(pipeRes==0); + pipeRead=p[0]; + pipeWrite=p[1]; } NetworkSocketPosix::~NetworkSocketPosix(){ - + close(pipeRead); + close(pipeWrite); } void NetworkSocketPosix::SetMaxPriority(){ #ifdef __APPLE__ int prio=NET_SERVICE_TYPE_VO; - int res=setsockopt(fd, SOL_SOCKET, SO_NET_SERVICE_TYPE, &prio, sizeof(prio)); - if(res<0){ - LOGE("error setting darwin-specific net priority: %d / %s", errno, strerror(errno)); - } + int res=setsockopt(fd, SOL_SOCKET, SO_NET_SERVICE_TYPE, &prio, sizeof(prio)); + if(res<0){ + LOGE("error setting darwin-specific net priority: %d / %s", errno, strerror(errno)); + } #else int prio=5; int res=setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)); @@ -52,6 +67,74 @@ void NetworkSocketPosix::Send(NetworkPacket *packet){ LOGW("tried to send null packet"); return; } + if(packet->protocol==PROTO_TCP){ + //LOGV("Sending TCP packet to %s:%u", packet->address->ToString().c_str(), packet->port); + IPv4Address* v4addr=dynamic_cast(packet->address); + if(v4addr){ + TCPSocket* _socket=NULL; + for(std::vector::iterator itr=tcpSockets.begin();itr!=tcpSockets.end();++itr){ + if(itr->address==*v4addr && itr->port==packet->port){ + _socket=&*itr; + break; + } + } + if(!_socket){ + TCPSocket s; + s.fd=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + s.port=packet->port; + s.address=IPv4Address(*v4addr); + int opt=1; + setsockopt(s.fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)); + timeval timeout; + timeout.tv_sec=1; + timeout.tv_usec=0; + setsockopt(s.fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + timeout.tv_sec=60; + setsockopt(s.fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + sockaddr_in addr; + addr.sin_family=AF_INET; + addr.sin_addr.s_addr=s.address.GetAddress(); + addr.sin_port=htons(s.port); + int res=connect(s.fd, (const sockaddr*) &addr, sizeof(addr)); + if(res!=0){ + LOGW("error connecting TCP socket to %s:%u: %d / %s; %d / %s", s.address.ToString().c_str(), s.port, res, strerror(res), errno, strerror(errno)); + close(s.fd); + return; + }else{ + //LOGI("connected successfully, fd=%d", s.fd); + char c=1; + write(pipeWrite, &c, 1); + } + unsigned char buf[64]; + GenerateTCPO2States(buf, &s.recvState, &s.sendState); + send(s.fd, buf, sizeof(buf), 0); + tcpSockets.push_back(s); + _socket=&tcpSockets[tcpSockets.size()-1]; + } + if(_socket){ + //LOGV("sending to %s:%u, fd=%d, size=%d (%d)", _socket->address.ToString().c_str(), _socket->port, _socket->fd, packet->length, packet->length%4); + BufferOutputStream os(packet->length+4); + size_t len=packet->length/4; + if(len<0x7F){ + os.WriteByte((unsigned char)len); + }else{ + os.WriteByte(0x7F); + os.WriteByte((unsigned char)(len & 0xFF)); + os.WriteByte((unsigned char)((len >> 8) & 0xFF)); + os.WriteByte((unsigned char)((len >> 16) & 0xFF)); + } + os.WriteBytes(packet->data, packet->length); + EncryptForTCPO2(os.GetBuffer(), os.GetLength(), &_socket->sendState); + int res=send(_socket->fd, os.GetBuffer(), os.GetLength(), 0); + if(res<0){ + LOGW("error sending to TCP: %d / %s; %d / %s", res, strerror(res), errno, strerror(errno)); + } + } + }else{ + LOGW("TCP over IPv6 isn't supported yet"); + } + return; + } sockaddr_in6 addr; IPv4Address* v4addr=dynamic_cast(packet->address); if(v4addr){ @@ -103,6 +186,7 @@ void NetworkSocketPosix::Send(NetworkPacket *packet){ }else{ IPv6Address* v6addr=dynamic_cast(packet->address); assert(v6addr!=NULL); + memcpy(addr.sin6_addr.s6_addr, v6addr->GetAddress(), 16); } addr.sin6_port=htons(packet->port); char buf[INET6_ADDRSTRLEN]; @@ -118,30 +202,119 @@ void NetworkSocketPosix::Send(NetworkPacket *packet){ } void NetworkSocketPosix::Receive(NetworkPacket *packet){ - int addrLen=sizeof(sockaddr_in6); - sockaddr_in6 srcAddr; - ssize_t len=recvfrom(fd, packet->data, packet->length, 0, (sockaddr *) &srcAddr, (socklen_t *) &addrLen); - if(len>0) - packet->length=(size_t) len; - else{ - LOGE("error receiving %d / %s", errno, strerror(errno)); - packet->length=0; - return; + while(true){ + fd_set readSet, errSet; + FD_ZERO(&readSet); + FD_ZERO(&errSet); + + FD_SET(pipeRead, &readSet); + FD_SET(fd, &readSet); + FD_SET(fd, &errSet); + int maxfd=pipeRead>fd ? pipeRead : fd; + + for(std::vector::iterator itr=tcpSockets.begin(); itr!=tcpSockets.end(); ++itr){ + FD_SET(itr->fd, &readSet); + FD_SET(itr->fd, &errSet); + if(itr->fd>maxfd) + maxfd=itr->fd; + } + + int res=select(maxfd+1, &readSet, NULL, &errSet, NULL); + + if(FD_ISSET(pipeRead, &readSet)){ + char d; + read(pipeRead, &d, 1); + if(closing){ + packet->length=0; + return; + } + continue; + } + + if(FD_ISSET(fd, &readSet) || FD_ISSET(fd, &errSet)){ + int addrLen=sizeof(sockaddr_in6); + sockaddr_in6 srcAddr; + ssize_t len=recvfrom(fd, packet->data, packet->length, 0, (sockaddr *) &srcAddr, (socklen_t *) &addrLen); + if(len>0) + packet->length=(size_t) len; + else{ + LOGE("error receiving %d / %s", errno, strerror(errno)); + packet->length=0; + return; + } + //LOGV("Received %d bytes from %s:%d at %.5lf", len, inet_ntoa(srcAddr.sin_addr), ntohs(srcAddr.sin_port), GetCurrentTime()); + if(!isV4Available && IN6_IS_ADDR_V4MAPPED(&srcAddr.sin6_addr)){ + isV4Available=true; + LOGI("Detected IPv4 connectivity, will not try IPv6"); + } + if(IN6_IS_ADDR_V4MAPPED(&srcAddr.sin6_addr) || (nat64Present && memcmp(nat64Prefix, srcAddr.sin6_addr.s6_addr, 12)==0)){ + in_addr v4addr=*((in_addr *) &srcAddr.sin6_addr.s6_addr[12]); + lastRecvdV4=IPv4Address(v4addr.s_addr); + packet->address=&lastRecvdV4; + }else{ + lastRecvdV6=IPv6Address(srcAddr.sin6_addr.s6_addr); + packet->address=&lastRecvdV6; + } + packet->protocol=PROTO_UDP; + packet->port=ntohs(srcAddr.sin6_port); + return; + } + + for(std::vector::iterator itr=tcpSockets.begin(); itr!=tcpSockets.end();){ + if(FD_ISSET(itr->fd, &readSet)){ + unsigned char len1; + size_t packetLen=0; + size_t offset=0; + ssize_t len=recv(itr->fd, &len1, 1, 0); + if(len<=0) + goto failed; + EncryptForTCPO2(&len1, 1, &itr->recvState); + + if(len1<0x7F){ + packetLen=(size_t)len1*4; + }else{ + unsigned char len2[3]; + len=recv(itr->fd, len2, 3, 0); + if(len<=0) + goto failed; + EncryptForTCPO2(len2, 3, &itr->recvState); + packetLen=((size_t)len2[0] | ((size_t)len2[1] << 8) | ((size_t)len2[2] << 16))*4; + } + + if(packetLen>packet->length){ + LOGW("packet too big to fit into buffer"); + packet->length=0; + return; + } + + while(offsetfd, packet->data+offset, packetLen-offset, 0); + if(len<=0) + goto failed; + offset+=len; + } + EncryptForTCPO2(packet->data, packetLen, &itr->recvState); + packet->address=&itr->address; + packet->length=packetLen; + packet->port=itr->port; + packet->protocol=PROTO_TCP; + + return; + + failed: + packet->length=0; + close(itr->fd); + itr=tcpSockets.erase(itr); + continue; + } + if(FD_ISSET(itr->fd, &errSet)){ + close(itr->fd); + itr=tcpSockets.erase(itr); + continue; + } + ++itr; + } } - //LOGV("Received %d bytes from %s:%d at %.5lf", len, inet_ntoa(srcAddr.sin_addr), ntohs(srcAddr.sin_port), GetCurrentTime()); - if(!isV4Available && IN6_IS_ADDR_V4MAPPED(&srcAddr.sin6_addr)){ - isV4Available=true; - LOGI("Detected IPv4 connectivity, will not try IPv6"); - } - if(IN6_IS_ADDR_V4MAPPED(&srcAddr.sin6_addr) || (nat64Present && memcmp(nat64Prefix, srcAddr.sin6_addr.s6_addr, 12)==0)){ - in_addr v4addr=*((in_addr *) &srcAddr.sin6_addr.s6_addr[12]); - lastRecvdV4=IPv4Address(v4addr.s_addr); - packet->address=&lastRecvdV4; - }else{ - lastRecvdV6=IPv6Address(srcAddr.sin6_addr.s6_addr); - packet->address=&lastRecvdV6; - } - packet->port=ntohs(srcAddr.sin6_port); } void NetworkSocketPosix::Open(){ @@ -193,8 +366,15 @@ void NetworkSocketPosix::Open(){ } void NetworkSocketPosix::Close(){ + closing=true; + char c=1; + write(pipeWrite, &c, 1); shutdown(fd, SHUT_RDWR); close(fd); + for(std::vector::iterator itr=tcpSockets.begin(); itr!=tcpSockets.end();++itr){ + shutdown(itr->fd, SHUT_RDWR); + close(itr->fd); + } } void NetworkSocketPosix::OnActiveInterfaceChanged(){ diff --git a/os/posix/NetworkSocketPosix.h b/os/posix/NetworkSocketPosix.h index b71d7c6ebb..e57d703e31 100644 --- a/os/posix/NetworkSocketPosix.h +++ b/os/posix/NetworkSocketPosix.h @@ -1,14 +1,27 @@ // -// Created by Grishka on 10.04.17. +// libtgvoip is free and unencumbered public domain software. +// For more information, see http://unlicense.org or the UNLICENSE file +// you should have received with this source code distribution. // #ifndef LIBTGVOIP_NETWORKSOCKETPOSIX_H #define LIBTGVOIP_NETWORKSOCKETPOSIX_H #include "../../NetworkSocket.h" +#include +#include +#include namespace tgvoip { +struct TCPSocket{ + int fd; + IPv4Address address; + uint16_t port; + TCPO2State recvState; + TCPO2State sendState; +}; + class NetworkSocketPosix : public NetworkSocket{ public: NetworkSocketPosix(); @@ -31,12 +44,17 @@ protected: private: int fd; + std::vector tcpSockets; bool needUpdateNat64Prefix; bool nat64Present; double switchToV6at; bool isV4Available; + bool useTCP; + bool closing; IPv4Address lastRecvdV4; IPv6Address lastRecvdV6; + int pipeRead; + int pipeWrite; }; } diff --git a/os/windows/AudioInputWASAPI.h b/os/windows/AudioInputWASAPI.h index e9997396e1..3851f61fb2 100644 --- a/os/windows/AudioInputWASAPI.h +++ b/os/windows/AudioInputWASAPI.h @@ -19,7 +19,9 @@ #include #pragma warning(push) #pragma warning(disable : 4201) +#ifndef TGVOIP_WP_SILVERLIGHT #include +#endif #ifdef TGVOIP_WINDOWS_DESKTOP #include #include diff --git a/os/windows/AudioOutputWASAPI.h b/os/windows/AudioOutputWASAPI.h index 3c32a43f7b..6fd22c2695 100644 --- a/os/windows/AudioOutputWASAPI.h +++ b/os/windows/AudioOutputWASAPI.h @@ -19,7 +19,9 @@ #include #pragma warning(push) #pragma warning(disable : 4201) +#ifndef TGVOIP_WP_SILVERLIGHT #include +#endif #ifdef TGVOIP_WINDOWS_DESKTOP #include #include diff --git a/os/windows/NetworkSocketWinsock.cpp b/os/windows/NetworkSocketWinsock.cpp index 906a84fc4f..bce7e31606 100644 --- a/os/windows/NetworkSocketWinsock.cpp +++ b/os/windows/NetworkSocketWinsock.cpp @@ -23,6 +23,7 @@ NetworkSocketWinsock::NetworkSocketWinsock() : lastRecvdV4(0), lastRecvdV6("::0" nat64Present=false; switchToV6at=0; isV4Available=false; + closing=false; #ifdef TGVOIP_WINXP_COMPAT DWORD version=GetVersion(); @@ -45,6 +46,70 @@ void NetworkSocketWinsock::SetMaxPriority(){ } void NetworkSocketWinsock::Send(NetworkPacket *packet){ + if(packet->protocol==PROTO_TCP){ + //LOGV("Sending TCP packet to %s:%u", packet->address->ToString().c_str(), packet->port); + IPv4Address* v4addr=dynamic_cast(packet->address); + if(v4addr){ + TCPSocket* _socket=NULL; + for(std::vector::iterator itr=tcpSockets.begin();itr!=tcpSockets.end();++itr){ + if(itr->address==*v4addr && itr->port==packet->port){ + _socket=&*itr; + break; + } + } + if(!_socket){ + TCPSocket s; + s.fd=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + s.port=packet->port; + s.address=IPv4Address(*v4addr); + int opt=1; + setsockopt(s.fd, IPPROTO_TCP, TCP_NODELAY, (const char*)&opt, sizeof(opt)); + timeval timeout; + timeout.tv_sec=1; + timeout.tv_usec=0; + setsockopt(s.fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout)); + timeout.tv_sec=60; + setsockopt(s.fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)); + sockaddr_in addr; + addr.sin_family=AF_INET; + addr.sin_addr.s_addr=s.address.GetAddress(); + addr.sin_port=htons(s.port); + int res=connect(s.fd, (const sockaddr*) &addr, sizeof(addr)); + if(res!=0){ + LOGW("error connecting TCP socket to %s:%u: %d / %s; %d / %s", s.address.ToString().c_str(), s.port, res, strerror(res), errno, strerror(errno)); + closesocket(s.fd); + return; + } + unsigned char buf[64]; + GenerateTCPO2States(buf, &s.recvState, &s.sendState); + send(s.fd, (const char*)buf, sizeof(buf), 0); + tcpSockets.push_back(s); + _socket=&tcpSockets[tcpSockets.size()-1]; + } + if(_socket){ + //LOGV("sending to %s:%u, fd=%d, size=%d (%d)", _socket->address.ToString().c_str(), _socket->port, _socket->fd, packet->length, packet->length%4); + BufferOutputStream os(packet->length+4); + size_t len=packet->length/4; + if(len<0x7F){ + os.WriteByte((unsigned char)len); + }else{ + os.WriteByte(0x7F); + os.WriteByte((unsigned char)(len & 0xFF)); + os.WriteByte((unsigned char)((len >> 8) & 0xFF)); + os.WriteByte((unsigned char)((len >> 16) & 0xFF)); + } + os.WriteBytes(packet->data, packet->length); + EncryptForTCPO2(os.GetBuffer(), os.GetLength(), &_socket->sendState); + int res=send(_socket->fd, (const char*)os.GetBuffer(), os.GetLength(), 0); + if(res<0){ + LOGW("error sending to TCP: %d / %s; %d / %s", res, strerror(res), errno, strerror(errno)); + } + } + }else{ + LOGW("TCP over IPv6 isn't supported yet"); + } + return; + } sockaddr_in6 addr; IPv4Address* v4addr=dynamic_cast(packet->address); if(v4addr){ @@ -130,49 +195,131 @@ void NetworkSocketWinsock::Send(NetworkPacket *packet){ } void NetworkSocketWinsock::Receive(NetworkPacket *packet){ - sockaddr_in6 srcAddr; - sockaddr_in srcAddr4; - sockaddr* addr; - int addrLen; - if(isAtLeastVista){ - addr=(sockaddr*)&srcAddr; - addrLen=sizeof(srcAddr); - }else{ - addr=(sockaddr*)&srcAddr4; - addrLen=sizeof(srcAddr4); - } - //DWORD len; - //WSABUF buf; - //buf.buf=(char*) packet->data; - //buf.len=packet->length; - //int res=WSARecvFrom(fd, &buf, 1, &len, 0, (sockaddr*) &srcAddr, &addrLen, NULL, NULL); - int res=recvfrom(fd, (char*)packet->data, packet->length, 0, addr, &addrLen); - if(res!=SOCKET_ERROR) - packet->length=(size_t) res; - else{ - packet->length=0; - int error=WSAGetLastError(); - LOGE("error receiving: %d", error); - return; - } - //LOGV("Received %d bytes from %s:%d at %.5lf", len, inet_ntoa(srcAddr.sin_addr), ntohs(srcAddr.sin_port), GetCurrentTime()); - if(addr->sa_family==AF_INET){ - packet->port=ntohs(srcAddr4.sin_port); - lastRecvdV4=IPv4Address(srcAddr4.sin_addr.s_addr); - packet->address=&lastRecvdV4; - }else{ - packet->port=ntohs(srcAddr.sin6_port); - if(!isV4Available && IN6_IS_ADDR_V4MAPPED(&srcAddr.sin6_addr)){ - isV4Available=true; - LOGI("Detected IPv4 connectivity, will not try IPv6"); + + fd_set readSet; + fd_set errSet; + timeval timeout={0, 500000}; + + while(true){ + + do{ + FD_ZERO(&readSet); + FD_ZERO(&errSet); + FD_SET(fd, &readSet); + FD_SET(fd, &errSet); + for(std::vector::iterator itr=tcpSockets.begin(); itr!=tcpSockets.end(); ++itr){ + FD_SET(itr->fd, &readSet); + FD_SET(itr->fd, &errSet); + } + }while(select(0, &readSet, NULL, &errSet, &timeout)==0); + + if(closing){ + packet->length=0; + return; } - if(IN6_IS_ADDR_V4MAPPED(&srcAddr.sin6_addr) || (nat64Present && memcmp(nat64Prefix, srcAddr.sin6_addr.s6_addr, 12)==0)){ - in_addr v4addr=*((in_addr *)&srcAddr.sin6_addr.s6_addr[12]); - lastRecvdV4=IPv4Address(v4addr.s_addr); - packet->address=&lastRecvdV4; - }else{ - lastRecvdV6=IPv6Address(srcAddr.sin6_addr.s6_addr); - packet->address=&lastRecvdV6; + + if(FD_ISSET(fd, &readSet) || FD_ISSET(fd, &errSet)){ + sockaddr_in6 srcAddr; + sockaddr_in srcAddr4; + sockaddr* addr; + int addrLen; + if(isAtLeastVista){ + addr=(sockaddr*)&srcAddr; + addrLen=sizeof(srcAddr); + }else{ + addr=(sockaddr*)&srcAddr4; + addrLen=sizeof(srcAddr4); + } + //DWORD len; + //WSABUF buf; + //buf.buf=(char*) packet->data; + //buf.len=packet->length; + //int res=WSARecvFrom(fd, &buf, 1, &len, 0, (sockaddr*) &srcAddr, &addrLen, NULL, NULL); + int res=recvfrom(fd, (char*)packet->data, packet->length, 0, addr, &addrLen); + if(res!=SOCKET_ERROR) + packet->length=(size_t) res; + else{ + packet->length=0; + int error=WSAGetLastError(); + LOGE("error receiving: %d", error); + return; + } + //LOGV("Received %d bytes from %s:%d at %.5lf", len, inet_ntoa(srcAddr.sin_addr), ntohs(srcAddr.sin_port), GetCurrentTime()); + if(addr->sa_family==AF_INET){ + packet->port=ntohs(srcAddr4.sin_port); + lastRecvdV4=IPv4Address(srcAddr4.sin_addr.s_addr); + packet->address=&lastRecvdV4; + }else{ + packet->port=ntohs(srcAddr.sin6_port); + if(!isV4Available && IN6_IS_ADDR_V4MAPPED(&srcAddr.sin6_addr)){ + isV4Available=true; + LOGI("Detected IPv4 connectivity, will not try IPv6"); + } + if(IN6_IS_ADDR_V4MAPPED(&srcAddr.sin6_addr) || (nat64Present && memcmp(nat64Prefix, srcAddr.sin6_addr.s6_addr, 12)==0)){ + in_addr v4addr=*((in_addr *)&srcAddr.sin6_addr.s6_addr[12]); + lastRecvdV4=IPv4Address(v4addr.s_addr); + packet->address=&lastRecvdV4; + }else{ + lastRecvdV6=IPv6Address(srcAddr.sin6_addr.s6_addr); + packet->address=&lastRecvdV6; + } + } + return; + } + + for(std::vector::iterator itr=tcpSockets.begin(); itr!=tcpSockets.end();){ + if(FD_ISSET(itr->fd, &readSet)){ + unsigned char len1; + size_t packetLen=0; + size_t offset=0; + int len=recv(itr->fd, (char*)&len1, 1, 0); + if(len<=0) + goto failed; + EncryptForTCPO2(&len1, 1, &itr->recvState); + + if(len1<0x7F){ + packetLen=(size_t)len1*4; + }else{ + unsigned char len2[3]; + len=recv(itr->fd, (char*)len2, 3, 0); + if(len<=0) + goto failed; + EncryptForTCPO2(len2, 3, &itr->recvState); + packetLen=((size_t)len2[0] | ((size_t)len2[1] << 8) | ((size_t)len2[2] << 16))*4; + } + + if(packetLen>packet->length){ + LOGW("packet too big to fit into buffer"); + packet->length=0; + return; + } + + while(offsetfd, (char*)packet->data+offset, packetLen-offset, 0); + if(len<=0) + goto failed; + offset+=len; + } + EncryptForTCPO2(packet->data, packetLen, &itr->recvState); + packet->address=&itr->address; + packet->length=packetLen; + packet->port=itr->port; + packet->protocol=PROTO_TCP; + + return; + + failed: + packet->length=0; + closesocket(itr->fd); + itr=tcpSockets.erase(itr); + continue; + } + if(FD_ISSET(itr->fd, &errSet)){ + closesocket(itr->fd); + itr=tcpSockets.erase(itr); + continue; + } + ++itr; } } } @@ -258,7 +405,12 @@ void NetworkSocketWinsock::Open(){ } void NetworkSocketWinsock::Close(){ + closing=true; closesocket(fd); + for(std::vector::iterator itr=tcpSockets.begin(); itr!=tcpSockets.end();++itr){ + //shutdown(itr->fd, SHUT_RDWR); + closesocket(itr->fd); + } } void NetworkSocketWinsock::OnActiveInterfaceChanged(){ diff --git a/os/windows/NetworkSocketWinsock.h b/os/windows/NetworkSocketWinsock.h index 4e24d354af..6419c0e588 100644 --- a/os/windows/NetworkSocketWinsock.h +++ b/os/windows/NetworkSocketWinsock.h @@ -9,9 +9,18 @@ #include "../../NetworkSocket.h" #include +#include namespace tgvoip { +struct TCPSocket{ + uintptr_t fd; + IPv4Address address; + uint16_t port; + TCPO2State recvState; + TCPO2State sendState; +}; + class NetworkSocketWinsock : public NetworkSocket{ public: NetworkSocketWinsock(); @@ -41,6 +50,8 @@ private: IPv4Address lastRecvdV4; IPv6Address lastRecvdV6; bool isAtLeastVista; + std::vector tcpSockets; + bool closing; }; diff --git a/os/windows/WindowsSandboxUtils.cpp b/os/windows/WindowsSandboxUtils.cpp index dd0963d488..8fbba664e4 100644 --- a/os/windows/WindowsSandboxUtils.cpp +++ b/os/windows/WindowsSandboxUtils.cpp @@ -8,11 +8,16 @@ #include "WindowsSandboxUtils.h" #include #include +#ifdef TGVOIP_WP_SILVERLIGHT +#include +#endif using namespace tgvoip; using namespace Microsoft::WRL; + IAudioClient2* WindowsSandboxUtils::ActivateAudioDevice(const wchar_t* devID, HRESULT* callRes, HRESULT* actRes) { +#ifndef TGVOIP_WP_SILVERLIGHT // Did I say that I hate pointlessly asynchronous things? HANDLE event = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS); ActivationHandler activationHandler(event); @@ -26,8 +31,18 @@ IAudioClient2* WindowsSandboxUtils::ActivateAudioDevice(const wchar_t* devID, HR if (actRes) *actRes = activationHandler.actResult; return activationHandler.client; +#else + IAudioClient2* client; + HRESULT res=ActivateAudioInterface(devID, __uuidof(IAudioClient2), (void**)&client); + if(callRes) + *callRes=S_OK; + if(actRes) + *actRes=res; + return client; +#endif } +#ifndef TGVOIP_WP_SILVERLIGHT ActivationHandler::ActivationHandler(HANDLE _event) : event(_event) { @@ -48,4 +63,6 @@ STDMETHODIMP ActivationHandler::ActivateCompleted(IActivateAudioInterfaceAsyncOp SetEvent(event); return hr; -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/os/windows/WindowsSandboxUtils.h b/os/windows/WindowsSandboxUtils.h index bdbfa797cf..54654f7c81 100644 --- a/os/windows/WindowsSandboxUtils.h +++ b/os/windows/WindowsSandboxUtils.h @@ -7,7 +7,9 @@ #include #include +#ifndef TGVOIP_WP_SILVERLIGHT #include +#endif #include #include @@ -15,6 +17,7 @@ using namespace Microsoft::WRL; namespace tgvoip { +#ifndef TGVOIP_WP_SILVERLIGHT class ActivationHandler : public RuntimeClass< RuntimeClassFlags< ClassicCom >, FtmBase, IActivateAudioInterfaceCompletionHandler > { @@ -26,6 +29,7 @@ namespace tgvoip { IAudioClient2* client; HRESULT actResult; }; +#endif class WindowsSandboxUtils { public: