From d61c48f4ab2cf4d5ea98701eaa1227a0ae283251 Mon Sep 17 00:00:00 2001 From: DistractADD Date: Mon, 17 Aug 2026 10:26:16 +1000 Subject: [PATCH] Upload files to "smtpproxy.net" --- smtpproxy.net/Email.cs | 29 ++ smtpproxy.net/Program.cs | 44 +++ smtpproxy.net/ProjectInstaller.cs | 48 +++ smtpproxy.net/SMTPProxy.cs | 564 ++++++++++++++++++++++++++++++ 4 files changed, 685 insertions(+) create mode 100644 smtpproxy.net/Email.cs create mode 100644 smtpproxy.net/Program.cs create mode 100644 smtpproxy.net/ProjectInstaller.cs create mode 100644 smtpproxy.net/SMTPProxy.cs diff --git a/smtpproxy.net/Email.cs b/smtpproxy.net/Email.cs new file mode 100644 index 0000000..1abe342 --- /dev/null +++ b/smtpproxy.net/Email.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; + +namespace smtpproxy.net; + +public class Email +{ + public string FromIP; + + public string sHELO; + + public string sMAIL; + + public string sDATA; + + public List sRCPTs; + + public string GetRecipientEmail(string sRCPTTo) + { + return sRCPTTo.ToLower().Replace("rcpt to:", "").Replace("<", "") + .Replace(">", "") + .Replace("\r", "") + .Replace("\n", ""); + } + + public Email() + { + sRCPTs = new List(); + } +} diff --git a/smtpproxy.net/Program.cs b/smtpproxy.net/Program.cs new file mode 100644 index 0000000..26a8d7b --- /dev/null +++ b/smtpproxy.net/Program.cs @@ -0,0 +1,44 @@ +using System.Configuration.Install; +using System.Reflection; +using System.ServiceProcess; + +namespace smtpproxy.net; + +internal static class Program +{ + private static void Main(string[] args) + { + SMTPProxy sMTPProxy = new SMTPProxy(); + ServiceBase[] services = new ServiceBase[1] { sMTPProxy }; + if (args.Length != 0) + { + switch (args[0]) + { + case "-install": + ManagedInstallerClass.InstallHelper(new string[1] { Assembly.GetExecutingAssembly().Location }); + break; + case "-uninstall": + ManagedInstallerClass.InstallHelper(new string[2] + { + "/u", + Assembly.GetExecutingAssembly().Location + }); + break; + case "-console": + if (args.Length > 1 && args[1] == "-reload") + { + sMTPProxy.Reload(args[2]); + } + else + { + sMTPProxy.Start(); + } + break; + } + } + else + { + ServiceBase.Run(services); + } + } +} diff --git a/smtpproxy.net/ProjectInstaller.cs b/smtpproxy.net/ProjectInstaller.cs new file mode 100644 index 0000000..d219800 --- /dev/null +++ b/smtpproxy.net/ProjectInstaller.cs @@ -0,0 +1,48 @@ +using System.ComponentModel; +using System.Configuration.Install; +using System.ServiceProcess; + +namespace smtpproxy.net; + +[RunInstaller(true)] +public class ProjectInstaller : Installer +{ + private ServiceInstaller smtpproxyInstaller; + + private ServiceProcessInstaller processInstaller; + + private IContainer components; + + public ProjectInstaller() + { + processInstaller = new ServiceProcessInstaller(); + InitializeComponent(); + smtpproxyInstaller = new ServiceInstaller(); + processInstaller.Account = ServiceAccount.NetworkService; + smtpproxyInstaller.StartType = ServiceStartMode.Automatic; + smtpproxyInstaller.ServiceName = "SMTPProxy.NET"; + base.Installers.Add(processInstaller); + base.Installers.Add(smtpproxyInstaller); + } + + private void serviceInstaller1_AfterInstall(object sender, InstallEventArgs e) + { + } + + private void serviceProcessInstaller1_AfterInstall(object sender, InstallEventArgs e) + { + } + + protected override void Dispose(bool disposing) + { + if (disposing && components != null) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + private void InitializeComponent() + { + } +} diff --git a/smtpproxy.net/SMTPProxy.cs b/smtpproxy.net/SMTPProxy.cs new file mode 100644 index 0000000..6eaef41 --- /dev/null +++ b/smtpproxy.net/SMTPProxy.cs @@ -0,0 +1,564 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.ComponentModel; +using System.Configuration; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.ServiceProcess; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using Newtonsoft.Json.Linq; + +namespace smtpproxy.net; + +public class SMTPProxy : ServiceBase +{ + private ConcurrentQueue EmailQueue; + + private ManualResetEvent mre; + + private bool bRunning; + + private Thread Sending; + + private Thread Listening; + + private long ThreadCount; + + private bool ConsoleMode; + + private static readonly object _syncObject = new object(); + + private IContainer components; + + public SMTPProxy() + { + InitializeComponent(); + base.ServiceName = "SMTPProxy.net"; + ConsoleMode = false; + EmailQueue = new ConcurrentQueue(); + mre = new ManualResetEvent(initialState: false); + bRunning = true; + ThreadCount = 0L; + } + + protected override void OnStart(string[] args) + { + Listening = new Thread(StartListening); + Listening.Start(); + Sending = new Thread(StartSending); + Sending.Start(); + } + + public void Start() + { + ConsoleMode = true; + Listening = new Thread(StartListening); + Listening.Start(); + Sending = new Thread(StartSending); + Sending.Start(); + } + + protected override void OnStop() + { + bRunning = false; + mre.Set(); + } + + public void Reload(string Folder) + { + foreach (string item in Directory.EnumerateFiles(Folder, "*.eml").ToList()) + { + Email email = new Email(); + email.sDATA = File.ReadAllText(item); + email.sHELO = File.ReadAllText(item.Replace(".eml", ".HELO")); + email.sMAIL = File.ReadAllText(item.Replace(".eml", ".MAIL")); + foreach (string item2 in File.ReadAllLines(item.Replace(".eml", ".RCPTS")).ToList()) + { + if (item2.Length > 0) + { + email.sRCPTs.Add(item2 + "\r\n"); + } + } + email.FromIP = "127.0.0.1"; + EmailQueue.Enqueue(email); + } + SendQueuedEmails(); + } + + public void StartSending() + { + while (!mre.WaitOne(30000) && bRunning) + { + SendQueuedEmails(); + } + } + + public void StartListening() + { + Dns.GetHostEntry(Dns.GetHostName()); + IPAddress iPAddress = IPAddress.Parse(ConfigurationManager.AppSettings["ListeningAddress"]); + IPEndPoint localEP = new IPEndPoint(iPAddress, 25); + Socket socket = new Socket(iPAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + Log("Started Listening on port 25 at " + iPAddress.ToString()); + socket.Bind(localEP); + socket.Listen(100); + while (true) + { + try + { + Socket socket2 = socket.Accept(); + if (socket2.Connected) + { + Log("New Connection:" + DateTime.Now.ToString("G", CultureInfo.CreateSpecificCulture("en-AU"))); + ThreadPool.QueueUserWorkItem(ProcessMailClient, socket2); + } + } + catch (SocketException ex) + { + Log($"SocketException : {ex.ToString()}"); + } + catch (Exception ex2) + { + Log($"Unexpected exception : {ex2.ToString()}"); + } + } + } + + private void ProcessMailClient(object stateinfo) + { + Socket socket = (Socket)stateinfo; + socket.ReceiveTimeout = 180000; + socket.SendTimeout = 180000; + byte[] bytes = Encoding.ASCII.GetBytes("220 Connected to SBC SMTPProxy.net\r\n"); + socket.Send(bytes); + IPEndPoint iPEndPoint = socket.RemoteEndPoint as IPEndPoint; + string text = ConfigurationManager.AppSettings["LogFolder"] + "in"; + Directory.CreateDirectory(text); + string logFileName = $"{text}\\{Guid.NewGuid()}_{iPEndPoint.Address}.log"; + Log("New Client Connected from " + iPEndPoint.Address, logFileName); + new List(); + List list = new List(); + byte[] array = new byte[2000]; + StringBuilder stringBuilder = new StringBuilder(); + string text2 = ""; + string text3 = ""; + string text4 = ""; + string text5 = ""; + string text6 = ""; + int num = 0; + bool flag = false; + do + { + try + { + num = socket.Receive(array, 1999, SocketFlags.None); + if (num > 0) + { + array[num] = 0; + string text7 = Encoding.Default.GetString(array, 0, num); + text3 = text2 + text7; + text2 = text7; + stringBuilder.Append(text7); + if (num == 1999 || !text3.Contains("\r\n")) + { + continue; + } + string text8 = stringBuilder.ToString(); + string text9 = ((text8.Length < 4) ? text8 : text8.Substring(0, 4)); + string text10 = "500 Invalid Command\r\n"; + bool flag2 = false; + if (flag) + { + text6 += text8; + if (text8 == ".\r\n" || text8.Contains("\r\n.\r\n")) + { + flag = false; + text10 = "250 OK\r\n"; + Email email = new Email(); + email.sHELO = text4; + email.sMAIL = text5; + email.sRCPTs = new List(list); + email.sDATA = text6; + email.FromIP = iPEndPoint.Address.ToString(); + EmailQueue.Enqueue(email); + text5 = (text6 = ""); + list.Clear(); + } + } + else + { + switch (text9) + { + case "HELO": + case "EHLO": + text4 = stringBuilder.ToString(); + text10 = "250 Hi\r\n"; + break; + case "MAIL": + if (text4.Length == 0) + { + text10 = "503 Do HELO first\r\n"; + break; + } + if (text5.Length > 0) + { + text10 = "503 Already have MAIL\r\n"; + break; + } + text5 = stringBuilder.ToString(); + text10 = "250 MAIL command recorded\r\n"; + break; + case "RCPT": + if (text4.Length == 0) + { + text10 = "503 Do HELO first\r\n"; + break; + } + if (text5.Length == 0) + { + text10 = "503 Do MAIL first\r\n"; + break; + } + list.Add(stringBuilder.ToString()); + text10 = "250 RCPT command recorded\r\n"; + break; + case "DATA": + if (text4.Length == 0) + { + text10 = "503 Do HELO first\r\n"; + } + else if (text5.Length == 0) + { + text10 = "503 Do MAIL first\r\n"; + } + else if (list.Count == 0) + { + text10 = "503 Do RCPT first\r\n"; + } + text10 = "354 DATA\r\n"; + flag2 = true; + break; + case "RSET": + text10 = "250 OK\r\n"; + text5 = (text6 = ""); + list.Clear(); + break; + case "QUIT": + socket.Shutdown(SocketShutdown.Both); + socket.Close(); + num = 0; + goto end_IL_00ed; + default: + Log("Unknown:" + text9, logFileName); + break; + } + } + if (!flag) + { + byte[] bytes2 = Encoding.ASCII.GetBytes(text10); + socket.Send(bytes2); + Log(text10, logFileName); + } + if (flag2) + { + flag = true; + } + stringBuilder.Clear(); + array[0] = 0; + continue; + } + if (num == 0) + { + Log("Client disconnected.", logFileName); + continue; + } + Log("Client reset the connection.", logFileName); + socket.Shutdown(SocketShutdown.Both); + socket.Close(); + num = 0; + end_IL_00ed:; + } + catch (SocketException ex) + { + Log($"SocketException : {ex.ToString()}", logFileName); + num = 0; + } + catch (Exception ex2) + { + Log($"Unexpected exception : {ex2.ToString()}", logFileName); + num = 0; + } + } + while (socket.Connected && num > 0); + if (EmailQueue.Count > 5 && Interlocked.Read(ref ThreadCount) < Convert.ToUInt32(ConfigurationManager.AppSettings["ThreadMax"])) + { + Interlocked.Increment(ref ThreadCount); + SendQueuedEmails(); + Interlocked.Decrement(ref ThreadCount); + } + } + + private void Log(string info, string LogFileName = null) + { + if (ConsoleMode) + { + Console.WriteLine(info.Replace("\r\n", "")); + } + if (!string.IsNullOrEmpty(LogFileName)) + { + using (StreamWriter streamWriter = File.AppendText(LogFileName)) + { + streamWriter.WriteLine("{0}", info); + streamWriter.Flush(); + return; + } + } + lock (_syncObject) + { + DateTime now = DateTime.Now; + using StreamWriter streamWriter2 = File.AppendText(string.Format("{0}\\{1:00}{2:00}{3:00}.log", ConfigurationManager.AppSettings["LogFolder"] + "in", now.Year, now.Month, now.Day)); + streamWriter2.WriteLine("{0}", info); + streamWriter2.Flush(); + } + } + + private void SendQueuedEmails() + { + JToken val2 = default(JToken); + while (!EmailQueue.IsEmpty) + { + if (!EmailQueue.TryDequeue(out var result)) + { + continue; + } + string sMAIL = result.sMAIL; + sMAIL = sMAIL.Replace(" ", ""); + sMAIL = sMAIL.Replace("MAILFROM:", ""); + sMAIL = sMAIL.Replace("<", ""); + sMAIL = sMAIL.Replace(">", ""); + sMAIL = sMAIL.Replace("\r", ""); + sMAIL = sMAIL.Replace("\n", ""); + string path = ConfigurationManager.AppSettings["Profiles"] + result.FromIP + "\\" + sMAIL + ".json"; + if (!File.Exists(path)) + { + int num = sMAIL.IndexOf('@'); + string text = sMAIL.Substring(num + 1); + path = ConfigurationManager.AppSettings["Profiles"] + result.FromIP + "\\default@" + text + ".json"; + if (!File.Exists(path)) + { + path = ConfigurationManager.AppSettings["Profiles"] + result.FromIP + "\\default.json"; + } + } + JObject val; + try + { + val = JObject.Parse(File.ReadAllText(path)); + } + catch (Exception ex) + { + Log(ex.Message); + continue; + } + int num2 = Convert.ToInt32(val["logging"][(object)"enabled"]); + bool bHideRecipients = Convert.ToInt32(val["HideRecipients"]) == 1; + bool num3 = num2 == 1; + string text2 = ""; + string text3 = ((object)val["logging"][(object)"path"]).ToString() + "\\log"; + Directory.CreateDirectory(text3); + Guid guid = Guid.NewGuid(); + string recipientEmail = result.GetRecipientEmail(result.sRCPTs[0]); + text2 = $"{text3}\\{recipientEmail}.{guid}.log"; + string text4 = ((object)val["logging"][(object)"path"]).ToString() + "\\out"; + Directory.CreateDirectory(text4); + if (num3) + { + File.WriteAllText($"{text4}\\{recipientEmail}.{guid}.eml", result.sDATA); + File.WriteAllText($"{text4}\\{recipientEmail}.{guid}.HELO", result.sHELO); + File.WriteAllText($"{text4}\\{recipientEmail}.{guid}.MAIL", result.sMAIL); + File.WriteAllLines($"{text4}\\{recipientEmail}.{guid}.RCPTS", result.sRCPTs); + } + IPAddress iPAddress = Dns.GetHostEntry(((object)val["smtpserver"]).ToString()).AddressList[0]; + IPEndPoint remoteEP = new IPEndPoint(iPAddress, Convert.ToInt32(((object)val["smtpserverport"]).ToString())); + int num4 = Convert.ToInt32(val["recipientreplacement"][(object)"enabled"]); + List list = val["recipientreplacement"][(object)"replacements"].ToObject>(); + int num5 = Convert.ToInt32(val["recipientfilter"][(object)"enabled"]); + List list2 = val["recipientfilter"][(object)"whitelist"].ToObject>(); + bool flag = false; + List list3 = null; + if (val.TryGetValue("bcc", ref val2)) + { + flag = Extensions.Value((IEnumerable)val2[(object)"enabled"]) == 1; + list3 = val2[(object)"recipients"].ToObject>(); + } + Socket socket = null; + bool flag2 = num4 == 1; + bool flag3 = num5 == 1; + try + { + socket = new Socket(iPAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + socket.Connect(remoteEP); + if (!CheckAndSend("220", result.sHELO, socket, text2) || !CheckAndSend("250", result.sMAIL, socket, text2)) + { + continue; + } + if (flag2) + { + foreach (string item in list) + { + string nextCommand = $"RCPT TO: {item}\r\n"; + CheckAndSend("250", nextCommand, socket, text2); + } + goto IL_053d; + } + int num6 = 0; + foreach (string sRCPT in result.sRCPTs) + { + if (sRCPT.Length < 8) + { + continue; + } + if (flag3) + { + string r = result.GetRecipientEmail(sRCPT); + if (!list2.Exists((string prd) => prd.Equals(r))) + { + Log("Filtering Recipient " + r, text2); + continue; + } + Log("Recipient " + r + " OK", text2); + } + if (CheckAndSend("250", sRCPT, socket, text2)) + { + num6++; + } + } + if (num6 != 0) + { + goto IL_053d; + } + Log("No Valid recipients, aborting", text2); + socket.Shutdown(SocketShutdown.Both); + socket.Close(); + goto end_IL_03a8; + IL_053d: + if (flag) + { + foreach (string item2 in list3) + { + string nextCommand2 = $"RCPT TO: {item2}\r\n"; + CheckAndSend("250", nextCommand2, socket, text2); + } + } + if (CheckAndSend("250", "DATA\r\n", socket, text2) && CheckAndSend("354", CleanseDataBlock(result.sDATA, bHideRecipients), socket, text2) && CheckAndSend("250", "RSET\r\n", socket, text2) && CheckAndSend("250", "QUIT\r\n", socket, text2)) + { + socket.Shutdown(SocketShutdown.Both); + socket.Close(); + } + end_IL_03a8:; + } + catch (ArgumentNullException ex2) + { + Log($"ArgumentNullException : {ex2.ToString()}", text2); + } + catch (SocketException ex3) + { + Log($"SocketException : {ex3.ToString()}", text2); + } + catch (Exception ex4) + { + Log($"Unexpected exception : {ex4.ToString()}", text2); + } + } + } + + private static string CleanseDataBlock(string sData, bool bHideRecipients) + { + if (bHideRecipients) + { + StringBuilder stringBuilder = new StringBuilder(); + using StringReader stringReader = new StringReader(sData); + string pattern = "^[Tt][Oo]: *$"; + string pattern2 = "^[Dd][Aa][Tt][Ee]:.*$"; + bool flag = false; + bool flag2 = false; + for (string text = stringReader.ReadLine(); text != null; text = stringReader.ReadLine()) + { + if (flag2) + { + stringBuilder.Append(text + "\r\n"); + } + else if (!flag) + { + if (Regex.Match(text, pattern).Success) + { + flag = true; + } + stringBuilder.Append(text + "\r\n"); + } + else if (Regex.Match(text, pattern2).Success) + { + flag = false; + stringBuilder.Append(text + "\r\n"); + flag2 = true; + } + } + return stringBuilder.ToString(); + } + return sData; + } + + private bool CheckAndSend(string PrevSuccessCode, string NextCommand, Socket sock, string LogFileName) + { + byte[] array = new byte[2500]; + string text = ""; + int num = sock.Receive(array); + if (num <= 0) + { + Log("recv failed", LogFileName); + sock.Shutdown(SocketShutdown.Both); + sock.Close(); + return false; + } + array[num] = 0; + text = Encoding.ASCII.GetString(array, 0, num); + Log(text, LogFileName); + if (text.Substring(0, 3) != PrevSuccessCode) + { + sock.Shutdown(SocketShutdown.Both); + sock.Close(); + return false; + } + if (NextCommand.Length > 500) + { + Log(NextCommand.Substring(0, 500), LogFileName); + } + else + { + Log(NextCommand, LogFileName); + } + byte[] bytes = Encoding.ASCII.GetBytes(NextCommand); + sock.Send(bytes); + return true; + } + + protected override void Dispose(bool disposing) + { + if (disposing && components != null) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + private void InitializeComponent() + { + components = new Container(); + base.ServiceName = "Service1"; + } +}