1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 2 * Copyright (c) 2003-2012 by AG-Software * 3 * All Rights Reserved. * 4 * Contact information for AG-Software is available at http://www.ag-software.de * 5 * * 6 * Licence: * 7 * The agsXMPP SDK is released under a dual licence * 8 * agsXMPP can be used under either of two licences * 9 * * 10 * A commercial licence which is probably the most appropriate for commercial * 11 * corporate use and closed source projects. * 12 * * 13 * The GNU Public License (GPL) is probably most appropriate for inclusion in * 14 * other open source projects. * 15 * * 16 * See README.html for details. * 17 * * 18 * For general enquiries visit our website at: * 19 * http://www.ag-software.de * 20 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 21 22 // 23 // Bdev.Net.Dns by Rob Philpott, Big Developments Ltd. Please send all bugs/enhancements to 24 // rob@bigdevelopments.co.uk This file and the code contained within is freeware and may be 25 // distributed and edited without restriction. 26 // 27 28 using System; 29 using System.Collections; 30 using System.Net; 31 using System.Net.Sockets; 32 33 namespace agsXMPP.Net.Dns 34 { 35 /// <summary> 36 /// Summary description for Dns. 37 /// </summary> 38 public sealed class Resolver 39 { 40 const int _dnsPort = 53; 41 const int _udpRetryAttempts = 2; 42 static int _uniqueId; 43 const int _udpTimeout = 1000; 44 45 /// <summary> 46 /// Private constructor - this static class should never be instantiated 47 /// </summary> Resolver()48 private Resolver() 49 { 50 // no implementation 51 } 52 53 /// <summary> 54 /// Shorthand form to make SRV querying easier, essentially wraps up the retreival 55 /// of the SRV records, and sorts them by preference 56 /// </summary> 57 /// <param name="domain">domain name to retreive SRV RRs for</param> 58 /// <param name="dnsServer">the server we're going to ask</param> 59 /// <returns>An array of SRVRecords</returns> SRVLookup(string domain, IPAddress dnsServer)60 public static SRVRecord[] SRVLookup(string domain, IPAddress dnsServer) 61 { 62 // check the inputs 63 if (domain == null) throw new ArgumentNullException("domain"); 64 if (dnsServer == null) throw new ArgumentNullException("dnsServer"); 65 66 // create a request for this 67 Request request = new Request(); 68 69 // add one question - the SRV IN lookup for the supplied domain 70 request.AddQuestion(new Question(domain, DnsType.SRV, DnsClass.IN)); 71 72 // fire it off 73 Response response = Lookup(request, dnsServer); 74 75 // if we didn't get a response, then return null 76 if (response == null) return null; 77 78 // create a growable array of SRV records 79 ArrayList resourceRecords = new ArrayList(); 80 81 // add each of the answers to the array 82 foreach (Answer answer in response.Answers) 83 { 84 // if the answer is an SRV record 85 if (answer.Type == DnsType.SRV) 86 { 87 // add it to our array 88 resourceRecords.Add(answer.Record); 89 } 90 } 91 92 // create array of MX records 93 SRVRecord[] srvRecords = new SRVRecord[resourceRecords.Count]; 94 95 // copy from the array list 96 resourceRecords.CopyTo(srvRecords); 97 98 // sort into lowest preference order 99 Array.Sort(srvRecords); 100 101 // and return 102 return srvRecords; 103 } 104 105 /// <summary> 106 /// The principal look up function, which sends a request message to the given 107 /// DNS server and collects a response. This implementation re-sends the message 108 /// via UDP up to two times in the event of no response/packet loss 109 /// </summary> 110 /// <param name="request">The logical request to send to the server</param> 111 /// <param name="dnsServer">The IP address of the DNS server we are querying</param> 112 /// <returns>The logical response from the DNS server or null if no response</returns> Lookup(Request request, IPAddress dnsServer)113 public static Response Lookup(Request request, IPAddress dnsServer) 114 { 115 // check the inputs 116 if (request == null) throw new ArgumentNullException("request"); 117 if (dnsServer == null) throw new ArgumentNullException("dnsServer"); 118 119 // We will not catch exceptions here, rather just refer them to the caller 120 121 // create an end point to communicate with 122 IPEndPoint server = new IPEndPoint(dnsServer, _dnsPort); 123 124 // get the message 125 byte[] requestMessage = request.GetMessage(); 126 127 // send the request and get the response 128 byte[] responseMessage = UdpTransfer(server, requestMessage); 129 130 // and populate a response object from that and return it 131 return new Response(responseMessage); 132 } 133 UdpTransfer(IPEndPoint server, byte[] requestMessage)134 private static byte[] UdpTransfer(IPEndPoint server, byte[] requestMessage) 135 { 136 // UDP can fail - if it does try again keeping track of how many attempts we've made 137 int attempts = 0; 138 139 // try repeatedly in case of failure 140 while (attempts <= _udpRetryAttempts) 141 { 142 // firstly, uniquely mark this request with an id 143 unchecked 144 { 145 // substitute in an id unique to this lookup, the request has no idea about this 146 requestMessage[0] = (byte)(_uniqueId >> 8); 147 requestMessage[1] = (byte)_uniqueId; 148 } 149 150 // we'll be send and receiving a UDP packet 151 Socket socket; 152 if (Socket.OSSupportsIPv6 && (server.AddressFamily == AddressFamily.InterNetworkV6)) 153 socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); // V6 154 else 155 socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); 156 157 // we will wait at most 1 second for a dns reply 158 socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, _udpTimeout); 159 160 // send it off to the server 161 socket.SendTo(requestMessage, requestMessage.Length, SocketFlags.None, server); 162 163 // RFC1035 states that the maximum size of a UDP datagram is 512 octets (bytes) 164 byte[] responseMessage = new byte[512]; 165 166 try 167 { 168 // wait for a response upto 1 second 169 socket.Receive(responseMessage); 170 171 // make sure the message returned is ours 172 if (responseMessage[0] == requestMessage[0] && responseMessage[1] == requestMessage[1]) 173 { 174 // its a valid response - return it, this is our successful exit point 175 return responseMessage; 176 } 177 } 178 catch (SocketException) 179 { 180 // failure - we better try again, but remember how many attempts 181 attempts++; 182 } 183 finally 184 { 185 // increase the unique id 186 _uniqueId++; 187 188 // close the socket 189 socket.Close(); 190 } 191 } 192 193 // the operation has failed, this is our unsuccessful exit point 194 throw new NoResponseException(); 195 } 196 } 197 } 198