/// <summary> /// Parses resource record from reply data. /// </summary> /// <param name="reply">DNS server reply data.</param> /// <param name="offset">Current offset in reply data.</param> /// <param name="rdLength">Resource record data length.</param> /// <param name="ttl">Time to live in seconds.</param> public static DNS_rr_HINFO Parse(byte[] reply, ref int offset, int rdLength, int ttl) { /* RFC 1035 3.3.2. HINFO RDATA format * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ * / CPU / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ * / OS / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ * * CPU A <character-string> which specifies the CPU type. * * OS A <character-string> which specifies the operating * system type. * * Standard values for CPU and OS can be found in [RFC-1010]. * */ // CPU string cpu = Dns_Client.ReadCharacterString(reply, ref offset); // OS string os = Dns_Client.ReadCharacterString(reply, ref offset); return(new DNS_rr_HINFO(cpu, os, ttl)); }
public bool IsValidMXRecord(string _domain) { try { Dns_Client.DnsServers = new string[] { "8.8.8.8" }; Dns_Client.UseDnsCache = true; using (Dns_Client dns = new Dns_Client()) { DnsServerResponse reponse = null; reponse = dns.Query(_domain, LumiSoft.Net.DNS.DNS_QType.MX); if (((LumiSoft.Net.DNS.DNS_rr_MX)reponse.Answers[0]).Host != null) { return(true); } else { return(false); } } } catch (Exception ex) { return(false); } }
/// <summary> /// Cleans up any resources being used. /// </summary> public void Dispose() { if (m_IsDisposed) { return; } try{ if (m_IsRunning) { Stop(); } } catch { } m_IsDisposed = true; // Release events. this.Error = null; this.SessionCompleted = null; m_pQueues = null; m_pSmartHosts = null; m_pDsnClient.Dispose(); m_pDsnClient = null; }
/// <summary> /// Parses resource record from reply data. /// </summary> /// <param name="name">DNS domain name that owns a resource record.</param> /// <param name="reply">DNS server reply data.</param> /// <param name="offset">Current offset in reply data.</param> /// <param name="rdLength">Resource record data length.</param> /// <param name="ttl">Time to live in seconds.</param> public static DNS_rr_TXT Parse(string name, byte[] reply, ref int offset, int rdLength, int ttl) { // TXT RR string text = Dns_Client.ReadCharacterString(reply, ref offset); return(new DNS_rr_TXT(name, text, ttl)); }
/// <summary> /// Start processing relay message. /// </summary> /// <param name="state">User data.</param> internal void Start(object state) { try{ m_pSmtpClient = new SMTP_Client(); m_pSmtpClient.LocalHostName = m_pLocalBindInfo.HostName; if (m_pServer.Logger != null) { m_pSmtpClient.Logger = new Logger(); m_pSmtpClient.Logger.WriteLog += new EventHandler <WriteLogEventArgs>(SmtpClient_WriteLog); } LogText("Starting to relay message '" + m_pRelayItem.MessageID + "' from '" + m_pRelayItem.From + "' to '" + m_pRelayItem.To + "'."); // Get all possible target hosts for active recipient. List <string> targetHosts = new List <string>(); if (m_RelayMode == Relay_Mode.Dns) { foreach (string host in SMTP_Client.GetDomainHosts(m_pRelayItem.To)) { try{ foreach (IPAddress ip in Dns_Client.Resolve(host)) { m_pTargets.Add(new Relay_Target(new IPEndPoint(ip, 25))); } } catch { // Failed to resolve host name. LogText("Failed to resolve host '" + host + "' name."); } } } else if (m_RelayMode == Relay_Mode.SmartHost) { foreach (Relay_SmartHost smartHost in m_pSmartHosts) { try{ m_pTargets.Add(new Relay_Target(new IPEndPoint(Dns_Client.Resolve(smartHost.Host)[0], smartHost.Port), smartHost.SslMode, smartHost.UserName, smartHost.Password)); } catch { // Failed to resolve smart host name. LogText("Failed to resolve smart host '" + smartHost.Host + "' name."); } } } BeginConnect(); } catch (Exception x) { Dispose(x); } }
/// <summary> /// Parses resource record from reply data. /// </summary> /// <param name="name">DNS domain name that owns a resource record.</param> /// <param name="reply">DNS server reply data.</param> /// <param name="offset">Current offset in reply data.</param> /// <param name="rdLength">Resource record data length.</param> /// <param name="ttl">Time to live in seconds.</param> public static DNS_rr_PTR Parse(string name, byte[] reply, ref int offset, int rdLength, int ttl) { string domainName = ""; if (Dns_Client.GetQName(reply, ref offset, ref domainName)) { return(new DNS_rr_PTR(name, domainName, ttl)); } else { throw new ArgumentException("Invalid PTR resource record data !"); } }
/// <summary> /// Parses resource record from reply data. /// </summary> /// <param name="name">DNS domain name that owns a resource record.</param> /// <param name="reply">DNS server reply data.</param> /// <param name="offset">Current offset in reply data.</param> /// <param name="rdLength">Resource record data length.</param> /// <param name="ttl">Time to live in seconds.</param> public static DNS_rr_CNAME Parse(string name, byte[] reply, ref int offset, int rdLength, int ttl) { string alias = ""; if (Dns_Client.GetQName(reply, ref offset, ref alias)) { return(new DNS_rr_CNAME(name, alias, ttl)); } else { throw new ArgumentException("Invalid CNAME resource record data !"); } }
/// <summary> /// Parses resource record from reply data. /// </summary> /// <param name="name">DNS domain name that owns a resource record.</param> /// <param name="reply">DNS server reply data.</param> /// <param name="offset">Current offset in reply data.</param> /// <param name="rdLength">Resource record data length.</param> /// <param name="ttl">Time to live in seconds.</param> public static DNS_rr_NS Parse(string name, byte[] reply, ref int offset, int rdLength, int ttl) { // Name server name string server = ""; if (Dns_Client.GetQName(reply, ref offset, ref server)) { return(new DNS_rr_NS(name, server, ttl)); } else { throw new ArgumentException("Invalid NS resource record data !"); } }
/// <summary> /// Parses resource record from reply data. /// </summary> /// <param name="name">DNS domain name that owns a resource record.</param> /// <param name="reply">DNS server reply data.</param> /// <param name="offset">Current offset in reply data.</param> /// <param name="rdLength">Resource record data length.</param> /// <param name="ttl">Time to live in seconds.</param> public static DNS_rr_SRV Parse(string name, byte[] reply, ref int offset, int rdLength, int ttl) { // Priority Weight Port Target // Priority int priority = reply[offset++] << 8 | reply[offset++]; // Weight int weight = reply[offset++] << 8 | reply[offset++]; // Port int port = reply[offset++] << 8 | reply[offset++]; // Target string target = ""; Dns_Client.GetQName(reply, ref offset, ref target); return(new DNS_rr_SRV(name, priority, weight, port, target, ttl)); }
/// <summary> /// Default constructor. /// </summary> /// <param name="owner">Owner DNS client.</param> /// <param name="id">Transaction ID.</param> /// <param name="qtype">QTYPE value.</param> /// <param name="qname">QNAME value.</param> /// <param name="timeout">Timeout in milliseconds.</param> /// <exception cref="ArgumentNullException">Is raised when <b>owner</b> or <b>qname</b> is null reference.</exception> internal DNS_ClientTransaction(Dns_Client owner, int id, DNS_QType qtype, string qname, int timeout) { if (owner == null) { throw new ArgumentNullException("owner"); } if (qname == null) { throw new ArgumentNullException("qname"); } m_pOwner = owner; m_ID = id; m_QName = qname; m_QType = qtype; m_CreateTime = DateTime.Now; m_pTimeoutTimer = new TimerEx(timeout); m_pTimeoutTimer.Elapsed += new System.Timers.ElapsedEventHandler(m_pTimeoutTimer_Elapsed); }
/// <summary> /// Cleans up any resource being used. /// </summary> public void Dispose() { lock (m_pLock){ if (this.State == DNS_ClientTransactionState.Disposed) { return; } SetState(DNS_ClientTransactionState.Disposed); m_pTimeoutTimer.Dispose(); m_pTimeoutTimer = null; m_pOwner = null; m_pResponse = null; this.StateChanged = null; this.Timeout = null; } }
/// <summary> /// Parses resource record from reply data. /// </summary> /// <param name="name">DNS domain name that owns a resource record.</param> /// <param name="reply">DNS server reply data.</param> /// <param name="offset">Current offset in reply data.</param> /// <param name="rdLength">Resource record data length.</param> /// <param name="ttl">Time to live in seconds.</param> public static DNS_rr_NAPTR Parse(string name, byte[] reply, ref int offset, int rdLength, int ttl) { /* RFC 3403. * The packet format for the NAPTR record is as follows * 1 1 1 1 1 1 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ORDER | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | PREFERENCE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | / FLAGS / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | / SERVICES / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | / REGEXP / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | / REPLACEMENT / | / / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ */ int order = reply[offset++] << 8 | reply[offset++]; int preference = reply[offset++] << 8 | reply[offset++]; string flags = Dns_Client.ReadCharacterString(reply, ref offset); string services = Dns_Client.ReadCharacterString(reply, ref offset); string regexp = Dns_Client.ReadCharacterString(reply, ref offset); string replacement = ""; Dns_Client.GetQName(reply, ref offset, ref replacement); return(new DNS_rr_NAPTR(name, order, preference, flags, services, regexp, replacement, ttl)); }
/// <summary> /// Parses resource record from reply data. /// </summary> /// <param name="name">DNS domain name that owns a resource record.</param> /// <param name="reply">DNS server reply data.</param> /// <param name="offset">Current offset in reply data.</param> /// <param name="rdLength">Resource record data length.</param> /// <param name="ttl">Time to live in seconds.</param> public static DNS_rr_MX Parse(string name, byte[] reply, ref int offset, int rdLength, int ttl) { /* RFC 1035 3.3.9. MX RDATA format * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | PREFERENCE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | / EXCHANGE / | / / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | where: | | PREFERENCE | A 16 bit integer which specifies the preference given to | this RR among others at the same owner. Lower values | are preferred. | | EXCHANGE | A <domain-name> which specifies a host willing to act as | a mail exchange for the owner name. */ int pref = reply[offset++] << 8 | reply[offset++]; string server = ""; if (Dns_Client.GetQName(reply, ref offset, ref server)) { return(new DNS_rr_MX(name, pref, server, ttl)); } else { throw new ArgumentException("Invalid MX resource record data !"); } }
//---- SMTP implementation ----// #region function SendMessageToServer private bool SendMessageToServer(string[] to, string reverse_path, Stream message) { // Get email from to string for (int i = 0; i < to.Length; i++) { to[i] = (new InfoControl.Net.Mail.Mime.eAddress(to[i])).Email; } ArrayList defectiveEmails = new ArrayList(); Socket so = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); so.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 15000); BufferedSocket socket = new BufferedSocket(so); SocketLogger logger = null; if (m_LogCmds && this.SessionLog != null) { logger = new SocketLogger(so, this.SessionLog); logger.SessionID = socket.GetHashCode().ToString(); socket.Logger = logger; } try{ string reply = ""; bool supports_SIZE = false; bool supports_8BIT = false; bool supports_BDAT = false; if (m_UseSmartHost) { socket.Connect(new IPEndPoint(System.Net.Dns.Resolve(m_SmartHost).AddressList[0], m_Port)); } else { //---- Parse e-domain -------------------------------// string domain = to[0]; // eg. Ivx <*****@*****.**> if (domain.IndexOf("<") > -1 && domain.IndexOf(">") > -1) { domain = domain.Substring(domain.IndexOf("<") + 1, domain.IndexOf(">") - domain.IndexOf("<") - 1); } if (domain.IndexOf("@") > -1) { domain = domain.Substring(domain.LastIndexOf("@") + 1); } if (domain.Trim().Length == 0) { if (logger != null) { logger.AddTextEntry("Destination address '" + to[0] + "' is invalid, aborting !"); } return(false); } //--- Get MX record -------------------------------------------// Dns_Client dns = new Dns_Client(); Dns_Client.DnsServers = m_DnsServers; DnsServerResponse dnsResponse = dns.Query(domain, QTYPE.MX); switch (dnsResponse.ResponseCode) { case RCODE.NO_ERROR: MX_Record[] mxRecords = dnsResponse.GetMXRecords(); // Try all available hosts by MX preference order, if can't connect specified host. foreach (MX_Record mx in mxRecords) { try{ if (logger != null) { logger.AddTextEntry("Connecting with mx record to: " + mx.Host); } socket.Connect(new IPEndPoint(System.Net.Dns.Resolve(mx.Host).AddressList[0], m_Port)); break; } catch { // Just skip and let for to try next host. if (logger != null) { logger.AddTextEntry("Failed connect to: " + mx.Host); } } } /* Rfc 2821 5 * If no MX records are found, but an A RR is found, the A RR is treated as * if it was associated with an implicit MX RR, with a preference of 0, * pointing to that host. * */ if (mxRecords.Length == 0) { // Try to connect with A record IPHostEntry ipEntry = null; try{ if (logger != null) { logger.AddTextEntry("No mx record, trying to get A record for: " + domain); } ipEntry = System.Net.Dns.Resolve(domain); } catch { if (logger != null) { logger.AddTextEntry("Invalid domain,no MX or A record: " + domain); } OnError(SMTP_ErrorType.InvalidEmailAddress, to, "email domain <" + domain + "> is invalid"); defectiveEmails.AddRange(to); if (logger != null) { logger.Flush(); } return(false); } try{ if (logger != null) { logger.AddTextEntry("Connecting with A record to:" + domain); } socket.Connect(new IPEndPoint(ipEntry.AddressList[0], m_Port)); } catch { if (logger != null) { logger.AddTextEntry("Failed connect to:" + domain); } } } break; case RCODE.NAME_ERROR: if (logger != null) { logger.AddTextEntry("Invalid domain,no MX or A record: " + domain); } OnError(SMTP_ErrorType.InvalidEmailAddress, to, "email domain <" + domain + "> is invalid"); defectiveEmails.AddRange(to); if (logger != null) { logger.Flush(); } return(false); case RCODE.SERVER_FAILURE: if (logger != null) { logger.AddTextEntry("Dns server unvailable."); } OnError(SMTP_ErrorType.UnKnown, to, "Dns server unvailable."); defectiveEmails.AddRange(to); if (logger != null) { logger.Flush(); } return(false); } } if (!socket.Connected) { OnError(SMTP_ErrorType.UnKnown, to, "Unable connect to server !"); if (logger != null) { logger.Flush(); } return(false); } #region Get 220 reply from server /* NOTE: reply may be multiline * 220 xx ready * or * 220-someBull * 200 xx */ // Server must reply 220 - Server OK reply = socket.ReadLine(); if (!IsReplyCode("220", reply)) { OnError(SMTP_ErrorType.UnKnown, to, reply); socket.SendLine("QUIT"); if (logger != null) { logger.Flush(); } return(false); } else { // 220-xxx<CRLF> // 220 aa<CRLF> - means end // reply isn't complete, get more while (reply.IndexOf("220 ") == -1) { reply += socket.ReadLine(); } } #endregion #region cmd EHLO/HELO // Send greeting to server socket.SendLine("EHLO " + m_HostName); reply = socket.ReadLine(); if (!IsReplyCode("250", reply)) { // EHLO failed, mayby server doesn't support it, try HELO socket.SendLine("HELO " + m_HostName); reply = socket.ReadLine(); if (!IsReplyCode("250", reply)) { OnError(SMTP_ErrorType.UnKnown, to, reply); socket.SendLine("QUIT"); defectiveEmails.AddRange(to); if (logger != null) { logger.Flush(); } return(false); } // else{ // supports_ESMTP = false; // } } else { // 250-xxx<CRLF> // 250 aa<CRLF> - means end // reply isn't complete, get more while (reply.IndexOf("250 ") == -1) { reply += socket.ReadLine(); } // Check if SIZE argument is supported if (reply.ToUpper().IndexOf("SIZE") > -1) { supports_SIZE = true; } // Check if 8BITMIME argument is supported if (reply.ToUpper().IndexOf("8BITMIME") > -1) { supports_8BIT = true; } // Check if CHUNKING argument is supported if (reply.ToUpper().IndexOf("CHUNKING") > -1) { supports_BDAT = true; } } #endregion //*** All server today support 8-bit, just skip it. // If server doesn't support 8bit mime, check if message is 8bit. // If is we MAY NOT send this message or loss of data /* if(!supports_8BIT){ * if(Is8BitMime(message)){ * OnError(SMTP_ErrorType.NotSupported,to,"Message is 8-Bit mime and server doesn't support it."); * socket.SendLine("QUIT"); * * if(logger != null){ * logger.Flush(); * } * return false; * } * }*/ #region cmd AUTH if (this.m_Username != null && m_Username.Length > 0 && m_Password != null && m_Password.Length > 0) { if (reply.ToUpper().IndexOf("AUTH") > -1) { if (reply.ToUpper().IndexOf("LOGIN") > -1) { socket.SendLine("AUTH LOGIN"); reply = socket.ReadLine(); if (!IsReplyCode("334", reply)) { OnError(SMTP_ErrorType.NotAuthenticated, to, "Failed to authenticate"); socket.SendLine("QUIT"); if (logger != null) { logger.Flush(); } return(false); } socket.SendLine(Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(m_Username.ToCharArray()))); reply = socket.ReadLine(); if (!IsReplyCode("334", reply)) { OnError(SMTP_ErrorType.NotAuthenticated, to, "Failed to authenticate"); socket.SendLine("QUIT"); if (logger != null) { logger.Flush(); } return(false); } socket.SendLine(Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(m_Password.ToCharArray()))); reply = socket.ReadLine(); if (!IsReplyCode("235", reply)) { OnError(SMTP_ErrorType.NotAuthenticated, to, "Failed to authenticate"); socket.SendLine("QUIT"); if (logger != null) { logger.Flush(); } return(false); } } // if(reply.ToUpper().IndexOf("CRAM-MD5") > -1) // { // socket.SendLine("AUTH CRAM-MD5"); // reply = socket.ReadLine(); // if (IsReplyCode("334",auth)) // { // socket.SendLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(m_Username.ToCharArray()))); // socket.SendLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(m_Password.ToCharArray()))); // } // } } else { //server did not support AUTH } } #endregion #region cmd MAIL // NOTE: Syntax:{MAIL FROM:<*****@*****.**> [SIZE=msgSize]<CRLF>} // Send Mail From if (supports_SIZE) { socket.SendLine("MAIL FROM:<" + reverse_path + "> SIZE=" + (message.Length - message.Position)); } else { socket.SendLine("MAIL FROM:<" + reverse_path + ">"); } reply = socket.ReadLine(); if (!IsReplyCode("250", reply)) { // To Do: Check if size exceeded error: OnError(SMTP_ErrorType.UnKnown, to, reply); socket.SendLine("QUIT"); defectiveEmails.AddRange(to); if (logger != null) { logger.Flush(); } return(false); } #endregion #region cmd RCPT // NOTE: Syntax:{RCPT TO:<*****@*****.**><CRLF>} bool isAnyValidEmail = false; foreach (string rcpt in to) { // Send Mail To socket.SendLine("RCPT TO:<" + rcpt + ">"); reply = socket.ReadLine(); if (!IsReplyCode("250", reply)) { // Is unknown user if (IsReplyCode("550", reply)) { OnError(SMTP_ErrorType.InvalidEmailAddress, new string[] { rcpt }, reply); } else { OnError(SMTP_ErrorType.UnKnown, new string[] { rcpt }, reply); } defectiveEmails.Add(rcpt); } else { isAnyValidEmail = true; } } // If there isn't any valid email - quit. if (!isAnyValidEmail) { socket.SendLine("QUIT"); if (logger != null) { logger.Flush(); } return(false); } //---------------------------------------------// #endregion #region cmd DATA if (!(supports_BDAT && m_UseBDAT)) { // Notify Data Start socket.SendLine("DATA"); reply = socket.ReadLine(); if (!IsReplyCode("354", reply)) { OnError(SMTP_ErrorType.UnKnown, to, reply); socket.SendLine("QUIT"); defectiveEmails.AddRange(to); if (logger != null) { logger.Flush(); } return(false); } //------- Do period handling -----------------------------------------// // If line starts with '.', add additional '.'.(Read rfc for more info) MemoryStream msgStrmPeriodOk = Core.DoPeriodHandling(message, true, false); //--------------------------------------------------------------------// // Check if message ends with <CRLF>, if not add it. -------// if (msgStrmPeriodOk.Length >= 2) { byte[] byteEnd = new byte[2]; msgStrmPeriodOk.Position = msgStrmPeriodOk.Length - 2; msgStrmPeriodOk.Read(byteEnd, 0, 2); if (byteEnd[0] != (byte)'\r' && byteEnd[1] != (byte)'\n') { msgStrmPeriodOk.Write(new byte[] { (byte)'\r', (byte)'\n' }, 0, 2); } } msgStrmPeriodOk.Position = 0; //-----------------------------------------------------------// //---- Send message --------------------------------------------// long totalSent = 0; long totalLength = msgStrmPeriodOk.Length; while (totalSent < totalLength) { byte[] dataBuf = new byte[4000]; int nCount = msgStrmPeriodOk.Read(dataBuf, 0, dataBuf.Length); int countSended = socket.Send(dataBuf, 0, nCount, SocketFlags.None); totalSent += countSended; if (countSended != nCount) { msgStrmPeriodOk.Position = totalSent; } OnPartOfMessageIsSent(countSended, totalSent, totalLength); } //-------------------------------------------------------------// msgStrmPeriodOk.Close(); // Notify End of Data socket.SendLine("."); reply = socket.ReadLine(); if (!IsReplyCode("250", reply)) { OnError(SMTP_ErrorType.UnKnown, to, reply); socket.SendLine("QUIT"); defectiveEmails.AddRange(to); if (logger != null) { logger.Flush(); } return(false); } } #endregion #region cmd BDAT if (supports_BDAT && m_UseBDAT) { socket.SendLine("BDAT " + (message.Length - message.Position) + " LAST"); //---- Send message --------------------------------------------// long totalSent = 0; long totalLength = message.Length - message.Position; while (totalSent < totalLength) { byte[] dataBuf = new byte[4000]; int nCount = message.Read(dataBuf, 0, dataBuf.Length); int countSended = socket.Send(dataBuf, 0, nCount, SocketFlags.None); totalSent += countSended; if (countSended != nCount) { message.Position = totalSent; } OnPartOfMessageIsSent(countSended, totalSent, totalLength); } //-------------------------------------------------------------// // Get store result reply = socket.ReadLine(); if (!reply.StartsWith("250")) { OnError(SMTP_ErrorType.UnKnown, to, reply); socket.SendLine("QUIT"); defectiveEmails.AddRange(to); if (logger != null) { logger.Flush(); } return(false); } } #endregion #region cmd QUIT // Notify server - server can exit now socket.SendLine("QUIT"); // reply = socket.ReadLine(); #endregion } catch (Exception x) { OnError(SMTP_ErrorType.UnKnown, to, x.Message); defectiveEmails.AddRange(to); if (logger != null) { logger.Flush(); } return(false); } finally{ // Raise event OnSendJobCompleted(Thread.CurrentThread.GetHashCode().ToString(), to, defectiveEmails); } if (logger != null) { logger.Flush(); } return(true); }
/// <summary> /// Gets host name. If fails returns ip address. /// </summary> /// <param name="ip">IP address which to reverse lookup.</param> /// <returns>Returns host name of specified IP address.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>ip</b> is null.</exception> public static string GetHostName(IPAddress ip) { if (ip == null) { throw new ArgumentNullException("ip"); } string retVal = ip.ToString(); try { Dns_Client dns = new Dns_Client(); DnsServerResponse response = dns.Query(ip.ToString(), QTYPE.PTR); if (response.ResponseCode == RCODE.NO_ERROR) { DNS_rr_PTR[] ptrs = response.GetPTRRecords(); if (ptrs.Length > 0) { retVal = ptrs[0].DomainName; } } } catch {} return retVal; }
/// <summary> /// Parses resource record from reply data. /// </summary> /// <param name="name">DNS domain name that owns a resource record.</param> /// <param name="reply">DNS server reply data.</param> /// <param name="offset">Current offset in reply data.</param> /// <param name="rdLength">Resource record data length.</param> /// <param name="ttl">Time to live in seconds.</param> public static DNS_rr_SOA Parse(string name, byte[] reply, ref int offset, int rdLength, int ttl) { /* RFC 1035 3.3.13. SOA RDATA format * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ * / MNAME / * / / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ * / RNAME / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | SERIAL | | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | REFRESH | | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | RETRY | | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | EXPIRE | | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | MINIMUM | | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | where: | | MNAME The <domain-name> of the name server that was the | original or primary source of data for this zone. | | RNAME A <domain-name> which specifies the mailbox of the | person responsible for this zone. | | SERIAL The unsigned 32 bit version number of the original copy | of the zone. Zone transfers preserve this value. This | value wraps and should be compared using sequence space | arithmetic. | | REFRESH A 32 bit time interval before the zone should be | refreshed. | | RETRY A 32 bit time interval that should elapse before a | failed refresh should be retried. | | EXPIRE A 32 bit time value that specifies the upper limit on | the time interval that can elapse before the zone is no | longer authoritative. | | MINIMUM The unsigned 32 bit minimum TTL field that should be | exported with any RR from this zone. */ //---- Parse record -------------------------------------------------------------// // MNAME string nameserver = ""; Dns_Client.GetQName(reply, ref offset, ref nameserver); // RNAME string adminMailBox = ""; Dns_Client.GetQName(reply, ref offset, ref adminMailBox); char[] adminMailBoxAr = adminMailBox.ToCharArray(); for (int i = 0; i < adminMailBoxAr.Length; i++) { if (adminMailBoxAr[i] == '.') { adminMailBoxAr[i] = '@'; break; } } adminMailBox = new string(adminMailBoxAr); // SERIAL long serial = reply[offset++] << 24 | reply[offset++] << 16 | reply[offset++] << 8 | reply[offset++]; // REFRESH long refresh = reply[offset++] << 24 | reply[offset++] << 16 | reply[offset++] << 8 | reply[offset++]; // RETRY long retry = reply[offset++] << 24 | reply[offset++] << 16 | reply[offset++] << 8 | reply[offset++]; // EXPIRE long expire = reply[offset++] << 24 | reply[offset++] << 16 | reply[offset++] << 8 | reply[offset++]; // MINIMUM long minimum = reply[offset++] << 24 | reply[offset++] << 16 | reply[offset++] << 8 | reply[offset++]; //--------------------------------------------------------------------------------// return(new DNS_rr_SOA(name, nameserver, adminMailBox, serial, refresh, retry, expire, minimum, ttl)); }
/// <summary> /// Connects to sepcified host. /// </summary> /// <param name="localEndpoint">Sets local endpoint. Pass null, to use default.</param> /// <param name="host">Host name or IP address.</param> /// <param name="port">Port where to connect.</param> /// <param name="ssl">Specifies if to connected via SSL. Default SMTP port is 25 and SSL port is 465.</param> public void Connect(IPEndPoint localEndpoint, string host, int port, bool ssl) { m_pSocket = new SocketEx(); if (localEndpoint != null) { m_pSocket.Bind(localEndpoint); } // Create logger if (SessionLog != null) { m_pLogger = new SocketLogger(m_pSocket.RawSocket, SessionLog); m_pLogger.SessionID = Guid.NewGuid().ToString(); m_pSocket.Logger = m_pLogger; } if (host.IndexOf("@") == -1) { m_pSocket.Connect(host, port, ssl); } else { //---- Parse e-domain -------------------------------// string domain = host; // eg. Ivx <*****@*****.**> if (domain.IndexOf("<") > -1 && domain.IndexOf(">") > -1) { domain = domain.Substring(domain.IndexOf("<") + 1, domain.IndexOf(">") - domain.IndexOf("<") - 1); } if (domain.IndexOf("@") > -1) { domain = domain.Substring(domain.LastIndexOf("@") + 1); } if (domain.Trim().Length == 0) { if (m_pLogger != null) { m_pLogger.AddTextEntry("Destination address '" + host + "' is invalid, aborting !"); } throw new Exception("Destination address '" + host + "' is invalid, aborting !"); } //--- Get MX record -------------------------------------------// Dns_Client dns = new Dns_Client(); Dns_Client.DnsServers = m_pDnsServers; DnsServerResponse dnsResponse = dns.Query(domain, QTYPE.MX); bool connected = false; switch (dnsResponse.ResponseCode) { case RCODE.NO_ERROR: DNS_rr_MX[] mxRecords = dnsResponse.GetMXRecords(); // Try all available hosts by MX preference order, if can't connect specified host. foreach (DNS_rr_MX mx in mxRecords) { try { if (m_pLogger != null) { m_pLogger.AddTextEntry("Connecting with mx record to: " + mx.Host); } m_pSocket.Connect(mx.Host, port, ssl); connected = true; break; } catch (Exception x) { // Just skip and let for to try next host. if (m_pLogger != null) { m_pLogger.AddTextEntry("Failed connect to: " + mx.Host + " error:" + x.Message); } } } // None of MX didn't connect if (mxRecords.Length > 0 && !connected) { throw new Exception("Destination email server is down"); } /* Rfc 2821 5 If no MX records are found, but an A RR is found, the A RR is treated as if it was associated with an implicit MX RR, with a preference of 0, pointing to that host. */ if (!connected) { // Try to connect with A record IPAddress[] ipEntry = null; try { if (m_pLogger != null) { m_pLogger.AddTextEntry("No mx record, trying to get A record for: " + domain); } ipEntry = Dns_Client.Resolve(domain); } catch { if (m_pLogger != null) { m_pLogger.AddTextEntry("Invalid domain,no MX or A record: " + domain); } throw new Exception("Invalid domain,no MX or A record: " + domain); } try { if (m_pLogger != null) { m_pLogger.AddTextEntry("Connecting with A record to:" + domain); } m_pSocket.Connect(domain, port, ssl); } catch { if (m_pLogger != null) { m_pLogger.AddTextEntry("Failed connect to:" + domain); } throw new Exception("Destination email server is down"); } } break; case RCODE.NAME_ERROR: if (m_pLogger != null) { m_pLogger.AddTextEntry("Invalid domain,no MX or A record: " + domain); } throw new Exception("Invalid domain,no MX or A record: " + domain); case RCODE.SERVER_FAILURE: if (m_pLogger != null) { m_pLogger.AddTextEntry("Dns server unvailable."); } throw new Exception("Dns server unvailable."); } } /* * Notes: Greeting may be single or multiline response. * * Examples: * 220<SP>SMTP server ready<CRLF> * * 220-SMTP server ready<CRLF> * 220-Addtitional text<CRLF> * 220<SP>final row<CRLF> * */ // Read server response string responseLine = m_pSocket.ReadLine(1000); while (!responseLine.StartsWith("220 ")) { // If lisne won't start with 220, then its error response if (!responseLine.StartsWith("220")) { throw new Exception(responseLine); } responseLine = m_pSocket.ReadLine(1000); } m_Connected = true; }
/// <summary> /// Gets specified email domain possible connect points. Values are in priority descending order. /// </summary> /// <param name="domain">Email address or domain name.</param> /// <returns>Returns possible email server connection points.</returns> public IPAddress[] GetDestinations(string domain) { /* 1) Add MX records 2) Add A records */ // We have email address, just get domain from it. if (domain.IndexOf('@') > -1) { domain = domain.Substring(domain.IndexOf('@') + 1); } List<IPAddress> retVal = new List<IPAddress>(); Dns_Client dns = new Dns_Client(); Dns_Client.DnsServers = DnsServers; DnsServerResponse response = dns.Query(domain, QTYPE.MX); // Add MX foreach (DNS_rr_MX mx in response.GetMXRecords()) { try { IPAddress[] ips = Dns.GetHostAddresses(mx.Host); foreach (IPAddress ip in ips) { if (!retVal.Contains(ip)) { retVal.Add(ip); } } } catch { // Probably wrong MX record, no A reocrd for it, so we don't get IP. Just skip it. } } // Add A records only if no MX records. if (retVal.Count == 0) { response = dns.Query(domain, QTYPE.A); foreach (DNS_rr_A a in response.GetARecords()) { IPAddress ip = a.IP; if (!retVal.Contains(ip)) { retVal.Add(ip); } } } return retVal.ToArray(); }
/// <summary> /// Default constructor. /// </summary> public SIP_Stack() { m_pTransportLayer = new SIP_TransportLayer(this); m_pTransactionLayer = new SIP_TransactionLayer(this); m_pNonceManager = new Auth_HttpDigest_NonceManager(); m_pProxyServers = new List<SIP_Uri>(); m_pRegistrations = new List<SIP_UA_Registration>(); m_pCredentials = new List<NetworkCredential>(); m_RegisterCallID = SIP_t_CallID.CreateCallID(); m_pLogger = new Logger(); m_pDnsClient = new Dns_Client(); }
/// <summary> /// Gets specified email domain SMTP hosts. Values are in descending priority order. /// </summary> /// <param name="domain">Domain name. This value can be email address too, then domain parsed automatically.</param> /// <returns>Returns specified email domain SMTP hosts.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>domain</b> is null.</exception> /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception> /// <exception cref="DNS_ClientException">Is raised when DNS query failure.</exception> public static string[] GetDomainHosts(string domain) { if (domain == null) { throw new ArgumentNullException("domain"); } if (string.IsNullOrEmpty(domain)) { throw new ArgumentException( "Invalid argument 'domain' value, you need to specify domain value."); } // We have email address, parse domain. if (domain.IndexOf("@") > -1) { domain = domain.Substring(domain.IndexOf('@') + 1); } List<string> retVal = new List<string>(); // Get MX records. Dns_Client dns = new Dns_Client(); DnsServerResponse response = dns.Query(domain, QTYPE.MX); if (response.ResponseCode == RCODE.NO_ERROR) { foreach (DNS_rr_MX mx in response.GetMXRecords()) { // Block invalid MX records. if (!string.IsNullOrEmpty(mx.Host)) { retVal.Add(mx.Host); } } } else { throw new DNS_ClientException(response.ResponseCode); } /* RFC 2821 5. If no MX records are found, but an A RR is found, the A RR is treated as if it was associated with an implicit MX RR, with a preference of 0, pointing to that host. */ if (retVal.Count == 0) { retVal.Add(domain); } return retVal.ToArray(); }
/// <summary> /// Default constructor. /// </summary> public Relay_Server() { m_pQueues = new List <Relay_Queue>(); m_pSmartHosts = new CircleCollection <Relay_SmartHost>(); m_pDsnClient = new Dns_Client(); }