Add support for an alternative managed DNS resolver
authorGonzalo Paniagua Javier <gonzalo.mono@gmail.com>
Thu, 17 Mar 2011 23:45:32 +0000 (19:45 -0400)
committerGonzalo Paniagua Javier <gonzalo.mono@gmail.com>
Sat, 8 Oct 2011 04:48:12 +0000 (00:48 -0400)
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.

26 files changed:
mcs/class/System/Mono.Dns/DnsClass.cs [new file with mode: 0644]
mcs/class/System/Mono.Dns/DnsHeader.cs [new file with mode: 0644]
mcs/class/System/Mono.Dns/DnsOpCode.cs [new file with mode: 0644]
mcs/class/System/Mono.Dns/DnsPacket.cs [new file with mode: 0644]
mcs/class/System/Mono.Dns/DnsQClass.cs [new file with mode: 0644]
mcs/class/System/Mono.Dns/DnsQType.cs [new file with mode: 0644]
mcs/class/System/Mono.Dns/DnsQuery.cs [new file with mode: 0644]
mcs/class/System/Mono.Dns/DnsQuestion.cs [new file with mode: 0644]
mcs/class/System/Mono.Dns/DnsRCode.cs [new file with mode: 0644]
mcs/class/System/Mono.Dns/DnsResourceRecord.cs [new file with mode: 0644]
mcs/class/System/Mono.Dns/DnsResourceRecordA.cs [new file with mode: 0644]
mcs/class/System/Mono.Dns/DnsResourceRecordAAAA.cs [new file with mode: 0644]
mcs/class/System/Mono.Dns/DnsResourceRecordCName.cs [new file with mode: 0644]
mcs/class/System/Mono.Dns/DnsResourceRecordIPAddress.cs [new file with mode: 0644]
mcs/class/System/Mono.Dns/DnsResourceRecordPTR.cs [new file with mode: 0644]
mcs/class/System/Mono.Dns/DnsResponse.cs [new file with mode: 0644]
mcs/class/System/Mono.Dns/DnsType.cs [new file with mode: 0644]
mcs/class/System/Mono.Dns/DnsUtil.cs [new file with mode: 0644]
mcs/class/System/Mono.Dns/Makefile [new file with mode: 0644]
mcs/class/System/Mono.Dns/ResolverAsyncOperation.cs [new file with mode: 0644]
mcs/class/System/Mono.Dns/ResolverError.cs [new file with mode: 0644]
mcs/class/System/Mono.Dns/SimpleResolver.cs [new file with mode: 0644]
mcs/class/System/Mono.Dns/SimpleResolverEventArgs.cs [new file with mode: 0644]
mcs/class/System/System.Net/Dns.cs
mcs/class/System/System.Net/DnsAsyncResult.cs [new file with mode: 0644]
mcs/class/System/System.dll.sources

