Exemple #1
0
        public DnsDatagram Resolve(DnsDatagram request)
        {
            int bytesRecv;

            byte[] responseBuffer  = null;
            int    nextServerIndex = 0;
            int    retries         = _retries;

            byte[]    requestBuffer;
            IDnsCache dnsCache = null;

            //serialize request
            using (MemoryStream mS = new MemoryStream(32))
            {
                if (_tcp)
                {
                    mS.Position = 2;
                }

                //write dns datagram
                request.WriteTo(mS);

                requestBuffer = mS.ToArray();

                if (_tcp)
                {
                    byte[] length = BitConverter.GetBytes(Convert.ToUInt16(requestBuffer.Length - 2));

                    requestBuffer[0] = length[1];
                    requestBuffer[1] = length[0];
                }
            }

            //init server selection parameters
            if (_servers.Length > 1)
            {
                retries = retries * _servers.Length; //retries on per server basis

                byte[] select = new byte[1];
                _rnd.GetBytes(select);

                nextServerIndex = select[0] % _servers.Length;
            }

            int retry = 0;

            while (retry < retries)
            {
                //select server
                NameServerAddress server;

                if (_servers.Length > 1)
                {
                    server          = _servers[nextServerIndex];
                    nextServerIndex = (nextServerIndex + 1) % _servers.Length;
                }
                else
                {
                    server = _servers[0];
                }

                if (server.EndPoint == null)
                {
                    if (dnsCache == null)
                    {
                        dnsCache = new SimpleDnsCache();
                    }

                    server.ResolveAddress(dnsCache, _proxy, _preferIPv6, _tcp, _retries);

                    if (server.EndPoint == null)
                    {
                        retry++;
                        continue;
                    }
                }

                //query server
                Socket _socket = null;
                SocksUdpAssociateRequestHandler proxyRequestHandler = null;

                try
                {
                    retry++;

                    DateTime sentAt = DateTime.UtcNow;
                    bool     dnsTcp;

                    if (_proxy == null)
                    {
                        if (_tcp)
                        {
                            _socket = new Socket(server.EndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

                            _socket.NoDelay        = true;
                            _socket.SendTimeout    = _sendTimeout;
                            _socket.ReceiveTimeout = _recvTimeout;

                            IAsyncResult result = _socket.BeginConnect(server.EndPoint, null, null);
                            if (!result.AsyncWaitHandle.WaitOne(_connectionTimeout))
                            {
                                throw new SocketException((int)SocketError.TimedOut);
                            }

                            if (!_socket.Connected)
                            {
                                throw new SocketException((int)SocketError.ConnectionRefused);
                            }
                        }
                        else
                        {
                            _socket = new Socket(server.EndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);

                            _socket.SendTimeout    = _sendTimeout;
                            _socket.ReceiveTimeout = _recvTimeout;
                        }

                        dnsTcp = _tcp;
                    }
                    else
                    {
                        switch (_proxy.Type)
                        {
                        case NetProxyType.Http:
                            _socket = _proxy.HttpProxy.Connect(server.EndPoint, _connectionTimeout);

                            _socket.NoDelay        = true;
                            _socket.SendTimeout    = _sendTimeout;
                            _socket.ReceiveTimeout = _recvTimeout;

                            dnsTcp = true;
                            break;

                        case NetProxyType.Socks5:
                            if (!_tcp)
                            {
                                try
                                {
                                    proxyRequestHandler = _proxy.SocksProxy.UdpAssociate(_connectionTimeout);
                                    proxyRequestHandler.ReceiveTimeout = _recvTimeout;

                                    dnsTcp = false;
                                    break;
                                }
                                catch (SocksClientException)
                                { }
                            }

                            using (SocksConnectRequestHandler requestHandler = _proxy.SocksProxy.Connect(server.EndPoint, _connectionTimeout))
                            {
                                _socket = requestHandler.GetSocket();

                                _socket.NoDelay        = true;
                                _socket.SendTimeout    = _sendTimeout;
                                _socket.ReceiveTimeout = _recvTimeout;

                                dnsTcp = true;
                            }

                            break;

                        default:
                            throw new NotSupportedException("Proxy type not supported by DnsClient.");
                        }
                    }

                    if (dnsTcp)
                    {
                        _socket.Send(requestBuffer);

                        if ((responseBuffer == null) || (responseBuffer.Length == 512))
                        {
                            responseBuffer = new byte[64 * 1024];
                        }

                        bytesRecv = _socket.Receive(responseBuffer, 0, 2, SocketFlags.None);
                        if (bytesRecv < 1)
                        {
                            throw new SocketException((int)SocketError.ConnectionReset);
                        }

                        Array.Reverse(responseBuffer, 0, 2);
                        ushort length = BitConverter.ToUInt16(responseBuffer, 0);

                        int offset = 0;
                        while (offset < length)
                        {
                            bytesRecv = _socket.Receive(responseBuffer, offset, length, SocketFlags.None);
                            if (bytesRecv < 1)
                            {
                                throw new SocketException((int)SocketError.ConnectionReset);
                            }

                            offset += bytesRecv;
                        }

                        bytesRecv = length;
                    }
                    else
                    {
                        if (responseBuffer == null)
                        {
                            responseBuffer = new byte[512];
                        }

                        if (proxyRequestHandler == null)
                        {
                            _socket.SendTo(requestBuffer, server.EndPoint);

                            EndPoint remoteEP;

                            if (server.EndPoint.AddressFamily == AddressFamily.InterNetworkV6)
                            {
                                remoteEP = new IPEndPoint(IPAddress.IPv6Any, 0);
                            }
                            else
                            {
                                remoteEP = new IPEndPoint(IPAddress.Any, 0);
                            }

                            bytesRecv = _socket.ReceiveFrom(responseBuffer, ref remoteEP);
                        }
                        else
                        {
                            proxyRequestHandler.SendTo(requestBuffer, 0, requestBuffer.Length, new SocksEndPoint(server.EndPoint));

                            bytesRecv = proxyRequestHandler.ReceiveFrom(responseBuffer, 0, responseBuffer.Length, out SocksEndPoint socksRemoteEP);
                        }
                    }

                    //parse response
                    using (MemoryStream mS = new MemoryStream(responseBuffer, 0, bytesRecv, false))
                    {
                        double      rtt      = (DateTime.UtcNow - sentAt).TotalMilliseconds;
                        DnsDatagram response = new DnsDatagram(mS, server, (_tcp ? ProtocolType.Tcp : ProtocolType.Udp), rtt);

                        if (response.Header.Identifier == request.Header.Identifier)
                        {
                            return(response);
                        }
                    }
                }
                catch (SocketException)
                { }
                finally
                {
                    if (_socket != null)
                    {
                        _socket.Dispose();
                    }

                    if (proxyRequestHandler != null)
                    {
                        proxyRequestHandler.Dispose();
                    }
                }
            }

            throw new DnsClientException("DnsClient failed to resolve the request: no response from name servers.");
        }
        protected override void UpdateTracker(TrackerClientEvent @event, IPEndPoint clientEP)
        {
            SocksUdpAssociateRequestHandler proxyRequestHandler = null;
            UdpClient udpClient = null;

            if (_proxy == null)
            {
                udpClient = new UdpClient(_trackerURI.Host, _trackerURI.Port);
                udpClient.Client.ReceiveTimeout = 10000;
            }
            else
            {
                switch (_proxy.Type)
                {
                case NetProxyType.Socks5:
                    proxyRequestHandler = _proxy.SocksProxy.UdpAssociate();
                    proxyRequestHandler.ReceiveTimeout = 10000;
                    break;

                case NetProxyType.Http:
                    throw new NotSupportedException("Http proxy not supported by Udp tracker.");

                default:
                    throw new NotSupportedException("Proxy type not supported by Udp tracker.");
                }
            }

            try
            {
                for (int n = 0; n < 2; n++)
                {
                    if ((_connectionID == null) || (_connectionIDExpires <= DateTime.UtcNow))
                    {
                        //GET CONNECTION ID
                        _rnd.GetBytes(_transactionID);
                        _connectionID        = GetConnectionID(udpClient, proxyRequestHandler, _transactionID);
                        _connectionIDExpires = DateTime.UtcNow.AddMinutes(1);
                    }

                    try
                    {
                        _rnd.GetBytes(_transactionID);
                        byte[] announceResponse;
                        int    announceResponseLength = GetAnnounceResponse(udpClient, proxyRequestHandler, _transactionID, _connectionID, @event, clientEP, out announceResponse);

                        byte[] buffer = new byte[4];

                        Buffer.BlockCopy(announceResponse, 8, buffer, 0, 4);
                        Array.Reverse(buffer);
                        _interval = BitConverter.ToInt32(buffer, 0);

                        Buffer.BlockCopy(announceResponse, 12, buffer, 0, 4);
                        Array.Reverse(buffer);
                        _leachers = BitConverter.ToInt32(buffer, 0);

                        Buffer.BlockCopy(announceResponse, 16, buffer, 0, 4);
                        Array.Reverse(buffer);
                        _seeders = BitConverter.ToInt32(buffer, 0);

                        _peers.Clear();

                        if (_proxy == null)
                        {
                            if (udpClient.Client.AddressFamily == AddressFamily.InterNetworkV6)
                            {
                                ParsePeersIPv6(announceResponse, announceResponseLength, _peers);
                            }
                            else
                            {
                                ParsePeersIPv4(announceResponse, announceResponseLength, _peers);
                            }
                        }
                        else
                        {
                            int x = (announceResponseLength - 26) % 6;

                            if (x == 0)
                            {
                                ParsePeersIPv4(announceResponse, announceResponseLength, _peers);
                            }
                            else
                            {
                                ParsePeersIPv6(announceResponse, announceResponseLength, _peers);
                            }
                        }

                        return;
                    }
                    catch (SocketException ex)
                    {
                        if (ex.ErrorCode != (int)SocketError.TimedOut)
                        {
                            throw new TrackerClientException(ex.Message, ex);
                        }
                    }
                }

                throw new TrackerClientException("No response from tracker.");
            }
            finally
            {
                if (proxyRequestHandler != null)
                {
                    proxyRequestHandler.Dispose();
                }

                if (udpClient != null)
                {
                    udpClient.Close();
                }
            }
        }
        private byte[] GetConnectionID(UdpClient udpClient, SocksUdpAssociateRequestHandler proxyRequestHandler, byte[] transactionID)
        {
            //Connection_id 64bit 0x41727101980 + action 32bit + transaction_id 32bit (random)
            byte[] requestPacket = new byte[] { 0x0, 0x0, 0x4, 0x17, 0x27, 0x10, 0x19, 0x80, 0x0, 0x0, 0x0, 0x0, transactionID[0], transactionID[1], transactionID[2], transactionID[3] };

            for (int n = 0; n < 2; n++)
            {
                if (_proxy == null)
                {
                    udpClient.Client.ReceiveTimeout = 15 * (2 ^ n) * 1000;
                }
                else
                {
                    proxyRequestHandler.ReceiveTimeout = 15 * (2 ^ n) * 1000;
                }

                try
                {
                    //SEND CONNECT REQUEST
                    if (_proxy == null)
                    {
                        udpClient.Send(requestPacket, requestPacket.Length);
                    }
                    else
                    {
                        proxyRequestHandler.SendTo(requestPacket, 0, requestPacket.Length, new SocksEndPoint(_trackerURI.Host, _trackerURI.Port));
                    }

                    //RECV CONNECT RESPONSE

                    byte[] response;
                    int    responseLength;

                    if (_proxy == null)
                    {
                        IPEndPoint remoteEP = null;
                        response       = udpClient.Receive(ref remoteEP);
                        responseLength = response.Length;
                    }
                    else
                    {
                        response = new byte[128];
                        SocksEndPoint remoteEP;
                        responseLength = proxyRequestHandler.ReceiveFrom(response, 0, response.Length, out remoteEP);
                    }

                    //check response length
                    if (responseLength < 16)
                    {
                        throw new TrackerClientException("Invalid response received for connection request.");
                    }

                    //check transaction id
                    for (int j = 0; j < 4; j++)
                    {
                        if (response[4 + j] != transactionID[j])
                        {
                            throw new TrackerClientException("Invalid transaction id received for connection request.");
                        }
                    }

                    //check action
                    for (int j = 0; j < 4; j++)
                    {
                        if (response[j] != 0)
                        {
                            throw new TrackerClientException("Invalid action received for connection request.");
                        }
                    }

                    byte[] connectionID = new byte[8];

                    Buffer.BlockCopy(response, 8, connectionID, 0, 8);

                    return(connectionID);
                }
                catch (SocketException ex)
                {
                    if (ex.ErrorCode != (int)SocketError.TimedOut)
                    {
                        throw new TrackerClientException(ex.Message, ex);
                    }
                }
            }

            throw new TrackerClientException("No response from tracker.");
        }
        private int GetAnnounceResponse(UdpClient udpClient, SocksUdpAssociateRequestHandler proxyRequestHandler, byte[] transactionID, byte[] connectionID, TrackerClientEvent @event, IPEndPoint clientEP, out byte[] response)
        {
            byte[] request = new byte[98];

            //connection_id 64bit
            Buffer.BlockCopy(connectionID, 0, request, 0, 8);

            //action 32bit
            request[11] = 1;

            //transaction_id 32bit
            Buffer.BlockCopy(transactionID, 0, request, 12, 4);

            //info_hash 20 bytes
            Buffer.BlockCopy(_infoHash, 0, request, 16, 20);

            //peer_id 20 bytes
            Buffer.BlockCopy(_clientID.PeerID, 0, request, 36, 20);

            //downloaded 64bit
            //left 64bit
            //uploaded 64bit

            //event 32bit
            request[83] = Convert.ToByte(@event);

            //ip address 32bit
            if (clientEP.Address.AddressFamily == AddressFamily.InterNetwork)
            {
                switch (clientEP.Address.ToString())
                {
                case "0.0.0.0":
                case "127.0.0.1":
                    break;

                default:
                    Buffer.BlockCopy(clientEP.Address.GetAddressBytes(), 0, request, 84, 4);
                    break;
                }
            }

            //key 32bit
            Buffer.BlockCopy(_clientID.ClientKey, 0, request, 88, 4);

            //num_want
            Buffer.BlockCopy(BitConverter.GetBytes(_clientID.NumWant), 0, request, 92, 4);

            //port 16bit
            byte[] portBuffer = BitConverter.GetBytes(clientEP.Port);
            Array.Reverse(portBuffer);
            Buffer.BlockCopy(portBuffer, 2, request, 96, 2);

            int responseLength;

            if (_proxy == null)
            {
                //SEND ANNOUNCE REQUEST
                udpClient.Send(request, request.Length);

                //RECV ANNOUNCE RESPONSE
                IPEndPoint remoteEP = null;
                response       = udpClient.Receive(ref remoteEP);
                responseLength = response.Length;
            }
            else
            {
                //SEND ANNOUNCE REQUEST
                proxyRequestHandler.SendTo(request, 0, request.Length, new SocksEndPoint(_trackerURI.Host, _trackerURI.Port));

                //RECV ANNOUNCE RESPONSE
                response = new byte[1024];
                SocksEndPoint remoteEP;
                responseLength = proxyRequestHandler.ReceiveFrom(response, 0, response.Length, out remoteEP);
            }

            //check response length
            if (responseLength < 20)
            {
                throw new TrackerClientException("Invalid response received for announce request.");
            }

            //check response transaction id
            for (int j = 0; j < 4; j++)
            {
                if (response[4 + j] != transactionID[j])
                {
                    throw new TrackerClientException("Invalid transaction id received for announce request.");
                }
            }

            //check response action
            if (response[3] != 1)
            {
                throw new TrackerClientException("Invalid action received for announce request.");
            }

            return(responseLength);
        }