public override DnsResponseMessage Query( IPEndPoint server, DnsRequestMessage request, TimeSpan timeout) { UdpClient udpClient = GetNextUdpClient(server.AddressFamily); // -1 indicates infinite int timeoutInMillis = timeout.TotalMilliseconds >= int.MaxValue ? -1 : (int)timeout.TotalMilliseconds; udpClient.Client.ReceiveTimeout = timeoutInMillis; udpClient.Client.SendTimeout = timeoutInMillis; bool mustDispose = false; try { using (var writer = new DnsDatagramWriter()) { GetRequestData(request, writer); udpClient.Client.SendTo(writer.Data.Array, writer.Data.Offset, writer.Data.Count, SocketFlags.None, server); } var readSize = udpClient.Available > MaxSize ? udpClient.Available : MaxSize; using (var memory = new PooledBytes(readSize)) { var received = udpClient.Client.Receive(memory.Buffer, 0, readSize, SocketFlags.None); var response = GetResponseMessage(new ArraySegment <byte>(memory.Buffer, 0, received)); Enqueue(server.AddressFamily, udpClient); return(response); } } catch { mustDispose = true; throw; } finally { if (!_enableClientQueue || mustDispose) { try { #if !NET45 udpClient.Dispose(); #else udpClient.Close(); #endif } catch { } } } }
public virtual void GetRequestData(DnsRequestMessage request, DnsDatagramWriter writer) { var question = request.Question; /* * 1 1 1 1 1 1 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ID | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |QR| Opcode |AA|TC|RD|RA| Z | RCODE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QDCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ANCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | NSCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ARCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ * */ // 4 more bytes for the type and class writer.WriteInt16NetworkOrder((short)request.Header.Id); writer.WriteUInt16NetworkOrder(request.Header.RawFlags); writer.WriteInt16NetworkOrder(1); // we support single question only... (as most DNS servers anyways). writer.WriteInt16NetworkOrder(0); writer.WriteInt16NetworkOrder(0); writer.WriteInt16NetworkOrder(1); // one additional for the Opt record. writer.WriteHostName(question.QueryName); writer.WriteUInt16NetworkOrder((ushort)question.QuestionType); writer.WriteUInt16NetworkOrder((ushort)question.QuestionClass); /* +------------+--------------+------------------------------+ | Field Name | Field Type | Description | +------------+--------------+------------------------------+ | NAME | domain name | MUST be 0 (root domain) | | TYPE | u_int16_t | OPT (41) | | CLASS | u_int16_t | requestor's UDP payload size | | TTL | u_int32_t | extended RCODE and flags | | RDLEN | u_int16_t | length of all RDATA | | RDATA | octet stream | {attribute,value} pairs | +------------+--------------+------------------------------+ * */ var opt = new OptRecord(); writer.WriteHostName(""); writer.WriteUInt16NetworkOrder((ushort)opt.RecordType); writer.WriteUInt16NetworkOrder((ushort)opt.RecordClass); writer.WriteUInt32NetworkOrder((ushort)opt.InitialTimeToLive); writer.WriteUInt16NetworkOrder(0); }
public virtual void GetRequestData(DnsRequestMessage request, DnsDatagramWriter writer) { var question = request.Question; /* * 1 1 1 1 1 1 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ID | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |QR| Opcode |AA|TC|RD|RA| Z | RCODE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QDCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ANCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | NSCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ARCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ * */ // 4 more bytes for the type and class writer.WriteInt16NetworkOrder((short)request.Header.Id); writer.WriteUInt16NetworkOrder(request.Header.RawFlags); writer.WriteInt16NetworkOrder(1); // we support single question only... (as most DNS servers anyways). writer.WriteInt16NetworkOrder(0); writer.WriteInt16NetworkOrder(0); if (request.QuerySettings.UseExtendedDns) { writer.WriteInt16NetworkOrder(1); // one additional for the Opt record. } else { writer.WriteInt16NetworkOrder(0); } writer.WriteHostName(question.QueryName); writer.WriteUInt16NetworkOrder((ushort)question.QuestionType); writer.WriteUInt16NetworkOrder((ushort)question.QuestionClass); if (request.QuerySettings.UseExtendedDns) { var opt = new OptRecord(size: request.QuerySettings.ExtendedDnsBufferSize, doFlag: request.QuerySettings.RequestDnsSecRecords); writer.WriteHostName(""); writer.WriteUInt16NetworkOrder((ushort)opt.RecordType); writer.WriteUInt16NetworkOrder((ushort)opt.RecordClass); writer.WriteUInt32NetworkOrder((ushort)opt.InitialTimeToLive); writer.WriteUInt16NetworkOrder(0); } }
public override async Task <DnsResponseMessage> QueryAsync( IPEndPoint server, DnsRequestMessage request, CancellationToken cancellationToken, Action <Action> cancelationCallback) { cancellationToken.ThrowIfCancellationRequested(); UdpClient udpClient = GetNextUdpClient(server.AddressFamily); bool mustDispose = false; try { // setup timeout cancelation, dispose socket (the only way to acutally cancel the request in async... cancelationCallback(() => { #if PORTABLE udpClient.Dispose(); #else udpClient.Close(); #endif }); using (var writer = new DnsDatagramWriter()) { GetRequestData(request, writer); await udpClient.SendAsync(writer.Data.Array, writer.Data.Count, server).ConfigureAwait(false); } var readSize = udpClient.Available > MaxSize ? udpClient.Available : MaxSize; using (var memory = new PooledBytes(readSize)) { #if PORTABLE int received = await udpClient.Client.ReceiveAsync(new ArraySegment <byte>(memory.Buffer), SocketFlags.None).ConfigureAwait(false); var response = GetResponseMessage(new ArraySegment <byte>(memory.Buffer, 0, received)); #else var result = await udpClient.ReceiveAsync().ConfigureAwait(false); var response = GetResponseMessage(new ArraySegment <byte>(result.Buffer, 0, result.Buffer.Length)); #endif if (request.Header.Id != response.Header.Id) { throw new DnsResponseException("Header id missmatch."); } Enqueue(server.AddressFamily, udpClient); return(response); } } catch (ObjectDisposedException) { // we disposed it in case of a timeout request, lets indicate it actually timed out... throw new TimeoutException(); } catch { mustDispose = true; throw; } finally { if (!_enableClientQueue || mustDispose) { try { #if PORTABLE udpClient.Dispose(); #else udpClient.Close(); #endif } catch { } } } }
public override async Task <DnsResponseMessage> QueryAsync( IPEndPoint server, DnsRequestMessage request, CancellationToken cancellationToken, Action <Action> cancelationCallback) { cancellationToken.ThrowIfCancellationRequested(); using (var client = new TcpClient(server.AddressFamily)) { cancelationCallback(() => { #if PORTABLE client.Dispose(); #else client.Close(); #endif }); cancellationToken.ThrowIfCancellationRequested(); await client.ConnectAsync(server.Address, server.Port).ConfigureAwait(false); using (var stream = client.GetStream()) { // use a pooled buffer to writer the data + the length of the data later into the frist two bytes using (var memory = new PooledBytes(DnsDatagramWriter.BufferSize + 2)) using (var writer = new DnsDatagramWriter(new ArraySegment <byte>(memory.Buffer, 2, memory.Buffer.Length - 2))) { GetRequestData(request, writer); int dataLength = writer.Index; memory.Buffer[0] = (byte)((dataLength >> 8) & 0xff); memory.Buffer[1] = (byte)(dataLength & 0xff); //var sendData = new byte[dataLength + 2]; //sendData[0] = (byte)((dataLength >> 8) & 0xff); //sendData[1] = (byte)(dataLength & 0xff); //Array.Copy(data, 0, sendData, 2, dataLength); await stream.WriteAsync(memory.Buffer, 0, dataLength + 2, cancellationToken).ConfigureAwait(false); await stream.FlushAsync(cancellationToken).ConfigureAwait(false); } int length = stream.ReadByte() << 8 | stream.ReadByte(); if (length <= 0) { throw new DnsResponseException("Received no answer."); } var resultData = new byte[length]; int bytesReceived = 0; while (bytesReceived < length) { int read = await stream.ReadAsync(resultData, bytesReceived, length - bytesReceived, cancellationToken).ConfigureAwait(false); bytesReceived += read; if (read == 0 && bytesReceived < length) { // disconnected throw new SocketException(-1); } } var response = GetResponseMessage(new ArraySegment <byte>(resultData, 0, bytesReceived)); if (request.Header.Id != response.Header.Id) { throw new DnsResponseException("Header id missmatch."); } return(response); } } }
private async Task <DnsResponseMessage> QueryAsyncInternal( Stream stream, DnsRequestMessage request, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); // use a pooled buffer to writer the data + the length of the data later into the frist two bytes using (var memory = new PooledBytes(DnsDatagramWriter.BufferSize + 2)) { var writer = new DnsDatagramWriter(new ArraySegment <byte>(memory.Buffer, 2, memory.Buffer.Length - 2)); GetRequestData(request, writer); var dataLength = writer.Index; memory.Buffer[0] = (byte)((dataLength >> 8) & 0xff); memory.Buffer[1] = (byte)(dataLength & 0xff); //await client.Client.SendAsync(new ArraySegment<byte>(memory.Buffer, 0, dataLength + 2), SocketFlags.None).ConfigureAwait(false); await stream.WriteAsync(memory.Buffer, 0, dataLength + 2, cancellationToken).ConfigureAwait(false); await stream.FlushAsync(cancellationToken).ConfigureAwait(false); } if (!stream.CanRead) { return(null); } cancellationToken.ThrowIfCancellationRequested(); int length; try { length = stream.ReadByte() << 8 | stream.ReadByte(); } catch (Exception ex) when(ex is IOException || ex is SocketException) { return(null); } if (length <= 0) { // server signals close/disconnecting return(null); } using (var memory = new PooledBytes(length)) { var bytesReceived = 0; var readSize = length > 4096 ? 4096 : length; while ((bytesReceived += await stream.ReadAsync(memory.Buffer, bytesReceived, readSize).ConfigureAwait(false)) < length) { if (bytesReceived <= 0) { // disconnected return(null); } if (bytesReceived + readSize > length) { readSize = length - bytesReceived; if (readSize <= 0) { break; } } } var response = GetResponseMessage(new ArraySegment <byte>(memory.Buffer, 0, bytesReceived)); if (request.Header.Id != response.Header.Id) { throw new DnsResponseException("Header id mismatch."); } return(response); } }
private async Task <DnsResponseMessage> QueryAsyncInternal(TcpClient client, DnsRequestMessage request, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var stream = client.GetStream(); // use a pooled buffer to writer the data + the length of the data later into the first two bytes using (var memory = new PooledBytes(DnsDatagramWriter.BufferSize + 2)) using (var writer = new DnsDatagramWriter(new ArraySegment <byte>(memory.Buffer, 2, memory.Buffer.Length - 2))) { GetRequestData(request, writer); int dataLength = writer.Index; memory.Buffer[0] = (byte)((dataLength >> 8) & 0xff); memory.Buffer[1] = (byte)(dataLength & 0xff); //await client.Client.SendAsync(new ArraySegment<byte>(memory.Buffer, 0, dataLength + 2), SocketFlags.None).ConfigureAwait(false); await stream.WriteAsync(memory.Buffer, 0, dataLength + 2, cancellationToken).ConfigureAwait(false); await stream.FlushAsync(cancellationToken).ConfigureAwait(false); } if (!stream.CanRead) { return(null); } cancellationToken.ThrowIfCancellationRequested(); var responses = new List <DnsResponseMessage>(); do { int length; try { //length = stream.ReadByte() << 8 | stream.ReadByte(); using (var lengthBuffer = new PooledBytes(2)) { int bytesReceived = 0, read; while ((bytesReceived += (read = await stream.ReadAsync(lengthBuffer.Buffer, bytesReceived, 2, cancellationToken).ConfigureAwait(false))) < 2) { if (read <= 0) { // disconnected, might retry return(null); } } length = lengthBuffer.Buffer[0] << 8 | lengthBuffer.Buffer[1]; } } catch (Exception ex) when(ex is IOException || ex is SocketException) { return(null); } if (length <= 0) { // server signals close/disconnecting return(null); } using (var memory = new PooledBytes(length)) { int bytesReceived = 0, read; int readSize = length > 4096 ? 4096 : length; while (!cancellationToken.IsCancellationRequested && (bytesReceived += (read = await stream.ReadAsync(memory.Buffer, bytesReceived, readSize, cancellationToken).ConfigureAwait(false))) < length) { if (read <= 0) { // disconnected return(null); } if (bytesReceived + readSize > length) { readSize = length - bytesReceived; if (readSize <= 0) { break; } } } DnsResponseMessage response = GetResponseMessage(new ArraySegment <byte>(memory.Buffer, 0, bytesReceived)); if (request.Header.Id != response.Header.Id) { throw new DnsResponseException("Header id mismatch."); } responses.Add(response); } } while (stream.DataAvailable && !cancellationToken.IsCancellationRequested); return(DnsResponseMessage.Combine(responses)); }
public override async Task <DnsResponseMessage> QueryAsync( IPEndPoint server, DnsRequestMessage request, CancellationToken cancellationToken, Action <Action> cancelationCallback) { cancellationToken.ThrowIfCancellationRequested(); var udpClient = GetNextUdpClient(server.AddressFamily); var mustDispose = false; try { // setup timeout cancelation, dispose socket (the only way to acutally cancel the request in async... cancelationCallback(() => { udpClient.Close(); }); using (var pool = new PooledBytes(DnsDatagramWriter.BufferSize)) { var writer = new DnsDatagramWriter(new ArraySegment <byte>(pool.Buffer)); GetRequestData(request, writer); await udpClient.SendAsync(writer.Data.Array, writer.Data.Count, server).ConfigureAwait(false); } var result = await udpClient.ReceiveAsync().ConfigureAwait(false); var response = GetResponseMessage(new ArraySegment <byte>(result.Buffer, 0, result.Buffer.Length)); if (request.Header.Id != response.Header.Id) { throw new DnsResponseException("Header id mismatch."); } Enqueue(server.AddressFamily, udpClient); return(response); } catch (ObjectDisposedException) { // we disposed it in case of a timeout request, lets indicate it actually timed out... throw new TimeoutException(); } catch { mustDispose = true; throw; } finally { if (!_enableClientQueue || mustDispose) { try { udpClient.Close(); } catch { // ignored } } } }
public override DnsResponseMessage Query( IPEndPoint server, DnsRequestMessage request, TimeSpan timeout) { var udpClient = GetNextUdpClient(server.AddressFamily); // -1 indicates infinite var timeoutInMillis = timeout.TotalMilliseconds >= int.MaxValue ? -1 : (int)timeout.TotalMilliseconds; udpClient.Client.ReceiveTimeout = timeoutInMillis; udpClient.Client.SendTimeout = timeoutInMillis; var mustDispose = false; try { using (var pool = new PooledBytes(DnsDatagramWriter.BufferSize)) { var writer = new DnsDatagramWriter(new ArraySegment <byte>(pool.Buffer)); GetRequestData(request, writer); Debug.Assert(writer.Data.Array != null); udpClient.Client.SendTo(writer.Data.Array, writer.Data.Offset, writer.Data.Count, SocketFlags.None, server); } var readSize = udpClient.Available > MaxSize ? udpClient.Available : MaxSize; using (var memory = new PooledBytes(readSize)) { var received = udpClient.Client.Receive(memory.Buffer, 0, readSize, SocketFlags.None); var response = GetResponseMessage(new ArraySegment <byte>(memory.Buffer, 0, received)); if (request.Header.Id != response.Header.Id) { throw new DnsResponseException("Header id mismatch."); } Enqueue(server.AddressFamily, udpClient); return(response); } } catch { mustDispose = true; throw; } finally { if (!_enableClientQueue || mustDispose) { try { udpClient.Close(); } catch { // ignored } } } }