/// <summary> /// Send a custom message to the dns server and returns the answer asynchronously. /// </summary> /// <param name="message">Message, that should be send to the dns server</param> /// <param name="requestCallback">An <see cref="System.AsyncCallback"/> delegate that references the method to invoked then the operation is complete.</param> /// <param name="state">A user-defined object that contains information about the receive operation. This object is passed to the <paramref name="requestCallback"/> delegate when the operation is complete.</param> /// <returns>An <see cref="System.IAsyncResult"/> IAsyncResult object that references the asynchronous receive.</returns> public IAsyncResult BeginSendMessage(DnsMessage message, AsyncCallback requestCallback, object state) { if (message == null) { throw new ArgumentNullException("message"); } if ((message.Questions == null) || (message.Questions.Count == 0)) { throw new ArgumentException("At least one question must be provided", "message"); } if (message.TransactionID == 0) { message.TransactionID = (ushort)new Random().Next(0xffff); } byte[] queryData; int queryLength = message.Encode(out queryData, true); DnsAsyncState asyncResult = new DnsAsyncState() { QueryData = queryData, QueryLength = queryLength, UserCallback = requestCallback, AsyncState = state, Servers = _dnsServers, ServerIndex = 0 }; if (message.TSigOptions != null) { asyncResult.TSigKeySelector = (a, n) => message.TSigOptions.KeyData; asyncResult.TSigOriginalMac = message.TSigOptions.OriginalMac; } bool sendByTcp = ((queryLength > 512) || (message.Questions[0].RecordType == RecordType.Axfr) || (message.Questions[0].RecordType == RecordType.Ixfr)); if (sendByTcp) { TcpBeginConnect(asyncResult); } else { UdpBeginSend(asyncResult); } return(asyncResult); }
private async void HandleUdpListenerAsync() { try { UdpReceiveResult receiveResult; try { receiveResult = await _udpListener.ReceiveAsync(); } catch (ObjectDisposedException) { return; } finally { lock (_listenerLock) { _hasActiveUdpListener = false; } } ClientConnectedEventArgs clientConnectedEventArgs = new ClientConnectedEventArgs(ProtocolType.Udp, receiveResult.RemoteEndPoint); await ClientConnected.RaiseAsync(this, clientConnectedEventArgs); if (clientConnectedEventArgs.RefuseConnect) { return; } StartUdpListenerTask(); byte[] buffer = receiveResult.Buffer; DnsMessageBase query; byte[] originalMac; try { query = DnsMessageBase.CreateByFlag(buffer, TsigKeySelector, null); originalMac = query.TSigOptions?.Mac; } catch (Exception e) { throw new Exception("Error parsing dns query", e); } DnsMessageBase response; try { response = await ProcessMessageAsync(query, ProtocolType.Udp, receiveResult.RemoteEndPoint); } catch (Exception ex) { OnExceptionThrownAsync(ex); response = null; } if (response == null) { response = query; query.IsQuery = false; query.ReturnCode = ReturnCode.ServerFailure; } int length = response.Encode(false, originalMac, out buffer); #region Truncating DnsMessage message = response as DnsMessage; if (message != null) { int maxLength = 512; if (query.IsEDnsEnabled && message.IsEDnsEnabled) { maxLength = Math.Max(512, (int)message.EDnsOptions.UdpPayloadSize); } while (length > maxLength) { // First step: remove data from additional records except the opt record if ((message.IsEDnsEnabled && (message.AdditionalRecords.Count > 1)) || (!message.IsEDnsEnabled && (message.AdditionalRecords.Count > 0))) { for (int i = message.AdditionalRecords.Count - 1; i >= 0; i--) { if (message.AdditionalRecords[i].RecordType != RecordType.Opt) { message.AdditionalRecords.RemoveAt(i); } } length = message.Encode(false, originalMac, out buffer); continue; } int savedLength = 0; if (message.AuthorityRecords.Count > 0) { for (int i = message.AuthorityRecords.Count - 1; i >= 0; i--) { savedLength += message.AuthorityRecords[i].MaximumLength; message.AuthorityRecords.RemoveAt(i); if ((length - savedLength) < maxLength) { break; } } message.IsTruncated = true; length = message.Encode(false, originalMac, out buffer); continue; } if (message.AnswerRecords.Count > 0) { for (int i = message.AnswerRecords.Count - 1; i >= 0; i--) { savedLength += message.AnswerRecords[i].MaximumLength; message.AnswerRecords.RemoveAt(i); if ((length - savedLength) < maxLength) { break; } } message.IsTruncated = true; length = message.Encode(false, originalMac, out buffer); continue; } if (message.Questions.Count > 0) { for (int i = message.Questions.Count - 1; i >= 0; i--) { savedLength += message.Questions[i].MaximumLength; message.Questions.RemoveAt(i); if ((length - savedLength) < maxLength) { break; } } message.IsTruncated = true; length = message.Encode(false, originalMac, out buffer); } } } #endregion await _udpListener.SendAsync(buffer, length, receiveResult.RemoteEndPoint); } catch (Exception ex) { OnExceptionThrownAsync(ex); } finally { lock (_listenerLock) { _availableUdpListener++; } StartUdpListenerTask(); } }
private void EndUdpReceive(IAsyncResult ar) { try { lock (_udpListener) { _hasActiveUdpListener = false; } StartUdpListen(); IPEndPoint endpoint; byte[] buffer = _udpListener.EndReceive(ar, out endpoint); DnsMessageBase query; byte[] originalMac; try { query = DnsMessageBase.CreateByFlag(buffer, TsigKeySelector, null); originalMac = (query.TSigOptions == null) ? null : query.TSigOptions.Mac; } catch (Exception e) { throw new Exception("Error parsing dns query", e); } DnsMessageBase response; try { response = ProcessMessage(query, endpoint.Address, ProtocolType.Udp); } catch (Exception ex) { OnExceptionThrown(ex); response = null; } if (response == null) { response = query; query.IsQuery = false; query.ReturnCode = ReturnCode.ServerFailure; } int length = response.Encode(false, originalMac, out buffer); #region Truncating DnsMessage message = response as DnsMessage; if (message != null) { int maxLength = 512; if (query.IsEDnsEnabled && message.IsEDnsEnabled) { maxLength = Math.Max(512, (int)message.EDnsOptions.UdpPayloadSize); } while (length > maxLength) { // First step: remove data from additional records except the opt record if ((message.IsEDnsEnabled && (message.AdditionalRecords.Count > 1)) || (!message.IsEDnsEnabled && (message.AdditionalRecords.Count > 0))) { for (int i = message.AdditionalRecords.Count - 1; i >= 0; i--) { if (message.AdditionalRecords[i].RecordType != RecordType.Opt) { message.AdditionalRecords.RemoveAt(i); } } length = message.Encode(false, originalMac, out buffer); continue; } int savedLength = 0; if (message.AuthorityRecords.Count > 0) { for (int i = message.AuthorityRecords.Count - 1; i >= 0; i--) { savedLength += message.AuthorityRecords[i].MaximumLength; message.AuthorityRecords.RemoveAt(i); if ((length - savedLength) < maxLength) { break; } } message.IsTruncated = true; length = message.Encode(false, originalMac, out buffer); continue; } if (message.AnswerRecords.Count > 0) { for (int i = message.AnswerRecords.Count - 1; i >= 0; i--) { savedLength += message.AnswerRecords[i].MaximumLength; message.AnswerRecords.RemoveAt(i); if ((length - savedLength) < maxLength) { break; } } message.IsTruncated = true; length = message.Encode(false, originalMac, out buffer); continue; } if (message.Questions.Count > 0) { for (int i = message.Questions.Count - 1; i >= 0; i--) { savedLength += message.Questions[i].MaximumLength; message.Questions.RemoveAt(i); if ((length - savedLength) < maxLength) { break; } } message.IsTruncated = true; length = message.Encode(false, originalMac, out buffer); } } } #endregion _udpListener.BeginSend(buffer, 0, length, endpoint, EndUdpSend, null); } catch (Exception e) { HandleUdpException(e); } }
public async Task <ArraySegment <byte>?> HandleUdpMessage(IPEndPoint remoteEp, byte[] buffer) { try { ClientConnectedEventArgs clientConnectedEventArgs = new ClientConnectedEventArgs(ProtocolType.Udp, remoteEp); await ClientConnected.RaiseAsync(this, clientConnectedEventArgs); if (clientConnectedEventArgs.RefuseConnect) { return(null); } DnsMessageBase query; byte[] originalMac; try { query = DnsMessageBase.CreateByFlag(buffer, TsigKeySelector, null); originalMac = query.TSigOptions?.Mac; } catch (Exception e) { throw new Exception("Error parsing dns query", e); } DnsMessageBase response; try { response = await ProcessMessageAsync(query, ProtocolType.Udp, remoteEp); } catch (Exception ex) { OnExceptionThrownAsync(ex); response = null; } if (response == null) { response = query; query.IsQuery = false; query.ReturnCode = ReturnCode.ServerFailure; } int length = response.Encode(false, originalMac, out buffer); #region Truncating DnsMessage message = response as DnsMessage; if (message != null) { int maxLength = 512; if (query.IsEDnsEnabled && message.IsEDnsEnabled) { maxLength = Math.Max(512, (int)message.EDnsOptions.UdpPayloadSize); } while (length > maxLength) { // First step: remove data from additional records except the opt record if ((message.IsEDnsEnabled && (message.AdditionalRecords.Count > 1)) || (!message.IsEDnsEnabled && (message.AdditionalRecords.Count > 0))) { for (int i = message.AdditionalRecords.Count - 1; i >= 0; i--) { if (message.AdditionalRecords[i].RecordType != RecordType.Opt) { message.AdditionalRecords.RemoveAt(i); } } length = message.Encode(false, originalMac, out buffer); continue; } int savedLength = 0; if (message.AuthorityRecords.Count > 0) { for (int i = message.AuthorityRecords.Count - 1; i >= 0; i--) { savedLength += message.AuthorityRecords[i].MaximumLength; message.AuthorityRecords.RemoveAt(i); if ((length - savedLength) < maxLength) { break; } } message.IsTruncated = true; length = message.Encode(false, originalMac, out buffer); continue; } if (message.AnswerRecords.Count > 0) { for (int i = message.AnswerRecords.Count - 1; i >= 0; i--) { savedLength += message.AnswerRecords[i].MaximumLength; message.AnswerRecords.RemoveAt(i); if ((length - savedLength) < maxLength) { break; } } message.IsTruncated = true; length = message.Encode(false, originalMac, out buffer); continue; } if (message.Questions.Count > 0) { for (int i = message.Questions.Count - 1; i >= 0; i--) { savedLength += message.Questions[i].MaximumLength; message.Questions.RemoveAt(i); if ((length - savedLength) < maxLength) { break; } } message.IsTruncated = true; length = message.Encode(false, originalMac, out buffer); } } } #endregion return(new ArraySegment <byte>(buffer, 0, length)); } catch (Exception ex) { OnExceptionThrownAsync(ex); return(null); } }
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); }
public static void ReturnDnsMessageServerFailure(DnsMessage message, out byte[] buffer) { message.ReturnCode = ReturnCode.ServerFailure; message.IsQuery = false; message.Encode(false, out buffer); }