/// <summary>
 /// Reads data from a file that contains database entries that were
 /// persisted from a previous session. If the data files cannot
 /// be found, the action is aborted. If invalid entries are
 /// found in the file, they will be skipped, and the method
 /// will continue reading the file after that entry. If
 /// any other unforeseen I/O errors occur, the method will
 /// simply stop where it is in the file and return.
 ///
 /// It is possible to record any errors reading the file
 /// in the system event logs, but this is not done here
 /// for simplicity.
 /// </summary>
 /// <param name="sourcePath">The path to the file that the data should be read from.</param>
 public void ReadPersistedData(string sourcePath)
 {
     try
     {
         using (StreamReader source = new StreamReader(File.OpenRead(sourcePath)))
         {
             lock (entryCountLock)
             {
                 while (!source.EndOfStream && this.entryCount < this.maxEntries)
                 {
                     MailFilterEntry newEntry = MailFilterEntry.TryParse(source.ReadLine());
                     if (newEntry != null)
                     {
                         this.SaveEntry(newEntry);
                     }
                 }
             }
         }
     }
     catch (IOException e)
     {
         Debug.WriteLine(e.ToString());
         return;
     }
     catch (UnauthorizedAccessException e)
     {
         Debug.WriteLine(e.ToString());
         return;
     }
 }
        /// <summary>
        /// Records an entry in the database, either by updating an existing
        /// entry or by creating a new one.
        /// </summary>
        /// <param name="entry">The entry to be added to the database.</param>
        public void SaveEntry(MailFilterEntry entry)
        {
            int index = this.CalcIndex(entry.TripletHash);

            LinkedList <MailFilterEntry> currentRow = (LinkedList <MailFilterEntry>) this.table[index];

            lock (((ICollection)currentRow).SyncRoot)
            {
                // If the triplet is already there, remove it
                // so that you can put it back at the start of the list.
                if (currentRow.Contains(entry))
                {
                    currentRow.Remove(entry);
                    this.DecrementEntryCount();
                }

                // Insert the triplet at the front of the list.
                entry.TimeStamp = DateTime.UtcNow;
                currentRow.AddFirst(entry);
                this.IncrementEntryCount();

                // If the bucket is full, delete the oldest entry in it.
                if (currentRow.Count > this.bucketSize)
                {
                    currentRow.RemoveLast();
                    this.DecrementEntryCount();
                }
            }
        }
Beispiel #3
0
        /// <summary>
        /// An override of the Object class's Equals method.
        /// </summary>
        /// <param name="obj">The other object to be compared to.</param>
        /// <returns>True if the other object is a MailFilterEntry and has the same TripletHash as this.</returns>
        public override bool Equals(Object obj)
        {
            MailFilterEntry entry = obj as MailFilterEntry;

            if (entry == null)
            {
                return(false);
            }

            return(this.Equals(entry));
        }
        /// <summary>
        /// The core of the MailFilter algorithm. Determines whether
        /// a triplet matches a known sender/recipient relationship and should
        /// be accepted.
        /// </summary>
        /// <param name="remoteIP">The remote host's IP address.</param>
        /// <param name="sender">The sender's address.</param>
        /// <param name="recipient">The recipient's address.</param>
        /// <returns>Whether the triplet is verified by the MailFilter.</returns>
        private bool VerifyTriplet(IPAddress remoteIP, RoutingAddress sender, RoutingAddress recipient)
        {
            // Create a MailFilterEntry object for the current session.
            // This code uses senderAddress.DomainPart to truncate
            // the sender's address to use only the domain. To use the full
            // address, use ToString() as with recipient.
            UInt64 tripletHash = this.HashTriplet(
                remoteIP,
                sender.DomainPart,
                recipient.ToString());

            MailFilterEntry currentEntry = new MailFilterEntry(tripletHash);

            // Determine whether a matching entry is in the verified database.
            // If it is, save with an updated time stamp.
            // This ensures that the most recent entries are
            // at the start of the bucket lists in the array tables.
            if (this.verifiedDatabase.GetEntry(currentEntry.TripletHash) != null)
            {
                currentEntry.TimeStamp = DateTime.UtcNow;
                this.verifiedDatabase.SaveEntry(currentEntry);
                return(true);
            }

            // If the entry is in the unverified table passed in
            // the initial blocking period, remove it.
            MailFilterEntry entry = this.unverifiedDatabase.GetEntry(currentEntry.TripletHash);

            if (entry != null)
            {
                if (entry.IsPastPeriod(this.settings.InitialBlockingPeriod))
                {
                    this.verifiedDatabase.SaveEntry(currentEntry);
                    this.unverifiedDatabase.DeleteEntry(currentEntry);
                    return(true);
                }
                else
                {
                    // The entry was in the table of unverified entries,
                    // but the blocking period has not passed yet.
                    return(false);
                }
            }
            else
            {
                // The triplet is not in either database. Send a rejection and
                // put it in the unverified database.
                this.unverifiedDatabase.SaveEntry(currentEntry);
                return(false);
            }
        }
        /// <summary>
        /// Removes an entry from the database.
        /// </summary>
        /// <param name="entry">The entry to be removed.</param>
        /// <returns>True if the entry was removed; false if it could not be found.</returns>
        public bool DeleteEntry(MailFilterEntry entry)
        {
            // The return value.
            bool retval = false;

            // Calculate the hash index.
            int index = this.CalcIndex(entry.TripletHash);

            LinkedList <MailFilterEntry> currentRow = (LinkedList <MailFilterEntry>) this.table[index];

            lock (((ICollection)currentRow).SyncRoot)
            {
                retval = currentRow.Remove(entry);
            }

            if (retval)
            {
                this.DecrementEntryCount();
            }

            return(retval);
        }
Beispiel #6
0
        /// <summary>
        /// Tries to parse the contents of a string into a new entry.
        /// </summary>
        /// <param name="currentLine">The string to be parsed.</param>
        /// <returns>The new entry, or null if unsuccessful.</returns>
        public static MailFilterEntry TryParse(String currentLine)
        {
            // Set up a return value variable.
            MailFilterEntry retval = null;

            // Split the two values in the line.
            string[] splitLine = currentLine.Split(',');

            // Verify that you got exactly two values from the line in the file.
            if (splitLine.Length == 2)
            {
                // Try parsing the hash code from the file. If you
                // can't parse one, do not return an
                // entry object at all.
                UInt64 inputHash;
                if (!UInt64.TryParse(splitLine[0].Trim(), out inputHash))
                {
                    return(null);
                }

                long     inputTicks;
                DateTime inputTime;
                if (long.TryParse(splitLine[1].Trim(), out inputTicks))
                {
                    inputTime = new DateTime(inputTicks);
                }
                else
                {
                    return(null);
                }

                retval           = new MailFilterEntry(inputHash);
                retval.timeStamp = inputTime;
            }

            return(retval);
        }
Beispiel #7
0
 /// <summary>
 /// The copy constructor.
 /// </summary>
 /// <param name="other">The entry object to be copied.</param>
 public MailFilterEntry(MailFilterEntry other)
 {
     this.tripletHash = other.TripletHash;
     this.timeStamp   = other.TimeStamp;
 }
Beispiel #8
0
 /// <summary>
 /// Compares this MailFilterEntry's tripletHash to that of another MailFilterEntry.
 /// </summary>
 /// <param name="entry">The other MailFilterEntry that this will be compared to.</param>
 /// <returns>True if the other entry has the same hash as this entry.</returns>
 public bool Equals(MailFilterEntry entry)
 {
     return(this.tripletHash == entry.tripletHash);
 }