/// <summary> /// Gets MX records.(MX records are sorted by preference, lower array element is prefered) /// </summary> /// <param name="domain"></param> /// <param name="mxRecords"></param> /// <returns></returns> public DnsReplyCode GetMXRecords(string domain, out MX_Record[] mxRecords) { //--- Try to get MX records from cache mxRecords = DnsCache.GetMXFromCache(domain); if (mxRecords != null) { // Console.WriteLine("domain:" + domain + " from cahce."); return(DnsReplyCode.Ok); } //------------------------------------// Dns_Header header = new Dns_Header(DnsEx.ID, OPCODE.IQUERY); Dns_Query query = new Dns_Query(domain, QTYPE.MX, 1); byte[] bQuery = query.GetQuery(header); byte[] reply = GetQuery(bQuery, header.ID); if (reply != null) { Dns_Answers answers = new Dns_Answers(); if (answers.ParseAnswers(reply, header.ID)) { mxRecords = answers.GetMxRecordsFromAnswers(); if (mxRecords != null) { // Add to cache DnsCache.AddMXToCache(domain, mxRecords); return(DnsReplyCode.Ok); } else { return(DnsReplyCode.NoEntries); } } } return(DnsReplyCode.TempError); }
/// <summary> /// Parses answer. /// </summary> /// <param name="reply"></param> /// <param name="queryID"></param> /// <returns>Returns true if answer parsed successfully.</returns> internal bool ParseAnswers(byte[] reply, int queryID) { try { /* * 1 1 1 1 1 1 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | | / / | / NAME / | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | TYPE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | CLASS | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | TTL | | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | RDLENGTH | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| | / RDATA / | / / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ */ //------- Parse result -----------------------------------// Dns_Header replyHeader = new Dns_Header(); if (!replyHeader.ParseHeader(reply)) { return(false); } // Check that it's query what we want if (queryID != replyHeader.ID) { return(false); } int pos = 12; //----- Parse question part ------------// for (int q = 0; q < replyHeader.QDCOUNT; q++) { string dummy = ""; GetQName(reply, ref pos, ref dummy); //qtype + qclass pos += 4; } //--------------------------------------// ArrayList answers = new ArrayList(); //---- Start parsing aswers ------------------------------------------------------------------// for (int i = 0; i < replyHeader.ANCOUNT; i++) { string name = ""; if (!GetQName(reply, ref pos, ref name)) { return(false); } int type = reply[pos++] << 8 | reply[pos++]; int rdClass = reply[pos++] << 8 | reply[pos++]; int ttl = reply[pos++] << 24 | reply[pos++] << 16 | reply[pos++] << 8 | reply[pos++]; int rdLength = reply[pos++] << 8 | reply[pos++]; object answerObj = null; switch ((QTYPE)type) { case QTYPE.MX: answerObj = ParseMxRecord(reply, ref pos); break; default: answerObj = "sgas"; // Dummy place holder for now pos += rdLength; break; } // Add answer to answer list if (answerObj != null) { answers.Add(new Dns_Answer(name, (QTYPE)type, rdClass, ttl, rdLength, answerObj)); } else { return(false); // Parse failed } } //-------------------------------------------------------------------------------------------// if (answers.Count > 0) { m_Answers = new Dns_Answer[answers.Count]; answers.CopyTo(m_Answers); } return(true); } catch { return(false); } }
/// <summary> /// Gets query. /// </summary> /// <param name="header"></param> /// <returns></returns> public byte[] GetQuery(Dns_Header header) { byte[] query = null; try { /* 4.1.2. Question section format * 1 1 1 1 1 1 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | | / QNAME / | / / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QTYPE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QCLASS | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | QNAME | a domain name represented as a sequence of labels, where | each label consists of a length octet followed by that | number of octets. The domain name terminates with the | zero length octet for the null label of the root. Note | that this field may be an odd number of octets; no | padding is used. */ query = new byte[521]; // Copy dns header to query byte[] head = header.GetHeader(); head.CopyTo(query, 0); //--------- Query part ------------------------------------// string[] labels = m_QNAME.Split(new char[] { '.' }); int position = 12; // Copy all domain parts(labels) to query // eg. lumisoft.ee = 2 labels, lumisoft and ee. // format = label.length + label(bytes) foreach (string label in labels) { // add label lenght to query query[position++] = (byte)(label.Length); // convert label string to byte array byte[] b = Encoding.ASCII.GetBytes(label.ToCharArray()); b.CopyTo(query, position); // Move position by label length position += b.Length; } // Terminate domain (see note above) query[position++] = (byte)0; // Set QTYPE query[position++] = (byte)0; query[position++] = (byte)m_QTYPE; // Set QCLASS query[position++] = (byte)0; query[position++] = (byte)m_QCALSS; //-------------------------------------------------------// } catch { query = null; } return(query); }