public override DnsDatagram Query(DnsDatagram request)
        {
            //serialize request
            byte[] requestBuffer;

            using (MemoryStream mS = new MemoryStream(32))
            {
                request.WriteTo(mS);
                requestBuffer = mS.ToArray();
            }

            //DoH wire format request
            DateTime sentAt = DateTime.UtcNow;

            byte[] responseBuffer;

            using (WebClientEx wC = new WebClientEx())
            {
                wC.AddHeader("content-type", "application/dns-message");
                wC.AddHeader("accept", "application/dns-message");
                wC.AddHeader("host", _server.DnsOverHttpEndPoint.Host + ":" + _server.DnsOverHttpEndPoint.Port);
                wC.UserAgent = "DoH client";
                wC.Proxy     = _proxy;
                wC.Timeout   = _timeout;

                if (_proxy == null)
                {
                    if (_server.IPEndPoint == null)
                    {
                        _server.RecursiveResolveIPAddress();
                    }

                    responseBuffer = wC.UploadData(new Uri(_server.DnsOverHttpEndPoint.Scheme + "://" + _server.IPEndPoint.ToString() + _server.DnsOverHttpEndPoint.PathAndQuery), requestBuffer);
                }
                else
                {
                    responseBuffer = wC.UploadData(_server.DnsOverHttpEndPoint, requestBuffer);
                }
            }

            //parse response
            using (MemoryStream mS = new MemoryStream(responseBuffer, false))
            {
                DnsDatagram response = new DnsDatagram(mS);

                response.SetMetadata(new DnsDatagramMetadata(_server, _protocol, responseBuffer.Length, (DateTime.UtcNow - sentAt).TotalMilliseconds));

                if (response.Header.Identifier == request.Header.Identifier)
                {
                    return(response);
                }
            }

            return(null);
        }
        public override DnsDatagram Query(DnsDatagram request)
        {
            //DoH JSON format request
            DateTime sentAt = DateTime.UtcNow;

            byte[] responseBuffer;

            using (WebClientEx wC = new WebClientEx())
            {
                wC.AddHeader("accept", "application/dns-json");
                wC.AddHeader("host", _server.DnsOverHttpEndPoint.Host + ":" + _server.DnsOverHttpEndPoint.Port);
                wC.UserAgent = "DoH client";
                wC.Proxy     = _proxy;
                wC.Timeout   = _timeout;

                Uri queryUri;

                if (_proxy == null)
                {
                    if (_server.IPEndPoint == null)
                    {
                        _server.RecursiveResolveIPAddress(new SimpleDnsCache());
                    }

                    queryUri = new Uri(_server.DnsOverHttpEndPoint.Scheme + "://" + _server.IPEndPoint.ToString() + _server.DnsOverHttpEndPoint.PathAndQuery);
                }
                else
                {
                    queryUri = _server.DnsOverHttpEndPoint;
                }

                wC.QueryString.Clear();
                wC.QueryString.Add("name", request.Question[0].Name);
                wC.QueryString.Add("type", Convert.ToString(((int)request.Question[0].Type)));

                responseBuffer = wC.DownloadData(queryUri);
            }

            //parse response
            dynamic jsonResponse = JsonConvert.DeserializeObject(Encoding.ASCII.GetString(responseBuffer));

            DnsDatagram response = new DnsDatagram(jsonResponse);

            response.SetMetadata(new DnsDatagramMetadata(_server, _protocol, responseBuffer.Length, (DateTime.UtcNow - sentAt).TotalMilliseconds));

            return(response);
        }
        protected override void UpdateTracker(TrackerClientEvent @event, IPEndPoint clientEP)
        {
            string queryString;

            queryString = "?info_hash=" + Uri.EscapeDataString(Encoding.ASCII.GetString(_infoHash)) +
                          "&peer_id=" + Uri.EscapeDataString(Encoding.ASCII.GetString(_clientID.PeerID));

            switch (clientEP.Address.ToString())
            {
            case "0.0.0.0":
            case "127.0.0.1":
            case "::":
            case "::1":
                break;

            default:
                if (clientEP.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
                {
                    queryString += "&ipv6=" + clientEP.Address.ToString();
                }
                else
                {
                    queryString += "&ip=" + clientEP.Address.ToString();
                }

                break;
            }

            queryString += "&port=" + clientEP.Port +
                           "&uploaded=0&downloaded=0&left=0&corrupt=0" +
                           "&key=" + BitConverter.ToString(_clientID.ClientKey).Replace("-", "");

            switch (@event)
            {
            case TrackerClientEvent.Started:
                queryString += "&event=started";
                break;

            case TrackerClientEvent.Stopped:
                queryString += "&event=stopped";
                break;

            case TrackerClientEvent.Completed:
                queryString += "&event=completed";
                break;
            }

            queryString += "&numwant=" + _clientID.NumWant;

            if (_clientID.Compact)
            {
                queryString += "&compact=1";
            }
            else
            {
                queryString += "&compact=0";
            }

            if (_clientID.NoPeerID)
            {
                queryString += "&no_peer_id=1";
            }

            using (WebClientEx webClient = new WebClientEx())
            {
                webClient.Proxy     = _proxy;
                webClient.Timeout   = 30000; //30 sec timeout
                webClient.UserAgent = _clientID.HttpUserAgent;
                webClient.AddHeader("Accept-Encoding", _clientID.HttpAcceptEncoding);
                webClient.KeepAlive = false;

                using (Stream responseStream = webClient.OpenRead(_trackerURI.AbsoluteUri + queryString))
                {
                    switch (@event)
                    {
                    case TrackerClientEvent.None:
                    case TrackerClientEvent.Started:
                        Bencoding x = Bencoding.Decode(responseStream);

                        switch (x.Type)
                        {
                        case BencodingType.Dictionary:
                            _peers.Clear();

                            foreach (var item in x.ValueDictionary)
                            {
                                switch (item.Key)
                                {
                                case "peers":
                                    switch (item.Value.Type)
                                    {
                                    case BencodingType.String:
                                        ParseCompactPeersIPv4(item.Value.Value as byte[], _peers);
                                        break;

                                    case BencodingType.List:
                                        foreach (var peerObj in item.Value.ValueList)
                                        {
                                            var peer = peerObj.ValueDictionary;

                                            _peers.Add(new IPEndPoint(IPAddress.Parse(peer["ip"].ValueString), Convert.ToInt32(peer["port"].ValueInteger)));
                                        }
                                        break;
                                    }
                                    break;

                                case "peers_ipv6":
                                case "peers6":
                                    switch (item.Value.Type)
                                    {
                                    case BencodingType.String:
                                        ParseCompactPeersIPv6(item.Value.Value as byte[], _peers);
                                        break;
                                    }
                                    break;

                                case "interval":
                                    if (item.Value.Type == BencodingType.Integer)
                                    {
                                        _interval = Convert.ToInt32(item.Value.Value);
                                    }
                                    break;

                                case "min interval":
                                    if (item.Value.Type == BencodingType.Integer)
                                    {
                                        _minInterval = Convert.ToInt32(item.Value.Value);
                                    }
                                    break;
                                }
                            }
                            break;

                        default:
                            throw new TrackerClientException("Invalid data received from tracker. Expected bencoded dictionary.");
                        }
                        break;
                    }
                }
            }
        }