using System; using System.Diagnostics; using System.IO; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using Microsoft.Singularity.Smtp; namespace Microsoft.Singularity.Smtp.Clients { public class FileServer { private String[] sentDirectories; private Mutex fileServerMutex; private int index; public FileServer(string path) { fileServerMutex = new Mutex(); index = 0; Console.WriteLine("FileServer: Received file {0}\n", path); try { string fullPath = Path.GetFullPath(path); sentDirectories = File.ReadAllLines(fullPath); } catch (Exception e) { Console.WriteLine("FileServer: failed to get sent directories." + " Exception: {0}\n", e.Message); } } public string GetNextDirectory() { string dirName = null; lock(fileServerMutex) { if (index < sentDirectories.Length) { dirName = sentDirectories[index]; index++; } } return dirName; } } public class SmtpClient { public const int _port = 25; public static bool verbose = false; public static String server = "127.0.0.1"; public static FileServer fileServer; public static int transPerThread = -1; ////////////////////////////////////////////////////////////////////////////// // private static void Usage() { Console.WriteLine("Usage:"); Console.WriteLine(" cli [operation] [options]"); Console.WriteLine("Options:"); Console.WriteLine(" /s:server Send to server."); Console.WriteLine(" /c:conn Number of simultaneous connections [default 1: max 50]."); Console.WriteLine(" /t:trans Number of transcations [default: no limit]."); Console.WriteLine(" /f:file File Containing directories of mail to send."); Console.WriteLine(" /? Print this help information."); } public void Run() { String client = Dns.GetHostName(); Console.WriteLine("Self: {0}", client); Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { IPAddress addr = IPAddress.Parse(SmtpClient.server); socket.Connect(new IPEndPoint(addr, _port)); } catch (SocketException e) { Console.WriteLine("Socket connect failure: {0}", e.Message); return; } Session session = new Session(socket); session.Verbose = verbose; try { // Establish SMPT connection. string line; line = session.ReadLine7(); if (session.Result != 220) { Console.WriteLine(":: Aborting due to {0}", session.Result); session.Quit(); return; } session.WriteLine7("EHLO ", client); for (;;) { line = session.ReadLine7(); if (session.Result != 250) { Console.WriteLine(":: Aborting due to {0}", session.Result); session.Quit(); return; } if (line[3] != '-') { break; } } Stopwatch sw = new Stopwatch(); long fileCount = 0; string sentDir = SmtpClient.fileServer.GetNextDirectory(); while((sentDir != null) && (fileCount < SmtpClient.transPerThread)) { Console.WriteLine("Sending files in directory {0}\n", sentDir); String[] files = Directory.GetFiles(sentDir); // Send files. foreach (string file in files) { fileCount++; Console.WriteLine(":: {0}", file); String[] lines = File.ReadAllLines(file); //start timing this transaction sw.Start(); session.WriteLine7("NOOP ", file); line = session.ReadLine7(); if (session.Result != 250) { Console.WriteLine(":: Aborting due to {0}", session.Result); session.Quit(); return; } string from = FindFrom(lines).ToLower(); session.WriteLine7("MAIL FROM: <", from, ">"); line = session.ReadLine7(); if (session.Result != 250) { Console.WriteLine(":: Aborting due to {0}", session.Result); session.Quit(); return; } string to = FindTo("To: ", lines); string cc = FindTo("Cc: ", lines); string bcc = FindTo("Bcc: ", lines); if (to == null) { to = cc; cc = null; } if (to == null) { to = bcc; bcc = null; } if (cc != null) { to = to + "," + cc; } if (bcc != null) { to = to + "," + bcc; } if (to == null || to.Length == 0) { Console.WriteLine(":: Couldn't find receipients."); session.Quit(); return; } string[] rcpts = to.Split(new Char[]{','}); for (int i = 0; i < rcpts.Length; i++) { rcpts[i] = rcpts[i].Trim().ToLower(); if (rcpts[i].Length == 0) { rcpts[i] = null; } } for (int i = 0; i < rcpts.Length; i++) { for (int j = i + 1; j < rcpts.Length; j++) { if (rcpts[i] == rcpts[j]) { rcpts[j] = null; } } } foreach (string rcpt in rcpts) { if (rcpt != null) { session.WriteLine7("RCPT TO: <", rcpt, ">"); line = session.ReadLine7(); if (session.Result != 250) { Console.WriteLine(":: Aborting due to {0}", session.Result); session.Quit(); return; } } } session.WriteLine7("DATA"); line = session.ReadLine7(); if (session.Result != 354) { Console.WriteLine(":: Aborting due to {0}", session.Result); session.Quit(); return; } foreach (string data in lines) { if (data.Length > 0 && data[0] == '.') { session.WritePrefix((byte)'.'); } session.WriteLine7(data); } session.WriteLine7("."); line = session.ReadLine7(); if (session.Result != 250) { Console.WriteLine(":: Aborting due to {0}", session.Result); session.Quit(); return; } //stop timing this transaction sw.Stop(); if (fileCount > SmtpClient.transPerThread) { Console.WriteLine("Thread reached max transactions...done\n"); break; } } sentDir = SmtpClient.fileServer.GetNextDirectory(); } session.Quit(); double seconds = sw.ElapsedTicks / Stopwatch.Frequency; double transPerSecond = fileCount / seconds; Console.WriteLine("Thread complete: total transactions {0} total time {1}" + " transactions per second {2}\n", fileCount, seconds, transPerSecond); } catch (SocketException e) { Console.WriteLine("Caught socket exception: {0}", e.Message); } } public static int Main(string[] args) { bool needHelp = false; int numConnections = 1; int totalTransactions = 10000; if (args.Length == 0) { needHelp = true; } fileServer = null; for (int a = 0; a < args.Length && !needHelp; a++) { String arg = args[a]; if (arg[0] == '/') { int colon = arg.IndexOf(':'); String option; String values; if (colon > 1) { option = arg.Substring(1, colon - 1).ToLower(); values = arg.Substring(colon + 1); } else { option = arg.Substring(1).ToLower(); values = null; } switch (option[0]) { // Commands: case '?': // Help needHelp = true; break; case 's': // Server case 'S': server = values; break; case 'c': case 'C': numConnections = Convert.ToInt32(values); if (numConnections < 1 || numConnections > 50) { needHelp = true; } break; case 'v': // Verbose verbose = true; break; case 't': case 'T': totalTransactions = Convert.ToInt32(values); if( (totalTransactions < 1) || (totalTransactions > 10000)) { needHelp = true; Console.WriteLine("transactions must be between 1 and 10,000\n"); } break; case 'f': //file of directories String file = Path.GetFileName(values); if (file == String.Empty) { Console.WriteLine("File name given is not a file\n"); needHelp = true; break; } fileServer = new FileServer(values); break; default: Console.WriteLine("Unknown option: {0}", arg); needHelp = true; break; } } else { needHelp = true; } } if (fileServer == null) { needHelp = true; } if (needHelp) { Usage(); return 1; } bool highRes = Stopwatch.IsHighResolution; Console.WriteLine("About to spawn {0} threads StopWatch.IsHighResolution {1}\n", numConnections, highRes); Thread[] workers; workers = new Thread[numConnections]; int i; transPerThread = totalTransactions / numConnections; Console.WriteLine("Executing total transactions {0} per thread {1}\n", totalTransactions, transPerThread); for (i = 0; i < numConnections; i++) { SmtpClient client = new SmtpClient(); workers[i] = new Thread( new ThreadStart(client.Run)); workers[i].Start(); } for (i = 0; i< numConnections; i++) { workers[i].Join(); } return 0; } ////////////////////////////////////////////////////////////////////////////// public static String FindFrom(String[] lines) { foreach (string line in lines) { if (line.Length == 0) { return null; // Header ended. } if (line.StartsWith("From: ")) { return line.Substring(6); } } return null; } public static string FindTo(String prefix, String[] lines) { for (int i = 0; i < lines.Length; i++) { if (lines[i].Length == 0) { return null; // Header ended. } if (lines[i].StartsWith(prefix)) { string to = lines[i++].Substring(prefix.Length); while (lines[i].Length > 0 && (lines[i][0] == ' ' || lines[i][0] == '\t')) { to = to + lines[i++]; } return to; } } return null; } } }