From fdd5f1d33280652a5af7c34b35bd269cb858470d Mon Sep 17 00:00:00 2001 From: Gonzalo Paniagua Javier Date: Thu, 17 Mar 2011 19:45:32 -0400 Subject: [PATCH] Add support for an alternative managed DNS resolver The System.Net.Dns class will now use a fully managed implementation of a DNS resolver which is much better when doing multiple queries at a time. --- mcs/class/System/Mono.Dns/DnsClass.cs | 36 ++ mcs/class/System/Mono.Dns/DnsHeader.cs | 203 +++++++ mcs/class/System/Mono.Dns/DnsOpCode.cs | 34 ++ mcs/class/System/Mono.Dns/DnsPacket.cs | 136 +++++ mcs/class/System/Mono.Dns/DnsQClass.cs | 38 ++ mcs/class/System/Mono.Dns/DnsQType.cs | 99 ++++ mcs/class/System/Mono.Dns/DnsQuery.cs | 53 ++ mcs/class/System/Mono.Dns/DnsQuestion.cs | 63 +++ mcs/class/System/Mono.Dns/DnsRCode.cs | 47 ++ .../System/Mono.Dns/DnsResourceRecord.cs | 114 ++++ .../System/Mono.Dns/DnsResourceRecordA.cs | 37 ++ .../System/Mono.Dns/DnsResourceRecordAAAA.cs | 37 ++ .../System/Mono.Dns/DnsResourceRecordCName.cs | 48 ++ .../Mono.Dns/DnsResourceRecordIPAddress.cs | 52 ++ .../System/Mono.Dns/DnsResourceRecordPTR.cs | 47 ++ mcs/class/System/Mono.Dns/DnsResponse.cs | 142 +++++ mcs/class/System/Mono.Dns/DnsType.cs | 98 ++++ mcs/class/System/Mono.Dns/DnsUtil.cs | 144 +++++ mcs/class/System/Mono.Dns/Makefile | 31 ++ .../System/Mono.Dns/ResolverAsyncOperation.cs | 31 ++ mcs/class/System/Mono.Dns/ResolverError.cs | 37 ++ mcs/class/System/Mono.Dns/SimpleResolver.cs | 501 ++++++++++++++++++ .../Mono.Dns/SimpleResolverEventArgs.cs | 66 +++ mcs/class/System/System.Net/Dns.cs | 117 +++- mcs/class/System/System.Net/DnsAsyncResult.cs | 121 +++++ mcs/class/System/System.dll.sources | 23 + 26 files changed, 2343 insertions(+), 12 deletions(-) create mode 100644 mcs/class/System/Mono.Dns/DnsClass.cs create mode 100644 mcs/class/System/Mono.Dns/DnsHeader.cs create mode 100644 mcs/class/System/Mono.Dns/DnsOpCode.cs create mode 100644 mcs/class/System/Mono.Dns/DnsPacket.cs create mode 100644 mcs/class/System/Mono.Dns/DnsQClass.cs create mode 100644 mcs/class/System/Mono.Dns/DnsQType.cs create mode 100644 mcs/class/System/Mono.Dns/DnsQuery.cs create mode 100644 mcs/class/System/Mono.Dns/DnsQuestion.cs create mode 100644 mcs/class/System/Mono.Dns/DnsRCode.cs create mode 100644 mcs/class/System/Mono.Dns/DnsResourceRecord.cs create mode 100644 mcs/class/System/Mono.Dns/DnsResourceRecordA.cs create mode 100644 mcs/class/System/Mono.Dns/DnsResourceRecordAAAA.cs create mode 100644 mcs/class/System/Mono.Dns/DnsResourceRecordCName.cs create mode 100644 mcs/class/System/Mono.Dns/DnsResourceRecordIPAddress.cs create mode 100644 mcs/class/System/Mono.Dns/DnsResourceRecordPTR.cs create mode 100644 mcs/class/System/Mono.Dns/DnsResponse.cs create mode 100644 mcs/class/System/Mono.Dns/DnsType.cs create mode 100644 mcs/class/System/Mono.Dns/DnsUtil.cs create mode 100644 mcs/class/System/Mono.Dns/Makefile create mode 100644 mcs/class/System/Mono.Dns/ResolverAsyncOperation.cs create mode 100644 mcs/class/System/Mono.Dns/ResolverError.cs create mode 100644 mcs/class/System/Mono.Dns/SimpleResolver.cs create mode 100644 mcs/class/System/Mono.Dns/SimpleResolverEventArgs.cs create mode 100644 mcs/class/System/System.Net/DnsAsyncResult.cs diff --git a/mcs/class/System/Mono.Dns/DnsClass.cs b/mcs/class/System/Mono.Dns/DnsClass.cs new file mode 100644 index 00000000000..bbebe4f767a --- /dev/null +++ b/mcs/class/System/Mono.Dns/DnsClass.cs @@ -0,0 +1,36 @@ +// +// Mono.Dns.DnsClass +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) +// +// Copyright 2011 Gonzalo Paniagua Javier +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +namespace Mono.Dns { +#if !NET_2_0 + public +#endif + enum DnsClass : ushort { + Internet = 1, + IN = 1, + CSNET = 2, + CS = 2, + CHAOS = 3, + CH = 3, + Hesiod = 4, + HS = 4, + } +} + diff --git a/mcs/class/System/Mono.Dns/DnsHeader.cs b/mcs/class/System/Mono.Dns/DnsHeader.cs new file mode 100644 index 00000000000..054f7355278 --- /dev/null +++ b/mcs/class/System/Mono.Dns/DnsHeader.cs @@ -0,0 +1,203 @@ +// +// Mono.Dns.DnsHeader +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) +// +// Copyright 2011 Gonzalo Paniagua Javier +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +using System; +using System.Collections.Generic; +using System.Text; + +namespace Mono.Dns { +#if !NET_2_0 + public +#endif + class DnsHeader { + public const int DnsHeaderLength = 12; + ArraySegment bytes; + + public DnsHeader (byte [] bytes) + : this (bytes, 0) + { + } + + public DnsHeader (byte [] bytes, int offset) + : this (new ArraySegment (bytes, offset, DnsHeaderLength)) + { + } + + public DnsHeader (ArraySegment segment) + { + if (segment.Count != DnsHeaderLength) + throw new ArgumentException ("Count must be 12", "segment"); + + bytes = segment; + } + + public void Clear () + { + for (int i = 0; i < DnsHeaderLength; i++) + bytes.Array [i + bytes.Offset] = 0; + } + + public ushort ID { + get { + return (ushort)(bytes.Array [bytes.Offset] * 256 + bytes.Array [bytes.Offset + 1]); + } + set { + bytes.Array [bytes.Offset] = (byte) ((value & 0x0ff00) >> 8); + bytes.Array [bytes.Offset + 1] = (byte) (value & 0x0ff); + } + } + + public bool IsQuery { + get { return ((bytes.Array [2 + bytes.Offset] & 0x80) != 0); } + set { + if (!value) { + bytes.Array [2 + bytes.Offset] |= 0x80; + } else { + bytes.Array [2 + bytes.Offset] &= 0x7f; + } + } + } + + public DnsOpCode OpCode { + get { return (DnsOpCode) ((bytes.Array [2 + bytes.Offset] & 0x78) >> 3); } + set { + if (!Enum.IsDefined (typeof (DnsOpCode), value)) + throw new ArgumentOutOfRangeException ("value", "Invalid DnsOpCode value"); + + int v = (int) value; + v <<= 3; + int prev = (bytes.Array [2 + bytes.Offset] & 0x87); + v |= prev; + bytes.Array [2 + bytes.Offset] = (byte) v; + } + } + + public bool AuthoritativeAnswer { + get { return (bytes.Array [2 + bytes.Offset] & 4) != 0; } + set { + if(value) { + bytes.Array [2 + bytes.Offset] |= 4; + } else { + bytes.Array [2 + bytes.Offset] &= 0xFB; + } + } + } + + public bool Truncation { + get { return (bytes.Array [2 + bytes.Offset] & 2) != 0; } + set { + if(value) { + bytes.Array [2 + bytes.Offset] |= 2; + } else { + bytes.Array [2 + bytes.Offset] &= 0xFD; + } + } + } + + public bool RecursionDesired { + get { return (bytes.Array [2 + bytes.Offset] & 1) != 0; } + set { + if(value) { + bytes.Array [2 + bytes.Offset] |= 1; + } else { + bytes.Array [2 + bytes.Offset] &= 0xFE; + } + } + } + + public bool RecursionAvailable { + get { return (bytes.Array [3 + bytes.Offset] & 0x80) != 0; } + set { + if(value) { + bytes.Array [3 + bytes.Offset] |= 0x80; + } else { + bytes.Array [3 + bytes.Offset] &= 0x7F; + } + } + } + + // TODO: Add AuthenticData and Checking Disabled (bit 10 and 11 of Z) + public int ZReserved { + get { return (bytes.Array [3 + bytes.Offset] & 0x70) >> 4; } + set { + if(value < 0 || value > 7) { + throw new ArgumentOutOfRangeException("value", "Must be between 0 and 7"); + } + bytes.Array [3 + bytes.Offset] &= 0x8F; + bytes.Array [3 + bytes.Offset] |= (byte) ((value << 4) & 0x70); + } + } + + public DnsRCode RCode { + get { return (DnsRCode) (bytes.Array [3 + bytes.Offset] & 0x0f); } + set { + int v = (int)value; + //Info: Values > 15 are encoded in other records (OPT, TSIG, TKEY) + if (v < 0 || v > 15) + throw new ArgumentOutOfRangeException("value", "Must be between 0 and 15"); + + bytes.Array [3 + bytes.Offset] &= 0x0f; + bytes.Array [3 + bytes.Offset] |= (byte) v; + } + } + + static ushort GetUInt16 (byte [] bytes, int offset) + { + return (ushort)(bytes [offset] * 256 + bytes [offset + 1]); + } + + static void SetUInt16 (byte [] bytes, int offset, ushort val) + { + bytes [offset] = (byte) ((val & 0x0ff00) >> 8); + bytes [offset + 1] = (byte) (val & 0x0ff); + } + + public ushort QuestionCount { + get { return GetUInt16 (bytes.Array, 4); } + set { SetUInt16 (bytes.Array, 4, value); } + } + + public ushort AnswerCount { + get { return GetUInt16 (bytes.Array, 6); } + set { SetUInt16 (bytes.Array, 6, value); } + } + + public ushort AuthorityCount { + get { return GetUInt16 (bytes.Array, 8); } + set { SetUInt16 (bytes.Array, 8, value); } + } + + public ushort AdditionalCount { + get { return GetUInt16 (bytes.Array, 10); } + set { SetUInt16 (bytes.Array, 10, value); } + } + + public override string ToString () + { + StringBuilder sb = new StringBuilder(); + sb.AppendFormat ("ID: {0} QR: {1} Opcode: {2} AA: {3} TC: {4} RD: {5} RA: {6} \r\nRCode: {7} ", + ID, IsQuery, OpCode, AuthoritativeAnswer, Truncation, RecursionDesired, + RecursionAvailable, RCode); + sb.AppendFormat ("Q: {0} A: {1} NS: {2} AR: {3}\r\n", QuestionCount, AnswerCount, AuthorityCount, AdditionalCount); + return sb.ToString(); + } + } +} + diff --git a/mcs/class/System/Mono.Dns/DnsOpCode.cs b/mcs/class/System/Mono.Dns/DnsOpCode.cs new file mode 100644 index 00000000000..2558afa778f --- /dev/null +++ b/mcs/class/System/Mono.Dns/DnsOpCode.cs @@ -0,0 +1,34 @@ +// +// Mono.Dns.DnsOpCode +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) +// +// Copyright 2011 Gonzalo Paniagua Javier +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +using System; +namespace Mono.Dns { +#if !NET_2_0 + public +#endif + enum DnsOpCode : byte { + Query = 0, + [Obsolete] IQuery = 1, + Status = 2, + Notify = 4, + Update = 5, + } +} + diff --git a/mcs/class/System/Mono.Dns/DnsPacket.cs b/mcs/class/System/Mono.Dns/DnsPacket.cs new file mode 100644 index 00000000000..5e9155e74d5 --- /dev/null +++ b/mcs/class/System/Mono.Dns/DnsPacket.cs @@ -0,0 +1,136 @@ +// +// Mono.Dns.DnsPacket +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) +// +// Copyright 2011 Gonzalo Paniagua Javier +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace Mono.Dns { +#if !NET_2_0 + public +#endif + abstract class DnsPacket { + protected byte [] packet; + protected int position; + protected DnsHeader header; + + protected DnsPacket () + { + // Caller has to initialize packet, position and header + } + + protected DnsPacket (int length) + : this (new byte [length], length) + { + } + + protected DnsPacket (byte [] buffer, int length) + { + if (buffer == null) + throw new ArgumentNullException("buffer"); + if (length <= 0) + throw new ArgumentOutOfRangeException("length", "Must be greater than zero."); + + packet = buffer; + position = length; + header = new DnsHeader(new ArraySegment(packet, 0, 12)); + } + + public byte [] Packet { + get { return packet; } + } + + public int Length { + get { return position; } + } + + public DnsHeader Header { + get { return header; } + } + + protected void WriteUInt16 (ushort v) + { + packet [position++] = (byte) ((v & 0x0ff00) >> 8); + packet [position++] = (byte) (v & 0x0ff); + } + + protected void WriteStringBytes (string str, int offset, int count) + { + for (int i = offset, c = 0; c < count; c++, i++) + packet [position++] = (byte) str [i]; // Don't care about encoding. + } + + protected void WriteLabel (string str, int offset, int count) + { + packet [position++] = (byte) count; + WriteStringBytes (str, offset, count); + } + + protected void WriteDnsName (string name) + { + if (!DnsUtil.IsValidDnsName (name)) + throw new ArgumentException ("Invalid DNS name"); + + if (!String.IsNullOrEmpty (name)) { + int len = name.Length; + int label_start = 0; + int label_len = 0; + for (int i = 0; i < len; i++) { + char c = name [i]; + if (c != '.') { + label_len++; + } else { + if (i == 0) + break; // "." + WriteLabel (name, label_start, label_len); + label_start += label_len + 1; // Skip the dot + label_len = 0; + } + } + if (label_len > 0) + WriteLabel (name, label_start, label_len); + } + + packet [position++] = 0; + } + + protected internal string ReadName (ref int offset) + { + return DnsUtil.ReadName (packet, ref offset); + } + + protected internal static string ReadName (byte [] buffer, ref int offset) + { + return DnsUtil.ReadName (buffer, ref offset); + } + + protected internal ushort ReadUInt16 (ref int offset) + { + return (ushort)((packet[offset++] << 8) + packet[offset++]); + } + + protected internal int ReadInt32 (ref int offset) + { + return (packet [offset++] << 24) + (packet [offset++] << 16) + (packet [offset++] << 8) + packet [offset++]; + } + } +} + diff --git a/mcs/class/System/Mono.Dns/DnsQClass.cs b/mcs/class/System/Mono.Dns/DnsQClass.cs new file mode 100644 index 00000000000..cd6f6ec1735 --- /dev/null +++ b/mcs/class/System/Mono.Dns/DnsQClass.cs @@ -0,0 +1,38 @@ +// +// Mono.Dns.DnsQClass +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) +// +// Copyright 2011 Gonzalo Paniagua Javier +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +namespace Mono.Dns { +#if !NET_2_0 + public +#endif + enum DnsQClass : ushort { + Internet = 1, + IN = 1, + CSNET = 2, + CS = 2, + CHAOS = 3, + CH = 3, + Hesiod = 4, + HS = 4, + None = 254, + Any = 255 + } +} + diff --git a/mcs/class/System/Mono.Dns/DnsQType.cs b/mcs/class/System/Mono.Dns/DnsQType.cs new file mode 100644 index 00000000000..523fe82ab69 --- /dev/null +++ b/mcs/class/System/Mono.Dns/DnsQType.cs @@ -0,0 +1,99 @@ +// +// Mono.Dns.DnsQType +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) +// +// Copyright 2011 Gonzalo Paniagua Javier +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +using System; +namespace Mono.Dns { +#if !NET_2_0 + public +#endif + enum DnsQType : ushort { + A = 1, + NS = 2, + [Obsolete] MD = 3, + [Obsolete] MF = 4, + CNAME = 5, + SOA = 6, + [Obsolete] MB = 7, + [Obsolete] MG = 8, + [Obsolete] MR = 9, + [Obsolete] NULL = 10, + [Obsolete] WKS = 11, + PTR = 12, + [Obsolete] HINFO = 13, + [Obsolete] MINFO = 14, + MX = 15, + TXT = 16, + [Obsolete] RP = 17, + AFSDB = 18, + [Obsolete] X25 = 19, + [Obsolete] ISDN = 20, + [Obsolete] RT = 21, + [Obsolete] NSAP = 22, + [Obsolete] NSAPPTR = 23, + SIG = 24, + KEY = 25, + [Obsolete] PX = 26, + [Obsolete] GPOS = 27, + AAAA = 28, + LOC = 29, + [Obsolete] NXT = 30, + [Obsolete] EID = 31, + [Obsolete] NIMLOC = 32, + SRV = 33, + [Obsolete] ATMA = 34, + NAPTR = 35, + KX = 36, + CERT = 37, + [Obsolete] A6 = 38, + DNAME = 39, + [Obsolete] SINK = 40, + OPT = 41, + [Obsolete] APL = 42, + DS = 43, + SSHFP = 44, + IPSECKEY = 45, + RRSIG = 46, + NSEC = 47, + DNSKEY = 48, + DHCID = 49, + NSEC3 = 50, + NSEC3PARAM = 51, + HIP = 55, + NINFO = 56, + RKEY = 57, + TALINK = 58, + SPF = 99, + [Obsolete] UINFO = 100, + [Obsolete] UID = 101, + [Obsolete] GID = 102, + [Obsolete] UNSPEC = 103, + TKEY = 249, + TSIG = 250, + IXFR = 251, + AXFR = 252, + [Obsolete] MAILB = 253, + [Obsolete] MAILA = 254, + ALL = 255, + URI = 256, + TA = 32768, + DLV = 32769, + } +} + diff --git a/mcs/class/System/Mono.Dns/DnsQuery.cs b/mcs/class/System/Mono.Dns/DnsQuery.cs new file mode 100644 index 00000000000..2a0cfb0e036 --- /dev/null +++ b/mcs/class/System/Mono.Dns/DnsQuery.cs @@ -0,0 +1,53 @@ +// +// Mono.Dns.DnsQuery +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) +// +// Copyright 2011 Gonzalo Paniagua Javier +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Mono.Dns { +#if !NET_2_0 + public +#endif + class DnsQuery : DnsPacket { + public DnsQuery (string name, DnsQType qtype, DnsQClass qclass) + { + if (String.IsNullOrEmpty (name)) + throw new ArgumentNullException ("name"); + + int length = DnsUtil.GetEncodedLength (name); + if (length == -1) + throw new ArgumentException ("Invalid DNS name", "name"); + + length += 12 + 2 + 2; // Header + qtype + qclass + packet = new byte [length]; + header = new DnsHeader (packet, 0); + position = 12; + WriteDnsName (name); + WriteUInt16 ((ushort) qtype); + WriteUInt16 ((ushort) qclass); + Header.QuestionCount = 1; + Header.IsQuery = true; + Header.RecursionDesired = true; + } + } +} + diff --git a/mcs/class/System/Mono.Dns/DnsQuestion.cs b/mcs/class/System/Mono.Dns/DnsQuestion.cs new file mode 100644 index 00000000000..e4d9a38790c --- /dev/null +++ b/mcs/class/System/Mono.Dns/DnsQuestion.cs @@ -0,0 +1,63 @@ +// +// Mono.Dns.DnsQuestion +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) +// +// Copyright 2011 Gonzalo Paniagua Javier +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +using System; +using System.Collections.Generic; +using System.Text; + +namespace Mono.Dns { +#if !NET_2_0 + public +#endif + class DnsQuestion { + string name; + DnsQType type; + DnsQClass _class; + + internal DnsQuestion () + { + } + + internal int Init (DnsPacket packet, int offset) + { + name = packet.ReadName (ref offset); + type = (DnsQType) packet.ReadUInt16 (ref offset); + _class = (DnsQClass) packet.ReadUInt16 (ref offset); + return offset; + } + + public string Name { + get { return name; } + } + + public DnsQType Type { + get { return type; } + } + + public DnsQClass Class { + get { return _class; } + } + + public override string ToString() { + return String.Format("Name: {0} Type: {1} Class: {2}", Name, Type, Class); + } + } +} + diff --git a/mcs/class/System/Mono.Dns/DnsRCode.cs b/mcs/class/System/Mono.Dns/DnsRCode.cs new file mode 100644 index 00000000000..ae510e6b395 --- /dev/null +++ b/mcs/class/System/Mono.Dns/DnsRCode.cs @@ -0,0 +1,47 @@ +// +// Mono.Dns.DnsRCode +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) +// +// Copyright 2011 Gonzalo Paniagua Javier +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +namespace Mono.Dns { +#if !NET_2_0 + public +#endif + enum DnsRCode : ushort { + NoError = 0, + FormErr = 1, + ServFail = 2, + NXDomain = 3, + NotImp = 4, + Refused = 5, + YXDomain = 6, + YXRRSet = 7, + NXRRSet = 8, + NotAuth = 9, + NotZone = 10, + BadVers = 16, + BadSig = 16, + BadKey = 17, + BadTime = 18, + BadMode = 19, + BadName = 20, + BadAlg = 21, + BadTrunc = 22, + } +} + diff --git a/mcs/class/System/Mono.Dns/DnsResourceRecord.cs b/mcs/class/System/Mono.Dns/DnsResourceRecord.cs new file mode 100644 index 00000000000..5a190012dbd --- /dev/null +++ b/mcs/class/System/Mono.Dns/DnsResourceRecord.cs @@ -0,0 +1,114 @@ +// +// Mono.Dns.DnsResourceRecord +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) +// +// Copyright 2011 Gonzalo Paniagua Javier +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Mono.Dns { +#if !NET_2_0 + public +#endif + class DnsResourceRecord { + string name; + DnsType type; + DnsClass klass; + int ttl; + ushort rdlength; + ArraySegment m_rdata; + + internal DnsResourceRecord() { + } + + internal void CopyFrom(DnsResourceRecord rr) { + name = rr.name; + type = rr.type; + klass = rr.klass; + ttl = rr.ttl; + rdlength = rr.rdlength; + m_rdata = rr.m_rdata; + } + + static internal DnsResourceRecord CreateFromBuffer(DnsPacket packet, int size, ref int offset) { + string pname = packet.ReadName(ref offset); + DnsType ptype = (DnsType)packet.ReadUInt16(ref offset); + DnsClass pclass = (DnsClass)packet.ReadUInt16(ref offset); + int pttl = packet.ReadInt32(ref offset); + ushort prdlength = packet.ReadUInt16(ref offset); + DnsResourceRecord rr = new DnsResourceRecord(); + rr.name = pname; + rr.type = ptype; + rr.klass = pclass; + rr.ttl = pttl; + rr.rdlength = prdlength; + rr.m_rdata = new ArraySegment(packet.Packet, offset, prdlength); + offset += prdlength; + + switch(pclass) { + case DnsClass.IN: + switch(ptype) { + case DnsType.A: + rr = new DnsResourceRecordA(rr); + break; + case DnsType.AAAA: + rr = new DnsResourceRecordAAAA(rr); + break; + case DnsType.CNAME: + rr = new DnsResourceRecordCName(rr); + break; + case DnsType.PTR: + rr = new DnsResourceRecordPTR(rr); + break; + default: + break; + } + break; + default: + break; + } + return rr; + } + + public string Name { + get { return name; } + } + + public DnsType Type { + get { return type; } + } + + public DnsClass Class { + get { return klass; } + } + + public int Ttl { + get { return ttl; } + } + + public ArraySegment Data { + get { return m_rdata; } + } + + public override string ToString() { + return String.Format("Name: {0}, Type: {1}, Class: {2}, Ttl: {3}, Data length: {4}", name, type, klass, ttl, Data.Count); + } + } +} diff --git a/mcs/class/System/Mono.Dns/DnsResourceRecordA.cs b/mcs/class/System/Mono.Dns/DnsResourceRecordA.cs new file mode 100644 index 00000000000..b367d8dc66d --- /dev/null +++ b/mcs/class/System/Mono.Dns/DnsResourceRecordA.cs @@ -0,0 +1,37 @@ +// +// Mono.Dns.DnsResourceRecordA +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) +// +// Copyright 2011 Gonzalo Paniagua Javier +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace Mono.Dns { +#if !NET_2_0 + public +#endif + class DnsResourceRecordA : DnsResourceRecordIPAddress { + internal DnsResourceRecordA (DnsResourceRecord rr) + : base (rr, 4) + { + } + } +} + diff --git a/mcs/class/System/Mono.Dns/DnsResourceRecordAAAA.cs b/mcs/class/System/Mono.Dns/DnsResourceRecordAAAA.cs new file mode 100644 index 00000000000..c2d9571fb8c --- /dev/null +++ b/mcs/class/System/Mono.Dns/DnsResourceRecordAAAA.cs @@ -0,0 +1,37 @@ +// +// Mono.Dns.DnsResourceRecordAAAA +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) +// +// Copyright 2011 Gonzalo Paniagua Javier +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace Mono.Dns { +#if !NET_2_0 + public +#endif + class DnsResourceRecordAAAA : DnsResourceRecordIPAddress { + internal DnsResourceRecordAAAA (DnsResourceRecord rr) + : base (rr, 16) + { + } + } +} + diff --git a/mcs/class/System/Mono.Dns/DnsResourceRecordCName.cs b/mcs/class/System/Mono.Dns/DnsResourceRecordCName.cs new file mode 100644 index 00000000000..e2b58d78404 --- /dev/null +++ b/mcs/class/System/Mono.Dns/DnsResourceRecordCName.cs @@ -0,0 +1,48 @@ +// +// Mono.Dns.DnsResourceRecordCName +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) +// +// Copyright 2011 Gonzalo Paniagua Javier +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +using System; +using System.Collections.Generic; +using System.Text; + +namespace Mono.Dns { +#if !NET_2_0 + public +#endif + class DnsResourceRecordCName : DnsResourceRecord { + string cname; + + internal DnsResourceRecordCName (DnsResourceRecord rr) + { + CopyFrom (rr); + int offset = rr.Data.Offset; + cname = DnsPacket.ReadName (rr.Data.Array, ref offset); + } + + public string CName { + get { return cname; } + } + + public override string ToString () + { + return base.ToString () + " CNAME: " + cname.ToString (); + } + } +} diff --git a/mcs/class/System/Mono.Dns/DnsResourceRecordIPAddress.cs b/mcs/class/System/Mono.Dns/DnsResourceRecordIPAddress.cs new file mode 100644 index 00000000000..5c3c09ad203 --- /dev/null +++ b/mcs/class/System/Mono.Dns/DnsResourceRecordIPAddress.cs @@ -0,0 +1,52 @@ +// +// Mono.Dns.DnsResourceRecordIPAddress +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) +// +// Copyright 2011 Gonzalo Paniagua Javier +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace Mono.Dns { +#if !NET_2_0 + public +#endif + abstract class DnsResourceRecordIPAddress : DnsResourceRecord { + IPAddress address; + + internal DnsResourceRecordIPAddress (DnsResourceRecord rr, int address_size) + { + CopyFrom (rr); + ArraySegment segment = rr.Data; + byte [] bytes = new byte [address_size]; + Buffer.BlockCopy (segment.Array, segment.Offset, bytes, 0, address_size); + address = new IPAddress (bytes); + } + + public override string ToString () + { + return base.ToString() + " Address: " + address; + } + + public IPAddress Address { + get { return address; } + } + } +} + diff --git a/mcs/class/System/Mono.Dns/DnsResourceRecordPTR.cs b/mcs/class/System/Mono.Dns/DnsResourceRecordPTR.cs new file mode 100644 index 00000000000..5b74fdab11d --- /dev/null +++ b/mcs/class/System/Mono.Dns/DnsResourceRecordPTR.cs @@ -0,0 +1,47 @@ +// +// Mono.Dns.DnsResourceRecordPTR +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) +// +// Copyright 2011 Gonzalo Paniagua Javier +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +using System; +using System.Collections.Generic; +using System.Text; + +namespace Mono.Dns { +#if !NET_2_0 + public +#endif + class DnsResourceRecordPTR : DnsResourceRecord { + string dname; + + internal DnsResourceRecordPTR (DnsResourceRecord rr) + { + CopyFrom (rr); + int offset = rr.Data.Offset; + dname = DnsPacket.ReadName (rr.Data.Array, ref offset); + } + + public string DName { + get { return dname; } + } + + public override string ToString() { + return base.ToString () + " DNAME: " + dname.ToString (); + } + } +} diff --git a/mcs/class/System/Mono.Dns/DnsResponse.cs b/mcs/class/System/Mono.Dns/DnsResponse.cs new file mode 100644 index 00000000000..d5d9496c34c --- /dev/null +++ b/mcs/class/System/Mono.Dns/DnsResponse.cs @@ -0,0 +1,142 @@ +// +// Mono.Dns.DnsResponse +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) +// +// Copyright 2011 Gonzalo Paniagua Javier +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Text; + +namespace Mono.Dns { +#if !NET_2_0 + public +#endif + class DnsResponse : DnsPacket { + static readonly ReadOnlyCollection EmptyRR = new ReadOnlyCollection (new DnsResourceRecord [0]); + static readonly ReadOnlyCollection EmptyQS = new ReadOnlyCollection (new DnsQuestion [0]); + + ReadOnlyCollection question; + ReadOnlyCollection answer; + ReadOnlyCollection authority; + ReadOnlyCollection additional; + int offset = DnsHeader.DnsHeaderLength; + + public DnsResponse (byte [] buffer, int length) + : base (buffer, length) + { + } + + public void Reset () + { + question = null; + answer = null; + authority = null; + additional = null; + for (int i = 0; i < packet.Length; i++) + packet [i] = 0; + } + + ReadOnlyCollection GetRRs (int count) + { + if (count <= 0) + return EmptyRR; + + List records = new List(count); + for (int i = 0; i < count; i++) + records.Add (DnsResourceRecord.CreateFromBuffer (this, position, ref offset)); + return records.AsReadOnly (); + } + + ReadOnlyCollection GetQuestions (int count) + { + if (count <= 0) + return EmptyQS; + + List records = new List (count); + for(int i = 0; i < count; i++) { + DnsQuestion record = new DnsQuestion (); + offset = record.Init (this, offset); + records.Add (record); + } + return records.AsReadOnly (); + } + + public ReadOnlyCollection GetQuestions () + { + if (question == null) + question = GetQuestions (Header.QuestionCount); + return question; + } + + public ReadOnlyCollection GetAnswers () + { + if (answer == null) { + GetQuestions (); + answer = GetRRs (Header.AnswerCount); + } + return answer; + } + + public ReadOnlyCollection GetAuthority () + { + if (authority == null) { + GetQuestions (); + GetAnswers (); + authority = GetRRs (Header.AuthorityCount); + } + return authority; + } + + public ReadOnlyCollection GetAdditional () + { + if (additional == null) { + GetQuestions (); + GetAnswers (); + GetAuthority (); + additional = GetRRs (Header.AdditionalCount); + } + return additional; + } + + public override string ToString () + { + StringBuilder sb = new StringBuilder(); + sb.Append(Header); + sb.Append("Question:\r\n"); + foreach(DnsQuestion q in GetQuestions()) { + sb.AppendFormat("\t{0}\r\n", q); + } + sb.Append("Answer(s):\r\n"); + foreach(DnsResourceRecord q in GetAnswers()) { + sb.AppendFormat("\t{0}\r\n", q); + } + sb.Append("Authority:\r\n"); + foreach(DnsResourceRecord q in GetAuthority()) { + sb.AppendFormat("\t{0}\r\n", q); + } + sb.Append("Additional:\r\n"); + foreach(DnsResourceRecord q in GetAdditional()) { + sb.AppendFormat("\t{0}\r\n", q); + } + return sb.ToString(); + } + } +} + diff --git a/mcs/class/System/Mono.Dns/DnsType.cs b/mcs/class/System/Mono.Dns/DnsType.cs new file mode 100644 index 00000000000..d23cafbb5da --- /dev/null +++ b/mcs/class/System/Mono.Dns/DnsType.cs @@ -0,0 +1,98 @@ +// +// Mono.Dns.DnsType +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) +// +// Copyright 2011 Gonzalo Paniagua Javier +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +using System; +namespace Mono.Dns { +#if !NET_2_0 + public +#endif + enum DnsType : ushort { + A = 1, + NS = 2, + [Obsolete] MD = 3, + [Obsolete] MF = 4, + CNAME = 5, + SOA = 6, + [Obsolete] MB = 7, + [Obsolete] MG = 8, + [Obsolete] MR = 9, + [Obsolete] NULL = 10, + [Obsolete] WKS = 11, + PTR = 12, + [Obsolete] HINFO = 13, + [Obsolete] MINFO = 14, + MX = 15, + TXT = 16, + [Obsolete] RP = 17, + AFSDB = 18, + [Obsolete] X25 = 19, + [Obsolete] ISDN = 20, + [Obsolete] RT = 21, + [Obsolete] NSAP = 22, + [Obsolete] NSAPPTR = 23, + SIG = 24, + KEY = 25, + [Obsolete] PX = 26, + [Obsolete] GPOS = 27, + AAAA = 28, + LOC = 29, + [Obsolete] NXT = 30, + [Obsolete] EID = 31, + [Obsolete] NIMLOC = 32, + SRV = 33, + [Obsolete] ATMA = 34, + NAPTR = 35, + KX = 36, + CERT = 37, + [Obsolete] A6 = 38, + DNAME = 39, + [Obsolete] SINK = 40, + OPT = 41, + [Obsolete] APL = 42, + DS = 43, + SSHFP = 44, + IPSECKEY = 45, + RRSIG = 46, + NSEC = 47, + DNSKEY = 48, + DHCID = 49, + NSEC3 = 50, + NSEC3PARAM = 51, + HIP = 55, + NINFO = 56, + RKEY = 57, + TALINK = 58, + SPF = 99, + [Obsolete] UINFO = 100, + [Obsolete] UID = 101, + [Obsolete] GID = 102, + [Obsolete] UNSPEC = 103, + TKEY = 249, + TSIG = 250, + IXFR = 251, + AXFR = 252, + [Obsolete] MAILB = 253, + [Obsolete] MAILA = 254, + URI = 256, + TA = 32768, + DLV = 32769, + } +} + diff --git a/mcs/class/System/Mono.Dns/DnsUtil.cs b/mcs/class/System/Mono.Dns/DnsUtil.cs new file mode 100644 index 00000000000..fc722c3c95f --- /dev/null +++ b/mcs/class/System/Mono.Dns/DnsUtil.cs @@ -0,0 +1,144 @@ +// +// Mono.Dns.DnsUtil +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) +// +// Copyright 2011 Gonzalo Paniagua Javier +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +using System; +using System.Collections.Generic; +using System.Text; + +namespace Mono.Dns { +#if !NET_2_0 + public +#endif + static class DnsUtil { + // RFC 2181 - Section 11 + public static bool IsValidDnsName (string name) + { + if (name == null) + return false; + + int len = name.Length; + if (len > 255) + return false; + int part_length = 0; + for (int i = 0; i < len; i++) { + char c = name [i]; + if (c == '.') { + if (i == 0 && len > 1) + return false; // Can't begin with a dot unless it's "." + if (i > 0 && part_length == 0) + return false; // No ".." allowed + part_length = 0; + continue; + } + part_length++; + if (part_length > 63) + return false; + } + return true; + } + + public static int GetEncodedLength (string name) + { + if (!IsValidDnsName (name)) + return -1; + + if (name == String.Empty) + return 1; + + int len = name.Length; + if (name [len - 1] == '.') + return len + 1; // (length + label + ... + \0) + return len + 2; // need 1 more for the second to last label length + } + + public static int GetNameLength (byte [] buffer) + { + return GetNameLength (buffer, 0); + } + + public static int GetNameLength (byte [] buffer, int offset) + { + if (buffer == null) + throw new ArgumentNullException ("buffer"); + if (offset < 0 || offset >= buffer.Length) + throw new ArgumentOutOfRangeException ("offset"); + + int i = 0; + int len = 0; + while (len < 256) { + i = buffer [offset++]; + if (i == 0) + return len > 0 ? --len : 0; + int ptr = i & 0x0C0; + if (ptr == 0x0C0) { + i = ((ptr & 0x3F) << 8) + buffer[offset++]; + offset = i; + continue; + } else if (ptr >= 0x40) { + return -2; // Invalid ptr + } + len += i + 1; + offset += i; + } + return -1; // Invalid length + } + + public static string ReadName (byte [] buffer, ref int offset) + { + if (buffer == null) + throw new ArgumentNullException ("buffer"); + if (offset < 0 || offset >= buffer.Length) + throw new ArgumentOutOfRangeException ("offset"); + + StringBuilder sb = new StringBuilder (32); + int i = 0; + bool no_ptr = true; + int off = offset; + while (sb.Length < 256) { + i = buffer [off++]; + if (no_ptr) offset++; + if (i == 0) { + if (sb.Length > 0) + sb.Length--; + return sb.ToString (); + } + int ptr = i & 0x0C0; + if (ptr == 0x0C0) { + i = ((ptr & 0x3F) << 8) + buffer [off]; + if (no_ptr) offset++; + no_ptr = false; + off = i; + continue; + } else if (i >= 0x40) { + return null; // Invalid ptr + } + + for (int k = 0; k < i; k++) + sb.Append ((char) buffer [off + k]); + sb.Append ('.'); + off += i; + if (no_ptr) offset += i; + } + return null; // never reached + } + + } +} + diff --git a/mcs/class/System/Mono.Dns/Makefile b/mcs/class/System/Mono.Dns/Makefile new file mode 100644 index 00000000000..e43e5df938d --- /dev/null +++ b/mcs/class/System/Mono.Dns/Makefile @@ -0,0 +1,31 @@ +SOURCES= \ + DnsClass.cs \ + DnsHeader.cs \ + DnsPacket.cs \ + DnsQClass.cs \ + DnsQType.cs \ + DnsQuery.cs \ + DnsQuestion.cs \ + DnsResourceRecordA.cs \ + DnsResourceRecordCName.cs \ + DnsResourceRecordPTR.cs \ + DnsResourceRecord.cs \ + DnsResponse.cs \ + DnsType.cs \ + ResolverAsyncOperation.cs \ + Resolver.cs \ + ResolverError.cs \ + SimpleResolverEventArgs.cs + +EXES = resolver.exe \ + plainolddns.exe + +all: $(EXES) + +resolver.exe: $(SOURCES) + @gmcs -out:$@ -debug $(SOURCES) + +plainolddns.exe: plainolddns.cs + @gmcs -out:$@ -debug $^ +clean: + rm -f resolver.exe* plainolddns.exe* *.mdb diff --git a/mcs/class/System/Mono.Dns/ResolverAsyncOperation.cs b/mcs/class/System/Mono.Dns/ResolverAsyncOperation.cs new file mode 100644 index 00000000000..c97842a865d --- /dev/null +++ b/mcs/class/System/Mono.Dns/ResolverAsyncOperation.cs @@ -0,0 +1,31 @@ +// +// Mono.Dns.ResolverAsyncOperation +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) +// +// Copyright 2011 Gonzalo Paniagua Javier +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +namespace Mono.Dns { +#if !NET_2_0 + public +#endif + enum ResolverAsyncOperation { + None, + GetHostEntry, + GetHostAddresses, + } +} + diff --git a/mcs/class/System/Mono.Dns/ResolverError.cs b/mcs/class/System/Mono.Dns/ResolverError.cs new file mode 100644 index 00000000000..136901ca878 --- /dev/null +++ b/mcs/class/System/Mono.Dns/ResolverError.cs @@ -0,0 +1,37 @@ +// Mono.Dns.ResolverError +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) +// +// Copyright 2011 Gonzalo Paniagua Javier +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +namespace Mono.Dns { +#if !NET_2_0 + public +#endif + enum ResolverError { + NoError, // From DNS server + FormatError, // + ServerFailure, // + NameError, // + NotImplemented, // + Refused, // + // Resolver specific + ResponseHeaderError, + ResponseFormatError, + Timeout, + } +} + diff --git a/mcs/class/System/Mono.Dns/SimpleResolver.cs b/mcs/class/System/Mono.Dns/SimpleResolver.cs new file mode 100644 index 00000000000..ca790e60817 --- /dev/null +++ b/mcs/class/System/Mono.Dns/SimpleResolver.cs @@ -0,0 +1,501 @@ +// +// Mono.Dns.SimpleResolver +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) +// +// Copyright 2011 Gonzalo Paniagua Javier +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Net; +using System.Net.Sockets; +using System.Net.NetworkInformation; +using System.Text; +using System.Threading; + +namespace Mono.Dns { +#if !NET_2_0 + public +#endif + sealed class SimpleResolver : IDisposable { + static string [] EmptyStrings = new string [0]; + static IPAddress [] EmptyAddresses = new IPAddress [0]; + IPEndPoint [] endpoints; + Socket client; + Dictionary queries; + AsyncCallback receive_cb; + TimerCallback timeout_cb; + bool disposed; +#if REUSE_RESPONSES + Stack responses_avail = new Stack (); +#endif + + public SimpleResolver () + { + queries = new Dictionary (); + receive_cb = new AsyncCallback (OnReceive); + timeout_cb = new TimerCallback (OnTimeout); + InitFromSystem (); + InitSocket (); + } + + void IDisposable.Dispose () + { + if (!disposed) { + disposed = true; + if (client != null) { + client.Close (); + client = null; + } + } + } + + public void Close () + { + ((IDisposable) this).Dispose (); + } + + void GetLocalHost (SimpleResolverEventArgs args) + { + //FIXME + IPHostEntry entry = new IPHostEntry (); + entry.HostName = "localhost"; + entry.AddressList = new IPAddress [] { IPAddress.Loopback }; + entry.Aliases = EmptyStrings; + args.ResolverError = 0; + args.HostEntry = entry; + return; + +/* + List eps = new List (); + foreach (NetworkInterface iface in NetworkInterface.GetAllNetworkInterfaces ()) { + if (NetworkInterfaceType.Loopback == iface.NetworkInterfaceType) + continue; + + foreach (IPAddress addr in iface.GetIPProperties ().DnsAddresses) { + if (AddressFamily.InterNetworkV6 == addr.AddressFamily) + continue; + IPEndPoint ep = new IPEndPoint (addr, 53); + if (eps.Contains (ep)) + continue; + + eps.Add (ep); + } + } + endpoints = eps.ToArray (); +*/ + } + + // Type A query + // Might fill in Aliases + // -IPAddress -> return the same IPAddress + // -"" -> Local host ip addresses (filter out IPv6 if needed) + public bool GetHostAddressesAsync (SimpleResolverEventArgs args) + { + if (args == null) + throw new ArgumentNullException ("args"); + + if (args.HostName == null) + throw new ArgumentNullException ("args.HostName is null"); + + if (args.HostName.Length > 255) + throw new ArgumentException ("args.HostName is too long"); + + args.Reset (ResolverAsyncOperation.GetHostAddresses); + string host = args.HostName; + if (host == "") { + GetLocalHost (args); + return false; + } + IPAddress addr; + if (IPAddress.TryParse (host, out addr)) { + IPHostEntry entry = new IPHostEntry (); + entry.HostName = host; + entry.Aliases = EmptyStrings; + entry.AddressList = new IPAddress [1] { addr }; + args.HostEntry = entry; + return false; + } + + SendAQuery (args, true); + return true; + } + + // For names -> type A Query + // For IP addresses -> PTR + A -> will at least return itself + // Careful: for IP addresses with PTR, the hostname might yield different IP addresses! + public bool GetHostEntryAsync (SimpleResolverEventArgs args) + { + if (args == null) + throw new ArgumentNullException ("args"); + + if (args.HostName == null) + throw new ArgumentNullException ("args.HostName is null"); + + if (args.HostName.Length > 255) + throw new ArgumentException ("args.HostName is too long"); + + args.Reset (ResolverAsyncOperation.GetHostEntry); + string host = args.HostName; + if (host == "") { + GetLocalHost (args); + return false; + } + + IPAddress addr; + if (IPAddress.TryParse (host, out addr)) { + IPHostEntry entry = new IPHostEntry (); + entry.HostName = host; + entry.Aliases = EmptyStrings; + entry.AddressList = new IPAddress [1] { addr }; + args.HostEntry = entry; + args.PTRAddress = addr; + SendPTRQuery (args, true); + return true; + } + + // 3. For IP addresses: + // 3.1 Parsing IP succeeds + // 3.2 Reverse lookup of the IP fills in HostName -> fails? HostName = IP + // 3.3 The hostname resulting from this is used to query DNS again to get the IP addresses + // + // Exclude IPv6 addresses if not supported by the system + // .Aliases is always empty + // Length > 255 + SendAQuery (args, true); + return true; + } + + bool AddQuery (DnsQuery query, SimpleResolverEventArgs args) + { + lock (queries) { + if (queries.ContainsKey (query.Header.ID)) + return false; + queries [query.Header.ID] = args; + } + return true; + } + + static DnsQuery GetQuery (string host, DnsQType q, DnsQClass c) + { + return new DnsQuery (host, q, c); + } + + void SendAQuery (SimpleResolverEventArgs args, bool add_it) + { + SendAQuery (args, args.HostName, add_it); + } + + void SendAQuery (SimpleResolverEventArgs args, string host, bool add_it) + { + DnsQuery query = GetQuery (host, DnsQType.A, DnsQClass.IN); + SendQuery (args, query, add_it); + } + + static string GetPTRName (IPAddress address) + { + // TODO: IPv6 PTR query? + byte [] bytes = address.GetAddressBytes (); + // "XXX.XXX.XXX.XXX.in-addr.arpa".Length + StringBuilder sb = new StringBuilder (28); + for (int i = bytes.Length - 1; i >= 0; i--) { + sb.AppendFormat ("{0}.", bytes [i]); + } + sb.Append ("in-addr.arpa"); + return sb.ToString (); + } + + void SendPTRQuery (SimpleResolverEventArgs args, bool add_it) + { + DnsQuery query = GetQuery (GetPTRName (args.PTRAddress), DnsQType.PTR, DnsQClass.IN); + SendQuery (args, query, add_it); + } + + void SendQuery (SimpleResolverEventArgs args, DnsQuery query, bool add_it) + { + // TODO: not sure about reusing IDs when add_it == false + int count = 0; + if (add_it) { + do { + query.Header.ID = (ushort)new Random().Next(1, 65534); + if (count > 500) + throw new InvalidOperationException ("Too many pending queries (or really bad luck)"); + } while (AddQuery (query, args) == false); + args.QueryID = query.Header.ID; + } else { + query.Header.ID = args.QueryID; + } + if (args.Timer == null) + args.Timer = new Timer (timeout_cb, args, 5000, Timeout.Infinite); + else + args.Timer.Change (5000, Timeout.Infinite); + client.BeginSend (query.Packet, 0, query.Length, SocketFlags.None, null, null); + } + + byte [] GetFreshBuffer () + { +#if !REUSE_RESPONSES + return new byte [512]; +#else + + DnsResponse response = null; + lock (responses_avail) { + if (responses_avail.Count > 0) { + response = responses_avail.Pop (); + } + } + if (response == null) { + response = new DnsResponse (); + } else { + response.Reset (); + } + return response; +#endif + } + + void FreeBuffer (byte [] buffer) + { +#if REUSE_RESPONSES + // TODO: set some limit here. Configurable? + lock (responses_avail) { + responses_avail.Push (response); + } +#endif + } + + void InitSocket () + { + client = new Socket (AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + client.Blocking = true; + client.Bind (new IPEndPoint (IPAddress.Any, 0)); + client.Connect (endpoints [0]); + BeginReceive (); + } + + void BeginReceive () + { + byte [] buffer = GetFreshBuffer (); + client.BeginReceive (buffer, 0, buffer.Length, SocketFlags.None, receive_cb, buffer); + } + + void OnTimeout (object obj) + { + SimpleResolverEventArgs args = (SimpleResolverEventArgs) obj; + SimpleResolverEventArgs args2; + lock (queries) { + if (!queries.TryGetValue (args.QueryID, out args2)) { + return; // Already processed. + } + if (args != args2) + throw new Exception ("Should not happen: args != args2"); + args.Retries++; + if (args.Retries > 1) { + // Error timeout + args.ResolverError = ResolverError.Timeout; + args.OnCompleted (this); + } else { + SendAQuery (args, false); + } + } + } + + void OnReceive (IAsyncResult ares) + { + if (disposed) + return; + + int nread = 0; + EndPoint remote_ep = client.RemoteEndPoint; + try { + nread = client.EndReceive (ares); + } catch (Exception e) { + Console.Error.WriteLine (e); + } + + BeginReceive (); + + byte [] buffer = (byte []) ares.AsyncState; + if (nread > 12) { + DnsResponse response = new DnsResponse (buffer, nread); + int id = response.Header.ID; + SimpleResolverEventArgs args = null; + lock (queries) { + if (queries.TryGetValue (id, out args)) { + queries.Remove (id); + } + } + + if (args != null) { + Timer t = args.Timer; + if (t != null) + t.Change (Timeout.Infinite, Timeout.Infinite); + + try { + ProcessResponse (args, response, remote_ep); + } catch (Exception e) { + args.ResolverError = (ResolverError) (-1); + args.ErrorMessage = e.Message; + } + + IPHostEntry entry = args.HostEntry; + if (args.ResolverError != 0 && args.PTRAddress != null && entry != null && entry.HostName != null) { + args.PTRAddress = null; + SendAQuery (args, entry.HostName, true); + args.Timer.Change (5000, Timeout.Infinite); + } else { + args.OnCompleted (this); + } + } + } + FreeBuffer (buffer); + } + + void ProcessResponse (SimpleResolverEventArgs args, DnsResponse response, EndPoint server_ep) + { + DnsRCode status = response.Header.RCode; + if (status != 0) { + if (args.PTRAddress != null) { + // PTR query failed -> no error, we have the IP + return; + } + args.ResolverError = (ResolverError) status; + return; + } + + // TODO: verify IP of the server is in our list and the same one that got the query + IPEndPoint ep = (IPEndPoint) server_ep; + if (ep.Port != 53) { + args.ResolverError = ResolverError.ResponseHeaderError; + args.ErrorMessage = "Port"; + return; + } + + DnsHeader header = response.Header; + if (!header.IsQuery) { + args.ResolverError = ResolverError.ResponseHeaderError; + args.ErrorMessage = "IsQuery"; + return; + } + + // TODO: handle Truncation. Retry with bigger buffer? + + if (header.QuestionCount > 1) { + args.ResolverError = ResolverError.ResponseHeaderError; + args.ErrorMessage = "QuestionCount"; + return; + } + ReadOnlyCollection q = response.GetQuestions (); + if (q.Count != 1) { + args.ResolverError = ResolverError.ResponseHeaderError; + args.ErrorMessage = "QuestionCount 2"; + return; + } + DnsQuestion question = q [0]; + /* The answer might have dot at the end, etc... + if (String.Compare (question.Name, args.HostName) != 0) { + args.ResolverError = ResolverError.ResponseHeaderError; + args.ErrorMessage = "HostName - " + question.Name + " != " + args.HostName; + return; + } + */ + + DnsQType t = question.Type; + if (t != DnsQType.A && t != DnsQType.AAAA && t != DnsQType.PTR) { + args.ResolverError = ResolverError.ResponseHeaderError; + args.ErrorMessage = "QType " + question.Type; + return; + } + + if (question.Class != DnsQClass.IN) { + args.ResolverError = ResolverError.ResponseHeaderError; + args.ErrorMessage = "QClass " + question.Class; + return; + } + + ReadOnlyCollection records = response.GetAnswers (); + if (records.Count == 0) { + if (args.PTRAddress != null) { + // PTR query failed -> no error + return; + } + args.ResolverError = ResolverError.NameError; // is this ok? + args.ErrorMessage = "NoAnswers"; + return; + } + + List aliases = null; + List addresses = null; + foreach (DnsResourceRecord r in records) { + if (r.Class != DnsClass.IN) + continue; + if (r.Type == DnsType.A || r.Type == DnsType.AAAA) { + if (addresses == null) + addresses = new List (); + addresses.Add (((DnsResourceRecordIPAddress) r).Address); + } else if (r.Type == DnsType.CNAME) { + if (aliases == null) + aliases = new List (); + aliases.Add (((DnsResourceRecordCName) r).CName); + } else if (r.Type == DnsType.PTR) { + args.HostEntry.HostName = ((DnsResourceRecordPTR) r).DName; + args.HostEntry.Aliases = aliases == null ? EmptyStrings : aliases.ToArray (); + args.HostEntry.AddressList = EmptyAddresses; + return; + } + } + + IPHostEntry entry = args.HostEntry ?? new IPHostEntry (); + if (entry.HostName == null && aliases != null && aliases.Count > 0) { + entry.HostName = aliases [0]; + aliases.RemoveAt (0); + } + entry.Aliases = aliases == null ? EmptyStrings : aliases.ToArray (); + entry.AddressList = addresses == null ? EmptyAddresses : addresses.ToArray (); + args.HostEntry = entry; + if ((question.Type == DnsQType.A || question.Type == DnsQType.AAAA) && entry.AddressList == EmptyAddresses) { + args.ResolverError = ResolverError.NameError; + args.ErrorMessage = "No addresses in response"; + } else if (question.Type == DnsQType.PTR && entry.HostName == null) { + args.ResolverError = ResolverError.NameError; + args.ErrorMessage = "No PTR in response"; + } + + } + + void InitFromSystem () + { + List eps = new List (); + foreach (NetworkInterface iface in NetworkInterface.GetAllNetworkInterfaces ()) { + if (NetworkInterfaceType.Loopback == iface.NetworkInterfaceType) + continue; + + foreach (IPAddress addr in iface.GetIPProperties ().DnsAddresses) { + if (AddressFamily.InterNetworkV6 == addr.AddressFamily) + continue; + IPEndPoint ep = new IPEndPoint (addr, 53); + if (eps.Contains (ep)) + continue; + + eps.Add (ep); + } + } + endpoints = eps.ToArray (); + } + } +} + diff --git a/mcs/class/System/Mono.Dns/SimpleResolverEventArgs.cs b/mcs/class/System/Mono.Dns/SimpleResolverEventArgs.cs new file mode 100644 index 00000000000..599bebe7272 --- /dev/null +++ b/mcs/class/System/Mono.Dns/SimpleResolverEventArgs.cs @@ -0,0 +1,66 @@ +// +// Mono.Dns.SimpleResolverEventArgs +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) +// +// Copyright 2011 Gonzalo Paniagua Javier +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +using System; +using System.Net; +using System.Threading; + +namespace Mono.Dns { +#if !NET_2_0 + public +#endif + class SimpleResolverEventArgs : EventArgs { + public event EventHandler Completed; + + public SimpleResolverEventArgs () + { + } + + public ResolverError ResolverError { get; set; } + public string ErrorMessage { get; set; } + public ResolverAsyncOperation LastOperation; + public string HostName { get; set; } + public IPHostEntry HostEntry { get; internal set; } + public object UserToken { get; set; } + internal ushort QueryID; + internal ushort Retries; + internal Timer Timer; + internal IPAddress PTRAddress; + + internal void Reset (ResolverAsyncOperation op) + { + ResolverError = 0; + ErrorMessage = null; + HostEntry = null; + LastOperation = op; + QueryID = 0; + Retries = 0; + PTRAddress = null; + } + + protected internal void OnCompleted (object sender) + { + var handler = Completed; + if (handler != null) + handler (sender, this); + } + } +} + diff --git a/mcs/class/System/System.Net/Dns.cs b/mcs/class/System/System.Net/Dns.cs index ed61fc4012e..8419fe396eb 100644 --- a/mcs/class/System/System.Net/Dns.cs +++ b/mcs/class/System/System.Net/Dns.cs @@ -3,11 +3,14 @@ // Authors: // Mads Pultz (mpultz@diku.dk) // Lawrence Pit (loz@cable.a2000.nl) -// Marek Safar (marek.safar@gmail.com) +// Author: Mads Pultz (mpultz@diku.dk) +// Lawrence Pit (loz@cable.a2000.nl) +// Marek Safar (marek.safar@gmail.com) +// Gonzalo Paniagua Javier (gonzalo.mono@gmail.com) // // (C) Mads Pultz, 2001 -// Copyright 2011 Xamarin Inc. +// Copyright (c) 2011 Novell, Inc. // // Permission is hereby granted, free of charge, to any person obtaining @@ -41,11 +44,24 @@ using System.Runtime.Remoting.Messaging; using System.Threading.Tasks; #endif +using Mono.Dns; + namespace System.Net { public static class Dns { + static bool use_mono_dns; + static SimpleResolver resolver; + static Dns () { System.Net.Sockets.Socket.CheckProtocolSupport(); + if (Environment.GetEnvironmentVariable ("MONO_DNS") != null) { + resolver = new SimpleResolver (); + use_mono_dns = true; + } + } + + internal static bool UseMonoDns { + get { return use_mono_dns; } } #if !MOONLIGHT // global remove of async methods @@ -56,30 +72,82 @@ namespace System.Net { private delegate IPHostEntry GetHostEntryIPCallback (IPAddress hostAddress); private delegate IPAddress [] GetHostAddressesCallback (string hostName); + static void OnCompleted (object sender, SimpleResolverEventArgs e) + { + DnsAsyncResult ares = (DnsAsyncResult) e.UserToken; + IPHostEntry entry = e.HostEntry; + if (entry == null || e.ResolverError != 0) { + ares.SetCompleted (false, new Exception ("Error: " + e.ResolverError)); + return; + } + ares.SetCompleted (false, entry); + } + + static IAsyncResult BeginAsyncCallAddresses (string host, AsyncCallback callback, object state) + { + SimpleResolverEventArgs e = new SimpleResolverEventArgs (); + e.Completed += OnCompleted; + e.HostName = host; + DnsAsyncResult ares = new DnsAsyncResult (callback, state); + e.UserToken = ares; + if (resolver.GetHostAddressesAsync (e) == false) + ares.SetCompleted (true, e.HostEntry); // Completed synchronously + return ares; + } + + static IAsyncResult BeginAsyncCall (string host, AsyncCallback callback, object state) + { + SimpleResolverEventArgs e = new SimpleResolverEventArgs (); + e.Completed += OnCompleted; + e.HostName = host; + DnsAsyncResult ares = new DnsAsyncResult (callback, state); + e.UserToken = ares; + if (resolver.GetHostEntryAsync (e) == false) + ares.SetCompleted (true, e.HostEntry); // Completed synchronously + return ares; + } + + static IPHostEntry EndAsyncCall (DnsAsyncResult ares) + { + if (ares == null) + throw new ArgumentException ("Invalid asyncResult"); + if (!ares.IsCompleted) + ares.AsyncWaitHandle.WaitOne (); + if (ares.Exception != null) + throw ares.Exception; + IPHostEntry entry = ares.HostEntry; + if (entry == null || entry.AddressList == null || entry.AddressList.Length == 0) + throw new SocketException(11001); + return entry; + } + [Obsolete ("Use BeginGetHostEntry instead")] - public static IAsyncResult BeginGetHostByName (string hostName, - AsyncCallback requestCallback, object stateObject) + public static IAsyncResult BeginGetHostByName (string hostName, AsyncCallback requestCallback, object stateObject) { if (hostName == null) throw new ArgumentNullException ("hostName"); + if (use_mono_dns) + return BeginAsyncCall (hostName, requestCallback, stateObject); + GetHostByNameCallback c = new GetHostByNameCallback (GetHostByName); return c.BeginInvoke (hostName, requestCallback, stateObject); } [Obsolete ("Use BeginGetHostEntry instead")] - public static IAsyncResult BeginResolve (string hostName, - AsyncCallback requestCallback, object stateObject) + public static IAsyncResult BeginResolve (string hostName, AsyncCallback requestCallback, object stateObject) { if (hostName == null) throw new ArgumentNullException ("hostName"); + if (use_mono_dns) + return BeginAsyncCall (hostName, requestCallback, stateObject); + ResolveCallback c = new ResolveCallback (Resolve); return c.BeginInvoke (hostName, requestCallback, stateObject); } - public static IAsyncResult BeginGetHostAddresses (string hostNameOrAddress, - AsyncCallback requestCallback, object state) + public static IAsyncResult BeginGetHostAddresses (string hostNameOrAddress, AsyncCallback requestCallback, object stateObject) { if (hostNameOrAddress == null) throw new ArgumentNullException ("hostName"); @@ -89,12 +157,14 @@ namespace System.Net { "cannot use them as target address.", "hostNameOrAddress"); + if (use_mono_dns) + return BeginAsyncCallAddresses (hostNameOrAddress, requestCallback, stateObject); + GetHostAddressesCallback c = new GetHostAddressesCallback (GetHostAddresses); return c.BeginInvoke (hostNameOrAddress, requestCallback, state); } - public static IAsyncResult BeginGetHostEntry (string hostNameOrAddress, - AsyncCallback requestCallback, object stateObject) + public static IAsyncResult BeginGetHostEntry (string hostNameOrAddress, AsyncCallback requestCallback, object stateObject) { if (hostNameOrAddress == null) throw new ArgumentNullException ("hostName"); @@ -104,16 +174,21 @@ namespace System.Net { "cannot use them as target address.", "hostNameOrAddress"); + if (use_mono_dns) + return BeginAsyncCall (hostNameOrAddress, requestCallback, stateObject); + GetHostEntryNameCallback c = new GetHostEntryNameCallback (GetHostEntry); return c.BeginInvoke (hostNameOrAddress, requestCallback, stateObject); } - public static IAsyncResult BeginGetHostEntry (IPAddress address, - AsyncCallback requestCallback, object stateObject) + public static IAsyncResult BeginGetHostEntry (IPAddress address, AsyncCallback requestCallback, object stateObject) { if (address == null) throw new ArgumentNullException ("address"); + if (use_mono_dns) + return BeginAsyncCall (address.ToString (), requestCallback, stateObject); + GetHostEntryIPCallback c = new GetHostEntryIPCallback (GetHostEntry); return c.BeginInvoke (address, requestCallback, stateObject); } @@ -124,6 +199,9 @@ namespace System.Net { if (asyncResult == null) throw new ArgumentNullException ("asyncResult"); + if (use_mono_dns) + return EndAsyncCall (asyncResult as DnsAsyncResult); + AsyncResult async = (AsyncResult) asyncResult; GetHostByNameCallback cb = (GetHostByNameCallback) async.AsyncDelegate; return cb.EndInvoke(asyncResult); @@ -134,6 +212,10 @@ namespace System.Net { { if (asyncResult == null) throw new ArgumentNullException ("asyncResult"); + + if (use_mono_dns) + return EndAsyncCall (asyncResult as DnsAsyncResult); + AsyncResult async = (AsyncResult) asyncResult; ResolveCallback cb = (ResolveCallback) async.AsyncDelegate; return cb.EndInvoke(asyncResult); @@ -144,6 +226,13 @@ namespace System.Net { if (asyncResult == null) throw new ArgumentNullException ("asyncResult"); + if (use_mono_dns) { + IPHostEntry entry = EndAsyncCall (asyncResult as DnsAsyncResult); + if (entry == null) + return null; + return entry.AddressList; + } + AsyncResult async = (AsyncResult) asyncResult; GetHostAddressesCallback cb = (GetHostAddressesCallback) async.AsyncDelegate; return cb.EndInvoke(asyncResult); @@ -153,6 +242,10 @@ namespace System.Net { { if (asyncResult == null) throw new ArgumentNullException ("asyncResult"); + + if (use_mono_dns) + return EndAsyncCall (asyncResult as DnsAsyncResult); + AsyncResult async = (AsyncResult) asyncResult; if (async.AsyncDelegate is GetHostEntryIPCallback) return ((GetHostEntryIPCallback) async.AsyncDelegate).EndInvoke (asyncResult); diff --git a/mcs/class/System/System.Net/DnsAsyncResult.cs b/mcs/class/System/System.Net/DnsAsyncResult.cs new file mode 100644 index 00000000000..21d6e783d01 --- /dev/null +++ b/mcs/class/System/System.Net/DnsAsyncResult.cs @@ -0,0 +1,121 @@ +// +// System.Net.DnsAsyncResult +// +// Authors: +// Gonzalo Paniagua Javier (gonzalo@ximian.com) +// +// (C) 2003 Ximian, Inc (http://www.ximian.com) +// + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System.IO; +using System.Threading; +using Mono.Dns; + +namespace System.Net +{ + class DnsAsyncResult : IAsyncResult + { + static WaitCallback internal_cb = new WaitCallback (CB); + ManualResetEvent handle; + bool synch; + bool is_completed; + AsyncCallback callback; + object state; + IPHostEntry entry; + Exception exc; + + public DnsAsyncResult (AsyncCallback cb, object state) + { + this.callback = cb; + this.state = state; + } + + public void SetCompleted (bool synch, IPHostEntry entry, Exception e) + { + this.synch = synch; + this.entry = entry; + exc = e; + lock (this) { + if (is_completed) + return; + is_completed = true; + if (handle != null) + handle.Set (); + } + if (callback != null) + ThreadPool.QueueUserWorkItem (internal_cb, this); + } + + public void SetCompleted (bool synch, Exception e) + { + SetCompleted (synch, null, e); + } + + public void SetCompleted (bool synch, IPHostEntry entry) + { + SetCompleted (synch, entry, null); + } + + static void CB (object _this) + { + DnsAsyncResult ares = (DnsAsyncResult) _this; + ares.callback (ares); + } + + public object AsyncState { + get { return state; } + } + + public WaitHandle AsyncWaitHandle { + get { + lock (this) { + if (handle == null) + handle = new ManualResetEvent (is_completed); + } + return handle; + } + } + + public Exception Exception { + get { return exc; } + } + + public IPHostEntry HostEntry { + get { return entry; } + } + + public bool CompletedSynchronously { + get { return synch; } + } + + public bool IsCompleted { + get { + lock (this) { + return is_completed; + } + } + } + } +} + diff --git a/mcs/class/System/System.dll.sources b/mcs/class/System/System.dll.sources index d70d5e78eba..647d766a2aa 100644 --- a/mcs/class/System/System.dll.sources +++ b/mcs/class/System/System.dll.sources @@ -1052,3 +1052,26 @@ System.Collections.Concurrent/ConcurrentBag.cs ../corlib/System.Collections/CollectionDebuggerView.cs ../corlib/System.Collections.Generic/CollectionDebuggerView.cs ../corlib/Mono/DataConverter.cs +Mono.Dns/DnsClass.cs +Mono.Dns/DnsHeader.cs +Mono.Dns/DnsOpCode.cs +Mono.Dns/DnsPacket.cs +Mono.Dns/DnsQClass.cs +Mono.Dns/DnsQType.cs +Mono.Dns/DnsQuery.cs +Mono.Dns/DnsQuestion.cs +Mono.Dns/DnsRCode.cs +Mono.Dns/DnsResourceRecordA.cs +Mono.Dns/DnsResourceRecordAAAA.cs +Mono.Dns/DnsResourceRecordCName.cs +Mono.Dns/DnsResourceRecord.cs +Mono.Dns/DnsResourceRecordIPAddress.cs +Mono.Dns/DnsResourceRecordPTR.cs +Mono.Dns/DnsResponse.cs +Mono.Dns/DnsType.cs +Mono.Dns/DnsUtil.cs +Mono.Dns/ResolverAsyncOperation.cs +Mono.Dns/SimpleResolver.cs +Mono.Dns/ResolverError.cs +Mono.Dns/SimpleResolverEventArgs.cs +System.Net/DnsAsyncResult.cs -- 2.25.1