protected override byte[] GetBytes() { var dnsMessage = new DnsMessage(); var question = new DnsQuestion(DomainName.Parse(_domain), RecordType.Txt, RecordClass.INet); dnsMessage.Questions.Add(question); dnsMessage.IsRecursionDesired = true; return(dnsMessage.Encode()); }
private async Task ForwardMessage(DnsMessage message, UdpReceiveResult originalUdpMessage, IPEndPoint targetNameServer, int queryTimeout, bool useCompressionMutation) { DnsQuestion question = null; if (message.Questions.Count > 0) { question = message.Questions[0]; } byte[] responseBuffer = null; try { if ((Equals(targetNameServer.Address, IPAddress.Loopback) || Equals(targetNameServer.Address, IPAddress.IPv6Loopback)) && targetNameServer.Port == ((IPEndPoint)_udpListener.Client.LocalEndPoint).Port) { throw new InfiniteForwardingException(question); } byte[] sendBuffer; if (useCompressionMutation) { message.Encode(false, out sendBuffer, true); } else { sendBuffer = originalUdpMessage.Buffer; } _transactionClients[message.TransactionID] = originalUdpMessage.RemoteEndPoint; // Send to Forwarder await _udpForwarder.SendAsync(sendBuffer, sendBuffer.Length, targetNameServer); if (_transactionTimeoutCancellationTokenSources.ContainsKey(message.TransactionID)) { _transactionTimeoutCancellationTokenSources[message.TransactionID].Cancel(); } var cancellationTokenSource = new CancellationTokenSource(); _transactionTimeoutCancellationTokenSources[message.TransactionID] = cancellationTokenSource; // Timeout task to cancel the request try { await Task.Delay(queryTimeout, cancellationTokenSource.Token); if (!_transactionClients.ContainsKey(message.TransactionID)) { return; } IPEndPoint ignoreEndPoint; CancellationTokenSource ignoreTokenSource; _transactionClients.TryRemove(message.TransactionID, out ignoreEndPoint); _transactionTimeoutCancellationTokenSources.TryRemove(message.TransactionID, out ignoreTokenSource); var warningText = message.Questions.Count > 0 ? $"{message.Questions[0].Name} (Type {message.Questions[0].RecordType})" : $"Transaction #{message.TransactionID}"; Logger.Warning("Query timeout for: {0}", warningText); } catch (TaskCanceledException) { } } catch (InfiniteForwardingException e) { Logger.Warning("[Forwarder.Send] Infinite forwarding detected for: {0} (Type {1})", e.Question.Name, e.Question.RecordType); Utils.ReturnDnsMessageServerFailure(message, out responseBuffer); } catch (SocketException e) { if (e.SocketErrorCode == SocketError.ConnectionReset) // Target name server port unreachable { Logger.Warning("[Forwarder.Send] Name server port unreachable: {0}", targetNameServer); } else { Logger.Error("[Forwarder.Send] Unhandled socket error: {0}", e.Message); } Utils.ReturnDnsMessageServerFailure(message, out responseBuffer); } catch (Exception e) { Logger.Error("[Forwarder] Unexpected exception:\n{0}", e); Utils.ReturnDnsMessageServerFailure(message, out responseBuffer); } // If we got some errors if (responseBuffer != null) { await _udpListener.SendAsync(responseBuffer, responseBuffer.Length, originalUdpMessage.RemoteEndPoint); } }
private async void ProcessMessageAsync(UdpReceiveResult udpMessage) { await Task.Run(async() => { DnsMessage message = new DnsMessage(); DnsQuestion question; try { var respondedFromCache = false; try { message = DnsMessage.Parse(udpMessage.Buffer); question = message.Questions[0]; } catch (Exception) { throw new ParsingException(); } // Check for authorized subnet access var allowedClient = _options.AllowedClientIPs; var clientIP = udpMessage.RemoteEndPoint.Address; if ((allowedClient != null) && (allowedClient.Count > 0)) { if (allowedClient.All(ipNetwork => !IPNetwork.Contains(ipNetwork, clientIP))) { logger.Warn($"{clientIP} is not authorized."); throw new AuthorizationException(); } } logger.Info($"{clientIP} requested {question.Name} (#{message.TransactionID}, {question.RecordType})."); // Query cache if (_options.CacheResponse) { if (Cache.ContainsKey(question.Name) && Cache[question.Name].ContainsKey(question.RecordType)) { var entry = Cache[question.Name][question.RecordType]; if (!entry.IsExpired) { var cachedMessage = entry.Message; logger.Info($"-> #{message.TransactionID} served from cache."); cachedMessage.TransactionID = message.TransactionID; // Update transaction ID cachedMessage.TSigOptions = message.TSigOptions; // Update TSig _options message = cachedMessage; respondedFromCache = true; } } } var targetNameServer = _options.LocalNameServer; var useHttpQuery = _options.UseHttpQuery; var queryTimeout = _options.QueryTimeout; var useCompressionMutation = _options.CompressionMutation; // Match rules if (message.IsQuery && (question.RecordType == RecordType.A || question.RecordType == RecordType.Aaaa)) { for (var i = Rules.Count - 1; i >= 0; i--) { var match = Regex.Match(question.Name, Rules[i].Pattern); if (!match.Success) { continue; } // Domain name matched var recordType = question.RecordType; if (Rules[i].ForceAAAA != null && Rules[i].ForceAAAA.Value) // RecordType override { recordType = RecordType.Aaaa; } if (Rules[i].NameServer != null) // Name server override { targetNameServer = Rules[i].NameServer; } if (Rules[i].UseHttpQuery != null) // HTTP query override { useHttpQuery = Rules[i].UseHttpQuery.Value; } if (Rules[i].QueryTimeout != null) // Query timeout override { queryTimeout = Rules[i].QueryTimeout.Value; } if (Rules[i].CompressionMutation != null) // Compression pointer mutation override { useCompressionMutation = Rules[i].CompressionMutation.Value; } if (Rules[i].Address != null) { IPAddress ip; IPAddress.TryParse(Rules[i].Address, out ip); if (ip == null) // Invalid IP, may be a domain name { var address = string.Format(Rules[i].Address, match.Groups.Cast <object>().ToArray()); if (recordType == RecordType.A && useHttpQuery) { await ResolveWithHttp(targetNameServer, address, queryTimeout, message); } else { var serverEndpoint = Utils.CreateIpEndPoint(targetNameServer, 53); var dnsClient = new DnsClient(serverEndpoint.Address, queryTimeout, serverEndpoint.Port); var response = await Task <DnsMessage> .Factory.FromAsync(dnsClient.BeginResolve, dnsClient.EndResolve, address, recordType, question.RecordClass, null); if (response == null) { logger.Warn($"Remote resolve failed for {address}."); return; } foreach (var answerRecord in response.AnswerRecords) { answerRecord.Name = question.Name; message.AnswerRecords.Add(answerRecord); } message.ReturnCode = response.ReturnCode; message.IsQuery = false; } } else { if (recordType == RecordType.A && ip.AddressFamily == AddressFamily.InterNetwork) { message.AnswerRecords.Add(new ARecord(question.Name, 600, ip)); } else if (recordType == RecordType.Aaaa && ip.AddressFamily == AddressFamily.InterNetworkV6) { message.AnswerRecords.Add(new AaaaRecord(question.Name, 600, ip)); } else // Type mismatch { continue; } message.ReturnCode = ReturnCode.NoError; message.IsQuery = false; } } break; } } // TODO: Consider how to integrate System.Net.Dns with this project. // Using System.Net.Dns to forward query if compression mutation is disabled //if (message.IsQuery && !useCompressionMutation && // (question.RecordType == RecordType.A || question.RecordType == RecordType.Aaaa)) //{ // var dnsResponse = await Dns.GetHostAddressesAsync(question.Name); // if (question.RecordType == RecordType.A) // { // message.AnswerRecords.AddRange(dnsResponse.Where( // ip => ip.AddressFamily == AddressFamily.InterNetwork).Select( // ip => new ARecord(question.Name, 0, ip))); // else if (question.RecordType == RecordType.Aaaa) // { // message.AnswerRecords.AddRange(dnsResponse.Where( // ip => ip.AddressFamily == AddressFamily.InterNetworkV6).Select( // ip => new AaaaRecord(question.Name, 0, ip))); // } // message.ReturnCode = ReturnCode.NoError; // message.IsQuery = false; //} if (message.IsQuery && question.RecordType == RecordType.A && useHttpQuery) { await ResolveWithHttp(targetNameServer, question.Name, queryTimeout, message); } if (message.IsQuery) { // Use internal forwarder to forward query to another name server await ForwardMessage(message, udpMessage, Utils.CreateIpEndPoint(targetNameServer, 53), queryTimeout, useCompressionMutation); } else { // Already answered, directly return to the client byte[] responseBuffer; message.Encode(false, out responseBuffer); if (responseBuffer != null) { await _udpListener.SendAsync(responseBuffer, responseBuffer.Length, udpMessage.RemoteEndPoint); // Update cache if (_options.CacheResponse && !respondedFromCache) { Cache.Update(question, message, _options.CacheAge); } } } } catch (ParsingException) { } catch (AuthorizationException) { message.ReturnCode = ReturnCode.Refused; message.IsQuery = false; // Already answered, directly return to the client byte[] responseBuffer; message.Encode(false, out responseBuffer); if (responseBuffer != null) { await _udpListener.SendAsync(responseBuffer, responseBuffer.Length, udpMessage.RemoteEndPoint); } } catch (SocketException e) { logger.Error("[Listener.Send] Unexpected socket error:\n{0}", e); } catch (Exception e) { logger.Error("[Processor] Unexpected exception:\n{0}", e); } }); }
public static void ReturnDnsMessageServerFailure(DnsMessage message, out byte[] buffer) { message.ReturnCode = ReturnCode.ServerFailure; message.IsQuery = false; message.Encode(false, out buffer); }
private bool ForwardMessage(DnsMessage message, IPEndPoint revicepoint, byte[] buf, IPEndPoint targetNameServer, int queryTimeout, bool useCompressionMutation) { DnsQuestion question = null; if (message.Questions.Count > 0) { question = message.Questions[0]; } byte[] responseBuffer = null; try { if ((Equals(targetNameServer.Address, IPAddress.Loopback) || Equals(targetNameServer.Address, IPAddress.IPv6Loopback)) && targetNameServer.Port == ((IPEndPoint)_UdpListener.Client.LocalEndPoint).Port) { throw new InfiniteForwardingException(question); } byte[] sendBuffer; if (useCompressionMutation) { message.Encode(false, out sendBuffer, true); } else { sendBuffer = buf; } _transactionClients[message.TransactionID] = revicepoint; // Send to Forwarder /*await _udpForwarder.SendAsync(sendBuffer, sendBuffer.Length, targetNameServer);*/ _UdpForwarder.Send(sendBuffer, sendBuffer.Length, targetNameServer); if (_transactionTimeoutCancellationTokenSources.ContainsKey(message.TransactionID)) { _transactionTimeoutCancellationTokenSources[message.TransactionID].Cancel(); } MrTe.Threading.Tasks.CancellationTokenSource cancellationTokenSource = new MrTe.Threading.Tasks.CancellationTokenSource(); _transactionTimeoutCancellationTokenSources[message.TransactionID] = cancellationTokenSource; return(true); // Timeout task to cancel the request /*try * {*/ /*await Task.Delay(queryTimeout, cancellationTokenSource.Token);*/ Task.Delay(queryTimeout, cancellationTokenSource.Token); if (!_transactionClients.ContainsKey(message.TransactionID)) { return(false); } IPEndPoint ignoreEndPoint; MrTe.Threading.Tasks.CancellationTokenSource ignoreTokenSource; /* * _transactionClients.TryRemove(message.TransactionID, out ignoreEndPoint); * _transactionTimeoutCancellationTokenSources.TryRemove(message.TransactionID, * out ignoreTokenSource); */ ignoreEndPoint = _transactionClients[message.TransactionID]; ignoreTokenSource = _transactionTimeoutCancellationTokenSources[message.TransactionID]; string warningText = message.Questions.Count > 0 ? message.Questions[0].Name + " (Type " + message.Questions[0].RecordType + ")" : "Transaction #" + message.TransactionID + ""; Logger.Warning("Query timeout for: {0}", warningText); _transactionClients.Remove(message.TransactionID); _transactionTimeoutCancellationTokenSources.Remove(message.TransactionID); /* * } * catch (TaskCanceledException) * { * } */ } catch (InfiniteForwardingException e) { Logger.Warning("[Forwarder.Send] Infinite forwarding detected for: {0} (Type {1})", e.Question.Name, e.Question.RecordType); Utils.ReturnDnsMessageServerFailure(message, out responseBuffer); } catch (SocketException e) { if (e.SocketErrorCode == SocketError.ConnectionReset) // Target name server port unreachable { Logger.Warning("[Forwarder.Send] Name server port unreachable: {0}", targetNameServer); } else { Logger.Error("[Forwarder.Send] Unhandled socket error: {0}", e.Message); } Utils.ReturnDnsMessageServerFailure(message, out responseBuffer); } catch (Exception e) { Logger.Error("[Forwarder] Unexpected exception:\n{0}", e); Utils.ReturnDnsMessageServerFailure(message, out responseBuffer); } // If we got some errors if (responseBuffer != null) { /*await _udpListener.SendAsync(responseBuffer, responseBuffer.Length, originalUdpMessage.RemoteEndPoint);*/ _UdpListener.Send(responseBuffer, responseBuffer.Length, revicepoint); } return(true); }