diff --git a/BlockingQueue.cpp b/BlockingQueue.cpp index f307f3a..4a02d47 100644 --- a/BlockingQueue.cpp +++ b/BlockingQueue.cpp @@ -8,69 +8,3 @@ using namespace tgvoip; -BlockingQueue::BlockingQueue(size_t capacity) : semaphore(capacity, 0){ - this->capacity=capacity; - overflowCallback=NULL; - init_mutex(mutex); -} - -BlockingQueue::~BlockingQueue(){ - semaphore.Release(); - free_mutex(mutex); -} - -void BlockingQueue::Put(void *thing){ - MutexGuard sync(mutex); - queue.push_back(thing); - bool didOverflow=false; - while(queue.size()>capacity){ - didOverflow=true; - if(overflowCallback){ - overflowCallback(queue.front()); - queue.pop_front(); - }else{ - abort(); - } - } - if(!didOverflow) - semaphore.Release(); -} - -void *BlockingQueue::GetBlocking(){ - semaphore.Acquire(); - MutexGuard sync(mutex); - void* r=GetInternal(); - return r; -} - - -void *BlockingQueue::Get(){ - MutexGuard sync(mutex); - if(queue.size()>0) - semaphore.Acquire(); - void* r=GetInternal(); - return r; -} - -void *BlockingQueue::GetInternal(){ - if(queue.size()==0) - return NULL; - void* r=queue.front(); - queue.pop_front(); - return r; -} - - -unsigned int BlockingQueue::Size(){ - return queue.size(); -} - - -void BlockingQueue::PrepareDealloc(){ - -} - - -void BlockingQueue::SetOverflowCallback(void (*overflowCallback)(void *)){ - this->overflowCallback=overflowCallback; -} diff --git a/BlockingQueue.h b/BlockingQueue.h index df3fb8b..212b861 100644 --- a/BlockingQueue.h +++ b/BlockingQueue.h @@ -14,25 +14,80 @@ using namespace std; namespace tgvoip{ + +template class BlockingQueue{ public: - BlockingQueue(size_t capacity); - ~BlockingQueue(); - void Put(void* thing); - void* GetBlocking(); - void* Get(); - unsigned int Size(); - void PrepareDealloc(); - void SetOverflowCallback(void (*overflowCallback)(void*)); + BlockingQueue(size_t capacity) : semaphore(capacity, 0){ + this->capacity=capacity; + overflowCallback=NULL; + init_mutex(mutex); + }; + + ~BlockingQueue(){ + semaphore.Release(); + free_mutex(mutex); + } + + void Put(T thing){ + MutexGuard sync(mutex); + queue.push_back(thing); + bool didOverflow=false; + while(queue.size()>capacity){ + didOverflow=true; + if(overflowCallback){ + overflowCallback(queue.front()); + queue.pop_front(); + }else{ + abort(); + } + } + if(!didOverflow) + semaphore.Release(); + } + + T GetBlocking(){ + semaphore.Acquire(); + MutexGuard sync(mutex); + T r=GetInternal(); + return r; + } + + T Get(){ + MutexGuard sync(mutex); + if(queue.size()>0) + semaphore.Acquire(); + T r=GetInternal(); + return r; + } + + unsigned int Size(){ + return queue.size(); + } + + void PrepareDealloc(){ + + } + + void SetOverflowCallback(void (*overflowCallback)(T)){ + this->overflowCallback=overflowCallback; + } private: - void* GetInternal(); - list queue; + T GetInternal(){ + //if(queue.size()==0) + // return NULL; + T r=queue.front(); + queue.pop_front(); + return r; + } + + list queue; size_t capacity; //tgvoip_lock_t lock; Semaphore semaphore; tgvoip_mutex_t mutex; - void (*overflowCallback)(void*); + void (*overflowCallback)(T); }; } diff --git a/BufferOutputStream.cpp b/BufferOutputStream.cpp index e6a7ebf..e3d3872 100644 --- a/BufferOutputStream.cpp +++ b/BufferOutputStream.cpp @@ -5,6 +5,7 @@ // #include "BufferOutputStream.h" +#include #include using namespace tgvoip; @@ -13,10 +14,19 @@ BufferOutputStream::BufferOutputStream(size_t size){ buffer=(unsigned char*) malloc(size); offset=0; this->size=size; + bufferProvided=false; +} + +BufferOutputStream::BufferOutputStream(unsigned char *buffer, size_t size){ + this->buffer=buffer; + this->size=size; + offset=0; + bufferProvided=true; } BufferOutputStream::~BufferOutputStream(){ - free(buffer); + if(!bufferProvided) + free(buffer); } void BufferOutputStream::WriteByte(unsigned char byte){ @@ -69,6 +79,9 @@ size_t BufferOutputStream::GetLength(){ void BufferOutputStream::ExpandBufferIfNeeded(size_t need){ if(offset+need>size){ + if(bufferProvided){ + throw std::out_of_range("buffer overflow"); + } if(need<1024){ buffer=(unsigned char *) realloc(buffer, size+1024); size+=1024; diff --git a/BufferOutputStream.h b/BufferOutputStream.h index a93195d..a9eaccf 100644 --- a/BufferOutputStream.h +++ b/BufferOutputStream.h @@ -15,6 +15,7 @@ class BufferOutputStream{ public: BufferOutputStream(size_t size); + BufferOutputStream(unsigned char* buffer, size_t size); ~BufferOutputStream(); void WriteByte(unsigned char byte); void WriteInt64(int64_t i); @@ -30,6 +31,7 @@ private: unsigned char* buffer; size_t size; size_t offset; + bool bufferProvided; }; } diff --git a/BufferPool.cpp b/BufferPool.cpp index b9c5080..9dc6b48 100644 --- a/BufferPool.cpp +++ b/BufferPool.cpp @@ -21,6 +21,7 @@ BufferPool::BufferPool(unsigned int size, unsigned int count){ buffers[i]=buffers[0]+i*size; } usedBuffers=0; + this->size=size; } BufferPool::~BufferPool(){ @@ -56,3 +57,10 @@ void BufferPool::Reuse(unsigned char* buffer){ abort(); } +size_t BufferPool::GetSingleBufferSize(){ + return size; +} + +size_t BufferPool::GetBufferCount(){ + return (size_t) bufferCount; +} diff --git a/BufferPool.h b/BufferPool.h index 7902ef5..392acdf 100644 --- a/BufferPool.h +++ b/BufferPool.h @@ -17,10 +17,13 @@ public: ~BufferPool(); unsigned char* Get(); void Reuse(unsigned char* buffer); + size_t GetSingleBufferSize(); + size_t GetBufferCount(); private: uint64_t usedBuffers; int bufferCount; + size_t size; unsigned char* buffers[64]; tgvoip_mutex_t mutex; }; diff --git a/EchoCanceller.cpp b/EchoCanceller.cpp index 6027cc8..2ce1410 100644 --- a/EchoCanceller.cpp +++ b/EchoCanceller.cpp @@ -75,7 +75,7 @@ EchoCanceller::EchoCanceller(bool enableAEC, bool enableNS, bool enableAGC){ webrtc::WebRtcAec_set_config(aec, config); #endif - farendQueue=new BlockingQueue(11); + farendQueue=new BlockingQueue(11); farendBufferPool=new BufferPool(960*2, 10); running=true; @@ -170,7 +170,7 @@ void EchoCanceller::SpeakerOutCallback(unsigned char* data, size_t len){ WebRtcAecm_BufferFarend(state, (int16_t*)(data+offset), AEC_FRAME_SIZE); offset+=OFFSET_STEP; }*/ - unsigned char* buf=farendBufferPool->Get(); + int16_t* buf=(int16_t*)farendBufferPool->Get(); if(buf){ memcpy(buf, data, 960*2); farendQueue->Put(buf); @@ -184,7 +184,7 @@ void *EchoCanceller::StartBufferFarendThread(void *arg){ void EchoCanceller::RunBufferFarendThread(){ while(running){ - int16_t* samplesIn=(int16_t *) farendQueue->GetBlocking(); + int16_t* samplesIn=farendQueue->GetBlocking(); if(samplesIn){ webrtc::IFChannelBuffer* bufIn=(webrtc::IFChannelBuffer*) splittingFilterFarendIn; webrtc::IFChannelBuffer* bufOut=(webrtc::IFChannelBuffer*) splittingFilterFarendOut; diff --git a/EchoCanceller.h b/EchoCanceller.h index 601aafa..982bd4a 100644 --- a/EchoCanceller.h +++ b/EchoCanceller.h @@ -40,7 +40,7 @@ private: void* splittingFilterFarendIn; // webrtc::IFChannelBuffer void* splittingFilterFarendOut; // webrtc::IFChannelBuffer tgvoip_thread_t bufferFarendThread; - BlockingQueue* farendQueue; + BlockingQueue* farendQueue; BufferPool* farendBufferPool; bool running; void* ns; // NsxHandle diff --git a/NetworkSocket.cpp b/NetworkSocket.cpp index 5eaa013..65b8a94 100644 --- a/NetworkSocket.cpp +++ b/NetworkSocket.cpp @@ -9,21 +9,28 @@ #include #if defined(_WIN32) #include "os/windows/NetworkSocketWinsock.h" +#include #else #include "os/posix/NetworkSocketPosix.h" #endif #include "logging.h" #include "VoIPServerConfig.h" #include "VoIPController.h" +#include "BufferInputStream.h" #define MIN_UDP_PORT 16384 #define MAX_UDP_PORT 32768 using namespace tgvoip; -NetworkSocket::NetworkSocket(){ +NetworkSocket::NetworkSocket(NetworkProtocol protocol) : protocol(protocol){ ipv6Timeout=ServerConfig::GetSharedInstance()->GetDouble("nat64_fallback_timeout", 3); failed=false; + + proxyAddress=NULL; + proxyPort=0; + proxyUsername=NULL; + proxyPassword=NULL; } NetworkSocket::~NetworkSocket(){ @@ -46,14 +53,29 @@ bool NetworkSocket::IsFailed(){ return failed; } -NetworkSocket *NetworkSocket::Create(){ +NetworkSocket *NetworkSocket::Create(NetworkProtocol protocol){ #ifndef _WIN32 - return new NetworkSocketPosix(); + return new NetworkSocketPosix(protocol); #else - return new NetworkSocketWinsock(); + return new NetworkSocketWinsock(protocol); #endif } +IPv4Address *NetworkSocket::ResolveDomainName(std::string name){ +#ifndef _WIN32 + return NetworkSocketPosix::ResolveDomainName(name); +#else + return NetworkSocketWinsock::ResolveDomainName(name); +#endif +} + +void NetworkSocket::SetSocksProxy(IPv4Address *addr, uint16_t port, char *username, char *password){ + proxyAddress=addr; + proxyPort=port; + proxyUsername=username; + proxyPassword=password; +} + void NetworkSocket::GenerateTCPO2States(unsigned char* buffer, TCPO2State* recvState, TCPO2State* sendState){ memset(recvState, 0, sizeof(TCPO2State)); memset(sendState, 0, sizeof(TCPO2State)); @@ -87,6 +109,22 @@ void NetworkSocket::EncryptForTCPO2(unsigned char *buffer, size_t len, TCPO2Stat VoIPController::crypto.aes_ctr_encrypt(buffer, len, state->key, state->iv, state->ecount, &state->num); } +size_t NetworkSocket::Receive(unsigned char *buffer, size_t len){ + NetworkPacket pkt; + pkt.data=buffer; + pkt.length=len; + Receive(&pkt); + return pkt.length; +} + +size_t NetworkSocket::Send(unsigned char *buffer, size_t len){ + NetworkPacket pkt; + pkt.data=buffer; + pkt.length=len; + Send(&pkt); + return pkt.length; +} + bool NetworkAddress::operator==(const NetworkAddress &other){ IPv4Address* self4=dynamic_cast(this); IPv4Address* other4=dynamic_cast((NetworkAddress*)&other); @@ -173,3 +211,394 @@ std::string IPv6Address::ToString(){ const uint8_t *IPv6Address::GetAddress(){ return address; } + +bool NetworkSocket::Select(std::vector &readFds, std::vector &errorFds, SocketSelectCanceller *canceller){ +#ifndef _WIN32 + return NetworkSocketPosix::Select(readFds, errorFds, canceller); +#else + return NetworkSocketWinsock::Select(readFds, errorFds, canceller); +#endif +} + +SocketSelectCanceller::~SocketSelectCanceller(){ + +} + +SocketSelectCanceller *SocketSelectCanceller::Create(){ +#ifndef _WIN32 + return new SocketSelectCancellerPosix(); +#else + return new SocketSelectCancellerWin32(); +#endif +} + + + +NetworkSocketTCPObfuscated::NetworkSocketTCPObfuscated(NetworkSocket *wrapped) : NetworkSocketWrapper(PROTO_TCP){ + this->wrapped=wrapped; +} + +NetworkSocketTCPObfuscated::~NetworkSocketTCPObfuscated(){ + if(wrapped) + delete wrapped; +} + +NetworkSocket *NetworkSocketTCPObfuscated::GetWrapped(){ + return wrapped; +} + +void NetworkSocketTCPObfuscated::InitConnection(){ + unsigned char buf[64]; + GenerateTCPO2States(buf, &recvState, &sendState); + wrapped->Send(buf, 64); +} + +void NetworkSocketTCPObfuscated::Send(NetworkPacket *packet){ + 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(), &sendState); + wrapped->Send(os.GetBuffer(), os.GetLength()); + //LOGD("Sent %u bytes", os.GetLength()); +} + +void NetworkSocketTCPObfuscated::Receive(NetworkPacket *packet){ + unsigned char len1; + size_t packetLen=0; + size_t offset=0; + size_t len; + wrapped->Receive(&len1, 1); + /*if(len<=0) + goto failed;*/ + EncryptForTCPO2(&len1, 1, &recvState); + + if(len1<0x7F){ + packetLen=(size_t)len1*4; + }else{ + unsigned char len2[3]; + len=wrapped->Receive(len2, 3); + /*if(len<=0) + goto failed;*/ + EncryptForTCPO2(len2, 3, &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 (%u vs %u)", packetLen, packet->length); + packet->length=0; + return; + } + + while(offsetReceive(packet->data+offset, packetLen-offset); + /*if(len<=0) + goto failed;*/ + offset+=len; + } + EncryptForTCPO2(packet->data, packetLen, &recvState); + //packet->address=&itr->address; + packet->length=packetLen; + //packet->port=itr->port; + packet->protocol=PROTO_TCP; + packet->address=wrapped->GetConnectedAddress(); + packet->port=wrapped->GetConnectedPort(); +} + +void NetworkSocketTCPObfuscated::Open(){ + +} + +void NetworkSocketTCPObfuscated::Close(){ + wrapped->Close(); +} + +void NetworkSocketTCPObfuscated::Connect(NetworkAddress *address, uint16_t port){ + +} + +bool NetworkSocketTCPObfuscated::IsFailed(){ + return wrapped->IsFailed(); +} + +NetworkSocketSOCKS5Proxy::NetworkSocketSOCKS5Proxy(NetworkSocket *tcp, NetworkSocket *udp, std::string username, std::string password) : NetworkSocketWrapper(udp ? PROTO_UDP : PROTO_TCP){ + this->tcp=tcp; + this->udp=udp; + this->username=username; + this->password=password; + connectedAddress=NULL; +} + +NetworkSocketSOCKS5Proxy::~NetworkSocketSOCKS5Proxy(){ + delete tcp; + if(connectedAddress) + delete connectedAddress; +} + +void NetworkSocketSOCKS5Proxy::Send(NetworkPacket *packet){ + if(protocol==PROTO_TCP){ + tcp->Send(packet); + }else if(protocol==PROTO_UDP){ + unsigned char buf[1500]; + BufferOutputStream out(buf, sizeof(buf)); + out.WriteInt16(0); // RSV + out.WriteByte(0); // FRAG + IPv4Address* v4=dynamic_cast(packet->address); + IPv6Address* v6=dynamic_cast(packet->address); + if(v4){ + out.WriteByte(1); // ATYP (IPv4) + out.WriteInt32(v4->GetAddress()); + }else{ + out.WriteByte(4); // ATYP (IPv6) + out.WriteBytes((unsigned char *) v6->GetAddress(), 16); + } + out.WriteInt16(htons(packet->port)); + out.WriteBytes(packet->data, packet->length); + NetworkPacket p; + p.data=buf; + p.length=out.GetLength(); + p.address=connectedAddress; + p.port=connectedPort; + p.protocol=PROTO_UDP; + udp->Send(&p); + } +} + +void NetworkSocketSOCKS5Proxy::Receive(NetworkPacket *packet){ + if(protocol==PROTO_TCP){ + tcp->Receive(packet); + }else if(protocol==PROTO_UDP){ + unsigned char buf[1500]; + NetworkPacket p; + p.data=buf; + p.length=sizeof(buf); + udp->Receive(&p); + if(p.length && p.address && *p.address==*connectedAddress && p.port==connectedPort){ + BufferInputStream in(buf, p.length); + in.ReadInt16(); // RSV + in.ReadByte(); // FRAG + unsigned char atyp=in.ReadByte(); + if(atyp==1){ // IPv4 + lastRecvdV4=IPv4Address((uint32_t) in.ReadInt32()); + packet->address=&lastRecvdV4; + }else if(atyp==4){ // IPv6 + unsigned char addr[16]; + in.ReadBytes(addr, 16); + lastRecvdV6=IPv6Address(addr); + packet->address=&lastRecvdV6; + } + packet->port=ntohs(in.ReadInt16()); + if(packet->length>=in.Remaining()){ + packet->length=in.Remaining(); + in.ReadBytes(packet->data, in.Remaining()); + }else{ + packet->length=0; + LOGW("socks5: received packet too big"); + } + } + } +} + +void NetworkSocketSOCKS5Proxy::Open(){ + if(protocol==PROTO_UDP){ + unsigned char buf[1024]; + BufferOutputStream out(buf, sizeof(buf)); + out.WriteByte(5); // VER + out.WriteByte(3); // CMD (UDP ASSOCIATE) + out.WriteByte(0); // RSV + out.WriteByte(1); // ATYP (IPv4) + out.WriteInt32(0); // DST.ADDR + out.WriteInt16(0); // DST.PORT + tcp->Send(buf, out.GetLength()); + size_t l=tcp->Receive(buf, sizeof(buf)); + if(l<2 || tcp->IsFailed()){ + LOGW("socks5: udp associate failed"); + failed=true; + return; + } + try{ + BufferInputStream in(buf, l); + unsigned char ver=in.ReadByte(); + unsigned char rep=in.ReadByte(); + if(ver!=5){ + LOGW("socks5: udp associate: wrong ver in response"); + failed=true; + return; + } + if(rep!=0){ + LOGW("socks5: udp associate failed with error %02X", rep); + failed=true; + return; + } + in.ReadByte(); // RSV + unsigned char atyp=in.ReadByte(); + if(atyp==1){ + uint32_t addr=(uint32_t) in.ReadInt32(); + connectedAddress=new IPv4Address(addr); + }else if(atyp==3){ + unsigned char len=in.ReadByte(); + char domain[256]; + memset(domain, 0, sizeof(domain)); + in.ReadBytes((unsigned char*)domain, len); + LOGD("address type is domain, address=%s", domain); + connectedAddress=ResolveDomainName(std::string(domain)); + if(!connectedAddress){ + LOGW("socks5: failed to resolve domain name '%s'", domain); + failed=true; + return; + } + }else if(atyp==4){ + unsigned char addr[16]; + in.ReadBytes(addr, 16); + connectedAddress=new IPv6Address(addr); + }else{ + LOGW("socks5: unknown address type %d", atyp); + failed=true; + return; + } + connectedPort=(uint16_t)ntohs(in.ReadInt16()); + tcp->SetTimeouts(0, 0); + LOGV("socks5: udp associate successful, given endpoint %s:%d", connectedAddress->ToString().c_str(), connectedPort); + }catch(std::out_of_range& x){ + LOGW("socks5: udp associate response parse failed"); + failed=true; + } + } +} + +void NetworkSocketSOCKS5Proxy::Close(){ + tcp->Close(); +} + +void NetworkSocketSOCKS5Proxy::Connect(NetworkAddress *address, uint16_t port){ + if(!failed){ + unsigned char buf[1024]; + BufferOutputStream out(buf, sizeof(buf)); + out.WriteByte(5); // VER + out.WriteByte(1); // CMD (CONNECT) + out.WriteByte(0); // RSV + IPv4Address* v4=dynamic_cast(address); + IPv6Address* v6=dynamic_cast(address); + if(v4){ + out.WriteByte(1); // ATYP (IPv4) + out.WriteInt32(v4->GetAddress()); + }else if(v6){ + out.WriteByte(4); // ATYP (IPv6) + out.WriteBytes((unsigned char*)v6->GetAddress(), 16); + }else{ + LOGW("socks5: unknown address type"); + failed=true; + return; + } + out.WriteInt16(htons(port)); // DST.PORT + tcp->Send(buf, out.GetLength()); + size_t l=tcp->Receive(buf, sizeof(buf)); + if(l<2 || tcp->IsFailed()){ + LOGW("socks5: connect failed") + failed=true; + return; + } + BufferInputStream in(buf, l); + unsigned char ver=in.ReadByte(); + if(ver!=5){ + LOGW("socks5: connect: wrong ver in response"); + failed=true; + return; + } + unsigned char rep=in.ReadByte(); + if(rep!=0){ + LOGW("socks5: connect: failed with error %02X", rep); + failed=true; + return; + } + connectedAddress=v4 ? (NetworkAddress*)new IPv4Address(*v4) : (NetworkAddress*)new IPv6Address(*v6); + connectedPort=port; + LOGV("socks5: connect succeeded"); + } +} + +NetworkSocket *NetworkSocketSOCKS5Proxy::GetWrapped(){ + return protocol==PROTO_TCP ? tcp : udp; +} + +void NetworkSocketSOCKS5Proxy::InitConnection(){ + unsigned char buf[1024]; + BufferOutputStream p(buf, sizeof(buf)); + p.WriteByte(5); // VER + if(!username.empty()){ + p.WriteByte(2); // NMETHODS + p.WriteByte(0); // no auth + p.WriteByte(2); // user/pass + }else{ + p.WriteByte(1); // NMETHODS + p.WriteByte(0); // no auth + } + tcp->Send(buf, p.GetLength()); + size_t l=tcp->Receive(buf, sizeof(buf)); + if(l<2 || tcp->IsFailed()){ + failed=true; + return; + } + BufferInputStream in(buf, l); + unsigned char ver=in.ReadByte(); + unsigned char chosenMethod=in.ReadByte(); + LOGV("socks5: VER=%02X, METHOD=%02X", ver, chosenMethod); + if(ver!=5){ + LOGW("socks5: incorrect VER in response"); + failed=true; + return; + } + if(chosenMethod==0){ + // connected, no further auth needed + }else if(chosenMethod==2 && !username.empty()){ + p.Reset(); + p.WriteByte(1); // VER + p.WriteByte((unsigned char)(username.length()>255 ? 255 : username.length())); // ULEN + p.WriteBytes((unsigned char*)username.c_str(), username.length()>255 ? 255 : username.length()); // UNAME + p.WriteByte((unsigned char)(password.length()>255 ? 255 : password.length())); // PLEN + p.WriteBytes((unsigned char*)password.c_str(), password.length()>255 ? 255 : password.length()); // PASSWD + tcp->Send(buf, p.GetLength()); + l=tcp->Receive(buf, sizeof(buf)); + if(l<2 || tcp->IsFailed()){ + failed=true; + return; + } + in=BufferInputStream(buf, l); + ver=in.ReadByte(); + unsigned char status=in.ReadByte(); + LOGV("socks5: auth response VER=%02X, STATUS=%02X", ver, status); + if(ver!=1){ + LOGW("socks5: auth response VER is incorrect"); + failed=true; + return; + } + if(status!=0){ + LOGW("socks5: username/password auth failed"); + failed=true; + return; + } + }else{ + LOGW("socks5: unsupported auth method"); + failed=true; + return; + } +} + +bool NetworkSocketSOCKS5Proxy::IsFailed(){ + return NetworkSocket::IsFailed() || tcp->IsFailed(); +} + +NetworkAddress *NetworkSocketSOCKS5Proxy::GetConnectedAddress(){ + return connectedAddress; +} + +uint16_t NetworkSocketSOCKS5Proxy::GetConnectedPort(){ + return connectedPort; +} diff --git a/NetworkSocket.h b/NetworkSocket.h index d311727..e66015f 100644 --- a/NetworkSocket.h +++ b/NetworkSocket.h @@ -7,6 +7,7 @@ #include #include +#include namespace tgvoip { @@ -25,7 +26,6 @@ namespace tgvoip { class NetworkAddress{ public: virtual std::string ToString()=0; - //virtual sockaddr& ToSockAddr(uint16_t port)=0; bool operator==(const NetworkAddress& other); bool operator!=(const NetworkAddress& other); }; @@ -64,20 +64,37 @@ namespace tgvoip { }; typedef struct NetworkPacket NetworkPacket; + class SocketSelectCanceller{ + public: + virtual ~SocketSelectCanceller(); + virtual void CancelSelect()=0; + static SocketSelectCanceller* Create(); + }; + class NetworkSocket{ public: - NetworkSocket(); + NetworkSocket(NetworkProtocol protocol); virtual ~NetworkSocket(); virtual void Send(NetworkPacket* packet)=0; virtual void Receive(NetworkPacket* packet)=0; + size_t Receive(unsigned char* buffer, size_t len); + size_t Send(unsigned char* buffer, size_t len); virtual void Open()=0; virtual void Close()=0; - virtual uint16_t GetLocalPort()=0; + virtual uint16_t GetLocalPort(){ return 0; }; + virtual void Connect(NetworkAddress* address, uint16_t port)=0; virtual std::string GetLocalInterfaceInfo(IPv4Address* inet4addr, IPv6Address* inet6addr); - virtual void OnActiveInterfaceChanged()=0; - bool IsFailed(); + virtual void OnActiveInterfaceChanged(){}; + virtual NetworkAddress* GetConnectedAddress(){ return NULL; }; + virtual uint16_t GetConnectedPort(){ return 0; }; + virtual void SetTimeouts(int sendTimeout, int recvTimeout){}; - static NetworkSocket* Create(); + virtual bool IsFailed(); + void SetSocksProxy(IPv4Address* addr, uint16_t port, char* username, char* password); + + static NetworkSocket* Create(NetworkProtocol protocol); + static IPv4Address* ResolveDomainName(std::string name); + static bool Select(std::vector& readFds, std::vector& errorFds, SocketSelectCanceller* canceller); protected: virtual uint16_t GenerateLocalPort(); @@ -87,6 +104,67 @@ namespace tgvoip { double ipv6Timeout; unsigned char nat64Prefix[12]; bool failed; + NetworkProtocol protocol; + + IPv4Address* proxyAddress; + uint16_t proxyPort; + char* proxyUsername; + char* proxyPassword; + }; + + class NetworkSocketWrapper : public NetworkSocket{ + public: + NetworkSocketWrapper(NetworkProtocol protocol) : NetworkSocket(protocol){}; + virtual ~NetworkSocketWrapper(){}; + virtual NetworkSocket* GetWrapped()=0; + virtual void InitConnection()=0; + }; + + class NetworkSocketTCPObfuscated : public NetworkSocketWrapper{ + public: + NetworkSocketTCPObfuscated(NetworkSocket* wrapped); + virtual ~NetworkSocketTCPObfuscated(); + virtual NetworkSocket* GetWrapped(); + virtual void InitConnection(); + virtual void Send(NetworkPacket *packet); + virtual void Receive(NetworkPacket *packet); + virtual void Open(); + virtual void Close(); + virtual void Connect(NetworkAddress *address, uint16_t port); + + virtual bool IsFailed(); + + private: + NetworkSocket* wrapped; + TCPO2State recvState; + TCPO2State sendState; + }; + + class NetworkSocketSOCKS5Proxy : public NetworkSocketWrapper{ + public: + NetworkSocketSOCKS5Proxy(NetworkSocket* tcp, NetworkSocket* udp, std::string username, std::string password); + virtual ~NetworkSocketSOCKS5Proxy(); + virtual void Send(NetworkPacket *packet); + virtual void Receive(NetworkPacket *packet); + virtual void Open(); + virtual void Close(); + virtual void Connect(NetworkAddress *address, uint16_t port); + virtual NetworkSocket *GetWrapped(); + virtual void InitConnection(); + virtual bool IsFailed(); + virtual NetworkAddress *GetConnectedAddress(); + virtual uint16_t GetConnectedPort(); + + private: + NetworkSocket* tcp; + NetworkSocket* udp; + std::string username; + std::string password; + NetworkAddress* connectedAddress; + uint16_t connectedPort; + + IPv4Address lastRecvdV4; + IPv6Address lastRecvdV6; }; } diff --git a/OpusDecoder.cpp b/OpusDecoder.cpp index de29659..6f6626a 100644 --- a/OpusDecoder.cpp +++ b/OpusDecoder.cpp @@ -24,7 +24,7 @@ tgvoip::OpusDecoder::OpusDecoder(MediaStreamItf *dst) : semaphore(32, 0){ lastDecodedLen=0; outputBufferSize=0; lastDecodedOffset=0; - decodedQueue=new BlockingQueue(33); + decodedQueue=new BlockingQueue(33); bufferPool=new BufferPool(PACKET_SIZE, 32); echoCanceller=NULL; frameDuration=20; diff --git a/OpusDecoder.h b/OpusDecoder.h index 6e12933..51bbbac 100644 --- a/OpusDecoder.h +++ b/OpusDecoder.h @@ -37,7 +37,7 @@ private: static void* StartThread(void* param); void RunThread(); ::OpusDecoder* dec; - BlockingQueue* decodedQueue; + BlockingQueue* decodedQueue; BufferPool* bufferPool; unsigned char* buffer; unsigned char* lastDecoded; diff --git a/OpusEncoder.h b/OpusEncoder.h index 09e9be7..9329f89 100644 --- a/OpusEncoder.h +++ b/OpusEncoder.h @@ -42,7 +42,7 @@ private: uint32_t requestedBitrate; uint32_t currentBitrate; tgvoip_thread_t thread; - BlockingQueue queue; + BlockingQueue queue; BufferPool bufferPool; EchoCanceller* echoCanceller; int complexity; diff --git a/VoIPController.cpp b/VoIPController.cpp index d8f423f..6359904 100644 --- a/VoIPController.cpp +++ b/VoIPController.cpp @@ -24,6 +24,63 @@ #include #include #include +#include + + +#define PKT_INIT 1 +#define PKT_INIT_ACK 2 +#define PKT_STREAM_STATE 3 +#define PKT_STREAM_DATA 4 +#define PKT_UPDATE_STREAMS 5 +#define PKT_PING 6 +#define PKT_PONG 7 +#define PKT_STREAM_DATA_X2 8 +#define PKT_STREAM_DATA_X3 9 +#define PKT_LAN_ENDPOINT 10 +#define PKT_NETWORK_CHANGED 11 +#define PKT_SWITCH_PREF_RELAY 12 +#define PKT_SWITCH_TO_P2P 13 +#define PKT_NOP 14 + +#define IS_MOBILE_NETWORK(x) (x==NET_TYPE_GPRS || x==NET_TYPE_EDGE || x==NET_TYPE_3G || x==NET_TYPE_HSPA || x==NET_TYPE_LTE || x==NET_TYPE_OTHER_MOBILE) + +#define PROTOCOL_NAME 0x50567247 // "GrVP" in little endian (reversed here) +#define PROTOCOL_VERSION 3 +#define MIN_PROTOCOL_VERSION 3 + +#define STREAM_DATA_FLAG_LEN16 0x40 +#define STREAM_DATA_FLAG_HAS_MORE_FLAGS 0x80 + +#define STREAM_TYPE_AUDIO 1 +#define STREAM_TYPE_VIDEO 2 + +#define CODEC_OPUS 1 + +/*flags:# voice_call_id:flags.2?int128 in_seq_no:flags.4?int out_seq_no:flags.4?int + * recent_received_mask:flags.5?int proto:flags.3?int extra:flags.1?string raw_data:flags.0?string*/ +#define PFLAG_HAS_DATA 1 +#define PFLAG_HAS_EXTRA 2 +#define PFLAG_HAS_CALL_ID 4 +#define PFLAG_HAS_PROTO 8 +#define PFLAG_HAS_SEQ 16 +#define PFLAG_HAS_RECENT_RECV 32 + +#define INIT_FLAG_DATA_SAVING_ENABLED 1 + +#define TLID_DECRYPTED_AUDIO_BLOCK 0xDBF948C1 +#define TLID_SIMPLE_AUDIO_BLOCK 0xCC0D0E76 +#define TLID_UDP_REFLECTOR_PEER_INFO 0x27D9371C +#define TLID_UDP_REFLECTOR_PEER_INFO_IPV6 0x83fc73b1 +#define TLID_UDP_REFLECTOR_SELF_INFO 0xc01572c7 +#define PAD4(x) (4-(x+(x<=253 ? 1 : 0))%4) + +inline int pad4(int x){ + int r=PAD4(x); + if(r==4) + return 0; + return r; +} + using namespace tgvoip; @@ -98,7 +155,13 @@ voip_crypto_functions_t VoIPController::crypto; // set it yourself upon initiali extern FILE* tgvoipLogFile; -VoIPController::VoIPController() : activeNetItfName(""), currentAudioInput("default"), currentAudioOutput("default"){ +VoIPController::VoIPController() : activeNetItfName(""), + currentAudioInput("default"), + currentAudioOutput("default"), + proxyAddress(""), + proxyUsername(""), + proxyPassword(""), + outgoingPacketsBufferPool(1024, 20){ seq=1; lastRemoteSeq=0; state=STATE_WAIT_INIT; @@ -112,10 +175,7 @@ VoIPController::VoIPController() : activeNetItfName(""), currentAudioInput("defa audioTimestampOut=0; stopping=false; int i; - for(i=0;i<20;i++){ - emptySendBuffers.push_back(new BufferOutputStream(1024)); - } - sendQueue=new BlockingQueue(21); + sendQueue=new BlockingQueue(21); init_mutex(sendBufferMutex); memset(remoteAcks, 0, sizeof(double)*32); memset(sentPacketTimes, 0, sizeof(double)*32); @@ -129,9 +189,6 @@ VoIPController::VoIPController() : activeNetItfName(""), currentAudioInput("defa packetsRecieved=0; waitingForAcks=false; networkType=NET_TYPE_UNKNOWN; - audioPacketGrouping=3; - audioPacketsWritten=0; - currentAudioPacket=NULL; stateCallback=NULL; echoCanceller=NULL; dontSendPackets=0; @@ -154,10 +211,21 @@ VoIPController::VoIPController() : activeNetItfName(""), currentAudioInput("defa peerPreferredRelay=NULL; statsDump=NULL; useTCP=false; + useUDP=true; didAddTcpRelays=false; - enableTcpAt=0; + setEstablishedAt=0; + udpPingCount=0; + lastUdpPingTime=0; + openingTcpSocket=NULL; - socket=NetworkSocket::Create(); + proxyProtocol=PROXY_NONE; + proxyPort=0; + resolvedProxyAddress=NULL; + + selectCanceller=SocketSelectCanceller::Create(); + udpSocket=NetworkSocket::Create(PROTO_UDP); + realUdpSocket=udpSocket; + udpConnectivityState=UDP_UNKNOWN; maxAudioBitrate=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("audio_max_bitrate", 20000); maxAudioBitrateGPRS=(uint32_t) ServerConfig::GetSharedInstance()->GetInt("audio_max_bitrate_gprs", 8000); @@ -173,6 +241,7 @@ VoIPController::VoIPController() : activeNetItfName(""), currentAudioInput("defa relaySwitchThreshold=ServerConfig::GetSharedInstance()->GetDouble("relay_switch_threshold", 0.8); p2pToRelaySwitchThreshold=ServerConfig::GetSharedInstance()->GetDouble("p2p_to_relay_switch_threshold", 0.6); relayToP2pSwitchThreshold=ServerConfig::GetSharedInstance()->GetDouble("relay_to_p2p_switch_threshold", 0.8); + reconnectingTimeout=ServerConfig::GetSharedInstance()->GetDouble("reconnecting_state_timeout", 2.0); #ifdef __APPLE__ machTimestart=0; @@ -188,15 +257,6 @@ VoIPController::VoIPController() : activeNetItfName(""), currentAudioInput("defa stm->enabled=1; stm->frameDuration=60; outgoingStreams.push_back(stm); - - std::vector odevs=EnumerateAudioOutputs(); - for(int i=0;i idevs=EnumerateAudioInputs(); - for(int i=0;iClose(); - sendQueue->Put(NULL); + if(udpSocket) + udpSocket->Close(); + if(realUdpSocket!=udpSocket) + realUdpSocket->Close(); + selectCanceller->CancelSelect(); + sendQueue->Put(PendingOutgoingPacket{0}); + if(openingTcpSocket) + openingTcpSocket->Close(); LOGD("before join sendThread"); join_thread(sendThread); LOGD("before join recvThread"); @@ -219,18 +284,10 @@ VoIPController::~VoIPController(){ join_thread(tickThread); free_mutex(sendBufferMutex); LOGD("before close socket"); - if(socket) - delete socket; - LOGD("before free send buffers"); - while(emptySendBuffers.size()>0){ - delete emptySendBuffers[emptySendBuffers.size()-1]; - emptySendBuffers.pop_back(); - } - while(sendQueue->Size()>0){ - void* p=sendQueue->Get(); - if(p) - delete (BufferOutputStream*)p; - } + if(udpSocket) + delete udpSocket; + if(udpSocket!=realUdpSocket) + delete realUdpSocket; LOGD("before delete jitter buffer"); if(jitterBuffer){ delete jitterBuffer; @@ -279,9 +336,13 @@ VoIPController::~VoIPController(){ free(queuedPackets[i]); } delete conctl; - for(std::vector::iterator itr=endpoints.begin();itr!=endpoints.end();++itr) + for(std::vector::iterator itr=endpoints.begin();itr!=endpoints.end();++itr){ + if((*itr)->socket){ + (*itr)->socket->Close(); + delete (*itr)->socket; + } delete *itr; - LOGD("Left VoIPController::~VoIPController"); + } if(tgvoipLogFile){ FILE* log=tgvoipLogFile; tgvoipLogFile=NULL; @@ -289,6 +350,10 @@ VoIPController::~VoIPController(){ } if(statsDump) fclose(statsDump); + if(resolvedProxyAddress) + delete resolvedProxyAddress; + delete selectCanceller; + LOGD("Left VoIPController::~VoIPController"); } void VoIPController::SetRemoteEndpoints(std::vector endpoints, bool allowP2p){ @@ -336,10 +401,13 @@ void VoIPController::Start(){ 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(); + udpSocket->Open(); + if(udpSocket->IsFailed()){ + SetState(STATE_FAILED); + return; + } - - SendPacket(NULL, 0, currentEndpoint); + //SendPacket(NULL, 0, currentEndpoint); runReceiver=true; start_thread(recvThread, StartRecvThread, this); @@ -365,61 +433,45 @@ void VoIPController::HandleAudioInput(unsigned char *data, size_t len){ LOGV("waiting for RLC, dropping outgoing audio packet"); return; } - int audioPacketGrouping=1; - BufferOutputStream* pkt=NULL; - if(audioPacketsWritten==0){ - pkt=GetOutgoingPacketBuffer(); - if(!pkt){ - LOGW("Dropping data packet, queue overflow"); - return; - } - currentAudioPacket=pkt; - }else{ - pkt=currentAudioPacket; - } - unsigned char flags=(unsigned char) (len>255 ? STREAM_DATA_FLAG_LEN16 : 0); - pkt->WriteByte((unsigned char) (1 | flags)); // streamID + flags - if(len>255) - pkt->WriteInt16((int16_t)len); - else - pkt->WriteByte((unsigned char)len); - pkt->WriteInt32(audioTimestampOut); - pkt->WriteBytes(data, len); - audioPacketsWritten++; - if(audioPacketsWritten>=audioPacketGrouping){ - uint32_t pl=pkt->GetLength(); - unsigned char tmp[MSC_STACK_FALLBACK(pl, 1024)]; - memcpy(tmp, pkt->GetBuffer(), pl); - pkt->Reset(); - unsigned char type; - switch(audioPacketGrouping){ - case 2: - type=PKT_STREAM_DATA_X2; - break; - case 3: - type=PKT_STREAM_DATA_X3; - break; - default: - type=PKT_STREAM_DATA; - break; - } - WritePacketHeader(pkt, type, pl); - pkt->WriteBytes(tmp, pl); - //LOGI("payload size %u", pl); - if(pl<253) - pl+=1; - for(;pl%4>0;pl++) - pkt->WriteByte(0); - sendQueue->Put(pkt); - audioPacketsWritten=0; + + unsigned char* buf=outgoingPacketsBufferPool.Get(); + if(buf){ + BufferOutputStream pkt(buf, outgoingPacketsBufferPool.GetSingleBufferSize()); + + unsigned char flags=(unsigned char) (len>255 ? STREAM_DATA_FLAG_LEN16 : 0); + pkt.WriteByte((unsigned char) (1 | flags)); // streamID + flags + if(len>255) + pkt.WriteInt16((int16_t) len); + else + pkt.WriteByte((unsigned char) len); + pkt.WriteInt32(audioTimestampOut); + pkt.WriteBytes(data, len); + + PendingOutgoingPacket p{ + /*.seq=*/GenerateOutSeq(), + /*.type=*/PKT_STREAM_DATA, + /*.len=*/pkt.GetLength(), + /*.data=*/buf, + /*.endpoint=*/NULL, + }; + sendQueue->Put(p); } + audioTimestampOut+=outgoingStreams[0]->frameDuration; } void VoIPController::Connect(){ assert(state!=STATE_WAIT_INIT_ACK); + if(proxyProtocol==PROXY_SOCKS5){ + resolvedProxyAddress=NetworkSocket::ResolveDomainName(proxyAddress); + if(!resolvedProxyAddress){ + LOGW("Error resolving proxy address %s", proxyAddress.c_str()); + SetState(STATE_FAILED); + return; + } + InitUDPProxy(); + } connectionInitTime=GetCurrentTime(); - enableTcpAt=connectionInitTime+5; SendInit(); } @@ -435,7 +487,11 @@ void VoIPController::SetEncryptionKey(char *key, bool isOutgoing){ this->isOutgoing=isOutgoing; } -uint32_t VoIPController::WritePacketHeader(BufferOutputStream *s, unsigned char type, uint32_t length){ +uint32_t VoIPController::GenerateOutSeq(){ + return seq++; +} + +void VoIPController::WritePacketHeader(uint32_t pseq, BufferOutputStream *s, unsigned char type, uint32_t length){ uint32_t acks=0; int i; for(i=0;i<32;i++){ @@ -445,8 +501,6 @@ uint32_t VoIPController::WritePacketHeader(BufferOutputStream *s, unsigned char acks<<=1; } - uint32_t pseq=seq++; - if(state==STATE_WAIT_INIT || state==STATE_WAIT_INIT_ACK){ s->WriteInt32(TLID_DECRYPTED_AUDIO_BLOCK); int64_t randomID; @@ -517,8 +571,6 @@ uint32_t VoIPController::WritePacketHeader(BufferOutputStream *s, unsigned char sentPacketTimes[0]=GetCurrentTime(); lastSentSeq=pseq; //LOGI("packet header size %d", s->GetLength()); - - return pseq; } @@ -542,30 +594,65 @@ void VoIPController::UpdateAudioBitrate(){ void VoIPController::SendInit(){ - BufferOutputStream* out=new BufferOutputStream(1024); - WritePacketHeader(out, PKT_INIT, 15); - out->WriteInt32(PROTOCOL_VERSION); - out->WriteInt32(MIN_PROTOCOL_VERSION); - uint32_t flags=0; - if(dataSavingMode) - flags|=INIT_FLAG_DATA_SAVING_ENABLED; - out->WriteInt32(flags); - out->WriteByte(1); // audio codecs count - out->WriteByte(CODEC_OPUS); - out->WriteByte(0); // video codecs count lock_mutex(endpointsMutex); + uint32_t initSeq=GenerateOutSeq(); 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); + unsigned char* pkt=outgoingPacketsBufferPool.Get(); + if(!pkt){ + LOGW("can't send init, queue overflow"); + continue; + } + BufferOutputStream out(pkt, outgoingPacketsBufferPool.GetSingleBufferSize()); + //WritePacketHeader(out, PKT_INIT, 15); + out.WriteInt32(PROTOCOL_VERSION); + out.WriteInt32(MIN_PROTOCOL_VERSION); + uint32_t flags=0; + if(dataSavingMode) + flags|=INIT_FLAG_DATA_SAVING_ENABLED; + out.WriteInt32(flags); + out.WriteByte(1); // audio codecs count + out.WriteByte(CODEC_OPUS); + out.WriteByte(0); // video codecs count + sendQueue->Put(PendingOutgoingPacket{ + /*.seq=*/initSeq, + /*.type=*/PKT_INIT, + /*.len=*/out.GetLength(), + /*.data=*/pkt, + /*.endpoint=*/*itr + }); } unlock_mutex(endpointsMutex); SetState(STATE_WAIT_INIT_ACK); - delete out; } -void VoIPController::SendInitAck(){ - +void VoIPController::InitUDPProxy(){ + if(realUdpSocket!=udpSocket){ + udpSocket->Close(); + delete udpSocket; + udpSocket=realUdpSocket; + } + NetworkSocket* tcp=NetworkSocket::Create(PROTO_TCP); + tcp->Connect(resolvedProxyAddress, proxyPort); + if(tcp->IsFailed()){ + SetState(STATE_FAILED); + tcp->Close(); + delete tcp; + return; + } + NetworkSocketSOCKS5Proxy* udpProxy=new NetworkSocketSOCKS5Proxy(tcp, udpSocket, proxyUsername, proxyPassword); + udpProxy->InitConnection(); + udpProxy->Open(); + if(udpProxy->IsFailed()){ + udpProxy->Close(); + delete udpProxy; + useTCP=true; + useUDP=false; + udpConnectivityState=UDP_NOT_AVAILABLE; + }else{ + udpSocket=udpProxy; + } } void VoIPController::RunRecvThread(){ @@ -573,9 +660,65 @@ void VoIPController::RunRecvThread(){ unsigned char buffer[1024]; NetworkPacket packet; while(runReceiver){ - //LOGI("Before recv"); packet.data=buffer; packet.length=1024; + + std::vector readSockets; + std::vector errorSockets; + readSockets.push_back(realUdpSocket); + errorSockets.push_back(realUdpSocket); + + //if(useTCP){ + for(std::vector::iterator itr=endpoints.begin();itr!=endpoints.end();++itr){ + if((*itr)->type==EP_TYPE_TCP_RELAY){ + if((*itr)->socket){ + readSockets.push_back((*itr)->socket); + errorSockets.push_back((*itr)->socket); + } + } + } + //} + + bool selRes=NetworkSocket::Select(readSockets, errorSockets, selectCanceller); + if(!selRes){ + LOGV("Select canceled"); + continue; + } + if(!runReceiver) + return; + + if(!errorSockets.empty()){ + if(std::find(errorSockets.begin(), errorSockets.end(), realUdpSocket)!=errorSockets.end()){ + LOGW("UDP socket failed"); + SetState(STATE_FAILED); + return; + } + } + + NetworkSocket* socket=NULL; + + if(std::find(readSockets.begin(), readSockets.end(), realUdpSocket)!=readSockets.end()){ + socket=udpSocket; + }else if(readSockets.size()>0){ + socket=readSockets[0]; + }else{ + LOGI("no sockets to read from"); + lock_mutex(endpointsMutex); + for(std::vector::iterator itr=errorSockets.begin();itr!=errorSockets.end();++itr){ + for(std::vector::iterator e=endpoints.begin();e!=endpoints.end();++e){ + if((*e)->socket && (*e)->socket==*itr){ + (*e)->socket->Close(); + delete (*e)->socket; + (*e)->socket=NULL; + LOGI("Closing failed TCP socket for %s:%u", (*e)->address.ToString().c_str(), (*e)->port); + break; + } + } + } + unlock_mutex(endpointsMutex); + continue; + } + socket->Receive(&packet); if(!packet.address){ LOGE("Packet has null address. This shouldn't happen."); @@ -617,34 +760,37 @@ 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->type==EP_TYPE_TCP_RELAY ? srcEndpoint->peerTag : callID, 16)!=0){ + if(memcmp(buffer, srcEndpoint->type==EP_TYPE_UDP_RELAY || srcEndpoint->type==EP_TYPE_TCP_RELAY ? (void*)srcEndpoint->peerTag : (void*)callID, 16)!=0){ LOGW("Received packet has wrong peerTag"); continue; } in.Seek(16); - if(waitingForRelayPeerInfo && in.Remaining()>=32){ - bool isPublicIpResponse=true; - int i; - for(i=0;i<12;i++){ - if((unsigned char)buffer[in.GetOffset()+i]!=0xFF){ - isPublicIpResponse=false; - break; - } - } + if(in.Remaining()>=16 && (srcEndpoint->type==EP_TYPE_UDP_RELAY || srcEndpoint->type==EP_TYPE_TCP_RELAY) + && *reinterpret_cast(buffer+16)==0xFFFFFFFFFFFFFFFFLL && *reinterpret_cast(buffer+24)==0xFFFFFFFF){ + // relay special request response + in.Seek(16+12); + uint32_t tlid=(uint32_t) in.ReadInt32(); - if(isPublicIpResponse){ - waitingForRelayPeerInfo=false; - in.Seek(in.GetOffset()+12); - uint32_t tlid=(uint32_t) in.ReadInt32(); - if(tlid==TLID_UDP_REFLECTOR_PEER_INFO){ + if(tlid==TLID_UDP_REFLECTOR_SELF_INFO){ + if(srcEndpoint->type==EP_TYPE_UDP_RELAY && udpConnectivityState==UDP_PING_SENT && in.Remaining()>=32){ + int32_t date=in.ReadInt32(); + int64_t queryID=in.ReadInt64(); + unsigned char myIP[16]; + in.ReadBytes(myIP, 16); + int32_t myPort=in.ReadInt32(); + udpConnectivityState=UDP_AVAILABIE; + //LOGV("Received UDP ping reply from %s:%d: date=%d, queryID=%lld, my IP=%s, my port=%d", srcEndpoint->address.ToString().c_str(), srcEndpoint->port, date, queryID, IPv6Address(myIP).ToString().c_str(), myPort); + } + }else if(tlid==TLID_UDP_REFLECTOR_PEER_INFO){ + if(waitingForRelayPeerInfo && in.Remaining()>=16){ lock_mutex(endpointsMutex); uint32_t myAddr=(uint32_t) in.ReadInt32(); uint32_t myPort=(uint32_t) in.ReadInt32(); uint32_t peerAddr=(uint32_t) in.ReadInt32(); uint32_t peerPort=(uint32_t) in.ReadInt32(); - for(std::vector::iterator itrtr=endpoints.begin();itrtr!=endpoints.end();++itrtr){ - Endpoint* ep=*itrtr; + for(std::vector::iterator itrtr=endpoints.begin(); itrtr!=endpoints.end(); ++itrtr){ + Endpoint *ep=*itrtr; if(ep->type==EP_TYPE_UDP_P2P_INET){ if(currentEndpoint==ep) currentEndpoint=preferredRelay; @@ -653,8 +799,8 @@ void VoIPController::RunRecvThread(){ break; } } - for(std::vector::iterator itrtr=endpoints.begin();itrtr!=endpoints.end();++itrtr){ - Endpoint* ep=*itrtr; + for(std::vector::iterator itrtr=endpoints.begin(); itrtr!=endpoints.end(); ++itrtr){ + Endpoint *ep=*itrtr; if(ep->type==EP_TYPE_UDP_P2P_LAN){ if(currentEndpoint==ep) currentEndpoint=preferredRelay; @@ -671,23 +817,23 @@ void VoIPController::RunRecvThread(){ if(myAddr==peerAddr){ LOGW("Detected LAN"); IPv4Address lanAddr(0); - socket->GetLocalInterfaceInfo(&lanAddr, NULL); + udpSocket->GetLocalInterfaceInfo(&lanAddr, NULL); BufferOutputStream pkt(8); pkt.WriteInt32(lanAddr.GetAddress()); - pkt.WriteInt32(socket->GetLocalPort()); + pkt.WriteInt32(udpSocket->GetLocalPort()); SendPacketReliably(PKT_LAN_ENDPOINT, pkt.GetBuffer(), pkt.GetLength(), 0.5, 10); } unlock_mutex(endpointsMutex); - }else{ - LOGE("It looks like a reflector response but tlid is %08X, expected %08X", tlid, TLID_UDP_REFLECTOR_PEER_INFO); + waitingForRelayPeerInfo=false; } - - continue; + }else{ + LOGV("Received relay response with unknown tl id: 0x%08X", tlid); } + continue; } if(in.Remaining()<40){ - + LOGV("Received packet is too small"); continue; } @@ -864,7 +1010,7 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA unlock_mutex(queuedPacketsMutex); } - if(srcEndpoint!=currentEndpoint && srcEndpoint->type==EP_TYPE_UDP_RELAY && currentEndpoint->type!=EP_TYPE_UDP_RELAY){ + if(srcEndpoint!=currentEndpoint && (srcEndpoint->type==EP_TYPE_UDP_RELAY || srcEndpoint->type==EP_TYPE_TCP_RELAY) && ((currentEndpoint->type!=EP_TYPE_UDP_RELAY && currentEndpoint->type!=EP_TYPE_TCP_RELAY) || currentEndpoint->averageRTT==0)){ if(seqgt(lastSentSeq-32, lastRemoteAckSeq)){ currentEndpoint=srcEndpoint; LOGI("Peer network address probably changed, switching to relay"); @@ -911,26 +1057,30 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA in.ReadByte(); // ignore for now } - BufferOutputStream *out=new BufferOutputStream(1024); - WritePacketHeader(out, PKT_INIT_ACK, (peerVersion>=2 ? 10 : 2)+(peerVersion>=2 ? 6 : 4)*outgoingStreams.size()); - if(peerVersion>=2){ - out->WriteInt32(PROTOCOL_VERSION); - out->WriteInt32(MIN_PROTOCOL_VERSION); - } + unsigned char* buf=outgoingPacketsBufferPool.Get(); + if(buf){ + BufferOutputStream out(buf, outgoingPacketsBufferPool.GetSingleBufferSize()); + //WritePacketHeader(out, PKT_INIT_ACK, (peerVersion>=2 ? 10 : 2)+(peerVersion>=2 ? 6 : 4)*outgoingStreams.size()); - out->WriteByte((unsigned char) outgoingStreams.size()); - for(i=0; iWriteByte(outgoingStreams[i]->id); - out->WriteByte(outgoingStreams[i]->type); - out->WriteByte(outgoingStreams[i]->codec); - if(peerVersion>=2) - out->WriteInt16(outgoingStreams[i]->frameDuration); - else - outgoingStreams[i]->frameDuration=20; - out->WriteByte((unsigned char) (outgoingStreams[i]->enabled ? 1 : 0)); + out.WriteInt32(PROTOCOL_VERSION); + out.WriteInt32(MIN_PROTOCOL_VERSION); + + out.WriteByte((unsigned char) outgoingStreams.size()); + for(i=0; iid); + out.WriteByte(outgoingStreams[i]->type); + out.WriteByte(outgoingStreams[i]->codec); + out.WriteInt16(outgoingStreams[i]->frameDuration); + out.WriteByte((unsigned char) (outgoingStreams[i]->enabled ? 1 : 0)); + } + sendQueue->Put(PendingOutgoingPacket{ + /*.seq=*/GenerateOutSeq(), + /*.type=*/PKT_INIT_ACK, + /*.len=*/out.GetLength(), + /*.data=*/buf, + /*.endpoint=*/NULL + }); } - SendPacket(out->GetBuffer(), out->GetLength(), currentEndpoint); - delete out; } if(type==PKT_INIT_ACK){ LOGD("Received init ack"); @@ -1015,11 +1165,11 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA decoder->SetFrameDuration(incomingAudioStream->frameDuration); decoder->Start(); if(incomingAudioStream->frameDuration>50) - jitterBuffer->SetMinPacketCount(ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_60", 3)); + jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_60", 3)); else if(incomingAudioStream->frameDuration>30) - jitterBuffer->SetMinPacketCount(ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_40", 4)); + jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_40", 4)); else - jitterBuffer->SetMinPacketCount(ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_20", 6)); + jitterBuffer->SetMinPacketCount((uint32_t) ServerConfig::GetSharedInstance()->GetInt("jitter_initial_delay_20", 6)); //audioOutput->Start(); #ifdef TGVOIP_USE_AUDIO_SESSION #ifdef __APPLE__ @@ -1034,12 +1184,14 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA #endif #endif } - SetState(STATE_ESTABLISHED); + setEstablishedAt=GetCurrentTime()+ServerConfig::GetSharedInstance()->GetDouble("established_delay_if_no_stream_data", 1.5); if(allowP2p) SendPublicEndpointsRequest(); } } if(type==PKT_STREAM_DATA || type==PKT_STREAM_DATA_X2 || type==PKT_STREAM_DATA_X3){ + if(state!=STATE_ESTABLISHED && receivedInitAck) + SetState(STATE_ESTABLISHED); int count; switch(type){ case PKT_STREAM_DATA_X2: @@ -1076,27 +1228,24 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA } if(type==PKT_PING){ LOGD("Received ping from %s:%d", packet.address->ToString().c_str(), srcEndpoint->port); - if(srcEndpoint->type!=EP_TYPE_UDP_RELAY && !allowP2p){ + if(srcEndpoint->type!=EP_TYPE_UDP_RELAY && srcEndpoint->type!=EP_TYPE_TCP_RELAY && !allowP2p){ LOGW("Received p2p ping but p2p is disabled by manual override"); - continue; } - if(srcEndpoint==currentEndpoint){ - BufferOutputStream *pkt=GetOutgoingPacketBuffer(); - if(!pkt){ - LOGW("Dropping pong packet, queue overflow"); - - continue; - } - WritePacketHeader(pkt, PKT_PONG, 4); - pkt->WriteInt32(pseq); - sendQueue->Put(pkt); - }else{ - BufferOutputStream pkt(32); - WritePacketHeader(&pkt, PKT_PONG, 4); - pkt.WriteInt32(pseq); - SendPacket(pkt.GetBuffer(), pkt.GetLength(), srcEndpoint); + unsigned char* buf=outgoingPacketsBufferPool.Get(); + if(!buf){ + LOGW("Dropping pong packet, queue overflow"); + continue; } + BufferOutputStream pkt(buf, outgoingPacketsBufferPool.GetSingleBufferSize()); + pkt.WriteInt32(pseq); + sendQueue->Put(PendingOutgoingPacket{ + /*.seq=*/GenerateOutSeq(), + /*.type=*/PKT_PONG, + /*.len=*/pkt.GetLength(), + /*.data=*/buf, + /*.endpoint=*/srcEndpoint, + }); } if(type==PKT_PONG){ if(packetInnerLen>=4){ @@ -1164,38 +1313,6 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA UpdateAudioBitrate(); } } - /*if(type==PKT_SWITCH_PREF_RELAY){ - uint64_t relayId=(uint64_t) in.ReadInt64(); - int i; - for(i=0;itype==EP_TYPE_UDP_RELAY && endpoints[i]->id==relayId){ - preferredRelay=endpoints[i]; - LOGD("Switching preferred relay to %s:%d", inet_ntoa(preferredRelay->address), preferredRelay->port); - break; - } - } - if(currentEndpoint->type==EP_TYPE_UDP_RELAY) - currentEndpoint=preferredRelay; - }*/ - /*if(type==PKT_SWITCH_TO_P2P && allowP2p){ - voip_endpoint_t* p2p=GetEndpointByType(EP_TYPE_UDP_P2P_INET); - if(p2p){ - voip_endpoint_t* lan=GetEndpointByType(EP_TYPE_UDP_P2P_LAN); - if(lan && lan->_averageRtt>0){ - LOGI("Switching to p2p (LAN)"); - currentEndpoint=lan; - }else{ - if(lan) - lan->_lastPingTime=0; - if(p2p->_averageRtt>0){ - LOGI("Switching to p2p (Inet)"); - currentEndpoint=p2p; - }else{ - p2p->_lastPingTime=0; - } - } - } - }*/ }catch(std::out_of_range x){ LOGW("Error parsing packet: %s", x.what()); } @@ -1204,16 +1321,20 @@ simpleAudioBlock random_id:long random_bytes:string raw_data:string = DecryptedA } void VoIPController::RunSendThread(){ + unsigned char buf[1500]; while(runReceiver){ - BufferOutputStream* pkt=(BufferOutputStream *) sendQueue->GetBlocking(); - if(pkt){ + PendingOutgoingPacket pkt=sendQueue->GetBlocking(); + if(pkt.data){ lock_mutex(endpointsMutex); - SendPacket(pkt->GetBuffer(), pkt->GetLength(), currentEndpoint); + Endpoint *endpoint=pkt.endpoint ? pkt.endpoint : currentEndpoint; + if((endpoint->type==EP_TYPE_TCP_RELAY && useTCP) || (endpoint->type!=EP_TYPE_TCP_RELAY && useUDP)){ + BufferOutputStream p(buf, sizeof(buf)); + WritePacketHeader(pkt.seq, &p, pkt.type, pkt.len); + p.WriteBytes(pkt.data, pkt.len); + SendPacket(p.GetBuffer(), p.GetLength(), endpoint); + } unlock_mutex(endpointsMutex); - pkt->Reset(); - lock_mutex(sendBufferMutex); - emptySendBuffers.push_back(pkt); - unlock_mutex(sendBufferMutex); + outgoingPacketsBufferPool.Reuse(pkt.data); }else{ LOGE("tried to send null packet"); } @@ -1233,8 +1354,10 @@ void VoIPController::RunTickThread(){ Sleep(100); #endif tickCount++; + if(connectionInitTime==0) + continue; double time=GetCurrentTime(); - if(tickCount%5==0 && state==STATE_ESTABLISHED){ + if(tickCount%5==0 && (state==STATE_ESTABLISHED || state==STATE_RECONNECTING)){ memmove(&rttHistory[1], rttHistory, 31*sizeof(double)); rttHistory[0]=GetAverageRTT(); /*if(rttHistory[16]>0){ @@ -1267,27 +1390,24 @@ 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(useTCP && !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(state==STATE_ESTABLISHED && encoder && conctl){ if((audioInput && !audioInput->IsInitialized()) || (audioOutput && !audioOutput->IsInitialized())){ LOGE("Audio I/O failed"); lastError=TGVOIP_ERROR_AUDIO_IO; @@ -1344,33 +1464,22 @@ void VoIPController::RunTickThread(){ } if((waitingForAcks && tickCount%10==0) || (!areThereAnyEnabledStreams && tickCount%2==0)){ - BufferOutputStream* pkt=GetOutgoingPacketBuffer(); - if(!pkt){ - LOGW("Dropping ping packet, queue overflow"); - return; + unsigned char* buf=outgoingPacketsBufferPool.Get(); + if(buf){ + sendQueue->Put(PendingOutgoingPacket{ + /*.seq=*/(firstSentPing=GenerateOutSeq()), + /*.type=*/PKT_NOP, + /*.len=*/0, + /*.data=*/buf, + /*.endpoint=*/NULL + }); } - uint32_t seq=WritePacketHeader(pkt, PKT_NOP, 0); - firstSentPing=seq; - sendQueue->Put(pkt); - LOGV("sent ping"); } if(state==STATE_WAIT_INIT_ACK && GetCurrentTime()-stateChangeTime>.5){ SendInit(); } - /*if(needSendP2pPing){ - if(GetCurrentTime()-lastP2pPingTime>2){ - if(p2pPingCount<10){ // try hairpin routing first, even if we have a LAN address - SendP2pPing(EP_TYPE_UDP_P2P_INET); - } - if(p2pPingCount>=5 && p2pPingCount<15){ // last resort to get p2p - SendP2pPing(EP_TYPE_UDP_P2P_LAN); - } - p2pPingCount++; - } - }*/ - if(waitingForRelayPeerInfo && GetCurrentTime()-publicEndpointsReqTime>5){ LOGD("Resending peer relay info request"); SendPublicEndpointsRequest(); @@ -1389,9 +1498,9 @@ void VoIPController::RunTickThread(){ continue; } if(GetCurrentTime()-qp->lastSentTime>=qp->retryInterval){ - BufferOutputStream* pkt=GetOutgoingPacketBuffer(); - if(pkt){ - uint32_t seq=WritePacketHeader(pkt, qp->type, qp->length); + unsigned char* buf=outgoingPacketsBufferPool.Get(); + if(buf){ + uint32_t seq=GenerateOutSeq(); memmove(&qp->seqs[1], qp->seqs, 4*9); qp->seqs[0]=seq; qp->lastSentTime=GetCurrentTime(); @@ -1399,8 +1508,14 @@ void VoIPController::RunTickThread(){ if(qp->firstSentTime==0) qp->firstSentTime=qp->lastSentTime; if(qp->length) - pkt->WriteBytes(qp->data, qp->length); - sendQueue->Put(pkt); + memcpy(buf, qp->data, qp->length); + sendQueue->Put(PendingOutgoingPacket{ + /*.seq=*/seq, + /*.type=*/qp->type, + /*.len=*/qp->length, + /*.data=*/buf, + /*.endpoint=*/NULL + }); } } } @@ -1409,8 +1524,8 @@ void VoIPController::RunTickThread(){ if(jitterBuffer) jitterBuffer->Tick(); - if(state==STATE_ESTABLISHED){ - lock_mutex(endpointsMutex); + lock_mutex(endpointsMutex); + if(state==STATE_ESTABLISHED || state==STATE_RECONNECTING){ Endpoint* minPingRelay=preferredRelay; double minPing=preferredRelay->averageRTT; for(std::vector::iterator e=endpoints.begin();e!=endpoints.end();++e){ @@ -1419,11 +1534,17 @@ void VoIPController::RunTickThread(){ continue; if(GetCurrentTime()-endpoint->lastPingTime>=10){ LOGV("Sending ping to %s", endpoint->address.ToString().c_str()); - BufferOutputStream pkt(32); - uint32_t seq=WritePacketHeader(&pkt, PKT_PING, 0); + unsigned char* buf=outgoingPacketsBufferPool.Get(); + if(buf){ + sendQueue->Put(PendingOutgoingPacket{ + /*.seq=*/(endpoint->lastPingSeq=GenerateOutSeq()), + /*.type=*/PKT_PING, + /*.len=*/0, + /*.data=*/buf, + /*.endpoint=*/endpoint + }); + } endpoint->lastPingTime=GetCurrentTime(); - endpoint->lastPingSeq=seq; - SendPacket(pkt.GetBuffer(), pkt.GetLength(), endpoint); } if(endpoint->type==EP_TYPE_UDP_RELAY || (useTCP && endpoint->type==EP_TYPE_TCP_RELAY)){ double k=endpoint->type==EP_TYPE_UDP_RELAY ? 1 : 2; @@ -1468,11 +1589,37 @@ void VoIPController::RunTickThread(){ LogDebugInfo(); } } - unlock_mutex(endpointsMutex); } + if(udpConnectivityState==UDP_UNKNOWN){ + for(std::vector::iterator itr=endpoints.begin();itr!=endpoints.end();++itr){ + if((*itr)->type==EP_TYPE_UDP_RELAY){ + SendUdpPing(*itr); + } + } + udpConnectivityState=UDP_PING_SENT; + lastUdpPingTime=time; + udpPingCount=1; + }else if(udpConnectivityState==UDP_PING_SENT){ + if(time-lastUdpPingTime>=0.5){ + if(udpPingCount<4){ + for(std::vector::iterator itr=endpoints.begin();itr!=endpoints.end();++itr){ + if((*itr)->type==EP_TYPE_UDP_RELAY){ + SendUdpPing(*itr); + } + } + udpPingCount++; + lastUdpPingTime=time; + }else{ + LOGW("No UDP ping replies received; assuming no connectivity and trying TCP") + udpConnectivityState=UDP_NOT_AVAILABLE; + useTCP=true; + } + } + } + unlock_mutex(endpointsMutex); - if(state==STATE_ESTABLISHED){ - if(GetCurrentTime()-lastRecvPacketTime>=config.recv_timeout){ + if(state==STATE_ESTABLISHED || state==STATE_RECONNECTING){ + if(time-lastRecvPacketTime>=config.recv_timeout){ if(currentEndpoint && currentEndpoint->type!=EP_TYPE_UDP_RELAY && currentEndpoint->type!=EP_TYPE_TCP_RELAY){ LOGW("Packet receive timeout, switching to relay"); currentEndpoint=preferredRelay; @@ -1491,7 +1638,7 @@ void VoIPController::RunTickThread(){ BufferOutputStream s(4); s.WriteInt32(dataSavingMode ? INIT_FLAG_DATA_SAVING_ENABLED : 0); SendPacketReliably(PKT_NETWORK_CHANGED, s.GetBuffer(), s.GetLength(), 1, 20); - lastRecvPacketTime=GetCurrentTime(); + lastRecvPacketTime=time; }else{ LOGW("Packet receive timeout, disconnecting"); lastError=TGVOIP_ERROR_TIMEOUT; @@ -1506,6 +1653,16 @@ void VoIPController::RunTickThread(){ } } + if(state==STATE_ESTABLISHED && time-lastRecvPacketTime>=reconnectingTimeout){ + SetState(STATE_RECONNECTING); + } + + if(state!=STATE_ESTABLISHED && setEstablishedAt>0 && time>=setEstablishedAt){ + SetState(STATE_ESTABLISHED); + setEstablishedAt=0; + } + + if(statsDump){ //fprintf(statsDump, "Time\tRTT\tLISeq\tLASeq\tCWnd\tBitrate\tJitter\tJDelay\tAJDelay\n"); fprintf(statsDump, "%.3f\t%.3f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%.3f\t%.3f\t%.3f\n", @@ -1570,7 +1727,7 @@ void VoIPController::SendPacket(unsigned char *data, size_t len, Endpoint* ep){ out.WriteBytes(keyFingerprint, 8); out.WriteBytes((msgHash+(SHA1_LENGTH-16)), 16); KDF(msgHash+(SHA1_LENGTH-16), isOutgoing ? 0 : 8, key, iv); - unsigned char aesOut[MSC_STACK_FALLBACK(inner.GetLength(), 1024)]; + unsigned char aesOut[MSC_STACK_FALLBACK(inner.GetLength(), 1500)]; crypto.aes_ige_encrypt(inner.GetBuffer(), aesOut, inner.GetLength(), key, iv); out.WriteBytes(aesOut, inner.GetLength()); } @@ -1586,7 +1743,68 @@ void VoIPController::SendPacket(unsigned char *data, size_t len, Endpoint* ep){ pkt.length=out.GetLength(); pkt.data=out.GetBuffer(); pkt.protocol=ep->type==EP_TYPE_TCP_RELAY ? PROTO_TCP : PROTO_UDP; - socket->Send(&pkt); + //socket->Send(&pkt); + if(ep->type==EP_TYPE_TCP_RELAY){ + if(ep->socket){ + ep->socket->Send(&pkt); + }else{ + LOGI("connecting to tcp: %s:%u", ep->address.ToString().c_str(), ep->port); + NetworkSocket* s; + if(proxyProtocol==PROXY_NONE){ + s=NetworkSocket::Create(PROTO_TCP); + }else if(proxyProtocol==PROXY_SOCKS5){ + NetworkSocket* rawTcp=NetworkSocket::Create(PROTO_TCP); + openingTcpSocket=rawTcp; + rawTcp->Connect(resolvedProxyAddress, proxyPort); + if(rawTcp->IsFailed()){ + openingTcpSocket=NULL; + rawTcp->Close(); + delete rawTcp; + LOGW("Error connecting to SOCKS5 proxy"); + return; + } + NetworkSocketSOCKS5Proxy* proxy=new NetworkSocketSOCKS5Proxy(rawTcp, NULL, proxyUsername, proxyPassword); + openingTcpSocket=rawTcp; + proxy->InitConnection(); + if(proxy->IsFailed()){ + openingTcpSocket=NULL; + LOGW("Proxy initialization failed"); + proxy->Close(); + delete proxy; + return; + } + s=proxy; + }else if(proxyProtocol==PROXY_HTTP){ + s=NetworkSocket::Create(PROTO_TCP); + }else{ + LOGE("Unsupported proxy protocol %d", proxyProtocol); + SetState(STATE_FAILED); + return; + } + s->Connect(&ep->address, ep->port); + if(s->IsFailed()){ + s->Close(); + delete s; + LOGW("Error connecting to %s:%u", ep->address.ToString().c_str(), ep->port); + }else{ + NetworkSocketTCPObfuscated* tcpWrapper=new NetworkSocketTCPObfuscated(s); + openingTcpSocket=tcpWrapper; + tcpWrapper->InitConnection(); + openingTcpSocket=NULL; + if(tcpWrapper->IsFailed()){ + tcpWrapper->Close(); + delete tcpWrapper; + LOGW("Error initializing connection to %s:%u", ep->address.ToString().c_str(), ep->port); + }else{ + tcpWrapper->Send(&pkt); + ep->socket=tcpWrapper; + selectCanceller->CancelSelect(); + } + } + } + }else{ + udpSocket->Send(&pkt); + } } @@ -1594,9 +1812,9 @@ void VoIPController::SetNetworkType(int type){ networkType=type; UpdateDataSavingState(); UpdateAudioBitrate(); - std::string itfName=socket->GetLocalInterfaceInfo(NULL, NULL); + std::string itfName=udpSocket->GetLocalInterfaceInfo(NULL, NULL); if(itfName!=activeNetItfName){ - socket->OnActiveInterfaceChanged(); + udpSocket->OnActiveInterfaceChanged(); LOGI("Active network interface changed: %s -> %s", activeNetItfName.c_str(), itfName.c_str()); bool isFirstChange=activeNetItfName.length()==0; activeNetItfName=itfName; @@ -1605,6 +1823,7 @@ void VoIPController::SetNetworkType(int type){ if(currentEndpoint && currentEndpoint->type!=EP_TYPE_UDP_RELAY){ if(preferredRelay->type==EP_TYPE_UDP_RELAY) currentEndpoint=preferredRelay; + lock_mutex(endpointsMutex); for(std::vector::iterator itr=endpoints.begin();itr!=endpoints.end();){ Endpoint* endpoint=*itr; if(endpoint->type==EP_TYPE_UDP_RELAY && useTCP){ @@ -1613,6 +1832,8 @@ void VoIPController::SetNetworkType(int type){ preferredRelay=endpoint; currentEndpoint=endpoint; } + }else if(endpoint->type==EP_TYPE_TCP_RELAY && endpoint->socket){ + endpoint->socket->Close(); } //if(endpoint->type==EP_TYPE_UDP_P2P_INET){ endpoint->averageRTT=0; @@ -1625,13 +1846,20 @@ void VoIPController::SetNetworkType(int type){ ++itr; } } + unlock_mutex(endpointsMutex); } + udpConnectivityState=UDP_UNKNOWN; + udpPingCount=0; + lastUdpPingTime=0; + if(proxyProtocol==PROXY_SOCKS5) + InitUDPProxy(); if(allowP2p && currentEndpoint){ SendPublicEndpointsRequest(); } BufferOutputStream s(4); s.WriteInt32(dataSavingMode ? INIT_FLAG_DATA_SAVING_ENABLED : 0); SendPacketReliably(PKT_NETWORK_CHANGED, s.GetBuffer(), s.GetLength(), 1, 20); + selectCanceller->CancelSelect(); } LOGI("set network type: %d, active interface %s", type, activeNetItfName.c_str()); /*if(type==NET_TYPE_GPRS || type==NET_TYPE_EDGE) @@ -1764,19 +1992,6 @@ void VoIPController::UpdateAudioOutputState(){ } } - -BufferOutputStream *VoIPController::GetOutgoingPacketBuffer(){ - BufferOutputStream* pkt=NULL; - lock_mutex(sendBufferMutex); - if(emptySendBuffers.size()>0){ - pkt=emptySendBuffers[emptySendBuffers.size()-1]; - emptySendBuffers.pop_back(); - } - unlock_mutex(sendBufferMutex); - return pkt; -} - - void VoIPController::KDF(unsigned char* msgKey, size_t x, unsigned char* aesKey, unsigned char* aesIv){ uint8_t sA[SHA1_LENGTH], sB[SHA1_LENGTH], sC[SHA1_LENGTH], sD[SHA1_LENGTH]; BufferOutputStream buf(128); @@ -1884,6 +2099,8 @@ void VoIPController::SendPublicEndpointsRequest(){ } void VoIPController::SendPublicEndpointsRequest(Endpoint& relay){ + if(!useUDP) + return; LOGD("Sending public endpoints request to %s:%d", relay.address.ToString().c_str(), relay.port); publicEndpointsReqTime=GetCurrentTime(); waitingForRelayPeerInfo=true; @@ -1896,7 +2113,7 @@ void VoIPController::SendPublicEndpointsRequest(Endpoint& relay){ pkt.address=(NetworkAddress*)&relay.address; pkt.port=relay.port; pkt.protocol=PROTO_UDP; - socket->Send(&pkt); + udpSocket->Send(&pkt); } Endpoint* VoIPController::GetEndpointByType(int type){ @@ -1951,7 +2168,10 @@ void VoIPController::SetConfig(voip_config_t *cfg){ fclose(statsDump); if(strlen(cfg->statsDumpFilePath)){ statsDump=fopen(cfg->statsDumpFilePath, "w"); - fprintf(statsDump, "Time\tRTT\tLRSeq\tLSSeq\tLASeq\tLostR\tLostS\tCWnd\tBitrate\tLoss%%\tJitter\tJDelay\tAJDelay\n"); + if(statsDump) + fprintf(statsDump, "Time\tRTT\tLRSeq\tLSSeq\tLASeq\tLostR\tLostS\tCWnd\tBitrate\tLoss%%\tJitter\tJDelay\tAJDelay\n"); + else + LOGW("Failed to open stats dump file %s for writing", cfg->statsDumpFilePath); } UpdateDataSavingState(); UpdateAudioBitrate(); @@ -2156,6 +2376,34 @@ std::string VoIPController::GetCurrentAudioOutputID(){ return currentAudioOutput; } +void VoIPController::SetProxy(int protocol, std::string address, uint16_t port, std::string username, std::string password){ + proxyProtocol=protocol; + proxyAddress=address; + proxyPort=port; + proxyUsername=username; + proxyPassword=password; +} + +void VoIPController::SendUdpPing(Endpoint *endpoint){ + if(endpoint->type!=EP_TYPE_UDP_RELAY) + return; + LOGV("Sending UDP ping to %s:%d", endpoint->address.ToString().c_str(), endpoint->port); + BufferOutputStream p(1024); + p.WriteBytes(endpoint->peerTag, 16); + p.WriteInt32(-1); + p.WriteInt32(-1); + p.WriteInt32(-1); + p.WriteInt32(-2); + p.WriteInt64(12345); + NetworkPacket pkt; + pkt.address=&endpoint->address; + pkt.port=endpoint->port; + pkt.protocol=PROTO_UDP; + pkt.data=p.GetBuffer(); + pkt.length=p.GetLength(); + udpSocket->Send(&pkt); +} + Endpoint::Endpoint(int64_t id, uint16_t port, IPv4Address& _address, IPv6Address& _v6address, char type, unsigned char peerTag[16]) : address(_address), v6address(_v6address){ this->id=id; this->port=port; @@ -2167,6 +2415,7 @@ Endpoint::Endpoint(int64_t id, uint16_t port, IPv4Address& _address, IPv6Address lastPingTime=0; averageRTT=0; memset(rtts, 0, sizeof(rtts)); + socket=NULL; } Endpoint::Endpoint() : address(0), v6address("::0") { @@ -2174,6 +2423,7 @@ Endpoint::Endpoint() : address(0), v6address("::0") { lastPingTime=0; averageRTT=0; memset(rtts, 0, sizeof(rtts)); + socket=NULL; } #if defined(__APPLE__) && TARGET_OS_IPHONE diff --git a/VoIPController.h b/VoIPController.h index 0517411..6237814 100644 --- a/VoIPController.h +++ b/VoIPController.h @@ -29,27 +29,13 @@ #include "CongestionControl.h" #include "NetworkSocket.h" -#define LIBTGVOIP_VERSION "0.4.2" - -#define PKT_INIT 1 -#define PKT_INIT_ACK 2 -#define PKT_STREAM_STATE 3 -#define PKT_STREAM_DATA 4 -#define PKT_UPDATE_STREAMS 5 -#define PKT_PING 6 -#define PKT_PONG 7 -#define PKT_STREAM_DATA_X2 8 -#define PKT_STREAM_DATA_X3 9 -#define PKT_LAN_ENDPOINT 10 -#define PKT_NETWORK_CHANGED 11 -#define PKT_SWITCH_PREF_RELAY 12 -#define PKT_SWITCH_TO_P2P 13 -#define PKT_NOP 14 +#define LIBTGVOIP_VERSION "1.0" #define STATE_WAIT_INIT 1 #define STATE_WAIT_INIT_ACK 2 #define STATE_ESTABLISHED 3 #define STATE_FAILED 4 +#define STATE_RECONNECTING 5 #define TGVOIP_ERROR_UNKNOWN 0 #define TGVOIP_ERROR_INCOMPATIBLE 1 @@ -69,56 +55,19 @@ #define NET_TYPE_DIALUP 10 #define NET_TYPE_OTHER_MOBILE 11 -#define IS_MOBILE_NETWORK(x) (x==NET_TYPE_GPRS || x==NET_TYPE_EDGE || x==NET_TYPE_3G || x==NET_TYPE_HSPA || x==NET_TYPE_LTE || x==NET_TYPE_OTHER_MOBILE) - -#define PROTOCOL_NAME 0x50567247 // "GrVP" in little endian (reversed here) -#define PROTOCOL_VERSION 3 -#define MIN_PROTOCOL_VERSION 3 - -#define STREAM_DATA_FLAG_LEN16 0x40 -#define STREAM_DATA_FLAG_HAS_MORE_FLAGS 0x80 - -#define STREAM_TYPE_AUDIO 1 -#define STREAM_TYPE_VIDEO 2 - -#define CODEC_OPUS 1 - #define EP_TYPE_UDP_P2P_INET 1 #define EP_TYPE_UDP_P2P_LAN 2 #define EP_TYPE_UDP_RELAY 3 #define EP_TYPE_TCP_RELAY 4 -/*flags:# voice_call_id:flags.2?int128 in_seq_no:flags.4?int out_seq_no:flags.4?int - * recent_received_mask:flags.5?int proto:flags.3?int extra:flags.1?string raw_data:flags.0?string*/ -#define PFLAG_HAS_DATA 1 -#define PFLAG_HAS_EXTRA 2 -#define PFLAG_HAS_CALL_ID 4 -#define PFLAG_HAS_PROTO 8 -#define PFLAG_HAS_SEQ 16 -#define PFLAG_HAS_RECENT_RECV 32 - -#define INIT_FLAG_DATA_SAVING_ENABLED 1 - #define DATA_SAVING_NEVER 0 #define DATA_SAVING_MOBILE 1 #define DATA_SAVING_ALWAYS 2 -#define TLID_DECRYPTED_AUDIO_BLOCK 0xDBF948C1 -#define TLID_SIMPLE_AUDIO_BLOCK 0xCC0D0E76 -#define TLID_UDP_REFLECTOR_PEER_INFO 0x27D9371C -#define PAD4(x) (4-(x+(x<=253 ? 1 : 0))%4) - #ifdef _WIN32 #undef GetCurrentTime #endif -inline int pad4(int x){ - int r=PAD4(x); - if(r==4) - return 0; - return r; -} - struct voip_stream_t{ int32_t userID; unsigned char id; @@ -192,6 +141,12 @@ inline bool seqgt(uint32_t s1, uint32_t s2){ namespace tgvoip{ + enum{ + PROXY_NONE=0, + PROXY_SOCKS5, + //PROXY_HTTP + }; + class Endpoint{ friend class VoIPController; public: @@ -209,6 +164,7 @@ private: uint32_t lastPingSeq; double rtts[6]; double averageRTT; + NetworkSocket* socket; }; class AudioDevice{ @@ -231,41 +187,159 @@ public: VoIPController(); ~VoIPController(); + /** + * Set the initial endpoints (relays) + * @param endpoints Endpoints converted from phone.PhoneConnection TL objects + * @param allowP2p Whether p2p connectivity is allowed + */ void SetRemoteEndpoints(std::vector endpoints, bool allowP2p); + /** + * Initialize and start all the internal threads + */ void Start(); + /** + * Initiate connection + */ void Connect(); Endpoint& GetRemoteEndpoint(); + /** + * Get the debug info string to be displayed in client UI + * @param buffer The buffer to put the string into + * @param len The length of the buffer + */ void GetDebugString(char* buffer, size_t len); + /** + * Notify the library of network type change + * @param type The new network type + */ void SetNetworkType(int type); + /** + * Get the average round-trip time for network packets + * @return + */ double GetAverageRTT(); + /** + * Set the function to be called whenever the connection state changes + * @param f + */ void SetStateCallback(void (*f)(VoIPController*, int)); static double GetCurrentTime(); + /** + * Use this field to store any of your context data associated with this call + */ void* implData; + /** + * + * @param mute + */ void SetMicMute(bool mute); + /** + * + * @param key + * @param isOutgoing + */ void SetEncryptionKey(char* key, bool isOutgoing); + /** + * + * @param cfg + */ void SetConfig(voip_config_t* cfg); float GetOutputLevel(); void DebugCtl(int request, int param); + /** + * + * @param stats + */ void GetStats(voip_stats_t* stats); + /** + * + * @return + */ int64_t GetPreferredRelayID(); + /** + * + * @return + */ int GetLastError(); + /** + * + */ static voip_crypto_functions_t crypto; + /** + * + * @return + */ static const char* GetVersion(); #ifdef TGVOIP_USE_AUDIO_SESSION void SetAcquireAudioSession(void (^)(void (^)())); void ReleaseAudioSession(void (^completion)()); #endif + /** + * + * @return + */ std::string GetDebugLog(); + /** + * + * @param buffer + */ void GetDebugLog(char* buffer); size_t GetDebugLogLength(); + /** + * + * @return + */ static std::vector EnumerateAudioInputs(); + /** + * + * @return + */ static std::vector EnumerateAudioOutputs(); + /** + * + * @param id + */ void SetCurrentAudioInput(std::string id); + /** + * + * @param id + */ void SetCurrentAudioOutput(std::string id); + /** + * + * @return + */ std::string GetCurrentAudioInputID(); + /** + * + * @return + */ std::string GetCurrentAudioOutputID(); + /** + * Set the proxy server to route the data through. Call this before connecting. + * @param protocol PROXY_NONE, PROXY_SOCKS4, or PROXY_SOCKS5 + * @param address IP address or domain name of the server + * @param port Port of the server + * @param username Username; empty string for anonymous + * @param password Password; empty string if none + */ + void SetProxy(int protocol, std::string address, uint16_t port, std::string username, std::string password); private: + struct PendingOutgoingPacket{ + uint32_t seq; + unsigned char type; + size_t len; + unsigned char* data; + Endpoint* endpoint; + }; + enum{ + UDP_UNKNOWN=0, + UDP_PING_SENT, + UDP_AVAILABIE, + UDP_NOT_AVAILABLE + }; + static void* StartRecvThread(void* arg); static void* StartSendThread(void* arg); static void* StartTickThread(void* arg); @@ -278,17 +352,18 @@ private: void SetState(int state); void UpdateAudioOutputState(); void SendInit(); - void SendInitAck(); + void InitUDPProxy(); void UpdateDataSavingState(); void KDF(unsigned char* msgKey, size_t x, unsigned char* aesKey, unsigned char* aesIv); - BufferOutputStream* GetOutgoingPacketBuffer(); - uint32_t WritePacketHeader(BufferOutputStream* s, unsigned char type, uint32_t length); + void WritePacketHeader(uint32_t seq, BufferOutputStream* s, unsigned char type, uint32_t length); static size_t AudioInputCallback(unsigned char* data, size_t length, void* param); void SendPublicEndpointsRequest(); void SendPublicEndpointsRequest(Endpoint& relay); Endpoint* GetEndpointByType(int type); void SendPacketReliably(unsigned char type, unsigned char* data, size_t len, double retryInterval, double timeout); + uint32_t GenerateOutSeq(); void LogDebugInfo(); + void SendUdpPing(Endpoint* endpoint); int state; std::vector endpoints; Endpoint* currentEndpoint; @@ -310,9 +385,8 @@ private: JitterBuffer* jitterBuffer; OpusDecoder* decoder; OpusEncoder* encoder; - BlockingQueue* sendQueue; + BlockingQueue* sendQueue; EchoCanceller* echoCanceller; - std::vector emptySendBuffers; tgvoip_mutex_t sendBufferMutex; tgvoip_mutex_t endpointsMutex; bool stopping; @@ -327,13 +401,10 @@ private: double rttHistory[32]; bool waitingForAcks; int networkType; - int audioPacketGrouping; - int audioPacketsWritten; int dontSendPackets; int lastError; bool micMuted; uint32_t maxBitrate; - BufferOutputStream* currentAudioPacket; void (*stateCallback)(VoIPController*, int); std::vector outgoingStreams; std::vector incomingStreams; @@ -359,13 +430,29 @@ private: bool receivedInitAck; std::vector debugLogs; bool isOutgoing; - tgvoip::NetworkSocket* socket; + NetworkSocket* udpSocket; + NetworkSocket* realUdpSocket; FILE* statsDump; std::string currentAudioInput; std::string currentAudioOutput; bool useTCP; + bool useUDP; bool didAddTcpRelays; - double enableTcpAt; + double setEstablishedAt; + SocketSelectCanceller* selectCanceller; + NetworkSocket* openingTcpSocket; + + BufferPool outgoingPacketsBufferPool; + int udpConnectivityState; + double lastUdpPingTime; + int udpPingCount; + + int proxyProtocol; + std::string proxyAddress; + uint16_t proxyPort; + std::string proxyUsername; + std::string proxyPassword; + IPv4Address* resolvedProxyAddress; /*** server config values ***/ uint32_t maxAudioBitrate; @@ -382,24 +469,25 @@ private: double relaySwitchThreshold; double p2pToRelaySwitchThreshold; double relayToP2pSwitchThreshold; + double reconnectingTimeout; #ifdef TGVOIP_USE_AUDIO_SESSION - void (^acquireAudioSession)(void (^)()); - bool needNotifyAcquiredAudioSession; +void (^acquireAudioSession)(void (^)()); +bool needNotifyAcquiredAudioSession; #endif public: #ifdef __APPLE__ - static double machTimebase; - static uint64_t machTimestart; +static double machTimebase; +static uint64_t machTimestart; #if TARGET_OS_IPHONE - // temporary fix for nasty linking errors - void SetRemoteEndpoints(voip_legacy_endpoint_t* buffer, size_t count, bool allowP2P); +// temporary fix for nasty linking errors +void SetRemoteEndpoints(voip_legacy_endpoint_t* buffer, size_t count, bool allowP2P); #endif #endif #ifdef _WIN32 - static int64_t win32TimeScale; - static bool didInitWin32TimeScale; +static int64_t win32TimeScale; +static bool didInitWin32TimeScale; #endif }; diff --git a/client/android/tg_voip_jni.cpp b/client/android/tg_voip_jni.cpp index 8ed226e..f97f3d4 100644 --- a/client/android/tg_voip_jni.cpp +++ b/client/android/tg_voip_jni.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -88,6 +87,18 @@ extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_native ((VoIPController*)(intptr_t)inst)->Connect(); } +extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeSetProxy(JNIEnv* env, jobject thiz, jlong inst, jstring _address, jint port, jstring _username, jstring _password){ + const char* address=env->GetStringUTFChars(_address, NULL); + const char* username=_username ? env->GetStringUTFChars(_username, NULL) : NULL; + const char* password=_password ? env->GetStringUTFChars(_password, NULL) : NULL; + ((VoIPController*)(intptr_t)inst)->SetProxy(PROXY_SOCKS5, address, (uint16_t)port, username ? username : "", password ? password : ""); + env->ReleaseStringUTFChars(_address, address); + if(username) + env->ReleaseStringUTFChars(_username, username); + if(password) + env->ReleaseStringUTFChars(_password, password); +} + extern "C" JNIEXPORT void Java_org_telegram_messenger_voip_VoIPController_nativeSetEncryptionKey(JNIEnv* env, jobject thiz, jlong inst, jbyteArray key, jboolean isOutgoing){ jbyte* akey=env->GetByteArrayElements(key, NULL); ((VoIPController*)(intptr_t)inst)->SetEncryptionKey((char *) akey, isOutgoing); diff --git a/libtgvoip.WP81.vcxproj b/libtgvoip.WP81.vcxproj index cf5f90e..4c4993e 100644 --- a/libtgvoip.WP81.vcxproj +++ b/libtgvoip.WP81.vcxproj @@ -132,6 +132,7 @@ /bigobj %(AdditionalOptions) true webrtc_dsp;../TelegramClient/TelegramClient.Opus/opus/include;%(AdditionalIncludeDirectories) + true Console diff --git a/libtgvoip.xcodeproj/project.pbxproj b/libtgvoip.xcodeproj/project.pbxproj index 1232c2a..97a3fd3 100644 --- a/libtgvoip.xcodeproj/project.pbxproj +++ b/libtgvoip.xcodeproj/project.pbxproj @@ -56,6 +56,8 @@ 69791A4E1EE8262400BB85FB /* NetworkSocketPosix.h in Headers */ = {isa = PBXBuildFile; fileRef = 69791A4C1EE8262400BB85FB /* NetworkSocketPosix.h */; }; 69791A571EE8272A00BB85FB /* Resampler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 69791A551EE8272A00BB85FB /* Resampler.cpp */; }; 69791A581EE8272A00BB85FB /* Resampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 69791A561EE8272A00BB85FB /* Resampler.h */; }; + 69960A041EF85C2900F9D091 /* DarwinSpecific.h in Headers */ = {isa = PBXBuildFile; fileRef = 69960A021EF85C2900F9D091 /* DarwinSpecific.h */; }; + 69960A051EF85C2900F9D091 /* DarwinSpecific.mm in Sources */ = {isa = PBXBuildFile; fileRef = 69960A031EF85C2900F9D091 /* DarwinSpecific.mm */; }; 69A6DD941E95EC7700000E69 /* array_view.h in Headers */ = {isa = PBXBuildFile; fileRef = 69A6DD011E95EC7700000E69 /* array_view.h */; }; 69A6DD951E95EC7700000E69 /* atomicops.h in Headers */ = {isa = PBXBuildFile; fileRef = 69A6DD021E95EC7700000E69 /* atomicops.h */; }; 69A6DD961E95EC7700000E69 /* basictypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 69A6DD031E95EC7700000E69 /* basictypes.h */; }; @@ -235,6 +237,13 @@ remoteGlobalIDString = D020FB0A1D99637100F279AA; remoteInfo = LegacyDatabase; }; + 69960A0D1EF85C2900F9D091 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 692AB9071E675E8800706ACC /* Telegraph.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 099120C01EEAA63400F1366E; + remoteInfo = Widget; + }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ @@ -288,6 +297,8 @@ 69791A4C1EE8262400BB85FB /* NetworkSocketPosix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NetworkSocketPosix.h; path = os/posix/NetworkSocketPosix.h; sourceTree = SOURCE_ROOT; }; 69791A551EE8272A00BB85FB /* Resampler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Resampler.cpp; sourceTree = ""; }; 69791A561EE8272A00BB85FB /* Resampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Resampler.h; sourceTree = ""; }; + 69960A021EF85C2900F9D091 /* DarwinSpecific.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DarwinSpecific.h; sourceTree = ""; }; + 69960A031EF85C2900F9D091 /* DarwinSpecific.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DarwinSpecific.mm; sourceTree = ""; }; 69A6DD011E95EC7700000E69 /* array_view.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = array_view.h; sourceTree = ""; }; 69A6DD021E95EC7700000E69 /* atomicops.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = atomicops.h; sourceTree = ""; }; 69A6DD031E95EC7700000E69 /* basictypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = basictypes.h; sourceTree = ""; }; @@ -522,6 +533,8 @@ 692AB8C31E6759DD00706ACC /* AudioUnitIO.h */, 692AB8C41E6759DD00706ACC /* TGLogWrapper.h */, 692AB8C51E6759DD00706ACC /* TGLogWrapper.m */, + 69960A021EF85C2900F9D091 /* DarwinSpecific.h */, + 69960A031EF85C2900F9D091 /* DarwinSpecific.mm */, ); path = darwin; sourceTree = ""; @@ -546,6 +559,7 @@ 692AB9171E675E8800706ACC /* watchkitapp Extension.appex */, 692AB9191E675E8800706ACC /* SiriIntents.appex */, 692AB91B1E675E8800706ACC /* LegacyDatabase.framework */, + 69960A0E1EF85C2900F9D091 /* Widget.appex */, ); name = Products; sourceTree = ""; @@ -930,6 +944,7 @@ 69A6DD9A1E95EC7700000E69 /* safe_compare.h in Headers */, 69A6DDEF1E95EC7700000E69 /* digital_agc.h in Headers */, 69A6DDF21E95EC7700000E69 /* apm_data_dumper.h in Headers */, + 69960A041EF85C2900F9D091 /* DarwinSpecific.h in Headers */, 69A6DDC21E95EC7700000E69 /* spl_inl_mips.h in Headers */, 69791A581EE8272A00BB85FB /* Resampler.h in Headers */, 692AB8DB1E6759DD00706ACC /* EchoCanceller.h in Headers */, @@ -1045,6 +1060,13 @@ remoteRef = 692AB91A1E675E8800706ACC /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 69960A0E1EF85C2900F9D091 /* Widget.appex */ = { + isa = PBXReferenceProxy; + fileType = "wrapper.app-extension"; + path = Widget.appex; + remoteRef = 69960A0D1EF85C2900F9D091 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ @@ -1075,6 +1097,7 @@ 69A6DDA41E95EC7700000E69 /* fft4g.c in Sources */, 692AB9021E6759DD00706ACC /* VoIPController.cpp in Sources */, 69A6DDB61E95EC7700000E69 /* energy.c in Sources */, + 69960A051EF85C2900F9D091 /* DarwinSpecific.mm in Sources */, 69A6DDA11E95EC7700000E69 /* audio_util.cc in Sources */, 69A6DDE31E95EC7700000E69 /* echo_cancellation.cc in Sources */, 69A6DDD71E95EC7700000E69 /* sqrt_of_one_minus_x_squared.c in Sources */, @@ -1287,7 +1310,6 @@ MACH_O_TYPE = staticlib; MACOSX_DEPLOYMENT_TARGET = 10.6; OTHER_CFLAGS = ( - "-DTGVOIP_USE_AUDIO_SESSION", "-DTGVOIP_USE_CUSTOM_CRYPTO", "-DWEBRTC_APM_DEBUG_DUMP=0", "-DWEBRTC_POSIX", @@ -1323,7 +1345,6 @@ MACH_O_TYPE = staticlib; MACOSX_DEPLOYMENT_TARGET = 10.6; OTHER_CFLAGS = ( - "-DTGVOIP_USE_AUDIO_SESSION", "-DTGVOIP_USE_CUSTOM_CRYPTO", "-DWEBRTC_APM_DEBUG_DUMP=0", "-DWEBRTC_POSIX", @@ -1410,7 +1431,6 @@ MACH_O_TYPE = staticlib; MACOSX_DEPLOYMENT_TARGET = 10.6; OTHER_CFLAGS = ( - "-DTGVOIP_USE_AUDIO_SESSION", "-DTGVOIP_USE_CUSTOM_CRYPTO", "-DWEBRTC_APM_DEBUG_DUMP=0", "-DWEBRTC_POSIX", @@ -1491,7 +1511,6 @@ MACH_O_TYPE = staticlib; MACOSX_DEPLOYMENT_TARGET = 10.6; OTHER_CFLAGS = ( - "-DTGVOIP_USE_AUDIO_SESSION", "-DTGVOIP_USE_CUSTOM_CRYPTO", "-DWEBRTC_APM_DEBUG_DUMP=0", "-DWEBRTC_POSIX", diff --git a/logging.cpp b/logging.cpp index 064d792..bc90dcb 100644 --- a/logging.cpp +++ b/logging.cpp @@ -19,10 +19,8 @@ #ifdef __APPLE__ #include -#if TARGET_OS_OSX #include "os/darwin/DarwinSpecific.h" #endif -#endif FILE* tgvoipLogFile=NULL; @@ -69,11 +67,17 @@ void tgvoip_log_file_write_header(){ char systemVersion[128]; snprintf(systemVersion, sizeof(systemVersion), "%s %s (%s)", sysname.sysname, sysname.release, sysname.version); #endif -#elif defined(__APPLE__) && TARGET_OS_OSX +#elif defined(__APPLE__) char osxVer[128]; tgvoip::DarwinSpecific::GetSystemName(osxVer, sizeof(osxVer)); char systemVersion[128]; +#if TARGET_OS_OSX snprintf(systemVersion, sizeof(systemVersion), "OS X %s", osxVer); +#elif TARGET_OS_IPHONE + snprintf(systemVersion, sizeof(systemVersion), "iOS %s", osxVer); +#else + snprintf(systemVersion, sizeof(systemVersion), "Unknown Darwin %s", osxVer); +#endif #else const char* systemVersion="Unknown OS"; #endif diff --git a/logging.h b/logging.h index f850db3..b777756 100644 --- a/logging.h +++ b/logging.h @@ -37,11 +37,11 @@ void tgvoip_log_file_write_header(); #include "os/darwin/TGLogWrapper.h" -#define LOGV(msg, ...) __tgvoip_call_tglog("V/tgvoip: " msg, ##__VA_ARGS__) -#define LOGD(msg, ...) __tgvoip_call_tglog("D/tgvoip: " msg, ##__VA_ARGS__) -#define LOGI(msg, ...) __tgvoip_call_tglog("I/tgvoip: " msg, ##__VA_ARGS__) -#define LOGW(msg, ...) __tgvoip_call_tglog("W/tgvoip: " msg, ##__VA_ARGS__) -#define LOGE(msg, ...) __tgvoip_call_tglog("E/tgvoip: " msg, ##__VA_ARGS__) +#define LOGV(msg, ...) {__tgvoip_call_tglog("V/tgvoip: " msg, ##__VA_ARGS__); tgvoip_log_file_printf('V', msg, ##__VA_ARGS__);} +#define LOGD(msg, ...) {__tgvoip_call_tglog("D/tgvoip: " msg, ##__VA_ARGS__); tgvoip_log_file_printf('D', msg, ##__VA_ARGS__);} +#define LOGI(msg, ...) {__tgvoip_call_tglog("I/tgvoip: " msg, ##__VA_ARGS__); tgvoip_log_file_printf('I', msg, ##__VA_ARGS__);} +#define LOGW(msg, ...) {__tgvoip_call_tglog("W/tgvoip: " msg, ##__VA_ARGS__); tgvoip_log_file_printf('W', msg, ##__VA_ARGS__);} +#define LOGE(msg, ...) {__tgvoip_call_tglog("E/tgvoip: " msg, ##__VA_ARGS__); tgvoip_log_file_printf('E', msg, ##__VA_ARGS__);} #elif defined(_WIN32) && defined(_DEBUG) diff --git a/os/darwin/AudioUnitIO.cpp b/os/darwin/AudioUnitIO.cpp index be3fd89..334aba6 100644 --- a/os/darwin/AudioUnitIO.cpp +++ b/os/darwin/AudioUnitIO.cpp @@ -33,8 +33,13 @@ AudioUnitIO::AudioUnitIO(){ inBufferList.mBuffers[0].mData=malloc(10240); inBufferList.mBuffers[0].mDataByteSize=10240; inBufferList.mNumberBuffers=1; +#ifdef TGVOIP_USE_AUDIO_SESSION if(haveAudioSession) ProcessAudioSessionAcquired(); +#else + haveAudioSession=true; + ProcessAudioSessionAcquired(); +#endif } AudioUnitIO::~AudioUnitIO(){ @@ -98,9 +103,11 @@ void AudioUnitIO::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_ if(configured) return; +#ifdef TGVOIP_USE_AUDIO_SESSION runFakeIO=true; start_thread(fakeIOThread, AudioUnitIO::StartFakeIOThread, this); set_thread_priority(fakeIOThread, get_thread_max_priority()); +#endif if(haveAudioSession){ ActuallyConfigure(sampleRate, bitsPerSample, channels); diff --git a/os/darwin/TGLogWrapper.h b/os/darwin/TGLogWrapper.h index 64182dc..8f11177 100644 --- a/os/darwin/TGLogWrapper.h +++ b/os/darwin/TGLogWrapper.h @@ -11,7 +11,7 @@ extern "C" { #endif -void __tgvoip_call_tglog(char* format, ...); +void __tgvoip_call_tglog(const char* format, ...); #if defined __cplusplus }; diff --git a/os/darwin/TGLogWrapper.m b/os/darwin/TGLogWrapper.m index 9cf316b..6affe3d 100644 --- a/os/darwin/TGLogWrapper.m +++ b/os/darwin/TGLogWrapper.m @@ -2,9 +2,9 @@ #import -void __tgvoip_call_tglog(char* format, ...){ +void __tgvoip_call_tglog(const char* format, ...){ va_list args; va_start(args, format); - TGLogv([[NSString alloc]initWithCString:format], args); + TGLogv([[NSString alloc]initWithUTF8String:format], args); va_end(args); } diff --git a/os/posix/NetworkSocketPosix.cpp b/os/posix/NetworkSocketPosix.cpp index 2716d83..7fa7063 100644 --- a/os/posix/NetworkSocketPosix.cpp +++ b/os/posix/NetworkSocketPosix.cpp @@ -21,7 +21,7 @@ using namespace tgvoip; -NetworkSocketPosix::NetworkSocketPosix() : lastRecvdV4(0), lastRecvdV6("::0"){ +NetworkSocketPosix::NetworkSocketPosix(NetworkProtocol protocol) : NetworkSocket(protocol), lastRecvdV4(0), lastRecvdV6("::0"){ needUpdateNat64Prefix=true; nat64Present=false; switchToV6at=0; @@ -29,16 +29,13 @@ NetworkSocketPosix::NetworkSocketPosix() : lastRecvdV4(0), lastRecvdV6("::0"){ useTCP=false; closing=false; - int p[2]; - int pipeRes=pipe(p); - assert(pipeRes==0); - pipeRead=p[0]; - pipeWrite=p[1]; + tcpConnectedAddress=NULL; + tcpConnectedPort=0; } NetworkSocketPosix::~NetworkSocketPosix(){ - close(pipeRead); - close(pipeWrite); + if(tcpConnectedAddress) + delete tcpConnectedAddress; } void NetworkSocketPosix::SetMaxPriority(){ @@ -67,131 +64,66 @@ 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); + int res; + if(protocol==PROTO_UDP){ + sockaddr_in6 addr; + 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(needUpdateNat64Prefix && !isV4Available && VoIPController::GetCurrentTime()>switchToV6at && switchToV6at!=0){ + LOGV("Updating NAT64 prefix"); + nat64Present=false; + addrinfo *addr0; + int res=getaddrinfo("ipv4only.arpa", NULL, NULL, &addr0); 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; + LOGW("Error updating NAT64 prefix: %d / %s", res, gai_strerror(res)); }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){ - if(needUpdateNat64Prefix && !isV4Available && VoIPController::GetCurrentTime()>switchToV6at && switchToV6at!=0){ - LOGV("Updating NAT64 prefix"); - nat64Present=false; - addrinfo* addr0; - int res=getaddrinfo("ipv4only.arpa", NULL, NULL, &addr0); - if(res!=0){ - LOGW("Error updating NAT64 prefix: %d / %s", res, gai_strerror(res)); - }else{ - addrinfo* addrPtr; - unsigned char* addr170=NULL; - unsigned char* addr171=NULL; - for(addrPtr=addr0;addrPtr;addrPtr=addrPtr->ai_next){ - if(addrPtr->ai_family==AF_INET6){ - sockaddr_in6* translatedAddr=(sockaddr_in6*)addrPtr->ai_addr; - uint32_t v4part=*((uint32_t*)&translatedAddr->sin6_addr.s6_addr[12]); - if(v4part==0xAA0000C0 && !addr170){ - addr170=translatedAddr->sin6_addr.s6_addr; + addrinfo *addrPtr; + unsigned char *addr170=NULL; + unsigned char *addr171=NULL; + for(addrPtr=addr0; addrPtr; addrPtr=addrPtr->ai_next){ + if(addrPtr->ai_family==AF_INET6){ + sockaddr_in6 *translatedAddr=(sockaddr_in6 *) addrPtr->ai_addr; + uint32_t v4part=*((uint32_t *) &translatedAddr->sin6_addr.s6_addr[12]); + if(v4part==0xAA0000C0 && !addr170){ + addr170=translatedAddr->sin6_addr.s6_addr; + } + if(v4part==0xAB0000C0 && !addr171){ + addr171=translatedAddr->sin6_addr.s6_addr; + } + char buf[INET6_ADDRSTRLEN]; + LOGV("Got translated address: %s", inet_ntop(AF_INET6, &translatedAddr->sin6_addr, buf, sizeof(buf))); } - if(v4part==0xAB0000C0 && !addr171){ - addr171=translatedAddr->sin6_addr.s6_addr; - } - char buf[INET6_ADDRSTRLEN]; - LOGV("Got translated address: %s", inet_ntop(AF_INET6, &translatedAddr->sin6_addr, buf, sizeof(buf))); } + if(addr170 && addr171 && memcmp(addr170, addr171, 12)==0){ + nat64Present=true; + memcpy(nat64Prefix, addr170, 12); + char buf[INET6_ADDRSTRLEN]; + LOGV("Found nat64 prefix from %s", inet_ntop(AF_INET6, addr170, buf, sizeof(buf))); + }else{ + LOGV("Didn't find nat64"); + } + freeaddrinfo(addr0); } - if(addr170 && addr171 && memcmp(addr170, addr171, 12)==0){ - nat64Present=true; - memcpy(nat64Prefix, addr170, 12); - char buf[INET6_ADDRSTRLEN]; - LOGV("Found nat64 prefix from %s", inet_ntop(AF_INET6, addr170, buf, sizeof(buf))); - }else{ - LOGV("Didn't find nat64"); - } - freeaddrinfo(addr0); + needUpdateNat64Prefix=false; } - needUpdateNat64Prefix=false; - } - memset(&addr, 0, sizeof(sockaddr_in6)); - addr.sin6_family=AF_INET6; - *((uint32_t*)&addr.sin6_addr.s6_addr[12])=v4addr->GetAddress(); - if(nat64Present) - memcpy(addr.sin6_addr.s6_addr, nat64Prefix, 12); - else - addr.sin6_addr.s6_addr[11]=addr.sin6_addr.s6_addr[10]=0xFF; + memset(&addr, 0, sizeof(sockaddr_in6)); + addr.sin6_family=AF_INET6; + *((uint32_t *) &addr.sin6_addr.s6_addr[12])=v4addr->GetAddress(); + if(nat64Present) + memcpy(addr.sin6_addr.s6_addr, nat64Prefix, 12); + else + addr.sin6_addr.s6_addr[11]=addr.sin6_addr.s6_addr[10]=0xFF; + }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); + res=sendto(fd, packet->data, packet->length, 0, (const sockaddr *) &addr, sizeof(addr)); }else{ - IPv6Address* v6addr=dynamic_cast(packet->address); - assert(v6addr!=NULL); - memcpy(addr.sin6_addr.s6_addr, v6addr->GetAddress(), 16); + res=send(fd, packet->data, packet->length, 0); } - addr.sin6_port=htons(packet->port); - char buf[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, &addr.sin6_addr, buf, sizeof(buf)); - int res=sendto(fd, packet->data, packet->length, 0, (const sockaddr *) &addr, sizeof(addr)); if(res<0){ LOGE("error sending: %d / %s", errno, strerror(errno)); if(errno==ENETUNREACH && !isV4Available && VoIPController::GetCurrentTime()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); + if(protocol==PROTO_UDP){ + 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; } - - 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->protocol=PROTO_UDP; + packet->port=ntohs(srcAddr.sin6_port); + }else if(protocol==PROTO_TCP){ + int res=recv(fd, packet->data, packet->length, 0); + if(res<=0){ + LOGE("Error receiving from TCP socket: %d / %s", errno, strerror(errno)); + failed=true; + }else{ + packet->length=(size_t)res; + packet->address=tcpConnectedAddress; + packet->port=tcpConnectedPort; + packet->protocol=PROTO_TCP; } } } void NetworkSocketPosix::Open(){ + if(protocol!=PROTO_UDP) + return; fd=socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); if(fd<0){ LOGE("error creating socket: %d / %s", errno, strerror(errno)); + failed=true; + return; } int flag=0; int res=setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)); if(res<0){ LOGE("error enabling dual stack socket: %d / %s", errno, strerror(errno)); + failed=true; + return; } SetMaxPriority(); @@ -352,6 +215,7 @@ void NetworkSocketPosix::Open(){ if(res<0){ LOGE("error binding to port %u: %d / %s", ntohs(addr.sin6_port), errno, strerror(errno)); //SetState(STATE_FAILED); + failed=true; return; } } @@ -367,14 +231,61 @@ void NetworkSocketPosix::Open(){ void NetworkSocketPosix::Close(){ closing=true; - char c=1; - write(pipeWrite, &c, 1); + failed=true; 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::Connect(NetworkAddress *address, uint16_t port){ + IPv4Address* v4addr=dynamic_cast(address); + IPv6Address* v6addr=dynamic_cast(address); + sockaddr_in v4; + sockaddr_in6 v6; + sockaddr* addr=NULL; + size_t addrLen=0; + if(v4addr){ + v4.sin_family=AF_INET; + v4.sin_addr.s_addr=v4addr->GetAddress(); + v4.sin_port=htons(port); + addr=reinterpret_cast(&v4); + addrLen=sizeof(v4); + }else if(v6addr){ + v6.sin6_family=AF_INET6; + memcpy(v6.sin6_addr.s6_addr, v6addr->GetAddress(), 16); + v6.sin6_flowinfo=0; + v6.sin6_scope_id=0; + v6.sin6_port=htons(port); + addr=reinterpret_cast(&v6); + addrLen=sizeof(v6); + }else{ + LOGE("Unknown address type in TCP connect"); + failed=true; + return; } + fd=socket(addr->sa_family, SOCK_STREAM, IPPROTO_TCP); + if(fd==0){ + LOGE("Error creating TCP socket: %d / %s", errno, strerror(errno)); + failed=true; + return; + } + int opt=1; + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)); + timeval timeout; + timeout.tv_sec=5; + timeout.tv_usec=0; + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + timeout.tv_sec=60; + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + int res=connect(fd, (const sockaddr*) addr, addrLen); + if(res!=0){ + LOGW("error connecting TCP socket to %s:%u: %d / %s; %d / %s", address->ToString().c_str(), port, res, strerror(res), errno, strerror(errno)); + close(fd); + failed=true; + return; + } + tcpConnectedAddress=v4addr ? (NetworkAddress*)new IPv4Address(*v4addr) : (NetworkAddress*)new IPv6Address(*v6addr); + tcpConnectedPort=port; + LOGI("successfully connected to %s:%d", tcpConnectedAddress->ToString().c_str(), tcpConnectedPort); } void NetworkSocketPosix::OnActiveInterfaceChanged(){ @@ -469,3 +380,139 @@ void NetworkSocketPosix::StringToV6Address(std::string address, unsigned char *o inet_pton(AF_INET6, address.c_str(), &addr); memcpy(out, addr.s6_addr, 16); } + +IPv4Address *NetworkSocketPosix::ResolveDomainName(std::string name){ + addrinfo* addr0; + IPv4Address* ret=NULL; + int res=getaddrinfo(name.c_str(), NULL, NULL, &addr0); + if(res!=0){ + LOGW("Error updating NAT64 prefix: %d / %s", res, gai_strerror(res)); + }else{ + addrinfo* addrPtr; + for(addrPtr=addr0;addrPtr;addrPtr=addrPtr->ai_next){ + if(addrPtr->ai_family==AF_INET){ + sockaddr_in* addr=(sockaddr_in*)addrPtr->ai_addr; + ret=new IPv4Address(addr->sin_addr.s_addr); + break; + } + } + freeaddrinfo(addr0); + } + return ret; +} + +NetworkAddress *NetworkSocketPosix::GetConnectedAddress(){ + return tcpConnectedAddress; +} + +uint16_t NetworkSocketPosix::GetConnectedPort(){ + return tcpConnectedPort; +} + +void NetworkSocketPosix::SetTimeouts(int sendTimeout, int recvTimeout){ + timeval timeout; + timeout.tv_sec=sendTimeout; + timeout.tv_usec=0; + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + timeout.tv_sec=recvTimeout; + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); +} + +bool NetworkSocketPosix::Select(std::vector &readFds, std::vector &errorFds, SocketSelectCanceller* _canceller){ + fd_set readSet; + fd_set errorSet; + FD_ZERO(&readSet); + FD_ZERO(&errorSet); + SocketSelectCancellerPosix* canceller=dynamic_cast(_canceller); + if(canceller) + FD_SET(canceller->pipeRead, &readSet); + + int maxfd=canceller ? canceller->pipeRead : 0; + + for(std::vector::iterator itr=readFds.begin();itr!=readFds.end();++itr){ + int sfd=GetDescriptorFromSocket(*itr); + if(sfd==0){ + LOGW("can't select on one of sockets because it's not a NetworkSocketPosix instance"); + continue; + } + FD_SET(sfd, &readSet); + if(maxfd::iterator itr=errorFds.begin();itr!=errorFds.end();++itr){ + int sfd=GetDescriptorFromSocket(*itr); + if(sfd==0){ + LOGW("can't select on one of sockets because it's not a NetworkSocketPosix instance"); + continue; + } + anyFailed |= (*itr)->IsFailed(); + FD_SET(sfd, &errorSet); + if(maxfdpipeRead, &readSet) && !anyFailed){ + char c; + read(canceller->pipeRead, &c, 1); + return false; + }else if(anyFailed){ + FD_ZERO(&readSet); + FD_ZERO(&errorSet); + } + + std::vector::iterator itr=readFds.begin(); + while(itr!=readFds.end()){ + int sfd=GetDescriptorFromSocket(*itr); + if(sfd==0 || !FD_ISSET(sfd, &readSet)){ + itr=readFds.erase(itr); + }else{ + ++itr; + } + } + + itr=errorFds.begin(); + while(itr!=errorFds.end()){ + int sfd=GetDescriptorFromSocket(*itr); + if((sfd==0 || !FD_ISSET(sfd, &errorSet)) && !(*itr)->IsFailed()){ + itr=errorFds.erase(itr); + }else{ + ++itr; + } + } + //LOGV("select fds left: read=%d, error=%d", readFds.size(), errorFds.size()); + + return readFds.size()>0 || errorFds.size()>0; +} + +SocketSelectCancellerPosix::SocketSelectCancellerPosix(){ + int p[2]; + int pipeRes=pipe(p); + assert(pipeRes==0); + pipeRead=p[0]; + pipeWrite=p[1]; +} + +SocketSelectCancellerPosix::~SocketSelectCancellerPosix(){ + close(pipeRead); + close(pipeWrite); +} + +void SocketSelectCancellerPosix::CancelSelect(){ + char c=1; + write(pipeWrite, &c, 1); +} + +int NetworkSocketPosix::GetDescriptorFromSocket(NetworkSocket *socket){ + NetworkSocketPosix* sp=dynamic_cast(socket); + if(sp) + return sp->fd; + NetworkSocketWrapper* sw=dynamic_cast(socket); + if(sw) + return GetDescriptorFromSocket(sw->GetWrapped()); + return 0; +} diff --git a/os/posix/NetworkSocketPosix.h b/os/posix/NetworkSocketPosix.h index e57d703..86dbe31 100644 --- a/os/posix/NetworkSocketPosix.h +++ b/os/posix/NetworkSocketPosix.h @@ -14,22 +14,26 @@ namespace tgvoip { -struct TCPSocket{ - int fd; - IPv4Address address; - uint16_t port; - TCPO2State recvState; - TCPO2State sendState; +class SocketSelectCancellerPosix : public SocketSelectCanceller{ +friend class NetworkSocketPosix; +public: + SocketSelectCancellerPosix(); + virtual ~SocketSelectCancellerPosix(); + virtual void CancelSelect(); +private: + int pipeRead; + int pipeWrite; }; class NetworkSocketPosix : public NetworkSocket{ public: - NetworkSocketPosix(); + NetworkSocketPosix(NetworkProtocol protocol); virtual ~NetworkSocketPosix(); virtual void Send(NetworkPacket* packet); virtual void Receive(NetworkPacket* packet); virtual void Open(); virtual void Close(); + virtual void Connect(NetworkAddress* address, uint16_t port); virtual std::string GetLocalInterfaceInfo(IPv4Address* v4addr, IPv6Address* v6addr); virtual void OnActiveInterfaceChanged(); virtual uint16_t GetLocalPort(); @@ -38,13 +42,21 @@ public: static std::string V6AddressToString(unsigned char address[16]); static uint32_t StringToV4Address(std::string address); static void StringToV6Address(std::string address, unsigned char* out); + static IPv4Address* ResolveDomainName(std::string name); + static bool Select(std::vector& readFds, std::vector& errorFds, SocketSelectCanceller* canceller); + + virtual NetworkAddress *GetConnectedAddress(); + + virtual uint16_t GetConnectedPort(); + + virtual void SetTimeouts(int sendTimeout, int recvTimeout); protected: virtual void SetMaxPriority(); private: + static int GetDescriptorFromSocket(NetworkSocket* socket); int fd; - std::vector tcpSockets; bool needUpdateNat64Prefix; bool nat64Present; double switchToV6at; @@ -53,8 +65,8 @@ private: bool closing; IPv4Address lastRecvdV4; IPv6Address lastRecvdV6; - int pipeRead; - int pipeWrite; + NetworkAddress* tcpConnectedAddress; + uint16_t tcpConnectedPort; }; } diff --git a/os/windows/AudioInputWASAPI.cpp b/os/windows/AudioInputWASAPI.cpp index 295435b..ffe9cc2 100644 --- a/os/windows/AudioInputWASAPI.cpp +++ b/os/windows/AudioInputWASAPI.cpp @@ -347,10 +347,8 @@ void AudioInputWASAPI::RunThread() { }else if(waitResult==WAIT_OBJECT_0+2){ // audioSamplesReadyEvent if(!audioClient) continue; - if(bufferSize==0){ - res=audioClient->GetBufferSize(&bufferSize); - CHECK_RES(res, "audioClient->GetBufferSize"); - } + res=captureClient->GetNextPacketSize(&bufferSize); + CHECK_RES(res, "captureClient->GetNextPacketSize"); BYTE* data; uint32_t framesAvailable=bufferSize; DWORD flags; diff --git a/os/windows/CXWrapper.cpp b/os/windows/CXWrapper.cpp index dc380ca..424623b 100644 --- a/os/windows/CXWrapper.cpp +++ b/os/windows/CXWrapper.cpp @@ -30,9 +30,9 @@ SymmetricKeyAlgorithmProvider^ MicrosoftCryptoImpl::aesKeyProvider; };*/ VoIPControllerWrapper::VoIPControllerWrapper(){ - VoIPController::crypto.aes_ctr_encrypt=MicrosoftCryptoImpl::AesCtrEncrypt; VoIPController::crypto.aes_ige_decrypt=MicrosoftCryptoImpl::AesIgeDecrypt; VoIPController::crypto.aes_ige_encrypt=MicrosoftCryptoImpl::AesIgeEncrypt; + VoIPController::crypto.aes_ctr_encrypt = MicrosoftCryptoImpl::AesCtrEncrypt; VoIPController::crypto.sha1=MicrosoftCryptoImpl::SHA1; VoIPController::crypto.sha256=MicrosoftCryptoImpl::SHA256; VoIPController::crypto.rand_bytes=MicrosoftCryptoImpl::RandBytes; @@ -55,29 +55,28 @@ void VoIPControllerWrapper::Connect(){ controller->Connect(); } -void VoIPControllerWrapper::SetPublicEndpoints(Windows::Foundation::Collections::IIterable^ endpoints, bool allowP2P){ - Windows::Foundation::Collections::IIterator^ iterator=endpoints->First(); +void VoIPControllerWrapper::SetPublicEndpoints(const Platform::Array^ endpoints, bool allowP2P){ std::vector eps; - while(iterator->HasCurrent){ - libtgvoip::Endpoint^ _ep=iterator->Current; + for (int i = 0; i < endpoints->Length; i++) + { + libtgvoip::Endpoint^ _ep = endpoints[i]; tgvoip::Endpoint ep; - ep.id=_ep->id; - ep.type=EP_TYPE_UDP_RELAY; + ep.id = _ep->id; + ep.type = EP_TYPE_UDP_RELAY; char buf[128]; - if(_ep->ipv4){ + if (_ep->ipv4){ WideCharToMultiByte(CP_UTF8, 0, _ep->ipv4->Data(), -1, buf, sizeof(buf), NULL, NULL); - ep.address=IPv4Address(buf); + ep.address = IPv4Address(buf); } - if(_ep->ipv6){ + if (_ep->ipv6){ WideCharToMultiByte(CP_UTF8, 0, _ep->ipv6->Data(), -1, buf, sizeof(buf), NULL, NULL); - ep.v6address=IPv6Address(buf); + ep.v6address = IPv6Address(buf); } - ep.port=_ep->port; - if(_ep->peerTag->Length!=16) + ep.port = _ep->port; + if (_ep->peerTag->Length != 16) throw ref new Platform::InvalidArgumentException("Peer tag must be exactly 16 bytes long"); memcpy(ep.peerTag, _ep->peerTag->Data, 16); eps.push_back(ep); - iterator->MoveNext(); } controller->SetRemoteEndpoints(eps, allowP2P); } @@ -181,11 +180,6 @@ void VoIPControllerWrapper::UpdateServerConfig(Platform::String^ json){ } void VoIPControllerWrapper::SwitchSpeaker(bool external){ -#if WINAPI_FAMILY!=WINAPI_FAMILY_PHONE_APP - if (!Windows::Foundation::Metadata::ApiInformation::IsApiContractPresent("Windows.Phone.PhoneContract", 1)){ - return; - } -#endif auto routingManager = AudioRoutingManager::GetDefault(); if (external){ routingManager->SetAudioEndpoint(AudioRoutingEndpoint::Speakerphone); @@ -200,41 +194,6 @@ void VoIPControllerWrapper::SwitchSpeaker(bool external){ } } -#define GETU32(pt) (((uint32_t)(pt)[0] << 24) ^ ((uint32_t)(pt)[1] << 16) ^ ((uint32_t)(pt)[2] << 8) ^ ((uint32_t)(pt)[3])) -#define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } typedef uint8_t u8; -#define L_ENDIAN /* increment counter (128-bit int) by 2^64 */ -static void AES_ctr128_inc(unsigned char *counter) -{ - unsigned long c; /* Grab 3rd dword of counter and increment */ -#ifdef L_ENDIAN c = GETU32(counter + 8); c++; PUTU32(counter + 8, c); -#else c = GETU32(counter + 4); c++; PUTU32(counter + 4, c); -#endif /* if no overflow, we're done */ if (c) return; /* Grab top dword of counter and increment */ -#ifdef L_ENDIAN c = GETU32(counter + 12); c++; PUTU32(counter + 12, c); -#else c = GETU32(counter + 0); c++; PUTU32(counter + 0, c); -#endif -} - -void MicrosoftCryptoImpl::AesCtrEncrypt(uint8_t* inout, size_t len, uint8_t* key, uint8_t* counter, uint8_t* ecount_buf, uint32_t* num) -{ - unsigned int n; - unsigned long l=len; - IBuffer^ keybuf = IBufferFromPtr(key, 32); - CryptographicKey^ _key = aesKeyProvider->CreateSymmetricKey(keybuf); - n=*num; - while (l--){ - if (n==0) - { - IBuffer^ inbuf=IBufferFromPtr(counter, 16); - IBuffer^ outbuf=CryptographicEngine::Encrypt(_key, inbuf, nullptr); - IBufferToPtr(outbuf, 16, ecount_buf); - AES_ctr128_inc(counter); - } - *inout=*(inout++)^ecount_buf[n]; - n=(n+1)%16; - } - *num=n; -} - void MicrosoftCryptoImpl::AesIgeEncrypt(uint8_t* in, uint8_t* out, size_t len, uint8_t* key, uint8_t* iv){ IBuffer^ keybuf=IBufferFromPtr(key, 32); CryptographicKey^ _key=aesKeyProvider->CreateSymmetricKey(keybuf); @@ -291,6 +250,72 @@ void MicrosoftCryptoImpl::AesIgeDecrypt(uint8_t* in, uint8_t* out, size_t len, u } } +#define GETU32(pt) (((uint32_t)(pt)[0] << 24) ^ ((uint32_t)(pt)[1] << 16) ^ ((uint32_t)(pt)[2] << 8) ^ ((uint32_t)(pt)[3])) +#define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } + +typedef uint8_t u8; + +#define L_ENDIAN + +/* increment counter (128-bit int) by 2^64 */ +static void AES_ctr128_inc(unsigned char *counter) { + unsigned long c; + + /* Grab 3rd dword of counter and increment */ +#ifdef L_ENDIAN + c = GETU32(counter + 8); + c++; + PUTU32(counter + 8, c); +#else + c = GETU32(counter + 4); + c++; + PUTU32(counter + 4, c); +#endif + + /* if no overflow, we're done */ + if (c) + return; + + /* Grab top dword of counter and increment */ +#ifdef L_ENDIAN + c = GETU32(counter + 12); + c++; + PUTU32(counter + 12, c); +#else + c = GETU32(counter + 0); + c++; + PUTU32(counter + 0, c); +#endif + +} + +void MicrosoftCryptoImpl::AesCtrEncrypt(uint8_t* inout, size_t len, uint8_t* key, uint8_t* counter, uint8_t* ecount_buf, uint32_t* num){ + unsigned int n; + unsigned long l = len; + + //assert(in && out && key && counter && num); + //assert(*num < AES_BLOCK_SIZE); + + IBuffer^ keybuf = IBufferFromPtr(key, 32); + CryptographicKey^ _key = aesKeyProvider->CreateSymmetricKey(keybuf); + + n = *num; + + while (l--) { + if (n == 0) { + IBuffer^ inbuf = IBufferFromPtr(counter, 16); + IBuffer^ outbuf = CryptographicEngine::Encrypt(_key, inbuf, nullptr); + IBufferToPtr(outbuf, 16, ecount_buf); + //AES_encrypt(counter, ecount_buf, key); + AES_ctr128_inc(counter); + } + *inout = *(inout++) ^ ecount_buf[n]; + n = (n + 1) % 16; + } + + *num = n; +} + void MicrosoftCryptoImpl::SHA1(uint8_t* msg, size_t len, uint8_t* out){ //EnterCriticalSection(&hashMutex); diff --git a/os/windows/CXWrapper.h b/os/windows/CXWrapper.h index e2395d8..4d36c8a 100644 --- a/os/windows/CXWrapper.h +++ b/os/windows/CXWrapper.h @@ -63,7 +63,7 @@ namespace libtgvoip{ virtual ~VoIPControllerWrapper(); void Start(); void Connect(); - void SetPublicEndpoints(Windows::Foundation::Collections::IIterable^ endpoints, bool allowP2P); + void SetPublicEndpoints(const Platform::Array^ endpoints, bool allowP2P); void SetNetworkType(NetworkType type); void SetStateCallback(IStateCallback^ callback); void SetMicMute(bool mute); @@ -86,9 +86,9 @@ namespace libtgvoip{ ref class MicrosoftCryptoImpl{ public: - static void AesCtrEncrypt(uint8_t* inout, size_t len, uint8_t* key, uint8_t* counter, uint8_t* ecount_buf, uint32_t* num); static void AesIgeEncrypt(uint8_t* in, uint8_t* out, size_t len, uint8_t* key, uint8_t* iv); static void AesIgeDecrypt(uint8_t* in, uint8_t* out, size_t len, uint8_t* key, uint8_t* iv); + static void AesCtrEncrypt(uint8_t* inout, size_t len, uint8_t* key, uint8_t* iv, uint8_t* ecount, uint32_t* num); static void SHA1(uint8_t* msg, size_t len, uint8_t* out); static void SHA256(uint8_t* msg, size_t len, uint8_t* out); static void RandBytes(uint8_t* buffer, size_t len); diff --git a/os/windows/NetworkSocketWinsock.cpp b/os/windows/NetworkSocketWinsock.cpp index c54a96d..78a1e71 100644 --- a/os/windows/NetworkSocketWinsock.cpp +++ b/os/windows/NetworkSocketWinsock.cpp @@ -18,7 +18,7 @@ using namespace tgvoip; -NetworkSocketWinsock::NetworkSocketWinsock() : lastRecvdV4(0), lastRecvdV6("::0"){ +NetworkSocketWinsock::NetworkSocketWinsock(NetworkProtocol protocol) : NetworkSocket(protocol), lastRecvdV4(0), lastRecvdV6("::0"){ needUpdateNat64Prefix=true; nat64Present=false; switchToV6at=0; @@ -35,10 +35,12 @@ NetworkSocketWinsock::NetworkSocketWinsock() : lastRecvdV4(0), lastRecvdV6("::0" WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); LOGD("Initialized winsock, version %d.%d", wsaData.wHighVersion, wsaData.wVersion); + tcpConnectedAddress=NULL; } NetworkSocketWinsock::~NetworkSocketWinsock(){ - + if(tcpConnectedAddress) + delete tcpConnectedAddress; } void NetworkSocketWinsock::SetMaxPriority(){ @@ -46,148 +48,81 @@ 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"); - } + if(!packet || !packet->address){ + LOGW("tried to send null packet"); return; } - sockaddr_in6 addr; - IPv4Address* v4addr=dynamic_cast(packet->address); - if(v4addr){ - if(!isAtLeastVista){ - sockaddr_in _addr; - _addr.sin_family=AF_INET; - _addr.sin_addr.s_addr=v4addr->GetAddress(); - _addr.sin_port=htons(packet->port); - int res=sendto(fd, (char*)packet->data, packet->length, 0, (sockaddr*)&_addr, sizeof(_addr)); - if(res==SOCKET_ERROR){ - int error=WSAGetLastError(); - LOGE("error sending: %d", error); - } - return; - } - if(needUpdateNat64Prefix && !isV4Available && VoIPController::GetCurrentTime()>switchToV6at && switchToV6at!=0){ - LOGV("Updating NAT64 prefix"); - nat64Present=false; - addrinfo* addr0; - int res=getaddrinfo("ipv4only.arpa", NULL, NULL, &addr0); - if(res!=0){ - LOGW("Error updating NAT64 prefix: %d / %s", res, gai_strerror(res)); - }else{ - addrinfo* addrPtr; - unsigned char* addr170=NULL; - unsigned char* addr171=NULL; - for(addrPtr=addr0;addrPtr;addrPtr=addrPtr->ai_next){ - if(addrPtr->ai_family==AF_INET6){ - sockaddr_in6* translatedAddr=(sockaddr_in6*)addrPtr->ai_addr; - uint32_t v4part=*((uint32_t*)&translatedAddr->sin6_addr.s6_addr[12]); - if(v4part==0xAA0000C0 && !addr170){ - addr170=translatedAddr->sin6_addr.s6_addr; + int res; + if(protocol==PROTO_UDP){ + IPv4Address *v4addr=dynamic_cast(packet->address); + if(isAtLeastVista){ + sockaddr_in6 addr; + if(v4addr){ + if(needUpdateNat64Prefix && !isV4Available && VoIPController::GetCurrentTime()>switchToV6at && switchToV6at!=0){ + LOGV("Updating NAT64 prefix"); + nat64Present=false; + addrinfo *addr0; + int res=getaddrinfo("ipv4only.arpa", NULL, NULL, &addr0); + if(res!=0){ + LOGW("Error updating NAT64 prefix: %d / %s", res, gai_strerror(res)); + }else{ + addrinfo *addrPtr; + unsigned char *addr170=NULL; + unsigned char *addr171=NULL; + for(addrPtr=addr0; addrPtr; addrPtr=addrPtr->ai_next){ + if(addrPtr->ai_family==AF_INET6){ + sockaddr_in6 *translatedAddr=(sockaddr_in6 *) addrPtr->ai_addr; + uint32_t v4part=*((uint32_t *) &translatedAddr->sin6_addr.s6_addr[12]); + if(v4part==0xAA0000C0 && !addr170){ + addr170=translatedAddr->sin6_addr.s6_addr; + } + if(v4part==0xAB0000C0 && !addr171){ + addr171=translatedAddr->sin6_addr.s6_addr; + } + char buf[INET6_ADDRSTRLEN]; + //LOGV("Got translated address: %s", inet_ntop(AF_INET6, &translatedAddr->sin6_addr, buf, sizeof(buf))); + } } - if(v4part==0xAB0000C0 && !addr171){ - addr171=translatedAddr->sin6_addr.s6_addr; + if(addr170 && addr171 && memcmp(addr170, addr171, 12)==0){ + nat64Present=true; + memcpy(nat64Prefix, addr170, 12); + char buf[INET6_ADDRSTRLEN]; + //LOGV("Found nat64 prefix from %s", inet_ntop(AF_INET6, addr170, buf, sizeof(buf))); + }else{ + LOGV("Didn't find nat64"); } - char buf[INET6_ADDRSTRLEN]; - //LOGV("Got translated address: %s", inet_ntop(AF_INET6, &translatedAddr->sin6_addr, buf, sizeof(buf))); + freeaddrinfo(addr0); } + needUpdateNat64Prefix=false; } - if(addr170 && addr171 && memcmp(addr170, addr171, 12)==0){ - nat64Present=true; - memcpy(nat64Prefix, addr170, 12); - char buf[INET6_ADDRSTRLEN]; - //LOGV("Found nat64 prefix from %s", inet_ntop(AF_INET6, addr170, buf, sizeof(buf))); - }else{ - LOGV("Didn't find nat64"); - } - freeaddrinfo(addr0); - } - needUpdateNat64Prefix=false; - } - memset(&addr, 0, sizeof(sockaddr_in6)); - addr.sin6_family=AF_INET6; - *((uint32_t*)&addr.sin6_addr.s6_addr[12])=v4addr->GetAddress(); - if(nat64Present) - memcpy(addr.sin6_addr.s6_addr, nat64Prefix, 12); - else - addr.sin6_addr.s6_addr[11]=addr.sin6_addr.s6_addr[10]=0xFF; + memset(&addr, 0, sizeof(sockaddr_in6)); + addr.sin6_family=AF_INET6; + *((uint32_t *) &addr.sin6_addr.s6_addr[12])=v4addr->GetAddress(); + if(nat64Present) + memcpy(addr.sin6_addr.s6_addr, nat64Prefix, 12); + else + addr.sin6_addr.s6_addr[11]=addr.sin6_addr.s6_addr[10]=0xFF; - }else{ - IPv6Address* v6addr=dynamic_cast(packet->address); - assert(v6addr!=NULL); - if(!isAtLeastVista){ - return; + }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); + res=sendto(fd, (const char*)packet->data, packet->length, 0, (const sockaddr *) &addr, sizeof(addr)); + }else if(v4addr){ + sockaddr_in addr; + addr.sin_addr.s_addr=v4addr->GetAddress(); + addr.sin_port=htons(packet->port); + addr.sin_family=AF_INET; + res=sendto(fd, (const char*)packet->data, packet->length, 0, (const sockaddr*)&addr, sizeof(addr)); } + }else{ + res=send(fd, (const char*)packet->data, packet->length, 0); } - addr.sin6_port=htons(packet->port); - - //WSABUF wsaBuf; - //wsaBuf.buf=(char*) packet->data; - //wsaBuf.len=packet->length; - //int res=WSASendTo(fd, &wsaBuf, 1, NULL, 0, (const sockaddr*)&addr, sizeof(addr), NULL, NULL); - int res=sendto(fd, (char*)packet->data, packet->length, 0, (sockaddr*)&addr, sizeof(addr)); if(res==SOCKET_ERROR){ - int error=WSAGetLastError(); - LOGE("error sending: %d", error); - if(error==WSAENETUNREACH && !isV4Available && VoIPController::GetCurrentTime()::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(FD_ISSET(fd, &readSet) || FD_ISSET(fd, &errSet)){ + if(protocol==PROTO_UDP){ + if(isAtLeastVista){ + int addrLen=sizeof(sockaddr_in6); 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); + int res=recvfrom(fd, (char*)packet->data, packet->length, 0, (sockaddr *) &srcAddr, (socklen_t *) &addrLen); if(res!=SOCKET_ERROR) packet->length=(size_t) res; else{ + LOGE("error receiving %d", WSAGetLastError()); packet->length=0; - int error=WSAGetLastError(); - LOGE("error receiving: %d", error); return; } - //LOGV("Received %d bytes from %s:%d at %.5lf", res, inet_ntoa(srcAddr.sin6_addr), ntohs(srcAddr.sin6_port), GetCurrentTime()); - if(addr->sa_family==AF_INET){ - packet->port=ntohs(srcAddr4.sin_port); - lastRecvdV4=IPv4Address(srcAddr4.sin_addr.s_addr); + //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{ - 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; - } + lastRecvdV6=IPv6Address(srcAddr.sin6_addr.s6_addr); + packet->address=&lastRecvdV6; } - packet->protocol=PROTO_UDP; - 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->port=ntohs(srcAddr.sin6_port); + }else{ + int addrLen=sizeof(sockaddr_in); + sockaddr_in srcAddr; + int res=recvfrom(fd, (char*)packet->data, packet->length, 0, (sockaddr *) &srcAddr, (socklen_t *) &addrLen); + if(res!=SOCKET_ERROR) + packet->length=(size_t) res; + else{ + LOGE("error receiving %d", WSAGetLastError()); packet->length=0; - closesocket(itr->fd); - itr=tcpSockets.erase(itr); - continue; + return; } - if(FD_ISSET(itr->fd, &errSet)){ - closesocket(itr->fd); - itr=tcpSockets.erase(itr); - continue; - } - ++itr; + lastRecvdV4=IPv4Address(srcAddr.sin_addr.s_addr); + packet->address=&lastRecvdV4; + packet->port=ntohs(srcAddr.sin_port); + } + packet->protocol=PROTO_UDP; + }else if(protocol==PROTO_TCP){ + int res=recv(fd, (char*)packet->data, packet->length, 0); + if(res==SOCKET_ERROR){ + LOGE("Error receiving from TCP socket: %d", WSAGetLastError()); + failed=true; + }else{ + packet->length=(size_t)res; + packet->address=tcpConnectedAddress; + packet->port=tcpConnectedPort; + packet->protocol=PROTO_TCP; } } } void NetworkSocketWinsock::Open(){ - fd=socket(isAtLeastVista ? AF_INET6 : AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if(fd==INVALID_SOCKET){ - int error=WSAGetLastError(); - LOGE("error creating socket: %d", error); - failed=true; - return; - } - - int res; - if(isAtLeastVista){ - DWORD flag=0; - res=setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&flag, sizeof(flag)); - if(res==SOCKET_ERROR){ - LOGE("error enabling dual stack socket: %d", WSAGetLastError()); + if(protocol==PROTO_UDP){ + fd=socket(isAtLeastVista ? AF_INET6 : AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if(fd==INVALID_SOCKET){ + int error=WSAGetLastError(); + LOGE("error creating socket: %d", error); failed=true; return; } - } + + int res; + if(isAtLeastVista){ + DWORD flag=0; + res=setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&flag, sizeof(flag)); + if(res==SOCKET_ERROR){ + LOGE("error enabling dual stack socket: %d", WSAGetLastError()); + failed=true; + return; + } + } - SetMaxPriority(); + SetMaxPriority(); - int tries=0; - sockaddr* addr; - sockaddr_in addr4; - sockaddr_in6 addr6; - int addrLen; - if(isAtLeastVista){ - //addr.sin6_addr.s_addr=0; - memset(&addr6, 0, sizeof(sockaddr_in6)); - //addr.sin6_len=sizeof(sa_family_t); - addr6.sin6_family=AF_INET6; - addr=(sockaddr*)&addr6; - addrLen=sizeof(addr6); - }else{ + int tries=0; + sockaddr* addr; sockaddr_in addr4; - addr4.sin_addr.s_addr=0; - addr4.sin_family=AF_INET; - addr=(sockaddr*)&addr4; - addrLen=sizeof(addr4); - } - for(tries=0;tries<10;tries++){ - uint16_t port=htons(GenerateLocalPort()); - if(isAtLeastVista) - ((sockaddr_in6*)addr)->sin6_port=port; - else - ((sockaddr_in*)addr)->sin_port=port; - res=::bind(fd, addr, addrLen); - LOGV("trying bind to port %u", ntohs(port)); - if(res<0){ - LOGE("error binding to port %u: %d / %s", ntohs(port), errno, strerror(errno)); + sockaddr_in6 addr6; + int addrLen; + if(isAtLeastVista){ + //addr.sin6_addr.s_addr=0; + memset(&addr6, 0, sizeof(sockaddr_in6)); + //addr.sin6_len=sizeof(sa_family_t); + addr6.sin6_family=AF_INET6; + addr=(sockaddr*)&addr6; + addrLen=sizeof(addr6); }else{ - break; + sockaddr_in addr4; + addr4.sin_addr.s_addr=0; + addr4.sin_family=AF_INET; + addr=(sockaddr*)&addr4; + addrLen=sizeof(addr4); } - } - if(tries==10){ + for(tries=0;tries<10;tries++){ + uint16_t port=htons(GenerateLocalPort()); + if(isAtLeastVista) + ((sockaddr_in6*)addr)->sin6_port=port; + else + ((sockaddr_in*)addr)->sin_port=port; + res=::bind(fd, addr, addrLen); + LOGV("trying bind to port %u", ntohs(port)); + if(res<0){ + LOGE("error binding to port %u: %d / %s", ntohs(port), errno, strerror(errno)); + }else{ + break; + } + } + if(tries==10){ + if(isAtLeastVista) + ((sockaddr_in6*)addr)->sin6_port=0; + else + ((sockaddr_in*)addr)->sin_port=0; + res=::bind(fd, addr, addrLen); + if(res<0){ + LOGE("error binding to port %u: %d / %s", 0, errno, strerror(errno)); + //SetState(STATE_FAILED); + return; + } + } + getsockname(fd, addr, (socklen_t*) &addrLen); + uint16_t localUdpPort; if(isAtLeastVista) - ((sockaddr_in6*)addr)->sin6_port=0; + localUdpPort=ntohs(((sockaddr_in6*)addr)->sin6_port); else - ((sockaddr_in*)addr)->sin_port=0; - res=::bind(fd, addr, addrLen); - if(res<0){ - LOGE("error binding to port %u: %d / %s", 0, errno, strerror(errno)); - //SetState(STATE_FAILED); - return; - } - } - getsockname(fd, addr, (socklen_t*) &addrLen); - uint16_t localUdpPort; - if(isAtLeastVista) - localUdpPort=ntohs(((sockaddr_in6*)addr)->sin6_port); - else - localUdpPort=ntohs(((sockaddr_in*)addr)->sin_port); - LOGD("Bound to local UDP port %u", localUdpPort); + localUdpPort=ntohs(((sockaddr_in*)addr)->sin_port); + LOGD("Bound to local UDP port %u", localUdpPort); - needUpdateNat64Prefix=true; - isV4Available=false; - switchToV6at=VoIPController::GetCurrentTime()+ipv6Timeout; + needUpdateNat64Prefix=true; + isV4Available=false; + switchToV6at=VoIPController::GetCurrentTime()+ipv6Timeout; + } } void NetworkSocketWinsock::Close(){ closing=true; + failed=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(){ @@ -592,3 +452,185 @@ void NetworkSocketWinsock::StringToV6Address(std::string address, unsigned char #endif memcpy(out, addr.sin6_addr.s6_addr, 16); } + +void NetworkSocketWinsock::Connect(NetworkAddress *address, uint16_t port){ + IPv4Address* v4addr=dynamic_cast(address); + IPv6Address* v6addr=dynamic_cast(address); + sockaddr_in v4; + sockaddr_in6 v6; + sockaddr* addr=NULL; + size_t addrLen=0; + if(v4addr){ + v4.sin_family=AF_INET; + v4.sin_addr.s_addr=v4addr->GetAddress(); + v4.sin_port=htons(port); + addr=reinterpret_cast(&v4); + addrLen=sizeof(v4); + }else if(v6addr){ + v6.sin6_family=AF_INET6; + memcpy(v6.sin6_addr.s6_addr, v6addr->GetAddress(), 16); + v6.sin6_flowinfo=0; + v6.sin6_scope_id=0; + v6.sin6_port=htons(port); + addr=reinterpret_cast(&v6); + addrLen=sizeof(v6); + }else{ + LOGE("Unknown address type in TCP connect"); + failed=true; + return; + } + fd=socket(addr->sa_family, SOCK_STREAM, IPPROTO_TCP); + if(fd==0){ + LOGE("Error creating TCP socket: %d", WSAGetLastError()); + failed=true; + return; + } + int opt=1; + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (const char*)&opt, sizeof(opt)); + timeval timeout; + timeout.tv_sec=5; + timeout.tv_usec=0; + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout)); + timeout.tv_sec=60; + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)); + int res=connect(fd, (const sockaddr*) addr, addrLen); + if(res!=0){ + LOGW("error connecting TCP socket to %s:%u: %d", address->ToString().c_str(), port, WSAGetLastError()); + closesocket(fd); + failed=true; + return; + } + tcpConnectedAddress=v4addr ? (NetworkAddress*)new IPv4Address(*v4addr) : (NetworkAddress*)new IPv6Address(*v6addr); + tcpConnectedPort=port; + LOGI("successfully connected to %s:%d", tcpConnectedAddress->ToString().c_str(), tcpConnectedPort); +} + +IPv4Address *NetworkSocketWinsock::ResolveDomainName(std::string name){ + addrinfo* addr0; + IPv4Address* ret=NULL; + int res=getaddrinfo(name.c_str(), NULL, NULL, &addr0); + if(res!=0){ + LOGW("Error updating NAT64 prefix: %d / %s", res, gai_strerror(res)); + }else{ + addrinfo* addrPtr; + for(addrPtr=addr0;addrPtr;addrPtr=addrPtr->ai_next){ + if(addrPtr->ai_family==AF_INET){ + sockaddr_in* addr=(sockaddr_in*)addrPtr->ai_addr; + ret=new IPv4Address(addr->sin_addr.s_addr); + break; + } + } + freeaddrinfo(addr0); + } + return ret; +} + +NetworkAddress *NetworkSocketWinsock::GetConnectedAddress(){ + return tcpConnectedAddress; +} + +uint16_t NetworkSocketWinsock::GetConnectedPort(){ + return tcpConnectedPort; +} + +void NetworkSocketWinsock::SetTimeouts(int sendTimeout, int recvTimeout){ + timeval timeout; + timeout.tv_sec=sendTimeout; + timeout.tv_usec=0; + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout)); + timeout.tv_sec=recvTimeout; + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)); +} + +bool NetworkSocketWinsock::Select(std::vector &readFds, std::vector &errorFds, SocketSelectCanceller* _canceller){ + fd_set readSet; + fd_set errorSet; + SocketSelectCancellerWin32* canceller=dynamic_cast(_canceller); + timeval timeout={0, 10000}; + bool anyFailed=false; + int res=0; + + do{ + FD_ZERO(&readSet); + FD_ZERO(&errorSet); + + for(std::vector::iterator itr=readFds.begin();itr!=readFds.end();++itr){ + int sfd=GetDescriptorFromSocket(*itr); + if(sfd==0){ + LOGW("can't select on one of sockets because it's not a NetworkSocketWinsock instance"); + continue; + } + FD_SET(sfd, &readSet); + } + + + for(std::vector::iterator itr=errorFds.begin();itr!=errorFds.end();++itr){ + int sfd=GetDescriptorFromSocket(*itr); + if(sfd==0){ + LOGW("can't select on one of sockets because it's not a NetworkSocketWinsock instance"); + continue; + } + anyFailed |= (*itr)->IsFailed(); + FD_SET(sfd, &errorSet); + } + if(canceller && canceller->canceled) + break; + res=select(0, &readSet, NULL, &errorSet, &timeout); + //LOGV("select result %d", res); + if(res==SOCKET_ERROR) + LOGE("SELECT ERROR %d", WSAGetLastError()); + }while(res==0); + + + if(canceller && canceller->canceled && !anyFailed){ + canceller->canceled=false; + return false; + }else if(anyFailed){ + FD_ZERO(&readSet); + FD_ZERO(&errorSet); + } + + std::vector::iterator itr=readFds.begin(); + while(itr!=readFds.end()){ + int sfd=GetDescriptorFromSocket(*itr); + if(sfd==0 || !FD_ISSET(sfd, &readSet)){ + itr=readFds.erase(itr); + }else{ + ++itr; + } + } + + itr=errorFds.begin(); + while(itr!=errorFds.end()){ + int sfd=GetDescriptorFromSocket(*itr); + if((sfd==0 || !FD_ISSET(sfd, &errorSet)) && !(*itr)->IsFailed()){ + itr=errorFds.erase(itr); + }else{ + ++itr; + } + } + //LOGV("select fds left: read=%d, error=%d", readFds.size(), errorFds.size()); + + return readFds.size()>0 || errorFds.size()>0; +} + +SocketSelectCancellerWin32::SocketSelectCancellerWin32(){ + canceled=false; +} + +SocketSelectCancellerWin32::~SocketSelectCancellerWin32(){ +} + +void SocketSelectCancellerWin32::CancelSelect(){ + canceled=true; +} + +int NetworkSocketWinsock::GetDescriptorFromSocket(NetworkSocket *socket){ + NetworkSocketWinsock* sp=dynamic_cast(socket); + if(sp) + return sp->fd; + NetworkSocketWrapper* sw=dynamic_cast(socket); + if(sw) + return GetDescriptorFromSocket(sw->GetWrapped()); + return 0; +} diff --git a/os/windows/NetworkSocketWinsock.h b/os/windows/NetworkSocketWinsock.h index 6419c0e..101b792 100644 --- a/os/windows/NetworkSocketWinsock.h +++ b/os/windows/NetworkSocketWinsock.h @@ -13,17 +13,19 @@ namespace tgvoip { -struct TCPSocket{ - uintptr_t fd; - IPv4Address address; - uint16_t port; - TCPO2State recvState; - TCPO2State sendState; +class SocketSelectCancellerWin32 : public SocketSelectCanceller{ +friend class NetworkSocketWinsock; +public: + SocketSelectCancellerWin32(); + virtual ~SocketSelectCancellerWin32(); + virtual void CancelSelect(); +private: + bool canceled; }; class NetworkSocketWinsock : public NetworkSocket{ public: - NetworkSocketWinsock(); + NetworkSocketWinsock(NetworkProtocol protocol); virtual ~NetworkSocketWinsock(); virtual void Send(NetworkPacket* packet); virtual void Receive(NetworkPacket* packet); @@ -32,16 +34,26 @@ public: virtual std::string GetLocalInterfaceInfo(IPv4Address* v4addr, IPv6Address* v6addr); virtual void OnActiveInterfaceChanged(); virtual uint16_t GetLocalPort(); + virtual void Connect(NetworkAddress* address, uint16_t port); static std::string V4AddressToString(uint32_t address); static std::string V6AddressToString(unsigned char address[16]); static uint32_t StringToV4Address(std::string address); static void StringToV6Address(std::string address, unsigned char* out); + static IPv4Address* ResolveDomainName(std::string name); + static bool Select(std::vector& readFds, std::vector& errorFds, SocketSelectCanceller* canceller); + + virtual NetworkAddress *GetConnectedAddress(); + + virtual uint16_t GetConnectedPort(); + + virtual void SetTimeouts(int sendTimeout, int recvTimeout); protected: virtual void SetMaxPriority(); private: + static int GetDescriptorFromSocket(NetworkSocket* socket); uintptr_t fd; bool needUpdateNat64Prefix; bool nat64Present; @@ -50,9 +62,9 @@ private: IPv4Address lastRecvdV4; IPv6Address lastRecvdV6; bool isAtLeastVista; - std::vector tcpSockets; bool closing; - + NetworkAddress* tcpConnectedAddress; + uint16_t tcpConnectedPort; }; }