public void TestMX(string name, string exchange) { MXRecord record = new MXRecord(name, exchange); MXRecord parsedRecord = this.Roundtrip <MXRecord>(record); Assert.True(parsedRecord.Equals(record)); }
public MantaOutboundClientSendResult(MantaOutboundClientResult result, string message, VirtualMTA vmta = null, MXRecord mx = null) { MantaOutboundClientResult = result; Message = message; VirtualMTA = vmta; MXRecord = mx; }
/// <summary> /// /// </summary> /// <param name="vmta"></param> /// <param name="mxRecord"></param> /// <param name="maxConnections"></param> public MantaOutboundClientPool(VirtualMTA vmta, MXRecord mxRecord) { _LastUsedTimestamp = DateTime.UtcNow.Ticks; MXRecord = mxRecord; VirtualMTA = vmta; var maxMessagesHour = OutboundRuleManager.GetMaxMessagesDestinationHour(vmta, mxRecord); if (maxMessagesHour > 0) { MaxMessagesMinute = (int?)Math.Floor(maxMessagesHour / 60d); SentMessagesLog = new List <long>(); Logging.Debug("MantaOutboundClientPool> for: " + vmta.IPAddress + "-" + mxRecord.Host + " MAX MESSAGES MIN: " + MaxMessagesMinute); } else { MaxMessagesMinute = null; SentMessagesLog = null; } var maxConnections = OutboundRuleManager.GetMaxConnectionsToDestination(vmta, mxRecord); if (maxConnections > 0) { MaxConnections = maxConnections; Logging.Debug("MantaOutboundClientPool> for: " + vmta.IPAddress + "-" + mxRecord.Host + " MAX CONNECTION: " + MaxConnections); } else { MaxConnections = null; } SmtpClients = new List <MantaOutboundClient>(); }
/// <summary> /// Gets permission to attempt a send of a message. /// </summary> /// <param name="ipAddress">The IP Address we wan't to send from.</param> /// <param name="mxRecord">The MX Record of the destination.</param> /// <returns>TRUE if we can send FALSE if we should throttle.</returns> public bool TryGetSendAuth(VirtualMTA ipAddress, MXRecord mxRecord) { int mxPatternID = -1; int maxMessagesHour = OutboundRuleManager.GetMaxMessagesDestinationHour(ipAddress, mxRecord, out mxPatternID); // If the Max messages is -1 then unlimited so can just return true here. // No need for any logging or calculating. if (maxMessagesHour == -1) { return(true); } // Create or get this outbound IP/mx pattern send history. ThrottleManager.MxPatternThrottlingSendHistory mxSndHist = null; if (!this._sendHistory.TryGetValue(ipAddress.IPAddress.ToString(), out mxSndHist)) { mxSndHist = new ThrottleManager.MxPatternThrottlingSendHistory(); this._sendHistory.AddOrUpdate(ipAddress.IPAddress.ToString(), mxSndHist, (string s, ThrottleManager.MxPatternThrottlingSendHistory sh) => mxSndHist); } //this._sendHistory.GetOrAdd(ipAddress.IPAddress.ToString(), new ThrottleManager.MxPatternThrottlingSendHistory()); List <DateTime> sndHistory = mxSndHist.GetOrAdd(mxPatternID, new List <DateTime>()); // Only calculate if needed. if (mxSndHist.IntervalValuesNeedRecalcTimestamp <= DateTime.UtcNow) { int maxMessages = 0; int maxMessagesIntervalMinute = 0; while (maxMessages < 1 && maxMessagesIntervalMinute <= 60) { maxMessagesIntervalMinute++; maxMessages = (int)Math.Floor((double)maxMessagesHour / 60d * (double)maxMessagesIntervalMinute); } mxSndHist.IntervalMaxMessages = maxMessages; mxSndHist.IntervalMinutes = maxMessagesIntervalMinute; mxSndHist.IntervalValuesNeedRecalcTimestamp = DateTime.UtcNow.AddMinutes(MtaParameters.MTA_CACHE_MINUTES); } lock (sndHistory) { // Remove sends that happened over "Interval" minute(s) ago. DateTime sendsAfterTimestamp = DateTime.UtcNow.AddMinutes((double)(mxSndHist.IntervalMinutes * -1)); sndHistory.RemoveAll((DateTime d) => d <= sendsAfterTimestamp); // Check for throttling if (sndHistory.Count < mxSndHist.IntervalMaxMessages) { // Not hit throttle limit yet. // Log send and return true. sndHistory.Add(DateTime.UtcNow); return(true); } else { // THROTTLED! return(false); } } }
/// <summary> /// Gets the maximum amount of simultaneous connections to specified host. /// </summary> /// <param name="ipAddress">IP Address connecting from.</param> /// <param name="record">MXRecord of the destination.</param> /// <returns>Max number of connections.</returns> internal static int GetMaxConnectionsToDestination(VirtualMTA ipAddress, MXRecord record) { int mxPatternID = -1; OutboundRuleCollection rules = OutboundRuleManager.GetRules(record, ipAddress, out mxPatternID); for (int i = 0; i < rules.Count; i++) { if (rules[i].Type == OutboundRuleType.MaxConnections) { int tmp = 0; if (int.TryParse(rules[i].Value, out tmp)) { return(tmp); } else { Logging.Error("Failed to get max connections for " + record.Host + " using " + ipAddress.IPAddress.ToString() + " value wasn't valid [" + rules[i].Value + "], defaulting to 1"); return(1); } } } Logging.Error("Failed to get max connections for " + record.Host + " using " + ipAddress.IPAddress.ToString() + " defaulting to 1"); return(1); }
/// <summary> /// Gets the MAX number of messages allowed to be sent through the connection. /// </summary> /// <param name="record">MX Record for the destination.</param> /// <param name="ipAddress">IPAddress that we are sending from.</param> /// <returns>Max number of messages per connection.</returns> public static int GetMaxMessagesPerConnection(MXRecord record, VirtualMTA ipAddress) { int mxPatternID = 0; OutboundRuleCollection rules = GetRules(record, ipAddress, out mxPatternID); for (int i = 0; i < rules.Count; i++) { if (rules[i].Type == OutboundRuleType.MaxMessagesConnection) { int tmp = 0; if (int.TryParse(rules[i].Value, out tmp)) { return(tmp); } else { Logging.Error("Failed to get max messages per connection for " + record.Host + " using " + ipAddress.IPAddress.ToString() + " value wasn't valid [" + rules[i].Value + "], defaulting to 1"); return(1); } } } Logging.Error("Failed to get max messages per connection for " + record.Host + " using " + ipAddress.IPAddress.ToString() + " defaulting to 1"); return(1); }
/// <summary> /// Attempt to connect to the specified MX server. /// </summary> /// <param name="mx">MX Record of the server to connect to.</param> public async Task <bool> ConnectAsync(MXRecord mx) { _LastActive = DateTime.UtcNow; IsActive = true; await base.ConnectAsync(mx.Host, MtaParameters.Client.SMTP_PORT); _LastActive = DateTime.UtcNow; SmtpStream = new SmtpStreamHandler(this as TcpClient); _MXRecord = mx; // Read the Server greeting. string response = SmtpStream.ReadAllLines(); _LastActive = DateTime.UtcNow; if (!response.StartsWith("2")) { // If the MX is actively denying use service access, SMTP code 421 then we should inform // the ServiceNotAvailableManager manager so it limits our attepts to this MX to 1/minute. if (response.StartsWith("421")) { ServiceNotAvailableManager.Add(SmtpStream.LocalAddress.ToString(), MXRecord.Host, DateTime.UtcNow); } base.Close(); return(false); } IsActive = false; _LastActive = DateTime.UtcNow; return(true); }
/// <summary> /// /// </summary> /// <param name="vmta"></param> /// <param name="mxRecord"></param> private void AddClientPool(VirtualMTA vmta, MXRecord mxRecord) { var key = GetPoolKey(vmta, mxRecord); lock (AddClientPoolLock) { if (!ClientPools.ContainsKey(key)) { ClientPools[key] = new MantaOutboundClientPool(vmta, mxRecord); } } }
private MXRecord DeserializeMxRr(string name, int ttl, byte[] bytes, int start, out int bytesRead) { var mx = new MXRecord() { Name = name, TimeToLive = ttl, }; mx.ExchangeDName = this.dnsSerializer.ParseQuestionName(bytes, start, out bytesRead); bytesRead += 2; // preference return(mx); }
/// <summary> /// Gets MX record collection. /// Each string is a single record formatted as "Priority,Host,TTL" /// If Null then no MX servers where found. /// </summary> /// <param name="domain">The domain to get MXs for.</param> /// <returns>See description.</returns> /// <throws>Win32 Exception. (123)</throws> internal static string[] GetMXRecords(string domain) { // Pointer to the first DNS result. IntPtr ptrFirstRecord = IntPtr.Zero; ArrayList results = new ArrayList(); try { // Do the DNS query. int retVal = dnsapi.DnsQuery_W(ref domain, QueryTypes.DNS_TYPE_MX, QueryOptions.DNS_QUERY_STANDARD, 0, ref ptrFirstRecord, 0); // If the retVal isn't 0 then something went wrong if (retVal != 0) { // There are no DNS records of type MX. if (retVal == 9003 || retVal == 9501) { return(null); } else if (retVal == 123) { throw new DNSDomainNotFoundException(); } else { //throw new Win32Exception(retVal); System.Diagnostics.Trace.WriteLine("DNS_API:" + domain + " (" + retVal + ")"); return(null); } } MXRecord recMx = (MXRecord)Marshal.PtrToStructure(ptrFirstRecord, typeof(MXRecord));; IntPtr ptrCurrentRecord = IntPtr.Zero; for (ptrCurrentRecord = ptrFirstRecord; !ptrCurrentRecord.Equals(IntPtr.Zero); ptrCurrentRecord = recMx.pNext) { recMx = (MXRecord)Marshal.PtrToStructure(ptrCurrentRecord, typeof(MXRecord)); if (recMx.wType == 15) { string line = recMx.wPreference.ToString() + "," + Marshal.PtrToStringAuto(recMx.pNameExchange) + "," + recMx.dwTtl.ToString(); results.Add(line); } } } finally { // Always cleanup. DnsRecordListFree(ptrFirstRecord, 0); } return((string[])results.ToArray(typeof(string))); }
/// <summary> /// Creates a SmtpOutboundClient bound to the specified endpoint. /// </summary> /// <param name="ipAddress">The local IP address to bind to.</param> public MantaOutboundClient(VirtualMTA vmta, MXRecord mx) : base() { _VirtualMta = vmta; _MXRecord = mx; TcpClient = CreateTcpClient(); _CanPipeline = false; _MaxMessagesConnection = OutboundRuleManager.GetMaxMessagesPerConnection(mx, vmta); if (_MaxMessagesConnection < 1) { _MaxMessagesConnection = null; } }
/// <summary> /// Tests equality between this TXT record and the other <paramref name="record"/>. /// </summary> /// <param name="record">The other record.</param> /// <returns><c>true</c> if the RRs are equal, <c>false</c> otherwise.</returns> public override bool Equals(DnsResourceRecord record) { if (!base.Equals(record)) { return(false); } MXRecord mxRecord = record as MXRecord; if (mxRecord == null) { return(false); } return( DnsStandard.Equals(m_exchange, mxRecord.m_exchange) && this.Preference == mxRecord.Preference ); }
/// <summary> /// Gets the Outbound Rules for the specified destination MX and optionally IP Address. /// </summary> /// <param name="mxRecord">MXRecord for the destination MX.</param> /// <param name="mtaIpAddress">Outbound IP Address</param> /// <param name="mxPatternID">OUT: the ID of MxPattern that caused match.</param> /// <returns></returns> public static IList <OutboundRule> GetRules(MXRecord mxRecord, VirtualMTA mtaIpAddress, out int mxPatternID) { // Get the data from the database. This needs to be cleverer and reload every x minutes. if (OutboundRuleManager._MXPatterns == null) { OutboundRuleManager._MXPatterns = OutboundRuleDB.GetOutboundRulePatterns(); } if (OutboundRuleManager._Rules == null) { OutboundRuleManager._Rules = OutboundRuleDB.GetOutboundRules(); } int patternID = OutboundRuleManager.GetMxPatternID(mxRecord, mtaIpAddress); mxPatternID = patternID; return((from r in OutboundRuleManager._Rules where r.OutboundMxPatternID == patternID select r).ToList()); }
public DnsRecord ParseMX(string[] args) { string domainName = args.GetRequiredValue(0); string exchange = args.GetRequiredValue(1); int ttl = this.ValidateTTL(args.GetOptionalValue <int>(2, 0)); short pref = args.GetOptionalValue <short>(3, 10); string notes = args.GetOptionalValue(4, string.Empty); MXRecord record = new MXRecord(domainName , exchange , pref) { TTL = ttl }; DnsRecord dnsRecord = new DnsRecord(domainName, DnsStandard.RecordType.MX, record.Serialize(), notes); return(dnsRecord); }
/// <summary> /// Get the MX records for the specified domain name using the specified DNS server. /// </summary> /// <param name="address">The domain name.</param> /// <param name="host">The host name of the DNS server to use.</param> /// <param name="port">The port number of the DNS server to use.</param> /// <param name="timeout">The timeout in miliseconds.</param> /// <returns>A collection of Mx Records.</returns> public static ActiveUp.Net.Mail.MxRecordCollection GetMxRecords(string address, string host, int port, int timeout) { MxRecordCollection mxRecords = new MxRecordCollection(); DnsQuery query = new DnsQuery(IPAddress.Parse(host)); query.RecursiveQuery = true; query.DnsServer.Port = port; query.Domain = address; DnsAnswer answer = query.QueryServer(RecordType.MX, timeout); foreach (DnsEntry entry in answer.Answers) { MXRecord mxRecord = (MXRecord)entry.Data; mxRecords.Add(mxRecord.Domain, mxRecord.Preference); } return(mxRecords); }
/// <summary> /// Gets the maximum amount of messages to send per hour from each ip address to mx. /// </summary> /// <param name="ipAddress">Outbound IP address</param> /// <param name="record">MX Record of destination server.</param> /// <param name="mxPatternID">ID of the pattern used to identify the rule.</param> /// <returns>Maximum number of messages per hour or -1 for unlimited.</returns> public static int GetMaxMessagesDestinationHour(VirtualMTA ipAddress, MXRecord record, out int mxPatternID) { OutboundRuleCollection rules = OutboundRuleManager.GetRules(record, ipAddress, out mxPatternID); for (int i = 0; i < rules.Count; i++) { if (rules[i].Type == OutboundRuleType.MaxMessagesPerHour) { int tmp = 0; if (int.TryParse(rules[i].Value, out tmp)) { return(tmp); } else { Logging.Error("Failed to get max messages per hour for " + record.Host + " using " + ipAddress.IPAddress.ToString() + " value wasn't valid [" + rules[i].Value + "], defaulting to unlimited"); return(-1); } } } Logging.Error("Failed to get max messages per hour for " + record.Host + " using " + ipAddress.IPAddress.ToString() + " defaulting to unlimited"); return(-1); }
/// <summary> /// This method handles message deferal. /// Logs deferral /// Fails the message if timed out /// or /// Sets the next rety date time /// </summary> /// <param name="defMsg">The deferal message from the SMTP server.</param> /// <param name="ipAddress">IP Address that send was attempted from.</param> /// <param name="mxRecord">MX Record of the server tried to send too.</param> /// <param name="isServiceUnavailable">If false will backoff the retry, if true will use the MtaParameters.MtaRetryInterval, /// this is needed to reduce the tail when sending as a message could get multiple try again laters and soon be 1h+ before next retry.</param> public static void HandleDeliveryDeferral(MtaQueuedMessage msg, string defMsg, VirtualMTA ipAddress, MXRecord mxRecord, bool isServiceUnavailable = false) { HandleDeliveryDeferralAsync(msg, defMsg, ipAddress, mxRecord, isServiceUnavailable).Wait(); }
/// <summary> /// Gets an Array of MX Records for the specified domain. If none found returns null. /// </summary> /// <param name="domain"></param> /// <returns></returns> public static MXRecord[] GetMXRecords(string domain) { // Make sure the domain is all lower. domain = domain.ToLower(); // This is what we'll be returning. MXRecord[] mxRecords = null; // Try and get DNS from internal cache. if (DNSManager._Records.TryGetValue(domain, out mxRecords)) { // Found cached records. // Make sure they haven't expired. if (mxRecords.Count((MXRecord mx) => mx.Dead) < 1) { return(mxRecords); } } string[] records = null; try { // Get the records from DNS records = dnsapi.GetMXRecords(domain); } catch (DNSDomainNotFoundException) { // Ensure records is null. records = null; } // No MX records for domain. if (records == null) { // If there are no MX records use the hostname as per SMTP RFC. MXRecord[] mxs = new MXRecord[] { new MXRecord(domain, 10, 300u, MxRecordSrc.A) }; _Records.AddOrUpdate(domain, mxs, (string key, MXRecord[] existing) => mxs); return(mxs); } mxRecords = new MXRecord[records.Length]; for (int i = 0; i < mxRecords.Length; i++) { string[] split = records[i].Split(new char[] { ',' }); if (split.Length == 3) { mxRecords[i] = new MXRecord(split[1], int.Parse(split[0]), uint.Parse(split[2]), MxRecordSrc.MX); } } // Order by preferance mxRecords = ( from mx in mxRecords where mx != null orderby mx.Preference select mx).ToArray <MXRecord>(); DNSManager._Records.TryAdd(domain, mxRecords); return(mxRecords); }
/// <summary> /// Attempt to create a new connection using the specified ip address and mx record. /// </summary> /// <returns>A connected outbound client or NULL</returns> public async Task <CreateNewConnectionAsyncResult> CreateNewConnectionAsync(VirtualMTA ipAddress, MXRecord mxRecord) { SmtpOutboundClient smtpClient = null; // Get the maximum connections to the destination. int maximumConnections = OutboundRuleManager.GetMaxConnectionsToDestination(ipAddress, mxRecord); lock (this.SyncRoot) { // Get the currently active connections count. int currentConnections = InUseConnections.Count; lock (_ConnectionAttemptsInProgressLock) { // If the current connections count + current connection is less than // the maximum connections then we can create a new connection otherwise // we are maxed out so return null. if (maximumConnections <= (currentConnections + _ConnectionAttemptsInProgress)) { return(new CreateNewConnectionAsyncResult(new MaxConnectionsException())); } // Limit the amount of connection attempts or experiance massive delays 30s+ for client.connect() if (_ConnectionAttemptsInProgress >= SmtpClientQueue.MAX_SIMULTANEOUS_CLIENT_CONNECT_ATTEMPTS) { //Logging.Debug("Cannot attempt to create new connection."); return(new CreateNewConnectionAsyncResult(new MaxConnectionsException())); } //Logging.Debug("Attempting to create new connection."); _ConnectionAttemptsInProgress++; } } // Do the actual creating and connecting of the client outside of the lock // so we don't block other threads. try { // Create the new client and make the connection smtpClient = new SmtpOutboundClient(ipAddress); await smtpClient.ConnectAsync(mxRecord); smtpClient.IsActive = true; this.InUseConnections.Add(smtpClient); } catch (Exception ex) { // If something went wrong clear the client so we don't return something odd. if (smtpClient != null) { smtpClient.Close(); smtpClient.Dispose(); smtpClient = null; } if (ex is SocketException) { return(new CreateNewConnectionAsyncResult(ex)); } if (ex is AggregateException && ex.InnerException is System.IO.IOException) { return(new CreateNewConnectionAsyncResult(ex.InnerException)); } } finally { // Reduce the current attempts as were done. _ConnectionAttemptsInProgress--; if (smtpClient != null) { smtpClient.IsActive = false; } } // Return connected client or null. return(new CreateNewConnectionAsyncResult(smtpClient)); }
/// <summary> /// Gets the MxPatternID that matches the MX Record, Outbound IP Address combo. /// </summary> /// <param name="record"></param> /// <param name="ipAddress"></param> /// <returns></returns> private static int GetMxPatternID(MXRecord record, VirtualMTA ipAddress) { if (_matchedPatterns == null) { _matchedPatterns = new ConcurrentDictionary <string, MatchedMxPatternCollection>(); } MatchedMxPatternCollection matchedPatterns = null; if (!_matchedPatterns.TryGetValue(record.Host, out matchedPatterns)) { matchedPatterns = new MatchedMxPatternCollection(); _matchedPatterns.AddOrUpdate(record.Host, matchedPatterns, (string s, MatchedMxPatternCollection p) => matchedPatterns); } MatchedMxPattern matchedPattern = matchedPatterns.GetMatchedMxPattern(ipAddress); if (matchedPattern != null && matchedPattern.MatchedUtc.AddMinutes(MtaParameters.MTA_CACHE_MINUTES) > DateTime.UtcNow) { // Found a valid cached pattern ID so return it. return(matchedPattern.MxPatternID); } // Loop through all of the patterns for (int i = 0; i < _MXPatterns.Count; i++) { // The current pattern we're working with. OutboundMxPattern pattern = _MXPatterns[i]; // If the pattern applies only to a specified IP address then // only check for a match if getting rules for that IP. if (pattern.LimitedToOutboundIpAddressID.HasValue && pattern.LimitedToOutboundIpAddressID.Value != ipAddress.ID) { continue; } if (pattern.Type == OutboundMxPatternType.CommaDelimited) { // Pattern is a comma delimited list, so split the values string[] strings = pattern.Value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); // Loop though the values in the split string array. for (int c = 0; c < strings.Length; c++) { try { // If they are a match return the rules. if (strings[c].Equals(record.Host, StringComparison.OrdinalIgnoreCase)) { if (pattern.LimitedToOutboundIpAddressID.HasValue) { matchedPatterns.Add(pattern.ID, ipAddress); } else { matchedPatterns.Add(pattern.ID, null); } return(pattern.ID); } } catch (Exception) { } } continue; } else if (pattern.Type == OutboundMxPatternType.Regex) { // Pattern is Regex so just need to do an IsMatch if (Regex.IsMatch(record.Host, pattern.Value, RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture)) { // Found pattern match. if (pattern.LimitedToOutboundIpAddressID.HasValue) { matchedPatterns.Add(pattern.ID, ipAddress); } else { matchedPatterns.Add(pattern.ID, null); } return(pattern.ID); } else { continue; } } else { // Don't know what to do with this pattern so move on to the next. Logging.Error("Unknown OutboundMxPatternType : " + pattern.Type.ToString()); continue; } } // Should have been found by default at least, but hasn't. Logging.Fatal("No MX Pattern Rules! Default Deleted?"); MantaCoreEvents.InvokeMantaCoreStopping(); Environment.Exit(0); return(-1); }
public override int Compare(DnsRecord x, DnsRecord y) { if (x == y) { return(0); } if (x == null) { return(-1); } if (y == null) { return(1); } int res = 0; if ((res = x.Type.ToString().CompareTo(y.Type.ToString())) == 0 && (res = x.Owner.CompareTo(y.Owner)) == 0) { switch (x.Type) { case DnsRecordType.NS: res = ((NSRecord)x).Domain.CompareTo(((NSRecord)y).Domain); break; case DnsRecordType.CName: res = ((CNameRecord)x).Canonical.CompareTo(((CNameRecord)y).Canonical); break; case DnsRecordType.DN: res = ((DNRecord)x).Target.CompareTo(((DNRecord)y).Target); break; case DnsRecordType.Ptr: res = ((PtrRecord)x).Domain.CompareTo(((PtrRecord)y).Domain); break; case DnsRecordType.HInfo: HInfoRecord xHInfo = (HInfoRecord)x; HInfoRecord yHInfo = (HInfoRecord)y; if ((res = xHInfo.Cpu.CompareTo(yHInfo.Cpu)) == 0) { res = xHInfo.Os.CompareTo(yHInfo.Os); } break; case DnsRecordType.MInfo: MInfoRecord xMInfo = (MInfoRecord)x; MInfoRecord yMInfo = (MInfoRecord)y; if ((res = xMInfo.RMbox.CompareTo(yMInfo.RMbox)) == 0) { res = xMInfo.EMbox.CompareTo(yMInfo.EMbox); } break; case DnsRecordType.MX: MXRecord xMx = (MXRecord)x; MXRecord yMx = (MXRecord)y; if ((res = xMx.CompareTo(yMx)) == 0) { res = xMx.Exchange.CompareTo(yMx.Exchange); } break; case DnsRecordType.MB: res = ((MBRecord)x).Mailbox.CompareTo(((MBRecord)y).Mailbox); break; case DnsRecordType.MG: res = ((MGRecord)x).Mailbox.CompareTo(((MGRecord)y).Mailbox); break; case DnsRecordType.MR: res = ((MRRecord)x).NewMailbox.CompareTo(((MRRecord)y).NewMailbox); break; case DnsRecordType.Txt: res = ((TxtRecord)x).Text.CompareTo(((TxtRecord)y).Text); break; case DnsRecordType.Spf: res = ((SpfRecord)x).Specification.CompareTo(((SpfRecord)y).Specification); break; case DnsRecordType.Srv: SrvRecord xSrv = (SrvRecord)x; SrvRecord ySrv = (SrvRecord)y; if ((res = xSrv.CompareTo(ySrv)) == 0) { res = xSrv.Target.CompareTo(ySrv.Target); } break; default: res = 0; break; } } return(res); }
public static int GetMaxMessagesDestinationHour(VirtualMTA vmta, MXRecord mx) { int tmp; return(GetMaxMessagesDestinationHour(vmta, mx, out tmp)); }
public void CreateDnsResourceRecords(string domain) { DnsBuffer buff = new DnsBuffer(); byte[] bytes; AddressRecord arec = new AddressRecord(domain , "127.0.0.1") { TTL = 1000 }; arec.Serialize(buff); string path = Path.Combine(DNSRECORDSEPATH, string.Format("aname.{0}.bin", domain)); Console.WriteLine("Creating {0}", path); using (FileStream s = new FileStream(path, FileMode.OpenOrCreate)) { s.Write(buff.Buffer , 0 , buff.Buffer.Length); s.Close(); } //---------------------------------------------------------------------------------------------------- //---read the stream from the bytes using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read)) { Console.WriteLine("checking [{0}]", path); bytes = new BinaryReader(fs).ReadBytes((int)new FileInfo(path).Length); DnsBufferReader rdr = new DnsBufferReader(bytes, 0, bytes.Length); arec = (AddressRecord)DnsResourceRecord.Deserialize(ref rdr); } Console.WriteLine(arec.IPAddress); Console.WriteLine(arec.TTL); Console.WriteLine(arec.Name); //---------------------------------------------------------------------------------------------------------------- SOARecord soa = new SOARecord(domain , domain + ".dom" , "somebody" , 1 , 2 , 3 , 4 , 5) { TTL = 2000 }; buff = new DnsBuffer(); soa.Serialize(buff); path = Path.Combine(DNSRECORDSEPATH, string.Format("soa.{0}.bin", domain)); Console.WriteLine("Creating {0}", path); using (FileStream s = new FileStream(path, FileMode.OpenOrCreate)) { s.Write(buff.Buffer , 0 , buff.Buffer.Length); s.Close(); } //---------------------------------------------------------------------------------------------------- //---read the stream from the bytes using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read)) { Console.WriteLine("checking [{0}]", path); bytes = new BinaryReader(fs).ReadBytes((int)new FileInfo(path).Length); DnsBufferReader rdr = new DnsBufferReader(bytes, 0, bytes.Length); soa = (SOARecord)DnsResourceRecord.Deserialize(ref rdr); } Console.WriteLine(soa.ResponsibleName); Console.WriteLine(soa.SerialNumber); Console.WriteLine(soa.Retry); Console.WriteLine(soa.Refresh); Console.WriteLine(soa.Expire); Console.WriteLine(soa.Minimum); Console.WriteLine(soa.TTL); Console.WriteLine(soa.Name); //---------------------------------------------------------------------------------------------------------------- MXRecord mx = new MXRecord(domain , string.Format("mx.{0}", domain) , 1) { TTL = 2000 }; buff = new DnsBuffer(); mx.Serialize(buff); path = Path.Combine(DNSRECORDSEPATH, string.Format("mx.{0}.bin", domain)); Console.WriteLine("Creating {0}", path); using (FileStream s = new FileStream(path, FileMode.OpenOrCreate)) { s.Write(buff.Buffer , 0 , buff.Buffer.Length); s.Close(); } //---------------------------------------------------------------------------------------------------- //---read the stream from the bytes using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read)) { Console.WriteLine("checking [{0}]", path); bytes = new BinaryReader(fs).ReadBytes((int)new FileInfo(path).Length); DnsBufferReader rdr = new DnsBufferReader(bytes, 0, bytes.Length); mx = (MXRecord)DnsResourceRecord.Deserialize(ref rdr); } Console.WriteLine(mx.Exchange); Console.WriteLine(mx.Name); Console.WriteLine(mx.Preference); //---------------------------------------------------------------------------------------------------------------- //---create the cert on the fly CertRecord cert = new CertRecord(new DnsX509Cert(CreateNamedKeyCertificate(new CertData(domain , domain , string.Format("CN={0}", domain) , "")))) { TTL = 2000 }; buff = new DnsBuffer(); cert.Serialize(buff); path = Path.Combine(DNSRECORDSEPATH, string.Format("cert.{0}.bin", domain)); Console.WriteLine("Creating {0}", path); using (FileStream s = new FileStream(path, FileMode.OpenOrCreate)) { s.Write(buff.Buffer , 0 , buff.Buffer.Length); s.Close(); } //---------------------------------------------------------------------------------------------------- //---read the stream from the bytes using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read)) { Console.WriteLine("checking [{0}]", path); bytes = new BinaryReader(fs).ReadBytes((int)new FileInfo(path).Length); DnsBufferReader rdr = new DnsBufferReader(bytes, 0, bytes.Length); cert = (CertRecord)DnsResourceRecord.Deserialize(ref rdr); } Console.WriteLine(cert.Name); Console.WriteLine(cert.Cert.Certificate.NotBefore); Console.WriteLine(cert.Cert.Certificate.NotAfter); }
/// <summary> /// This method handles failure of delivery. /// Logs failure /// Deletes queued data /// </summary> /// <param name="failMsg"></param> public static async Task <bool> HandleDeliveryFailAsync(MtaQueuedMessage msg, string failMsg, VirtualMTA ipAddress, MXRecord mxRecord) { await MtaTransaction.LogTransactionAsync(msg, TransactionStatus.Failed, failMsg, ipAddress, mxRecord); try { // Send fails to Manta.Core.Events for (int i = 0; i < msg.RcptTo.Length; i++) { EmailProcessingDetails processingInfo = null; EventsManager.Instance.ProcessSmtpResponseMessage(failMsg, msg.RcptTo[i], msg.InternalSendID, out processingInfo); } } catch (Exception) { } msg.IsHandled = true; return(true); }
/// <summary> /// This method handle successful delivery. /// Logs success /// Deletes queued data /// </summary> public static async Task <bool> HandleDeliverySuccessAsync(MtaQueuedMessage msg, VirtualMTA ipAddress, MXRecord mxRecord, string response) { await MtaTransaction.LogTransactionAsync(msg, TransactionStatus.Success, response, ipAddress, mxRecord); msg.IsHandled = true; return(true); }
private string GetPoolKey(VirtualMTA vmta, MXRecord mxRecord) { return(vmta.ID.ToString() + "-" + mxRecord.Host.ToLower()); }
/// <summary> /// This method handles message throttle. /// Logs throttle /// Sets the next rety date time /// </summary> internal static async Task <bool> HandleDeliveryThrottleAsync(MtaQueuedMessage msg, VirtualMTA ipAddress, MXRecord mxRecord) { // Log deferral await MtaTransaction.LogTransactionAsync(msg, TransactionStatus.Throttled, string.Empty, ipAddress, mxRecord); // Set next retry time and release the lock. msg.AttemptSendAfterUtc = DateTime.UtcNow.AddMinutes(1); msg.RabbitMqPriority = RabbitMqPriority.Low; await Requeue(msg); return(true); }
/// <summary> /// /// </summary> /// <param name="ipAddress"></param> /// <param name="mxRecord"></param> /// <returns></returns> public static async Task <bool> HandleFailedToConnectAsync(MtaQueuedMessage msg, VirtualMTA ipAddress, MXRecord mxRecord) { // If there was no MX record in DNS, so using A, we should fail and not retry. if (mxRecord.MxRecordSrc == MxRecordSrc.A) { return(await HandleDeliveryFailAsync(msg, "550 Failed to connect", ipAddress, mxRecord)); } else { return(await HandleDeliveryDeferralAsync(msg, "Failed to connect", ipAddress, mxRecord, false, 15)); } }
/// <summary> /// /// </summary> /// <param name="vmta"></param> /// <param name="mxRecord"></param> /// <param name="rawMsg"></param> /// <returns></returns> private async Task <MantaOutboundClientSendResult> SendAsync(MailAddress mailFrom, MailAddress rcptTo, VirtualMTA vmta, MXRecord mxRecord, string msg) { var key = GetPoolKey(vmta, mxRecord); if (!ClientPools.ContainsKey(key)) { AddClientPool(vmta, mxRecord); } var pool = ClientPools[key]; return(await pool.SendAsync(mailFrom, rcptTo, msg)); }
/// <summary> /// This method handles message deferal. /// Logs deferral /// Fails the message if timed out /// or /// Sets the next rety date time /// </summary> /// <param name="defMsg">The deferal message from the SMTP server.</param> /// <param name="ipAddress">IP Address that send was attempted from.</param> /// <param name="mxRecord">MX Record of the server tried to send too.</param> /// <param name="isServiceUnavailable">If false will backoff the retry, if true will use the MtaParameters.MtaRetryInterval, /// this is needed to reduce the tail when sending as a message could get multiple try again laters and soon be 1h+ before next retry.</param> public static async Task <bool> HandleDeliveryDeferralAsync(MtaQueuedMessage msg, string defMsg, VirtualMTA ipAddress, MXRecord mxRecord, bool isServiceUnavailable = false, int?overrideTimeminutes = null) { // Log the deferral. await MtaTransaction.LogTransactionAsync(msg, TransactionStatus.Deferred, defMsg, ipAddress, mxRecord); // This holds the maximum interval between send retries. Should be put in the database. int maxInterval = 3 * 60; // Increase the defered count as the queued messages has been deferred. msg.DeferredCount++; // Hold the minutes to wait until next retry. double nextRetryInterval = MtaParameters.MtaRetryInterval; if (overrideTimeminutes.HasValue) { nextRetryInterval = overrideTimeminutes.Value; } else { if (!isServiceUnavailable) { // Increase the deferred wait interval by doubling for each retry. for (int i = 1; i < msg.DeferredCount; i++) { nextRetryInterval = nextRetryInterval * 2; } // If we have gone over the max interval then set to the max interval value. if (nextRetryInterval > maxInterval) { nextRetryInterval = maxInterval; } } else { nextRetryInterval = 1; // For service unavalible use 1 minute between retries. } } // Set next retry time and release the lock. msg.AttemptSendAfterUtc = DateTime.UtcNow.AddMinutes(nextRetryInterval); await Requeue(msg); return(true); }