diff --git a/mcs/class/System/Mono.Dns/DnsClass.cs b/mcs/class/System/Mono.Dns/DnsClass.cs
new file mode 100644 (file)
index 0000000..bbebe4f
--- /dev/null
@@ -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 (file)
index 0000000..054f735
--- /dev/null
@@ -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<byte> bytes;
+
+               public DnsHeader (byte [] bytes)
+                       : this (bytes, 0)
+               {
+               }
+
+               public DnsHeader (byte [] bytes, int offset)
+                       : this (new ArraySegment<byte> (bytes, offset, DnsHeaderLength))
+               {
+               }
+
+               public DnsHeader (ArraySegment<byte> 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 (file)
index 0000000..2558afa
--- /dev/null
@@ -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 (file)
index 0000000..5e9155e
--- /dev/null
@@ -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<byte>(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 (file)
index 0000000..cd6f6ec
--- /dev/null
@@ -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 (file)
index 0000000..523fe82
--- /dev/null
@@ -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 (file)
index 0000000..2a0cfb0
--- /dev/null
@@ -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 (file)
index 0000000..e4d9a38
--- /dev/null
@@ -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 (file)
index 0000000..ae510e6
--- /dev/null
@@ -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 (file)
index 0000000..5a19001
--- /dev/null
@@ -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<byte> 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<byte>(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<byte> 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 (file)
index 0000000..b367d8d
--- /dev/null
@@ -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 (file)
index 0000000..c2d9571
--- /dev/null
@@ -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 (file)
index 0000000..e2b58d7
--- /dev/null
@@ -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 (file)
index 0000000..5c3c09a
--- /dev/null
@@ -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<byte> 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 (file)
index 0000000..5b74fda
--- /dev/null
@@ -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 (file)
index 0000000..d5d9496
--- /dev/null
@@ -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<DnsResourceRecord> EmptyRR = new ReadOnlyCollection<DnsResourceRecord> (new DnsResourceRecord [0]);
+               static readonly ReadOnlyCollection<DnsQuestion> EmptyQS = new ReadOnlyCollection<DnsQuestion> (new DnsQuestion [0]);
+
+               ReadOnlyCollection<DnsQuestion> question;
+               ReadOnlyCollection<DnsResourceRecord> answer;
+               ReadOnlyCollection<DnsResourceRecord> authority;
+               ReadOnlyCollection<DnsResourceRecord> 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<DnsResourceRecord> GetRRs (int count)
+               {
+                       if (count <= 0)
+                               return EmptyRR;
+
+                       List<DnsResourceRecord> records = new List<DnsResourceRecord>(count);
+                       for (int i = 0; i < count; i++)
+                               records.Add (DnsResourceRecord.CreateFromBuffer (this, position, ref offset));
+                       return records.AsReadOnly ();
+               }
+
+               ReadOnlyCollection<DnsQuestion> GetQuestions (int count)
+               {
+                       if (count <= 0)
+                               return EmptyQS;
+
+                       List<DnsQuestion> records = new List<DnsQuestion> (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<DnsQuestion> GetQuestions ()
+               {
+                       if (question == null)
+                               question = GetQuestions (Header.QuestionCount);
+                       return question;
+               }
+
+               public ReadOnlyCollection<DnsResourceRecord> GetAnswers ()
+               {
+                       if (answer == null) {
+                               GetQuestions ();
+                               answer = GetRRs (Header.AnswerCount);
+                       }
+                       return answer;
+               }
+
+               public ReadOnlyCollection<DnsResourceRecord> GetAuthority ()
+               {
+                       if (authority == null) {
+                               GetQuestions ();
+                               GetAnswers ();
+                               authority = GetRRs (Header.AuthorityCount);
+                       }
+                       return authority;
+               }
+
+               public ReadOnlyCollection<DnsResourceRecord> 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 (file)
index 0000000..d23cafb
--- /dev/null
@@ -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 (file)
index 0000000..fc722c3
--- /dev/null
@@ -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 (file)
index 0000000..e43e5df
--- /dev/null
@@ -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 (file)
index 0000000..c97842a
--- /dev/null
@@ -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 (file)
index 0000000..136901c
--- /dev/null
@@ -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 (file)
index 0000000..ca790e6
--- /dev/null
@@ -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<int, SimpleResolverEventArgs> queries;
+               AsyncCallback receive_cb;
+               TimerCallback timeout_cb;
+               bool disposed;
+#if REUSE_RESPONSES
+               Stack<DnsResponse> responses_avail = new Stack<DnsResponse> ();
+#endif
+
+               public SimpleResolver ()
+               {
+                       queries = new Dictionary<int, SimpleResolverEventArgs> ();
+                       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<IPEndPoint> eps = new List<IPEndPoint> ();
+                       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<DnsQuestion> 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<DnsResourceRecord> 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<string> aliases = null;
+                       List<IPAddress> 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<IPAddress> ();
+                                       addresses.Add (((DnsResourceRecordIPAddress) r).Address);
+                               } else if (r.Type == DnsType.CNAME) {
+                                       if (aliases == null)
+                                               aliases = new List<string> ();
+                                       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<IPEndPoint> eps = new List<IPEndPoint> ();
+                       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 (file)
index 0000000..599bebe
--- /dev/null
@@ -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<SimpleResolverEventArgs> 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);
+               }
+       }
+}
+
index ed61fc4012e262862e37085b84b1c41a47075c3f..8419fe396eb151a7c585d7d8b20ac3f99d45bb7b 100644 (file)
@@ -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 (file)
index 0000000..21d6e78
--- /dev/null
@@ -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;
+                               }
+                       }
+               }
+       }
+}
+
index d70d5e78eba39ec6967bcfeb31fc5704fb751bc6..647d766a2aa81a478293fcdc08ccd7968451e53b 100644 (file)
@@ -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