392 lines
15 KiB
C#
392 lines
15 KiB
C#
|
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;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|