├── StreamExtended ├── StrongKey.snk ├── Properties │ └── AssemblyInfo.cs ├── BufferPool │ ├── IBufferPool.cs │ └── DefaultBufferPool.cs ├── Network │ ├── ICustomStreamWriter.cs │ ├── DataEventArgs.cs │ ├── ICustomStreamReader.cs │ ├── CopyStream.cs │ ├── ServerHelloAlpnAdderStream.cs │ ├── ClientHelloAlpnAdderStream.cs │ └── CustomBufferedPeekStream.cs ├── StreamExtended.Mono.csproj ├── StreamExtended.csproj ├── StreamExtended.NetCore.csproj ├── StreamExtended.nuspec ├── TaskExtended.cs ├── Models │ └── SslExtension.cs ├── StreamExtended.Docs.csproj ├── ServerHelloInfo.cs └── ClientHelloInfo.cs ├── SyslogParser ├── source.txt ├── SyslogParser.csproj ├── Code │ ├── EverythingListener.cs │ ├── RfcVisitor.cs │ └── CaseChangingCharStream.cs └── ParsingHelper.cs ├── SimpleTcpServer ├── SimpleTcpServer.csproj ├── Program.cs ├── TcpEchoClient.cs └── TcpEchoServer.cs ├── libSyslogServer ├── libSyslogServer.csproj ├── Classes │ ├── Settings.cs │ └── Common.cs ├── ReceiverThread.cs ├── WriterTask.cs └── SyslogServer.cs ├── SyslogServer ├── Common │ ├── SeverityType.cs │ ├── FacilityType.cs │ ├── Rfc3164SyslogMessage.cs │ ├── Rfc5424SyslogMessage.cs │ └── MessageHandler.cs ├── SyslogServer.csproj ├── SyslogServer.cs ├── UpdSyslogServer.cs ├── TcpSyslogServer.cs ├── syslog_info.txt ├── TlsSyslogServer.cs └── Program.cs ├── SelfSignedCertificate ├── SelfSignedCertificate.csproj └── Helpers │ ├── KeyValuePairList.cs │ ├── StringStream.cs │ ├── NonBackdooredPrng.cs │ └── CertificateInfo.cs ├── SimpleTlsServer ├── SimpleTlsServer.csproj └── Program.cs ├── NetCoreServer ├── NetCoreServer.csproj ├── HttpServer.cs ├── HttpsServer.cs ├── SslContext.cs ├── IWebSocket.cs ├── WsServer.cs ├── WssServer.cs ├── Buffer.cs ├── Utilities.cs ├── HttpSession.cs └── HttpsSession.cs ├── LICENSE.TXT ├── README.md ├── .gitattributes ├── NetCoreSyslogServer.sln └── .gitignore /StreamExtended/StrongKey.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ststeiger/NetCoreSyslogServer/HEAD/StreamExtended/StrongKey.snk -------------------------------------------------------------------------------- /StreamExtended/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ststeiger/NetCoreSyslogServer/HEAD/StreamExtended/Properties/AssemblyInfo.cs -------------------------------------------------------------------------------- /SyslogParser/source.txt: -------------------------------------------------------------------------------- 1 | 2 | https://raw.githubusercontent.com/ottobackwards/grok-v-antlr/master/src/main/antlr4/org/ottobackwards/dsl/generated/Rfc5424.g4 3 | -------------------------------------------------------------------------------- /SimpleTcpServer/SimpleTcpServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SyslogParser/SyslogParser.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /libSyslogServer/libSyslogServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /SyslogServer/Common/SeverityType.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SyslogServer 3 | { 4 | 5 | public enum SeverityType 6 | { 7 | Emergency 8 | , Alert 9 | , Critical 10 | , Error 11 | , Warning 12 | , Notice 13 | , Informational 14 | , Debug 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /SyslogServer/Common/FacilityType.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SyslogServer 3 | { 4 | 5 | public enum FacilityType 6 | { 7 | Kern, User, Mail, Daemon, Auth, Syslog, LPR 8 | , News, UUCP, Cron, AuthPriv, FTP, NTP, Audit, Audit2, CRON2 9 | , Local0, Local1, Local2, Local3, Local4, Local5, Local6, Local7 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /SelfSignedCertificate/SelfSignedCertificate.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /SimpleTlsServer/SimpleTlsServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net5.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /SimpleTlsServer/Program.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SimpleTlsServer 3 | { 4 | 5 | 6 | class Program 7 | { 8 | 9 | 10 | static void Main(string[] args) 11 | { 12 | ExampleTlsServer.Test(); 13 | System.Console.WriteLine(" --- Press any key to continue --- "); 14 | System.Console.ReadKey(); 15 | } // End Sub Main 16 | 17 | 18 | } // End Class Program 19 | 20 | 21 | } // End Namespace 22 | -------------------------------------------------------------------------------- /StreamExtended/BufferPool/IBufferPool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StreamExtended 4 | { 5 | /// 6 | /// Use this interface to implement custom buffer pool. 7 | /// To use the default buffer pool implementation use DefaultBufferPool class. 8 | /// 9 | public interface IBufferPool : IDisposable 10 | { 11 | byte[] GetBuffer(int bufferSize); 12 | void ReturnBuffer(byte[] buffer); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /SimpleTcpServer/Program.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SimpleTcpServer 3 | { 4 | 5 | 6 | class Program 7 | { 8 | 9 | 10 | static void Main(string[] args) 11 | { 12 | _ = TcpEchoServer.Test(); 13 | TcpEchoClient.Test(); 14 | 15 | System.Console.WriteLine(" --- Press any key to continue --- "); 16 | System.Console.ReadKey(); 17 | } // End Sub Main 18 | 19 | 20 | } // End Class Program 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /SyslogParser/Code/EverythingListener.cs: -------------------------------------------------------------------------------- 1 | 2 | using SyslogServer.grammars; 3 | 4 | 5 | namespace SyslogParser 6 | { 7 | 8 | public class EverythingListener 9 | : Rfc5424BaseListener 10 | { 11 | 12 | public override void EnterEveryRule([Antlr4.Runtime.Misc.NotNull] Antlr4.Runtime.ParserRuleContext context) 13 | { 14 | string s = context.GetText(); 15 | System.Console.WriteLine(s); 16 | 17 | } 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /SyslogServer/SyslogServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net5.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /StreamExtended/Network/ICustomStreamWriter.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace StreamExtended.Network 5 | { 6 | /// 7 | /// A concrete implementation of this interface is required when calling CopyStream. 8 | /// 9 | public interface ICustomStreamWriter 10 | { 11 | void Write(byte[] buffer, int i, int bufferLength); 12 | 13 | Task WriteAsync(byte[] buffer, int i, int bufferLength, CancellationToken cancellationToken); 14 | } 15 | } -------------------------------------------------------------------------------- /StreamExtended/StreamExtended.Mono.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net45 5 | True 6 | StrongKey.snk 7 | 1.0.0 8 | false 9 | Package Description 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /StreamExtended/StreamExtended.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net45;netstandard1.3 5 | True 6 | StrongKey.snk 7 | 1.0.0 8 | false 9 | Package Description 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /StreamExtended/StreamExtended.NetCore.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard1.3 5 | True 6 | StrongKey.snk 7 | 1.0.0 8 | false 9 | Package Description 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /libSyslogServer/Classes/Settings.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace libSyslogServer 3 | { 4 | 5 | 6 | public class Settings 7 | { 8 | public string Version { get; set; } 9 | public int UdpPort { get; set; } 10 | public bool DisplayTimestamps { get; set; } 11 | public string LogFileDirectory { get; set; } 12 | public string LogFilename { get; set; } 13 | public int LogWriterIntervalSec { get; set; } 14 | 15 | public static Settings Default() 16 | { 17 | Settings ret = new Settings(); 18 | ret.Version = "Watson Syslog Server v1.0.1"; 19 | ret.UdpPort = 514; 20 | ret.DisplayTimestamps = false; 21 | ret.LogFileDirectory = "logs\\"; 22 | ret.LogFilename = "log.txt"; 23 | ret.LogWriterIntervalSec = 10; 24 | return ret; 25 | } 26 | } 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /StreamExtended/Network/DataEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StreamExtended.Network 4 | { 5 | /// 6 | /// Wraps the data sent/received event argument. 7 | /// 8 | public class DataEventArgs : EventArgs 9 | { 10 | public DataEventArgs(byte[] buffer, int offset, int count) 11 | { 12 | Buffer = buffer; 13 | Offset = offset; 14 | Count = count; 15 | } 16 | 17 | /// 18 | /// The buffer with data. 19 | /// 20 | public byte[] Buffer { get; } 21 | 22 | /// 23 | /// Offset in buffer from which valid data begins. 24 | /// 25 | public int Offset { get; } 26 | 27 | /// 28 | /// Length from offset in buffer with valid data. 29 | /// 30 | public int Count { get; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /StreamExtended/StreamExtended.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | StreamExtended 5 | $version$ 6 | Extended SslStream 7 | honfika, justcoding121 8 | 9 | https://github.com/justcoding121/Stream-Extended/blob/master/LICENSE 10 | https://github.com/justcoding121/Stream-Extended 11 | false 12 | SNI for SslStream. 13 | 14 | Copyright © Stream-Extended All rights reserved. 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /StreamExtended/TaskExtended.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace StreamExtended 9 | { 10 | /// 11 | /// Mimic a Task but you can set AsyncState 12 | /// 13 | /// 14 | public class TaskResult : IAsyncResult 15 | { 16 | Task Task; 17 | object mAsyncState; 18 | 19 | public TaskResult(Task pTask, object state) 20 | { 21 | Task = pTask; 22 | mAsyncState = state; 23 | } 24 | 25 | public object AsyncState => mAsyncState; 26 | public WaitHandle AsyncWaitHandle => ((IAsyncResult)Task).AsyncWaitHandle; 27 | public bool CompletedSynchronously => ((IAsyncResult)Task).CompletedSynchronously; 28 | public bool IsCompleted => Task.IsCompleted; 29 | public T Result => Task.Result; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /NetCoreServer/NetCoreServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | 5.0.7.0 6 | Ivan Shynkarenka 7 | Copyright (c) 2019-2021 Ivan Shynkarenka 8 | https://github.com/chronoxor/NetCoreServer 9 | Ultra fast and low latency asynchronous socket server & client C# .NET Core library with support TCP, SSL, UDP, HTTP, HTTPS, WebSocket protocols and 10K connections problem solution 10 | MIT 11 | https://github.com/chronoxor/NetCoreServer 12 | async;client;server;tcp;udp;ssl;tls;http;https;websocket;low latency;performance 13 | 14 | 15 | 16 | true 17 | snupkg 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Marauder Software Inc 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /SyslogParser/Code/RfcVisitor.cs: -------------------------------------------------------------------------------- 1 | 2 | using Antlr4.Runtime.Misc; 3 | using SyslogServer.grammars; 4 | 5 | 6 | namespace SyslogParser 7 | { 8 | 9 | public class RfcVisitor 10 | : Rfc5424BaseVisitor 11 | { 12 | 13 | 14 | public override string VisitHeaderVersion([NotNull] Rfc5424Parser.HeaderVersionContext context) 15 | { 16 | string s = base.VisitChildren(context); 17 | return s; 18 | } 19 | 20 | 21 | public override string VisitHeaderAppName([NotNull] Rfc5424Parser.HeaderAppNameContext context) 22 | { 23 | string s = VisitChildren(context); 24 | return s; 25 | } 26 | 27 | 28 | public override string VisitHeaderNilAppName([NotNull] Rfc5424Parser.HeaderNilAppNameContext context) 29 | { 30 | string s = base.VisitChildren(context); 31 | return s; 32 | } 33 | 34 | 35 | public override string Visit([Antlr4.Runtime.Misc.NotNull] 36 | Antlr4.Runtime.Tree.IParseTree tree) 37 | { 38 | string t = tree.GetText(); 39 | System.Console.WriteLine(t); 40 | 41 | return base.Visit(tree); 42 | } 43 | 44 | } 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /SimpleTcpServer/TcpEchoClient.cs: -------------------------------------------------------------------------------- 1 | // This code is adapted from a sample found at the URL 2 | // "http://blogs.msdn.com/b/jmanning/archive/2004/12/19/325699.aspx" 3 | 4 | // https://thiscouldbebetter.wordpress.com/2015/01/13/an-echo-server-and-client-in-c-using-tcplistener-and-tcpclient/ 5 | namespace SimpleTcpServer 6 | { 7 | 8 | 9 | public class TcpEchoClient 10 | { 11 | 12 | 13 | public static void Test() 14 | { 15 | System.Console.WriteLine("Starting echo client..."); 16 | 17 | int port = 1234; 18 | System.Net.Sockets.TcpClient client = 19 | new System.Net.Sockets.TcpClient("localhost", port); 20 | 21 | System.Net.Sockets.NetworkStream stream = client.GetStream(); 22 | 23 | System.IO.StreamReader reader = new System.IO.StreamReader(stream); 24 | System.IO.StreamWriter writer = new System.IO.StreamWriter(stream) 25 | { AutoFlush = true }; 26 | 27 | while (true) 28 | { 29 | System.Console.Write("Enter text to send: "); 30 | string lineToSend = System.Console.ReadLine(); 31 | System.Console.WriteLine("Sending to server: " + lineToSend); 32 | writer.WriteLine(lineToSend); 33 | string lineReceived = reader.ReadLine(); 34 | System.Console.WriteLine("Received from server: " + lineReceived); 35 | } // Whend 36 | } // End Sub Test 37 | 38 | 39 | } // End Class TcpEchoClient 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /SelfSignedCertificate/Helpers/KeyValuePairList.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SelfSignedCertificate 3 | { 4 | 5 | 6 | public class KeyValuePairList 7 | : System.Collections.Generic.List> 8 | { 9 | public void Add(TKey key, TValue value) 10 | { 11 | this.Add(new System.Collections.Generic.KeyValuePair(key, value)); 12 | } 13 | 14 | public System.Collections.Generic.List Keys 15 | { 16 | get 17 | { 18 | 19 | System.Collections.Generic.List ls 20 | = new System.Collections.Generic.List(); 21 | 22 | for (int i = 0; i < this.Count; ++i) 23 | { 24 | ls.Add(this[i].Key); 25 | } 26 | 27 | return ls; 28 | } 29 | } 30 | 31 | public System.Collections.Generic.List Values 32 | { 33 | get 34 | { 35 | System.Collections.Generic.List ls 36 | = new System.Collections.Generic.List(); 37 | 38 | for (int i = 0; i < this.Count; ++i) 39 | { 40 | ls.Add(this[i].Value); 41 | } 42 | 43 | return ls; 44 | } 45 | } 46 | 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /StreamExtended/BufferPool/DefaultBufferPool.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | 3 | namespace StreamExtended 4 | { 5 | 6 | /// 7 | /// A concrete IBufferPool implementation using a thread-safe stack. 8 | /// Works well when all consumers ask for buffers with the same size. 9 | /// If your application would use variable size buffers consider implementing IBufferPool using System.Buffers library from Microsoft. 10 | /// 11 | public class DefaultBufferPool : IBufferPool 12 | { 13 | private readonly ConcurrentStack buffers = new ConcurrentStack(); 14 | 15 | /// 16 | /// Gets a buffer. 17 | /// 18 | /// Size of the buffer. 19 | /// 20 | public byte[] GetBuffer(int bufferSize) 21 | { 22 | if (!buffers.TryPop(out var buffer) || buffer.Length != bufferSize) 23 | { 24 | buffer = new byte[bufferSize]; 25 | } 26 | 27 | return buffer; 28 | } 29 | 30 | /// 31 | /// Returns the buffer. 32 | /// 33 | /// The buffer. 34 | public void ReturnBuffer(byte[] buffer) 35 | { 36 | if (buffer != null) 37 | { 38 | buffers.Push(buffer); 39 | } 40 | } 41 | 42 | public void Dispose() 43 | { 44 | buffers.Clear(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /StreamExtended/Models/SslExtension.cs: -------------------------------------------------------------------------------- 1 | namespace StreamExtended.Models 2 | { 3 | /// 4 | /// The SSL extension information. 5 | /// 6 | public class SslExtension 7 | { 8 | /// 9 | /// Gets the value. 10 | /// 11 | /// 12 | /// The value. 13 | /// 14 | public int Value { get; } 15 | 16 | /// 17 | /// Gets the name. 18 | /// 19 | /// 20 | /// The name. 21 | /// 22 | public string Name { get; } 23 | 24 | /// 25 | /// Gets the data. 26 | /// 27 | /// 28 | /// The data. 29 | /// 30 | public string Data { get; } 31 | 32 | /// 33 | /// Gets the position. 34 | /// 35 | /// 36 | /// The position. 37 | /// 38 | public int Position { get; } 39 | 40 | /// 41 | /// Initializes a new instance of the class. 42 | /// 43 | /// The value. 44 | /// The name. 45 | /// The data. 46 | /// The position. 47 | public SslExtension(int value, string name, string data, int position) 48 | { 49 | Value = value; 50 | Name = name; 51 | Data = data; 52 | Position = position; 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /SimpleTcpServer/TcpEchoServer.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SimpleTcpServer 3 | { 4 | 5 | 6 | // This code is adapted from a sample found at the URL 7 | // "http://blogs.msdn.com/b/jmanning/archive/2004/12/19/325699.aspx" 8 | 9 | 10 | // https://thiscouldbebetter.wordpress.com/2015/01/13/an-echo-server-and-client-in-c-using-tcplistener-and-tcpclient/ 11 | public class TcpEchoServer 12 | { 13 | 14 | 15 | public static async System.Threading.Tasks.Task Test() 16 | { 17 | System.Console.WriteLine("Starting echo server..."); 18 | 19 | int port = 1234; 20 | System.Net.IPAddress listenerAddress = System.Net.IPAddress.Loopback; 21 | 22 | System.Net.Sockets.TcpListener listener = 23 | new System.Net.Sockets.TcpListener(listenerAddress, port); 24 | 25 | listener.Start(); 26 | 27 | System.Net.Sockets.TcpClient client = await listener.AcceptTcpClientAsync(); 28 | System.Net.Sockets.NetworkStream stream = client.GetStream(); 29 | System.IO.StreamWriter writer = new System.IO.StreamWriter(stream, System.Text.Encoding.ASCII) 30 | { AutoFlush = true }; 31 | 32 | System.IO.StreamReader reader = new System.IO.StreamReader(stream, System.Text.Encoding.ASCII); 33 | 34 | while (true) 35 | { 36 | string inputLine = ""; 37 | while (inputLine != null) 38 | { 39 | inputLine = await reader.ReadLineAsync(); 40 | await writer.WriteLineAsync("Echoing string: " + inputLine); 41 | System.Console.WriteLine("Echoing string: " + inputLine); 42 | } 43 | 44 | System.Console.WriteLine("Server saw disconnect from client."); 45 | } 46 | } // End Task Test 47 | 48 | 49 | } // End Class TcpEchoServer 50 | 51 | 52 | } 53 | 54 | -------------------------------------------------------------------------------- /libSyslogServer/ReceiverThread.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace libSyslogServer 3 | { 4 | 5 | 6 | public partial class SyslogServer 7 | { 8 | 9 | 10 | static void ReceiverThread() 11 | { 12 | if (_ListenerUdp == null) _ListenerUdp = 13 | new System.Net.Sockets.UdpClient(_Settings.UdpPort); 14 | 15 | try 16 | { 17 | 18 | System.Net.IPEndPoint endpoint = 19 | new System.Net.IPEndPoint(System.Net.IPAddress.Any, _Settings.UdpPort); 20 | 21 | string receivedData; 22 | byte[] receivedBytes; 23 | 24 | while (true) 25 | { 26 | 27 | receivedBytes = _ListenerUdp.Receive(ref endpoint); 28 | // Encoding.ASCII ? 29 | receivedData = System.Text.Encoding.ASCII.GetString(receivedBytes, 0, receivedBytes.Length); 30 | string msg = null; 31 | if (_Settings.DisplayTimestamps) msg = System.DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss") + " "; 32 | msg += receivedData; 33 | System.Console.WriteLine(msg); 34 | 35 | 36 | lock (_WriterLock) 37 | { 38 | _MessageQueue.Add(msg); 39 | } 40 | 41 | } 42 | 43 | 44 | } 45 | catch (System.Exception e) 46 | { 47 | _ListenerUdp.Close(); 48 | _ListenerUdp = null; 49 | System.Console.WriteLine("***"); 50 | System.Console.WriteLine("ReceiverThread exiting due to exception: " + e.Message); 51 | return; 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NetCore Syslog Server 2 | 3 | 4 | ## Simple Syslog Server in C# 5 | 6 | 7 | ## Watson Syslog Server 8 | 9 | This project is based on Watson Syslog Server.
10 | Moved Watson Syslog Server to libSyslogServer for reference.
11 | Watson Syslog Server will automatically start using a default configuration listening on UDP/514 and storing log files in the ```logs\``` directory.
12 | If you wish to change this, create a file called ```syslog.json``` with the following structure: 13 | ``` 14 | { 15 | "Version": "Watson Syslog Server v1.0.0", 16 | "UdpPort": 514, 17 | "DisplayTimestamps": true, 18 | "LogFileDirectory": "logs\\", 19 | "LogFilename": "log.txt", 20 | "LogWriterIntervalSec": 10 21 | } 22 | ``` 23 | 24 | 25 | ## Starting the Server 26 | 27 | Build/compile and run the binary. 28 | 29 | ## Running under Windows Linux and MacOS 30 | 31 | This application should work well in all NetCore environments.
32 | Tested on: 33 | - Windows 10 34 | - Linux (Ubuntu 20.04 LTS "Focal Fossa" x64) 35 | - OSX (10.13 "High Sierra") 36 | 37 | 38 | 39 | ## Running under Mono 40 | 41 | This app should work well in Mono environments.
42 | It is recommended that when running under Mono, you execute the containing EXE using --server and after using the Mono Ahead-of-Time Compiler (AOT). 43 | ``` 44 | mono --aot=nrgctx-trampolines=8096,nimt-trampolines=8096,ntrampolines=4048 --server myapp.exe 45 | mono --server myapp.exe 46 | ``` 47 | 48 | 49 | ## Help or Feedback 50 | 51 | Do you need help or have feedback? 52 | See the "issues" tab. 53 | 54 | ## New in v2.0.0 55 | 56 | - Dependency on NetCoreServer 57 | - Support for 10'000 concurrent connections 58 | - Support for TCP 59 | - Support for TLS (Note TLS 1.2 REQUIRED, TLS 1 only supported if you modify the source) 60 | - Working Client for TLS (see https://github.com/ststeiger/SyslogNet) 61 | 62 | ## New in v1.0.0 63 | 64 | - Initial release, support for UDP 65 | -------------------------------------------------------------------------------- /SyslogServer/SyslogServer.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SyslogServer 3 | { 4 | 5 | public enum NetworkProtocol 6 | { 7 | UPD, 8 | TCP, 9 | TLS 10 | } // End Enum NetworkProtocol 11 | 12 | 13 | // TODO: create a composite-class for a general syslog server 14 | class SyslogServer 15 | { 16 | 17 | protected TlsSyslogServer TlsServer; 18 | protected UpdSyslogServer UdpServer; 19 | protected TcpSyslogServer TcpServer; 20 | 21 | protected NetCoreServer.SslSession Session; 22 | protected SyslogTcpSession TcpSession; 23 | 24 | public int Port; 25 | 26 | 27 | public SyslogServer() 28 | { 29 | 30 | } // End Constructor 31 | 32 | 33 | public void Start(NetworkProtocol protocol, System.Net.IPAddress address, int port) 34 | { 35 | switch (protocol) 36 | { 37 | case NetworkProtocol.TCP: 38 | TcpSyslogServer.Test(); 39 | break; 40 | case NetworkProtocol.TLS: 41 | TlsSyslogServer.Test(); 42 | break; 43 | default: 44 | UpdSyslogServer.Test(); 45 | break; 46 | } 47 | 48 | } // End Sub Start 49 | 50 | 51 | public void Start(NetworkProtocol protocol, System.Net.IPAddress address) 52 | { 53 | this.Port = 514; // UDP 54 | 55 | switch (protocol) 56 | { 57 | case NetworkProtocol.TCP: 58 | this.Port = 1468; // TCP 59 | break; 60 | case NetworkProtocol.TLS: 61 | this.Port = 6514; // TLS 62 | break; 63 | } 64 | 65 | Start(protocol, address, this.Port); 66 | } // End Sub Start 67 | 68 | 69 | public void Stop() 70 | { 71 | 72 | } // End Sub Stop 73 | 74 | 75 | } // End Class SyslogServer 76 | 77 | 78 | } // End Namespace SyslogServer 79 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /libSyslogServer/WriterTask.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace libSyslogServer 3 | { 4 | 5 | 6 | public partial class SyslogServer 7 | { 8 | static void WriterTask() 9 | { 10 | try 11 | { 12 | while (true) 13 | { 14 | System.Threading.Tasks.Task.Delay(1000).Wait(); 15 | 16 | if (System.DateTime.Compare(_LastWritten.AddSeconds(_Settings.LogWriterIntervalSec), System.DateTime.Now) < 0) 17 | { 18 | lock (_WriterLock) 19 | { 20 | if (_MessageQueue == null || _MessageQueue.Count < 1) 21 | { 22 | _LastWritten = System.DateTime.Now; 23 | continue; 24 | } 25 | 26 | foreach (string currMessage in _MessageQueue) 27 | { 28 | string currFilename = _Settings.LogFileDirectory + System.DateTime.Now.ToString("MMddyyyy") + "-" + _Settings.LogFilename; 29 | 30 | if (!System.IO.File.Exists(currFilename)) 31 | { 32 | System.Console.WriteLine("Creating file: " + currFilename + System.Environment.NewLine); 33 | { 34 | using (System.IO.FileStream fsCreate = 35 | System.IO.File.Create(currFilename)) 36 | { 37 | byte[] createData = new System.Text.UTF8Encoding(true).GetBytes("--- Creating log file at " + System.DateTime.Now + " ---" + System.Environment.NewLine); 38 | fsCreate.Write(createData, 0, createData.Length); 39 | } 40 | } 41 | } 42 | 43 | using (System.IO.StreamWriter swAppend = 44 | System.IO.File.AppendText(currFilename)) 45 | { 46 | swAppend.WriteLine(currMessage); 47 | } 48 | } 49 | 50 | _LastWritten = System.DateTime.Now; 51 | _MessageQueue = new System.Collections.Generic.List(); 52 | } 53 | } 54 | } 55 | } 56 | catch (System.Exception e) 57 | { 58 | System.Console.WriteLine("***"); 59 | System.Console.WriteLine("WriterTask exiting due to exception: " + e.Message); 60 | System.Environment.Exit(-1); 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /SyslogParser/ParsingHelper.cs: -------------------------------------------------------------------------------- 1 | 2 | // using Antlr4.Runtime.Misc; 3 | using SyslogServer.grammars; 4 | 5 | 6 | namespace SyslogParser 7 | { 8 | 9 | 10 | internal class ParsingHelper 11 | { 12 | 13 | 14 | public static System.Collections.Generic.List LexWithAntlr(string text) 15 | { 16 | System.Collections.Generic.List ls = new System.Collections.Generic.List(); 17 | 18 | System.IO.StringReader reader = new System.IO.StringReader(text); 19 | 20 | // Antlr4.Runtime.AntlrInputStream input = new Antlr4.Runtime.AntlrInputStream(reader); 21 | 22 | Antlr4.Runtime.ICharStream input1 = new Antlr4.Runtime.AntlrInputStream(reader); 23 | Antlr4.Runtime.CaseChangingCharStream input = new Antlr4.Runtime.CaseChangingCharStream(input1, true); 24 | 25 | 26 | 27 | Rfc5424Lexer lexer = new Rfc5424Lexer(input); 28 | 29 | Antlr4.Runtime.CommonTokenStream tokenStream = new Antlr4.Runtime.CommonTokenStream(lexer); 30 | tokenStream.Fill(); 31 | 32 | 33 | 34 | 35 | Rfc5424Parser parser = new Rfc5424Parser(tokenStream); 36 | 37 | Rfc5424Parser.Syslog_msgContext msgContext = parser.syslog_msg(); 38 | 39 | 40 | RfcVisitor vis = new RfcVisitor(); 41 | string s = vis.Visit(msgContext); 42 | 43 | 44 | Antlr4.Runtime.Tree.ParseTreeWalker walker = new Antlr4.Runtime.Tree.ParseTreeWalker(); 45 | // AntlrTsqListener listener = new AntlrTsqListener(); 46 | EverythingListener listener = new EverythingListener(); 47 | 48 | // walker.Walk(listener, msgContext); 49 | 50 | 51 | // new EverythingListener().EnterBom(parser.bom()); 52 | // new EverythingListener().EnterTimestamp(parser.timestamp()); 53 | // new EverythingListener().EnterEveryRule(parser.version()); 54 | // new EverythingListener().EnterEveryRule(parser.timestamp()); 55 | 56 | 57 | 58 | // var x = parser.msg(); 59 | var x = parser.timestamp(); 60 | 61 | 62 | 63 | Antlr4.Runtime.Misc.Interval msgInt = x.SourceInterval; // new Antlr4.Runtime.Misc.Interval(lastIndex, token.StopIndex); 64 | string extractedMsg = tokenStream.GetText(msgInt); 65 | System.Console.WriteLine(extractedMsg); 66 | 67 | 68 | 69 | 70 | int lastIndex = 0; 71 | 72 | foreach (Antlr4.Runtime.IToken token in tokenStream.GetTokens()) 73 | { 74 | // System.Console.WriteLine(token.Text); 75 | string tokenTypeName = lexer.Vocabulary.GetSymbolicName(token.Type); 76 | Antlr4.Runtime.Misc.Interval ival = new Antlr4.Runtime.Misc.Interval(lastIndex, token.StopIndex); 77 | string extracted = token.InputStream.GetText(ival); 78 | 79 | // table_name, cte_name: ID, SQUARE_BRACKET_ID 80 | // Local variables: LOCAL_ID 81 | 82 | lastIndex = token.StopIndex + 1; 83 | } // Next token 84 | 85 | return ls; 86 | } 87 | 88 | 89 | } 90 | 91 | 92 | } 93 | -------------------------------------------------------------------------------- /NetCoreServer/HttpServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.IO; 4 | 5 | namespace NetCoreServer 6 | { 7 | /// 8 | /// HTTP server is used to create HTTP Web server and communicate with clients using HTTP protocol. It allows to receive GET, POST, PUT, DELETE requests and send HTTP responses. 9 | /// 10 | /// Thread-safe. 11 | public class HttpServer : TcpServer 12 | { 13 | /// 14 | /// Initialize HTTP server with a given IP address and port number 15 | /// 16 | /// IP address 17 | /// Port number 18 | public HttpServer(IPAddress address, int port) : base(address, port) { Cache = new FileCache(); } 19 | /// 20 | /// Initialize HTTP server with a given IP address and port number 21 | /// 22 | /// IP address 23 | /// Port number 24 | public HttpServer(string address, int port) : base(address, port) { Cache = new FileCache(); } 25 | /// 26 | /// Initialize HTTP server with a given IP endpoint 27 | /// 28 | /// IP endpoint 29 | public HttpServer(IPEndPoint endpoint) : base(endpoint) { Cache = new FileCache(); } 30 | 31 | /// 32 | /// Get the static content cache 33 | /// 34 | public FileCache Cache { get; } 35 | 36 | /// 37 | /// Add static content cache 38 | /// 39 | /// Static content path 40 | /// Cache prefix (default is "/") 41 | /// Refresh cache timeout (default is 1 hour) 42 | public void AddStaticContent(string path, string prefix = "/", TimeSpan? timeout = null) 43 | { 44 | timeout ??= TimeSpan.FromHours(1); 45 | 46 | bool Handler(FileCache cache, string key, byte[] value, TimeSpan timespan) 47 | { 48 | HttpResponse header = new HttpResponse(); 49 | header.SetBegin(200); 50 | header.SetContentType(Path.GetExtension(key)); 51 | header.SetHeader("Cache-Control", $"max-age={timespan.Seconds}"); 52 | header.SetBody(value); 53 | return cache.Add(key, header.Cache.Data, timespan); 54 | } 55 | 56 | Cache.InsertPath(path, prefix, timeout.Value, Handler); 57 | } 58 | /// 59 | /// Remove static content cache 60 | /// 61 | /// Static content path 62 | public void RemoveStaticContent(string path) { Cache.RemovePath(path); } 63 | /// 64 | /// Clear static content cache 65 | /// 66 | public void ClearStaticContent() { Cache.Clear(); } 67 | 68 | /// 69 | /// Watchdog the static content cache 70 | /// 71 | public void Watchdog(DateTime utc) { Cache.Watchdog(utc); } 72 | 73 | protected override TcpSession CreateSession() { return new HttpSession(this); } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /SyslogParser/Code/CaseChangingCharStream.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved. 2 | * Use of this file is governed by the BSD 3-clause license that 3 | * can be found in the LICENSE.txt file in the project root. 4 | */ 5 | using System; 6 | using Antlr4.Runtime.Misc; 7 | 8 | // https://github.com/antlr/antlr4/tree/master/doc/resources 9 | namespace Antlr4.Runtime 10 | { 11 | 12 | 13 | /// 14 | /// This class supports case-insensitive lexing by wrapping an existing 15 | /// and forcing the lexer to see either upper or 16 | /// lowercase characters. Grammar literals should then be either upper or 17 | /// lower case such as 'BEGIN' or 'begin'. The text of the character 18 | /// stream is unaffected. Example: input 'BeGiN' would match lexer rule 19 | /// 'BEGIN' if constructor parameter upper=true but getText() would return 20 | /// 'BeGiN'. 21 | /// 22 | public class CaseChangingCharStream 23 | : ICharStream 24 | { 25 | private ICharStream stream; 26 | private bool upper; 27 | 28 | /// 29 | /// Constructs a new CaseChangingCharStream wrapping the given forcing 30 | /// all characters to upper case or lower case. 31 | /// 32 | /// The stream to wrap. 33 | /// If true force each symbol to upper case, otherwise force to lower. 34 | public CaseChangingCharStream(ICharStream stream, bool upper) 35 | { 36 | this.stream = stream; 37 | this.upper = upper; 38 | } 39 | 40 | public int Index 41 | { 42 | get 43 | { 44 | return stream.Index; 45 | } 46 | } 47 | 48 | public int Size 49 | { 50 | get 51 | { 52 | return stream.Size; 53 | } 54 | } 55 | 56 | public string SourceName 57 | { 58 | get 59 | { 60 | return stream.SourceName; 61 | } 62 | } 63 | 64 | public void Consume() 65 | { 66 | stream.Consume(); 67 | } 68 | 69 | [return: NotNull] 70 | public string GetText(Interval interval) 71 | { 72 | return stream.GetText(interval); 73 | } 74 | 75 | public int La(int i) 76 | { 77 | int c = stream.La(i); 78 | 79 | if (c <= 0) 80 | { 81 | return c; 82 | } 83 | 84 | char o = (char)c; 85 | 86 | if (upper) 87 | { 88 | return (int)char.ToUpperInvariant(o); 89 | } 90 | 91 | return (int)char.ToLowerInvariant(o); 92 | } 93 | 94 | 95 | public int Mark() 96 | { 97 | return stream.Mark(); 98 | } 99 | 100 | public void Release(int marker) 101 | { 102 | stream.Release(marker); 103 | } 104 | 105 | public void Seek(int index) 106 | { 107 | stream.Seek(index); 108 | } 109 | } 110 | 111 | 112 | } 113 | -------------------------------------------------------------------------------- /SelfSignedCertificate/Helpers/StringStream.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SelfSignedCertificate 3 | { 4 | 5 | 6 | public class StringStream 7 | : System.IO.Stream, System.IDisposable 8 | { 9 | 10 | protected System.IO.MemoryStream m_stream; 11 | protected System.IO.StreamWriter m_writer; 12 | private bool disposedValue; 13 | 14 | public StringStream(string s) 15 | { 16 | this.m_stream = new System.IO.MemoryStream(); 17 | this.m_writer = new System.IO.StreamWriter(this.m_stream); 18 | this.m_writer.Write(s); 19 | this.m_writer.Flush(); 20 | this.m_stream.Position = 0; 21 | } 22 | 23 | 24 | public override bool CanRead => this.m_stream.CanRead; 25 | 26 | public override bool CanSeek => this.m_stream.CanSeek; 27 | 28 | public override bool CanWrite => this.m_stream.CanWrite; 29 | 30 | public override long Length => this.m_stream.Length; 31 | 32 | public override long Position { get => this.m_stream.Position; set => this.m_stream.Position = value; } 33 | 34 | public override void Flush() 35 | { 36 | this.m_stream.Flush(); 37 | } 38 | 39 | public override int Read(byte[] buffer, int offset, int count) 40 | { 41 | return this.m_stream.Read(buffer, offset, count); 42 | } 43 | 44 | public override long Seek(long offset, System.IO.SeekOrigin origin) 45 | { 46 | return this.m_stream.Seek(offset, origin); 47 | } 48 | 49 | public override void SetLength(long value) 50 | { 51 | this.m_stream.SetLength(value); 52 | } 53 | 54 | public override void Write(byte[] buffer, int offset, int count) 55 | { 56 | this.m_stream.Write(buffer, offset, count); 57 | } 58 | 59 | protected void OnDispose(bool disposing) 60 | { 61 | if (!disposedValue) 62 | { 63 | if (disposing) 64 | { 65 | // TODO: Verwalteten Zustand (verwaltete Objekte) bereinigen 66 | if (this.m_writer != null) 67 | this.m_writer.Dispose(); 68 | 69 | if (this.m_stream != null) 70 | this.m_stream.Dispose(); 71 | } 72 | 73 | // TODO: Nicht verwaltete Ressourcen (nicht verwaltete Objekte) freigeben und Finalizer überschreiben 74 | // TODO: Große Felder auf NULL setzen 75 | disposedValue = true; 76 | } 77 | } // End Sub OnDispose 78 | 79 | // // TODO: Finalizer nur überschreiben, wenn "Dispose(bool disposing)" Code für die Freigabe nicht verwalteter Ressourcen enthält 80 | // ~StringStream() 81 | // { 82 | // // Ändern Sie diesen Code nicht. Fügen Sie Bereinigungscode in der Methode "Dispose(bool disposing)" ein. 83 | // Dispose(disposing: false); 84 | // } 85 | 86 | void System.IDisposable.Dispose() 87 | { 88 | // Ändern Sie diesen Code nicht. Fügen Sie Bereinigungscode in der Methode "Dispose(bool disposing)" ein. 89 | OnDispose(true); 90 | System.GC.SuppressFinalize(this); 91 | } // End Sub Dispose 92 | 93 | 94 | } // End Class StringStream 95 | 96 | 97 | } 98 | -------------------------------------------------------------------------------- /SyslogServer/UpdSyslogServer.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SyslogServer 3 | { 4 | 5 | 6 | public class UpdSyslogServer 7 | : NetCoreServer.UdpServer 8 | { 9 | 10 | protected MessageHandler m_messageHandler; 11 | 12 | 13 | public UpdSyslogServer(System.Net.IPAddress address, int port, MessageHandler handler) 14 | : base(address, port) 15 | { 16 | this.m_messageHandler = handler; 17 | } 18 | 19 | 20 | protected override void OnStarted() 21 | { 22 | // Start receive datagrams 23 | ReceiveAsync(); 24 | } // End Sub OnStarted 25 | 26 | 27 | protected override void OnReceived( 28 | System.Net.EndPoint endpoint 29 | , byte[] buffer 30 | , long offset 31 | , long size) 32 | { 33 | System.Console.WriteLine("Incoming: " + System.Text.Encoding.UTF8.GetString(buffer, (int)offset, (int)size)); 34 | this.m_messageHandler.OnReceived(endpoint, buffer, offset, size); 35 | 36 | 37 | // Echo the message back to the sender 38 | // SendAsync(endpoint, buffer, 0, size); 39 | } // End Sub OnReceived 40 | 41 | 42 | protected override void OnSent(System.Net.EndPoint endpoint, long sent) 43 | { 44 | // Continue receive datagrams 45 | ReceiveAsync(); 46 | } // End Sub OnSent 47 | 48 | 49 | protected override void OnError(System.Net.Sockets.SocketError error) 50 | { 51 | // System.Console.WriteLine($"Echo UDP server caught an error with code {error}"); 52 | this.m_messageHandler.OnError(error); 53 | } // End Sub OnError 54 | 55 | 56 | public static void Test() 57 | { 58 | // UDP server port 59 | int port = 514; 60 | 61 | System.Console.WriteLine($"UDP server port: {port}"); 62 | 63 | System.Console.WriteLine(); 64 | 65 | // Create a new UDP echo server 66 | UpdSyslogServer server = 67 | new UpdSyslogServer(System.Net.IPAddress.Any, port, MessageHandler.CreateInstance(123, port)); 68 | 69 | // Start the server 70 | System.Console.Write("Server starting..."); 71 | server.Start(); 72 | System.Console.WriteLine("Done!"); 73 | 74 | System.Console.WriteLine("Press Enter to stop the server or '!' to restart the server..."); 75 | 76 | // Perform text input 77 | for (; ; ) 78 | { 79 | string line = System.Console.ReadLine(); 80 | if (string.IsNullOrEmpty(line)) 81 | break; 82 | 83 | // Restart the server 84 | if (line == "!") 85 | { 86 | System.Console.Write("Server restarting..."); 87 | server.Restart(); 88 | System.Console.WriteLine("Done!"); 89 | } 90 | 91 | } // Next 92 | 93 | // Stop the server 94 | System.Console.Write("Server stopping..."); 95 | server.Stop(); 96 | System.Console.WriteLine("Done!"); 97 | } // End Sub Test 98 | 99 | 100 | } // End Class UpdSyslogServer 101 | 102 | 103 | } // End Namespace SyslogServer 104 | -------------------------------------------------------------------------------- /NetCoreServer/HttpsServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | 5 | namespace NetCoreServer 6 | { 7 | /// 8 | /// HTTPS server is used to create secured HTTPS Web server and communicate with clients using secure HTTPS protocol. It allows to receive GET, POST, PUT, DELETE requests and send HTTP responses. 9 | /// 10 | /// Thread-safe. 11 | public class HttpsServer : SslServer 12 | { 13 | /// 14 | /// Initialize HTTPS server with a given IP address and port number 15 | /// 16 | /// SSL context 17 | /// IP address 18 | /// Port number 19 | public HttpsServer(SslContext context, IPAddress address, int port) : base(context, address, port) { Cache = new FileCache(); } 20 | /// 21 | /// Initialize HTTPS server with a given IP address and port number 22 | /// 23 | /// SSL context 24 | /// IP address 25 | /// Port number 26 | public HttpsServer(SslContext context, string address, int port) : base(context, address, port) { Cache = new FileCache(); } 27 | /// 28 | /// Initialize HTTPS server with a given IP endpoint 29 | /// 30 | /// SSL context 31 | /// IP endpoint 32 | public HttpsServer(SslContext context, IPEndPoint endpoint) : base(context, endpoint) { Cache = new FileCache(); } 33 | 34 | /// 35 | /// Get the static content cache 36 | /// 37 | public FileCache Cache { get; } 38 | 39 | /// 40 | /// Add static content cache 41 | /// 42 | /// Static content path 43 | /// Cache prefix (default is "/") 44 | /// Refresh cache timeout (default is 1 hour) 45 | public void AddStaticContent(string path, string prefix = "/", TimeSpan? timeout = null) 46 | { 47 | timeout ??= TimeSpan.FromHours(1); 48 | 49 | bool Handler(FileCache cache, string key, byte[] value, TimeSpan timespan) 50 | { 51 | HttpResponse header = new HttpResponse(); 52 | header.SetBegin(200); 53 | header.SetContentType(Path.GetExtension(key)); 54 | header.SetHeader("Cache-Control", $"max-age={timespan.Seconds}"); 55 | header.SetBody(value); 56 | return cache.Add(key, header.Cache.Data, timespan); 57 | } 58 | 59 | Cache.InsertPath(path, prefix, timeout.Value, Handler); 60 | } 61 | /// 62 | /// Remove static content cache 63 | /// 64 | /// Static content path 65 | public void RemoveStaticContent(string path) { Cache.RemovePath(path); } 66 | /// 67 | /// Clear static content cache 68 | /// 69 | public void ClearStaticContent() { Cache.Clear(); } 70 | 71 | /// 72 | /// Watchdog the static content cache 73 | /// 74 | public void Watchdog(DateTime utc) { Cache.Watchdog(utc); } 75 | 76 | protected override SslSession CreateSession() { return new HttpsSession(this); } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /StreamExtended/Network/ICustomStreamReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace StreamExtended.Network 6 | { 7 | /// 8 | /// This concrete implemetation of interface acts as the source stream for CopyStream class. 9 | /// 10 | public interface ICustomStreamReader 11 | { 12 | int BufferSize { get; } 13 | 14 | int Available { get; } 15 | 16 | bool DataAvailable { get; } 17 | 18 | /// 19 | /// Fills the buffer asynchronous. 20 | /// 21 | /// 22 | Task FillBufferAsync(CancellationToken cancellationToken = default(CancellationToken)); 23 | 24 | /// 25 | /// Peeks a byte from buffer. 26 | /// 27 | /// The index. 28 | /// 29 | /// Index is out of buffer size 30 | byte PeekByteFromBuffer(int index); 31 | 32 | /// 33 | /// Peeks a byte asynchronous. 34 | /// 35 | /// The index. 36 | /// The cancellation token. 37 | /// 38 | Task PeekByteAsync(int index, CancellationToken cancellationToken = default(CancellationToken)); 39 | 40 | /// 41 | /// Peeks bytes asynchronous. 42 | /// 43 | /// The index. 44 | /// The cancellation token. 45 | /// 46 | Task PeekBytesAsync(int index, int size, CancellationToken cancellationToken = default(CancellationToken)); 47 | 48 | byte ReadByteFromBuffer(); 49 | 50 | /// 51 | /// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read. 52 | /// 53 | /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between and ( + - 1) replaced by the bytes read from the current source. 54 | /// The zero-based byte offset in at which to begin storing the data read from the current stream. 55 | /// The maximum number of bytes to be read from the current stream. 56 | /// 57 | /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached. 58 | /// 59 | int Read(byte[] buffer, int offset, int count); 60 | 61 | /// 62 | /// Read the specified number (or less) of raw bytes from the base stream to the given buffer to the specified offset 63 | /// 64 | /// 65 | /// 66 | /// 67 | /// 68 | /// The number of bytes read 69 | Task ReadAsync(byte[] buffer, int offset, int bytesToRead, 70 | CancellationToken cancellationToken = default(CancellationToken)); 71 | 72 | /// 73 | /// Read a line from the byte stream 74 | /// 75 | /// 76 | Task ReadLineAsync(CancellationToken cancellationToken = default(CancellationToken)); 77 | } 78 | } -------------------------------------------------------------------------------- /NetCoreServer/SslContext.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Security; 2 | using System.Security.Authentication; 3 | using System.Security.Cryptography.X509Certificates; 4 | 5 | namespace NetCoreServer 6 | { 7 | /// 8 | /// SSL context 9 | /// 10 | public class SslContext 11 | { 12 | /// 13 | /// Initialize SSL context with default protocols 14 | /// 15 | public SslContext() : this(SslProtocols.Tls12) {} 16 | /// 17 | /// Initialize SSL context with given protocols 18 | /// 19 | /// SSL protocols 20 | public SslContext(SslProtocols protocols) { Protocols = protocols; } 21 | /// 22 | /// Initialize SSL context with given protocols and certificate 23 | /// 24 | /// SSL protocols 25 | /// SSL certificate 26 | public SslContext(SslProtocols protocols, X509Certificate certificate) : this(protocols, certificate, null) {} 27 | /// 28 | /// Initialize SSL context with given protocols, certificate and validation callback 29 | /// 30 | /// SSL protocols 31 | /// SSL certificate 32 | /// SSL certificate 33 | public SslContext(SslProtocols protocols, X509Certificate certificate, RemoteCertificateValidationCallback certificateValidationCallback) 34 | { 35 | Protocols = protocols; 36 | Certificate = certificate; 37 | CertificateValidationCallback = certificateValidationCallback; 38 | } 39 | /// 40 | /// Initialize SSL context with given protocols and certificates collection 41 | /// 42 | /// SSL protocols 43 | /// SSL certificates collection 44 | public SslContext(SslProtocols protocols, X509Certificate2Collection certificates) : this(protocols, certificates, null) {} 45 | /// 46 | /// Initialize SSL context with given protocols, certificates collection and validation callback 47 | /// 48 | /// SSL protocols 49 | /// SSL certificates collection 50 | /// SSL certificate 51 | public SslContext(SslProtocols protocols, X509Certificate2Collection certificates, RemoteCertificateValidationCallback certificateValidationCallback) 52 | { 53 | Protocols = protocols; 54 | Certificates = certificates; 55 | CertificateValidationCallback = certificateValidationCallback; 56 | } 57 | 58 | /// 59 | /// SSL protocols 60 | /// 61 | public SslProtocols Protocols { get; set; } 62 | /// 63 | /// SSL certificate 64 | /// 65 | public X509Certificate Certificate { get; set; } 66 | /// 67 | /// SSL certificates collection 68 | /// 69 | public X509Certificate2Collection Certificates { get; set; } 70 | /// 71 | /// SSL certificate validation callback 72 | /// 73 | public RemoteCertificateValidationCallback CertificateValidationCallback { get; set; } 74 | 75 | /// 76 | /// Is the client is asked for a certificate for authentication. 77 | /// Note that this is only a request - if no certificate is provided, the server still accepts the connection request. 78 | /// 79 | public bool ClientCertificateRequired { get; set; } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /StreamExtended/StreamExtended.Docs.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {EBF2EA46-EA00-4350-BE1D-D86AFD699DB3} 8 | Library 9 | Properties 10 | StreamExtended 11 | StreamExtended 12 | v4.5 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | true 25 | bin\Debug\StreamExtended.xml 26 | latest 27 | 28 | 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 81 | -------------------------------------------------------------------------------- /NetCoreServer/IWebSocket.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Sockets; 2 | 3 | namespace NetCoreServer 4 | { 5 | public interface IWebSocket 6 | { 7 | /// 8 | /// Handle WebSocket client connecting notification 9 | /// 10 | /// Notification is called when WebSocket client is connecting to the server.You can handle the connection and change WebSocket upgrade HTTP request by providing your own headers. 11 | /// WebSocket upgrade HTTP request 12 | void OnWsConnecting(HttpRequest request) {} 13 | 14 | /// 15 | /// Handle WebSocket client connected notification 16 | /// 17 | /// WebSocket upgrade HTTP response 18 | void OnWsConnected(HttpResponse response) {} 19 | 20 | /// 21 | /// Handle WebSocket server session validating notification 22 | /// 23 | /// Notification is called when WebSocket client is connecting to the server.You can handle the connection and validate WebSocket upgrade HTTP request. 24 | /// WebSocket upgrade HTTP request 25 | /// WebSocket upgrade HTTP response 26 | /// return 'true' if the WebSocket update request is valid, 'false' if the WebSocket update request is not valid 27 | bool OnWsConnecting(HttpRequest request, HttpResponse response) { return true; } 28 | 29 | /// 30 | /// Handle WebSocket server session connected notification 31 | /// 32 | /// WebSocket upgrade HTTP request 33 | void OnWsConnected(HttpRequest request) {} 34 | 35 | /// 36 | /// Handle WebSocket client disconnected notification 37 | /// 38 | void OnWsDisconnected() {} 39 | 40 | /// 41 | /// Handle WebSocket received notification 42 | /// 43 | /// Received buffer 44 | /// Received buffer offset 45 | /// Received buffer size 46 | void OnWsReceived(byte[] buffer, long offset, long size) {} 47 | 48 | /// 49 | /// Handle WebSocket client close notification 50 | /// 51 | /// Received buffer 52 | /// Received buffer offset 53 | /// Received buffer size 54 | void OnWsClose(byte[] buffer, long offset, long size) {} 55 | 56 | /// 57 | /// Handle WebSocket ping notification 58 | /// 59 | /// Received buffer 60 | /// Received buffer offset 61 | /// Received buffer size 62 | void OnWsPing(byte[] buffer, long offset, long size) {} 63 | 64 | /// 65 | /// Handle WebSocket pong notification 66 | /// 67 | /// Received buffer 68 | /// Received buffer offset 69 | /// Received buffer size 70 | void OnWsPong(byte[] buffer, long offset, long size) {} 71 | 72 | /// 73 | /// Handle WebSocket error notification 74 | /// 75 | /// Error message 76 | void OnWsError(string error) {} 77 | 78 | /// 79 | /// Handle socket error notification 80 | /// 81 | /// Socket error 82 | void OnWsError(SocketError error) {} 83 | 84 | /// 85 | /// Send WebSocket server upgrade response 86 | /// 87 | /// WebSocket upgrade HTTP response 88 | void SendResponse(HttpResponse response) {} 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /StreamExtended/ServerHelloInfo.cs: -------------------------------------------------------------------------------- 1 | using StreamExtended.Models; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace StreamExtended 8 | { 9 | /// 10 | /// Wraps up the server SSL hello information. 11 | /// 12 | public class ServerHelloInfo 13 | { 14 | private static readonly string[] compressions = { 15 | "null", 16 | "DEFLATE" 17 | }; 18 | 19 | public int HandshakeVersion { get; set; } 20 | 21 | public int MajorVersion { get; set; } 22 | 23 | public int MinorVersion { get; set; } 24 | 25 | public byte[] Random { get; set; } 26 | 27 | public DateTime Time 28 | { 29 | get 30 | { 31 | DateTime time = DateTime.MinValue; 32 | if (Random.Length > 3) 33 | { 34 | time = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) 35 | .AddSeconds(((uint)Random[3] << 24) + ((uint)Random[2] << 16) + ((uint)Random[1] << 8) + (uint)Random[0]).ToLocalTime(); 36 | } 37 | 38 | return time; 39 | } 40 | } 41 | 42 | public byte[] SessionId { get; set; } 43 | 44 | public int CipherSuite { get; set; } 45 | 46 | public byte CompressionMethod { get; set; } 47 | 48 | internal int ServerHelloLength { get; set; } 49 | 50 | internal int EntensionsStartPosition { get; set; } 51 | 52 | public Dictionary Extensions { get; set; } 53 | 54 | private static string SslVersionToString(int major, int minor) 55 | { 56 | string str = "Unknown"; 57 | if (major == 3 && minor == 4) 58 | str = "TLS/1.3"; 59 | else if (major == 3 && minor == 3) 60 | str = "TLS/1.2"; 61 | else if (major == 3 && minor == 2) 62 | str = "TLS/1.1"; 63 | else if (major == 3 && minor == 1) 64 | str = "TLS/1.0"; 65 | else if (major == 3 && minor == 0) 66 | str = "SSL/3.0"; 67 | else if (major == 2 && minor == 0) 68 | str = "SSL/2.0"; 69 | 70 | return $"{major}.{minor} ({str})"; 71 | } 72 | 73 | /// 74 | /// Returns a that represents this instance. 75 | /// 76 | /// 77 | /// A that represents this instance. 78 | /// 79 | public override string ToString() 80 | { 81 | var sb = new StringBuilder(); 82 | sb.AppendLine($"A SSLv{HandshakeVersion}-compatible ServerHello handshake was found. Titanium extracted the parameters below."); 83 | sb.AppendLine(); 84 | sb.AppendLine($"Version: {SslVersionToString(MajorVersion, MinorVersion)}"); 85 | sb.AppendLine($"Random: {string.Join(" ", Random.Select(x => x.ToString("X2")))}"); 86 | sb.AppendLine($"\"Time\": {Time}"); 87 | sb.AppendLine($"SessionID: {string.Join(" ", SessionId.Select(x => x.ToString("X2")))}"); 88 | 89 | if (Extensions != null) 90 | { 91 | sb.AppendLine("Extensions:"); 92 | foreach (var extension in Extensions.Values.OrderBy(x => x.Position)) 93 | { 94 | sb.AppendLine($"{extension.Name}: {extension.Data}"); 95 | } 96 | } 97 | 98 | string compression = compressions.Length > CompressionMethod 99 | ? compressions[CompressionMethod] 100 | : $"unknown [0x{CompressionMethod:X2}]"; 101 | sb.AppendLine($"Compression: {compression}"); 102 | 103 | sb.Append("Cipher:"); 104 | if (!SslCiphers.Ciphers.TryGetValue(CipherSuite, out string cipherStr)) 105 | { 106 | cipherStr = "unknown"; 107 | } 108 | 109 | sb.AppendLine($"[0x{CipherSuite:X4}] {cipherStr}"); 110 | 111 | return sb.ToString(); 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /StreamExtended/ClientHelloInfo.cs: -------------------------------------------------------------------------------- 1 | using StreamExtended.Models; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace StreamExtended 8 | { 9 | /// 10 | /// Wraps up the client SSL hello information. 11 | /// 12 | public class ClientHelloInfo 13 | { 14 | private static readonly string[] compressions = { 15 | "null", 16 | "DEFLATE" 17 | }; 18 | 19 | public int HandshakeVersion { get; set; } 20 | 21 | public int MajorVersion { get; set; } 22 | 23 | public int MinorVersion { get; set; } 24 | 25 | public byte[] Random { get; set; } 26 | 27 | public DateTime Time 28 | { 29 | get 30 | { 31 | DateTime time = DateTime.MinValue; 32 | if (Random.Length > 3) 33 | { 34 | time = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) 35 | .AddSeconds(((uint)Random[3] << 24) + ((uint)Random[2] << 16) + ((uint)Random[1] << 8) + (uint)Random[0]).ToLocalTime(); 36 | } 37 | 38 | return time; 39 | } 40 | } 41 | 42 | public byte[] SessionId { get; set; } 43 | 44 | public int[] Ciphers { get; set; } 45 | 46 | public byte[] CompressionData { get; set; } 47 | 48 | internal int ClientHelloLength { get; set; } 49 | 50 | internal int EntensionsStartPosition { get; set; } 51 | 52 | public Dictionary Extensions { get; set; } 53 | 54 | private static string SslVersionToString(int major, int minor) 55 | { 56 | string str = "Unknown"; 57 | 58 | if (major == 3 && minor == 4) 59 | str = "TLS/1.3"; 60 | else if (major == 3 && minor == 3) 61 | str = "TLS/1.2"; 62 | else if (major == 3 && minor == 2) 63 | str = "TLS/1.1"; 64 | else if (major == 3 && minor == 1) 65 | str = "TLS/1.0"; 66 | else if (major == 3 && minor == 0) 67 | str = "SSL/3.0"; 68 | else if (major == 2 && minor == 0) 69 | str = "SSL/2.0"; 70 | 71 | return $"{major}.{minor} ({str})"; 72 | } 73 | 74 | /// 75 | /// Returns a that represents this instance. 76 | /// 77 | /// 78 | /// A that represents this instance. 79 | /// 80 | public override string ToString() 81 | { 82 | var sb = new StringBuilder(); 83 | sb.AppendLine($"A SSLv{HandshakeVersion}-compatible ClientHello handshake was found. Titanium extracted the parameters below."); 84 | sb.AppendLine(); 85 | sb.AppendLine($"Version: {SslVersionToString(MajorVersion, MinorVersion)}"); 86 | sb.AppendLine($"Random: {string.Join(" ", Random.Select(x => x.ToString("X2")))}"); 87 | sb.AppendLine($"\"Time\": {Time}"); 88 | sb.AppendLine($"SessionID: {string.Join(" ", SessionId.Select(x => x.ToString("X2")))}"); 89 | 90 | if (Extensions != null) 91 | { 92 | sb.AppendLine("Extensions:"); 93 | foreach (var extension in Extensions.Values.OrderBy(x => x.Position)) 94 | { 95 | sb.AppendLine($"{extension.Name}: {extension.Data}"); 96 | } 97 | } 98 | 99 | if (CompressionData != null && CompressionData.Length > 0) 100 | { 101 | int compressionMethod = CompressionData[0]; 102 | string compression = compressions.Length > compressionMethod 103 | ? compressions[compressionMethod] 104 | : $"unknown [0x{compressionMethod:X2}]"; 105 | sb.AppendLine($"Compression: {compression}"); 106 | } 107 | 108 | if (Ciphers.Length > 0) 109 | { 110 | sb.AppendLine("Ciphers:"); 111 | foreach (int cipherSuite in Ciphers) 112 | { 113 | if (!SslCiphers.Ciphers.TryGetValue(cipherSuite, out string cipherStr)) 114 | { 115 | cipherStr = "unknown"; 116 | } 117 | 118 | sb.AppendLine($"[0x{cipherSuite:X4}] {cipherStr}"); 119 | } 120 | } 121 | 122 | return sb.ToString(); 123 | } 124 | } 125 | } -------------------------------------------------------------------------------- /libSyslogServer/SyslogServer.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace libSyslogServer 3 | { 4 | 5 | 6 | public partial class SyslogServer 7 | { 8 | public static string _SettingsContents; 9 | public static Settings _Settings; 10 | 11 | public static System.Threading.Thread _ListenerThread; 12 | public static System.Net.Sockets.UdpClient _ListenerUdp; 13 | public static bool _ConsoleDisplay; 14 | 15 | public static System.DateTime _LastWritten; 16 | private static System.Collections.Generic.List _MessageQueue; 17 | private static readonly object _WriterLock; 18 | 19 | 20 | static SyslogServer() 21 | { 22 | _SettingsContents = ""; 23 | _ConsoleDisplay = true; 24 | _LastWritten = System.DateTime.Now; 25 | _WriterLock = new object(); 26 | _MessageQueue = new System.Collections.Generic.List(); 27 | } 28 | 29 | 30 | public static void StartServer() 31 | { 32 | 33 | if (System.IO.File.Exists("syslog.json")) 34 | { 35 | _SettingsContents = System.Text.Encoding.UTF8.GetString( 36 | System.IO.File.ReadAllBytes("syslog.json") 37 | ); 38 | } 39 | 40 | if (System.String.IsNullOrEmpty(_SettingsContents)) 41 | { 42 | System.Console.WriteLine("Unable to read syslog.json, using default configuration:"); 43 | _Settings = Settings.Default(); 44 | System.Console.WriteLine(Common.SerializeJson(_Settings)); 45 | } 46 | else 47 | { 48 | try 49 | { 50 | _Settings = Common.DeserializeJson(_SettingsContents); 51 | } 52 | catch (System.Exception) 53 | { 54 | System.Console.WriteLine("Unable to deserialize syslog.json, please check syslog.json for correctness, exiting"); 55 | System.Environment.Exit(-1); 56 | } 57 | } 58 | 59 | if (!System.IO.Directory.Exists(_Settings.LogFileDirectory)) 60 | System.IO.Directory.CreateDirectory(_Settings.LogFileDirectory); 61 | 62 | 63 | System.Console.WriteLine("---"); 64 | System.Console.WriteLine(_Settings.Version); 65 | System.Console.WriteLine("(c)2017 Joel Christner"); 66 | System.Console.WriteLine("---"); 67 | 68 | InternalStartServer(); 69 | 70 | 71 | 72 | 73 | while (true) 74 | { 75 | string userInput = Common.InputString("[syslog :: ? for help] >", null, false); 76 | switch (userInput) 77 | { 78 | case "?": 79 | System.Console.WriteLine("---"); 80 | System.Console.WriteLine(" q quit the application"); 81 | System.Console.WriteLine(" cls clear the screen"); 82 | break; 83 | 84 | case "q": 85 | System.Console.WriteLine("Exiting."); 86 | System.Environment.Exit(0); 87 | break; 88 | 89 | case "c": 90 | case "cls": 91 | System.Console.Clear(); 92 | break; 93 | 94 | default: 95 | System.Console.WriteLine("Unknown command. Type '?' for help."); 96 | continue; 97 | } 98 | } 99 | 100 | 101 | } 102 | 103 | static void InternalStartServer() 104 | { 105 | try 106 | { 107 | System.Console.WriteLine("Starting at " + System.DateTime.Now); 108 | 109 | _ListenerThread = new System.Threading.Thread(ReceiverThread); 110 | _ListenerThread.Start(); 111 | System.Console.WriteLine("Listening on UDP/" + _Settings.UdpPort + "."); 112 | 113 | System.Threading.Tasks.Task.Run(() => WriterTask()); 114 | System.Console.WriteLine("Writer thread started successfully"); 115 | } 116 | catch (System.Exception e) 117 | { 118 | System.Console.WriteLine("***"); 119 | System.Console.WriteLine("Exiting due to exception: " + e.Message); 120 | System.Environment.Exit(-1); 121 | } 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /SyslogServer/TcpSyslogServer.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SyslogServer 3 | { 4 | 5 | 6 | public class SyslogTcpSession 7 | : NetCoreServer.TcpSession 8 | { 9 | protected MessageHandler m_messageHandler; 10 | 11 | public SyslogTcpSession(NetCoreServer.TcpServer server, MessageHandler handler) 12 | : base(server) 13 | { 14 | this.m_messageHandler = handler; 15 | } 16 | 17 | protected override void OnConnected() 18 | { 19 | System.Console.WriteLine($"Syslog TCP session with Id {Id} connected!"); 20 | 21 | // Send invite message 22 | string message = "Hello from Syslog TCP session ! Please send a message or '!' to disconnect the client!"; 23 | SendAsync(message); 24 | } // End Sub OnConnected 25 | 26 | 27 | protected override void OnDisconnected() 28 | { 29 | System.Console.WriteLine($"Syslog TCP session with Id {Id} disconnected!"); 30 | } // End Sub OnDisconnected 31 | 32 | 33 | 34 | 35 | 36 | protected override void OnReceived(byte[] buffer, long offset, long size) 37 | { 38 | this.m_messageHandler.OnReceived(this.Socket.RemoteEndPoint, buffer, offset, size); 39 | 40 | // Multicast message to all connected sessions 41 | // Server.Multicast(message); 42 | 43 | // If the buffer starts with '!' the disconnect the current session 44 | // if (message == "!") 45 | // Disconnect(); 46 | 47 | } // End Sub OnReceived 48 | 49 | 50 | protected override void OnError(System.Net.Sockets.SocketError error) 51 | { 52 | // System.Console.WriteLine($"Syslog TCP session caught an error with code {error}"); 53 | this.m_messageHandler.OnError(error); 54 | } // End Sub OnError 55 | 56 | 57 | } // End Class SyslogTcpSession 58 | 59 | 60 | public class TcpSyslogServer 61 | : NetCoreServer.TcpServer 62 | { 63 | protected MessageHandler m_messageHandler; 64 | 65 | public TcpSyslogServer( 66 | System.Net.IPAddress address 67 | , int port 68 | ,MessageHandler handler 69 | ) 70 | : base(address, port) 71 | { 72 | this.m_messageHandler = handler; 73 | } 74 | 75 | 76 | protected override NetCoreServer.TcpSession CreateSession() 77 | { 78 | return new SyslogTcpSession(this, this.m_messageHandler); 79 | } // End Function CreateSession 80 | 81 | 82 | protected override void OnError(System.Net.Sockets.SocketError error) 83 | { 84 | // System.Console.WriteLine($"Syslog TCP server caught an error with code {error}"); 85 | this.m_messageHandler.OnError(error); 86 | } // End Sub OnError 87 | 88 | 89 | public static void Test() 90 | { 91 | // TCP server port 92 | int port = 1468; 93 | 94 | System.Console.WriteLine($"TCP server port: {port}"); 95 | 96 | System.Console.WriteLine(); 97 | 98 | // Create a new TCP Syslog server 99 | TcpSyslogServer server = 100 | new TcpSyslogServer(System.Net.IPAddress.Any, port, MessageHandler.CreateInstance(123, port)); 101 | 102 | // Start the server 103 | System.Console.Write("Server starting..."); 104 | server.Start(); 105 | System.Console.WriteLine("Done!"); 106 | 107 | System.Console.WriteLine("Press Enter to stop the server or '!' to restart the server..."); 108 | 109 | // Perform text input 110 | for (; ; ) 111 | { 112 | string line = System.Console.ReadLine(); 113 | if (string.IsNullOrEmpty(line)) 114 | break; 115 | 116 | // Restart the server 117 | if (line == "!") 118 | { 119 | System.Console.Write("Server restarting..."); 120 | server.Restart(); 121 | System.Console.WriteLine("Done!"); 122 | continue; 123 | } // End if (line == "!") 124 | 125 | // Multicast admin message to all sessions 126 | line = "(admin) " + line; 127 | server.Multicast(line); 128 | } // Next 129 | 130 | // Stop the server 131 | System.Console.Write("Server stopping..."); 132 | server.Stop(); 133 | System.Console.WriteLine("Done!"); 134 | } // End Sub Test 135 | 136 | 137 | } // End Class TcpSyslogServer 138 | 139 | 140 | } // End Namespace SyslogServer 141 | -------------------------------------------------------------------------------- /SyslogServer/syslog_info.txt: -------------------------------------------------------------------------------- 1 | 2 | https://tools.ietf.org/html/rfc6587 3 | https://tools.ietf.org/html/rfc5426 4 | https://tools.ietf.org/html/rfc5424 5 | https://tools.ietf.org/html/rfc3164 6 | 7 | 8 | RFC 5425 - TLS for Syslog is not supported yet 9 | 10 | 11 | RFC 6587 Transmission of Syslog Messages over TCP 12 | RFC 5426 Transmission of Syslog Messages over UDP 13 | RFC 5424 The Syslog Protocol - Obsoletes: RFC 3164 14 | RFC 3164 - The BSD syslog Protocol - Obsoleted by RFC 5424 15 | YOU SHOULD NO LONGER FOLLOW RFC 3164 except for legacy reasons (i.e. backwards compatibility). 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | RFC5424 removed the requirement of using only UDP for log sending, 24 | but still mandates UDP be supported (for at least backwards compatibility). 25 | 26 | One option available starting with RFC 5424 is TCP. 27 | 28 | To guarantee message delivery, right? 29 | WRONG! TCP DOES NOT GUARANTEE MESSAGE DELIVERY. 30 | VERY IMPORTANT: Message receipt acknowledgment (i.e. TCP handshake) != guaranteed message delivery 31 | 32 | But one knows that the server is up and running 33 | UDP doesn't give an error ! 34 | 35 | 36 | 37 | receivedData = Encoding.ASCII.GetString(receivedBytes, 0, receivedBytes.Length); 38 | 39 | https://labs.rebex.net/syslog 40 | https://sflanders.net/2018/08/22/syslog-and-what-protocol-to-send-events-over/ 41 | https://github.com/chronoxor/NetCoreServer#example-ssl-chat-client 42 | https://stackify.com/syslog-101 43 | http://web.archive.org/web/20201205213311/https://sflanders.net/2018/08/22/syslog-and-what-protocol-to-send-events-over/ 44 | 45 | https://github.com/jchristn/WatsonSyslogServer 46 | https://github.com/emertechie/SyslogNet 47 | https://gunnarpeipman.com/aspnet-core-syslog/ 48 | https://github.com/mguinness/syslogserver 49 | https://github.com/mguinness/syslog-framework-logging 50 | 51 | https://github.com/YallaDotNet/syslog 52 | http://yalladotnet.github.io/syslog/html/7b08f132-b375-4232-94b9-6df3585c206e.htm 53 | https://github.com/ststeiger/SyslogSharp 54 | http://syslogsharp.sourceforge.net/ 55 | 56 | 57 | 58 | https://www.alibabacloud.com/help/doc-detail/135042.htm 59 | Two Syslog protocols are commonly used in the industry: 60 | RFC 5424 issued in 2009 and RFC 3164 issued in 2001. 61 | This section describes the differences between the two protocols 62 | 63 | 64 | ============================================================================= 65 | RFC 5424 66 | Syslog messages that use the RFC 5424 protocol contain the following fields. 67 | For more information, see RFC 5424 - The Syslog Protocol. 68 | PRI VERSION SP TIMESTAMP SP HOSTNAME SP APP-NAME SP PROCID SP MSGID 69 | ============================================================================= 70 | 71 | The following examples describe these fields: 72 | """ 73 | Example1: 74 | <34>1 2019-07-11T22:14:15.003Z aliyun.example.com ali - ID47 - BOM'su root' failed for lonvick on /dev/pts/8 75 | """ 76 | PRI -- 34 77 | VERSION -- 1 78 | TIMESTAMP -- 2019-07-11T22:14:15.003Z 79 | HOSTNAME -- aliyun.example.com 80 | APP-NAME -- ali 81 | PROCID -- None 82 | MSGID -- ID47 83 | MESSAGE -- 'su root' failed for lonvick on /dev/pts/8 84 | """ 85 | Example2: 86 | <165>1 2019-07-11T22:14:15.000003-07:00 192.0.2.1 myproc 8710 - - %% It's time to make the do-nuts. 87 | """ 88 | PRI -- 165 89 | VERSION -- 1 90 | TIMESTAMP -- 2019-07-11T05:14:15.000003-07:00 91 | HOSTNAME -- 192.0.2.1 92 | APP-NAME -- myproc 93 | PROCID -- 8710 94 | STRUCTURED-DATA -- "-" 95 | MSGID -- "-" 96 | MESSAGE -- "%% It's time to make the do-nuts." 97 | """ 98 | Example3: - with STRUCTURED-DATA 99 | <165>1 2019-07-11T22:14:15.003Z aliyun.example.com 100 | evntslog - ID47 [exampleSDID@32473 iut="3" eventSource= 101 | "Application" eventID="1011"] BOMAn application 102 | event log entry... 103 | """ 104 | PRI -- 165 105 | VERSION -- 1 106 | TIMESTAMP -- 2019-07-11T22:14:15.003Z 107 | HOSTNAME -- aliyun.example.com 108 | APP-NAME -- evntslog 109 | PROCID -- "-" 110 | MSGID -- ID47 111 | STRUCTURED-DATA -- [exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"] 112 | MESSAGE -- An application event log entry... 113 | 114 | 115 | ============================================================================= 116 | RFC 3164 117 | Syslog messages that use the RFC 3164 protocol contain the following fields. 118 | For more information, see RFC 3164 - The BSD Syslog Protocol. 119 | PRI HEADER[TIME HOSTNAME] MSG 120 | ============================================================================= 121 | 122 | The following example describes these fields: 123 | """ 124 | <30>Oct 9 22:33:20 hlfedora auditd[1787]: The audit daemon is exiting. 125 | """ 126 | PRI -- 30 127 | HEADER 128 | - TIME -- Oct 9 22:33:20 129 | - HOSTNAME -- hlfedora 130 | MSG 131 | - TAG -- auditd[1787] 132 | - Content --The audit daemon is exiting. 133 | -------------------------------------------------------------------------------- /SyslogServer/Common/Rfc3164SyslogMessage.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SyslogServer 3 | { 4 | 5 | public class Rfc3164SyslogMessage 6 | { 7 | public FacilityType Facility { get; set; } 8 | public SeverityType Severity { get; set; } 9 | public System.DateTime Datestamp { get; set; } 10 | public string Hostname { get; set; } 11 | public string Content { get; set; } 12 | public string RemoteIP { get; set; } 13 | public System.DateTime LocalDate { get; set; } 14 | 15 | private const string RegexExpression = @"^ 16 | (?\<\d{1,3}\>)? 17 | (? 18 | (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s 19 | [0-3][0-9]\s 20 | [0-9]{2}\:[0-9]{2}\:[0-9]{2}\s 21 | [^ ]+?\s 22 | )? 23 | (?.+) 24 | "; 25 | 26 | 27 | // Rfc3164SyslogMessage.IsRfc3164SyslogMessage 28 | public static bool IsRfc3164SyslogMessage(string syslogMessage) 29 | { 30 | System.Text.RegularExpressions.Regex _re = 31 | new System.Text.RegularExpressions.Regex( 32 | RegexExpression 33 | , System.Text.RegularExpressions.RegexOptions.IgnorePatternWhitespace 34 | | System.Text.RegularExpressions.RegexOptions.Singleline 35 | | System.Text.RegularExpressions.RegexOptions.Compiled 36 | ); 37 | 38 | System.Text.RegularExpressions.Match m = _re.Match(syslogMessage); 39 | return m.Success; 40 | } 41 | 42 | 43 | public static Rfc3164SyslogMessage Parse(string syslogMessage) 44 | { 45 | Rfc3164SyslogMessage msg = null; 46 | 47 | System.Text.RegularExpressions.Regex _re = 48 | new System.Text.RegularExpressions.Regex(RegexExpression, System.Text.RegularExpressions.RegexOptions.IgnorePatternWhitespace 49 | | System.Text.RegularExpressions.RegexOptions.Singleline 50 | | System.Text.RegularExpressions.RegexOptions.Compiled); 51 | 52 | 53 | System.Text.RegularExpressions.Match m = _re.Match(syslogMessage); 54 | if (!m.Success) 55 | return msg; 56 | 57 | msg = new Rfc3164SyslogMessage(); 58 | 59 | if (m.Groups["PRI"].Success) 60 | { 61 | string pri = m.Groups["PRI"].Value; 62 | int priority = int.Parse(pri.Substring(1, pri.Length - 2)); 63 | msg.Facility = (FacilityType)System.Math.Floor((double)priority / 8); 64 | msg.Severity = (SeverityType)(priority % 8); 65 | } 66 | else 67 | { 68 | msg.Facility = FacilityType.User; 69 | msg.Severity = SeverityType.Notice; 70 | } 71 | 72 | if (m.Groups["HDR"].Success) 73 | { 74 | string hdr = m.Groups["HDR"].Value.TrimEnd(); 75 | int idx = hdr.LastIndexOf(' '); 76 | msg.Datestamp = System.DateTime.ParseExact(hdr.Substring(0, idx), "MMM dd HH:mm:ss", null); 77 | msg.Hostname = hdr.Substring(idx + 1); 78 | } 79 | else 80 | { 81 | msg.Datestamp = System.DateTime.Now; 82 | 83 | try 84 | { 85 | // IPHostEntry he = Dns.GetHostEntry(receiveResult.RemoteEndPoint.Address); 86 | // msg.Hostname = he.HostName; 87 | } 88 | catch (System.Net.Sockets.SocketException) 89 | { 90 | // msg.Hostname = receiveResult.RemoteEndPoint.Address.ToString(); 91 | } 92 | } // End else of if (m.Groups["HDR"].Success) 93 | 94 | msg.Content = m.Groups["MSG"].Value; 95 | // msg.RemoteIP = receiveResult.RemoteEndPoint.Address.ToString(); 96 | msg.LocalDate = System.DateTime.Now; 97 | 98 | // if (MessageReceived != null) MessageReceived(msg); 99 | 100 | msg.IsValid = true; 101 | msg.RawMessage = syslogMessage; 102 | msg.MessageReceivedTime = System.DateTime.UtcNow; 103 | 104 | return msg; 105 | } // End Function ParseMessage 106 | 107 | 108 | public string RawMessage { get; private set; } 109 | public System.Exception Exception { get; private set; } 110 | public bool IsValid { get; private set; } 111 | public System.DateTime MessageReceivedTime { get; private set; } 112 | 113 | 114 | public static Rfc3164SyslogMessage Invalid(string rawMessage, System.Exception ex) 115 | { 116 | return new Rfc3164SyslogMessage 117 | { 118 | RawMessage = rawMessage, 119 | IsValid = false, 120 | MessageReceivedTime = System.DateTime.UtcNow, 121 | Exception = ex 122 | }; 123 | } 124 | 125 | 126 | } 127 | 128 | 129 | } 130 | -------------------------------------------------------------------------------- /NetCoreSyslogServer.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30804.86 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "libSyslogServer", "libSyslogServer\libSyslogServer.csproj", "{3F003BC6-96BC-46D7-9BA4-59EFA5ECA430}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SyslogServer", "SyslogServer\SyslogServer.csproj", "{D9576E71-6909-460B-963A-0F6514213E4D}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleTlsServer", "SimpleTlsServer\SimpleTlsServer.csproj", "{C6B00692-3805-4B38-B80F-374D66755C7E}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleTcpServer", "SimpleTcpServer\SimpleTcpServer.csproj", "{7425DC58-2A92-4587-855C-B6703FAA0F09}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCoreServer", "NetCoreServer\NetCoreServer.csproj", "{12B646D2-96E3-449A-8AC5-9C0E0DC833DE}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SelfSignedCertificate", "SelfSignedCertificate\SelfSignedCertificate.csproj", "{652125DB-ECD4-4693-919B-054CE32A4772}" 17 | EndProject 18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{614FEBA8-85B2-449F-A3A9-40C128AB7EAF}" 19 | ProjectSection(SolutionItems) = preProject 20 | README.md = README.md 21 | EndProjectSection 22 | EndProject 23 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SyslogParser", "SyslogParser\SyslogParser.csproj", "{45D341B4-1355-4581-B17B-16009D534888}" 24 | EndProject 25 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StreamExtended", "StreamExtended\StreamExtended.csproj", "{C8424BCB-2AF7-40A2-84CA-D6BB0A1CAC66}" 26 | EndProject 27 | Global 28 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 29 | Debug|Any CPU = Debug|Any CPU 30 | Release|Any CPU = Release|Any CPU 31 | EndGlobalSection 32 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 33 | {3F003BC6-96BC-46D7-9BA4-59EFA5ECA430}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {3F003BC6-96BC-46D7-9BA4-59EFA5ECA430}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {3F003BC6-96BC-46D7-9BA4-59EFA5ECA430}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {3F003BC6-96BC-46D7-9BA4-59EFA5ECA430}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {D9576E71-6909-460B-963A-0F6514213E4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {D9576E71-6909-460B-963A-0F6514213E4D}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {D9576E71-6909-460B-963A-0F6514213E4D}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | {D9576E71-6909-460B-963A-0F6514213E4D}.Release|Any CPU.Build.0 = Release|Any CPU 41 | {C6B00692-3805-4B38-B80F-374D66755C7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {C6B00692-3805-4B38-B80F-374D66755C7E}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {C6B00692-3805-4B38-B80F-374D66755C7E}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {C6B00692-3805-4B38-B80F-374D66755C7E}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {7425DC58-2A92-4587-855C-B6703FAA0F09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 46 | {7425DC58-2A92-4587-855C-B6703FAA0F09}.Debug|Any CPU.Build.0 = Debug|Any CPU 47 | {7425DC58-2A92-4587-855C-B6703FAA0F09}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {7425DC58-2A92-4587-855C-B6703FAA0F09}.Release|Any CPU.Build.0 = Release|Any CPU 49 | {12B646D2-96E3-449A-8AC5-9C0E0DC833DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 50 | {12B646D2-96E3-449A-8AC5-9C0E0DC833DE}.Debug|Any CPU.Build.0 = Debug|Any CPU 51 | {12B646D2-96E3-449A-8AC5-9C0E0DC833DE}.Release|Any CPU.ActiveCfg = Release|Any CPU 52 | {12B646D2-96E3-449A-8AC5-9C0E0DC833DE}.Release|Any CPU.Build.0 = Release|Any CPU 53 | {652125DB-ECD4-4693-919B-054CE32A4772}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 54 | {652125DB-ECD4-4693-919B-054CE32A4772}.Debug|Any CPU.Build.0 = Debug|Any CPU 55 | {652125DB-ECD4-4693-919B-054CE32A4772}.Release|Any CPU.ActiveCfg = Release|Any CPU 56 | {652125DB-ECD4-4693-919B-054CE32A4772}.Release|Any CPU.Build.0 = Release|Any CPU 57 | {45D341B4-1355-4581-B17B-16009D534888}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 58 | {45D341B4-1355-4581-B17B-16009D534888}.Debug|Any CPU.Build.0 = Debug|Any CPU 59 | {45D341B4-1355-4581-B17B-16009D534888}.Release|Any CPU.ActiveCfg = Release|Any CPU 60 | {45D341B4-1355-4581-B17B-16009D534888}.Release|Any CPU.Build.0 = Release|Any CPU 61 | {C8424BCB-2AF7-40A2-84CA-D6BB0A1CAC66}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 62 | {C8424BCB-2AF7-40A2-84CA-D6BB0A1CAC66}.Debug|Any CPU.Build.0 = Debug|Any CPU 63 | {C8424BCB-2AF7-40A2-84CA-D6BB0A1CAC66}.Release|Any CPU.ActiveCfg = Release|Any CPU 64 | {C8424BCB-2AF7-40A2-84CA-D6BB0A1CAC66}.Release|Any CPU.Build.0 = Release|Any CPU 65 | EndGlobalSection 66 | GlobalSection(SolutionProperties) = preSolution 67 | HideSolutionNode = FALSE 68 | EndGlobalSection 69 | GlobalSection(ExtensibilityGlobals) = postSolution 70 | SolutionGuid = {7E185415-7455-410D-8AAD-3EB9A54407AF} 71 | EndGlobalSection 72 | GlobalSection(MonoDevelopProperties) = preSolution 73 | StartupItem = WatsonSyslog\SyslogServer.csproj 74 | EndGlobalSection 75 | EndGlobal 76 | -------------------------------------------------------------------------------- /StreamExtended/Network/CopyStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace StreamExtended.Network 6 | { 7 | /// 8 | /// Copies the source stream to destination stream. 9 | /// But this let users to peek and read the copying process. 10 | /// 11 | public class CopyStream : ICustomStreamReader, IDisposable 12 | { 13 | private readonly ICustomStreamReader reader; 14 | 15 | private readonly ICustomStreamWriter writer; 16 | 17 | private readonly IBufferPool bufferPool; 18 | 19 | public int BufferSize { get; } 20 | 21 | private int bufferLength; 22 | 23 | private byte[] buffer; 24 | 25 | private bool disposed; 26 | 27 | public int Available => reader.Available; 28 | 29 | public bool DataAvailable => reader.DataAvailable; 30 | 31 | public long ReadBytes { get; private set; } 32 | 33 | public CopyStream(ICustomStreamReader reader, ICustomStreamWriter writer, IBufferPool bufferPool, int bufferSize) 34 | { 35 | this.reader = reader; 36 | this.writer = writer; 37 | BufferSize = bufferSize; 38 | buffer = bufferPool.GetBuffer(bufferSize); 39 | } 40 | 41 | public async Task FillBufferAsync(CancellationToken cancellationToken = default(CancellationToken)) 42 | { 43 | await FlushAsync(cancellationToken); 44 | return await reader.FillBufferAsync(cancellationToken); 45 | } 46 | 47 | public byte PeekByteFromBuffer(int index) 48 | { 49 | return reader.PeekByteFromBuffer(index); 50 | } 51 | 52 | public Task PeekByteAsync(int index, CancellationToken cancellationToken = default(CancellationToken)) 53 | { 54 | return reader.PeekByteAsync(index, cancellationToken); 55 | } 56 | 57 | public Task PeekBytesAsync(int index, int size, CancellationToken cancellationToken = default(CancellationToken)) 58 | { 59 | return reader.PeekBytesAsync(index, size, cancellationToken); 60 | } 61 | 62 | public void Flush() 63 | { 64 | //send out the current data from from the buffer 65 | if (bufferLength > 0) 66 | { 67 | writer.Write(buffer, 0, bufferLength); 68 | bufferLength = 0; 69 | } 70 | } 71 | 72 | public async Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken)) 73 | { 74 | //send out the current data from from the buffer 75 | if (bufferLength > 0) 76 | { 77 | await writer.WriteAsync(buffer, 0, bufferLength, cancellationToken); 78 | bufferLength = 0; 79 | } 80 | } 81 | 82 | public byte ReadByteFromBuffer() 83 | { 84 | byte b = reader.ReadByteFromBuffer(); 85 | buffer[bufferLength++] = b; 86 | ReadBytes++; 87 | return b; 88 | } 89 | 90 | public int Read(byte[] buffer, int offset, int count) 91 | { 92 | int result = reader.Read(buffer, offset, count); 93 | if (result > 0) 94 | { 95 | if (bufferLength + result > BufferSize) 96 | { 97 | Flush(); 98 | } 99 | 100 | Buffer.BlockCopy(buffer, offset, this.buffer, bufferLength, result); 101 | bufferLength += result; 102 | ReadBytes += result; 103 | Flush(); 104 | } 105 | 106 | return result; 107 | } 108 | 109 | public async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = default(CancellationToken)) 110 | { 111 | int result = await reader.ReadAsync(buffer, offset, count, cancellationToken); 112 | if (result > 0) 113 | { 114 | if (bufferLength + result > BufferSize) 115 | { 116 | await FlushAsync(cancellationToken); 117 | } 118 | 119 | Buffer.BlockCopy(buffer, offset, this.buffer, bufferLength, result); 120 | bufferLength += result; 121 | ReadBytes += result; 122 | await FlushAsync(cancellationToken); 123 | } 124 | 125 | return result; 126 | } 127 | 128 | public Task ReadLineAsync(CancellationToken cancellationToken = default(CancellationToken)) 129 | { 130 | return CustomBufferedStream.ReadLineInternalAsync(this, bufferPool, cancellationToken); 131 | } 132 | 133 | public void Dispose() 134 | { 135 | if (!disposed) 136 | { 137 | disposed = true; 138 | var b = buffer; 139 | buffer = null; 140 | bufferPool.ReturnBuffer(b); 141 | } 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /StreamExtended/Network/ServerHelloAlpnAdderStream.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.IO; 3 | using System.Threading; 4 | 5 | namespace StreamExtended.Network 6 | { 7 | public class ServerHelloAlpnAdderStream : Stream 8 | { 9 | private readonly IBufferPool bufferPool; 10 | private readonly CustomBufferedStream stream; 11 | 12 | private bool called; 13 | 14 | public ServerHelloAlpnAdderStream(CustomBufferedStream stream, IBufferPool bufferPool) 15 | { 16 | this.bufferPool = bufferPool; 17 | this.stream = stream; 18 | } 19 | 20 | public override void Flush() 21 | { 22 | stream.Flush(); 23 | } 24 | 25 | public override long Seek(long offset, SeekOrigin origin) 26 | { 27 | return stream.Seek(offset, origin); 28 | } 29 | 30 | public override void SetLength(long value) 31 | { 32 | stream.SetLength(value); 33 | } 34 | 35 | [DebuggerStepThrough] 36 | public override int Read(byte[] buffer, int offset, int count) 37 | { 38 | return stream.Read(buffer, offset, count); 39 | } 40 | 41 | public override void Write(byte[] buffer, int offset, int count) 42 | { 43 | if (called) 44 | { 45 | stream.Write(buffer, offset, count); 46 | return; 47 | } 48 | 49 | called = true; 50 | var ms = new MemoryStream(buffer, offset, count); 51 | 52 | //this can be non async, because reads from a memory stream 53 | var cts = new CancellationTokenSource(); 54 | var serverHello = SslTools.PeekServerHello(new CustomBufferedStream(ms, bufferPool, (int)ms.Length), bufferPool, cts.Token).Result; 55 | if (serverHello != null) 56 | { 57 | // 0x00 0x10: ALPN identifier 58 | // 0x00 0x0e: length of ALPN data 59 | // 0x00 0x0c: length of ALPN data again:) 60 | var dataToAdd = new byte[] 61 | { 62 | 0x0, 0x10, 0x0, 0x5, 0x0, 0x3, 63 | 2, (byte)'h', (byte)'2' 64 | }; 65 | 66 | int newByteCount = serverHello.Extensions == null ? dataToAdd.Length + 2 : dataToAdd.Length; 67 | var buffer2 = new byte[buffer.Length + newByteCount]; 68 | 69 | for (int i = 0; i < buffer.Length; i++) 70 | { 71 | buffer2[i] = buffer[i]; 72 | } 73 | 74 | //this is a hacky solution, but works 75 | int length = (buffer[offset + 3] << 8) + buffer[offset + 4]; 76 | length += newByteCount; 77 | buffer2[offset + 3] = (byte)(length >> 8); 78 | buffer2[offset + 4] = (byte)length; 79 | 80 | length = (buffer[offset + 6] << 16) + (buffer[offset + 7] << 8) + buffer[offset + 8]; 81 | length += newByteCount; 82 | buffer2[offset + 6] = (byte)(length >> 16); 83 | buffer2[offset + 7] = (byte)(length >> 8); 84 | buffer2[offset + 8] = (byte)length; 85 | 86 | int pos = offset + serverHello.EntensionsStartPosition; 87 | int endPos = offset + serverHello.ServerHelloLength; 88 | if (serverHello.Extensions != null) 89 | { 90 | // update ALPN length 91 | length = (buffer[pos] << 8) + buffer[pos + 1]; 92 | length += newByteCount; 93 | buffer2[pos] = (byte)(length >> 8); 94 | buffer2[pos + 1] = (byte)length; 95 | } 96 | else 97 | { 98 | // add ALPN length 99 | length = dataToAdd.Length; 100 | buffer2[pos] = (byte)(length >> 8); 101 | buffer2[pos + 1] = (byte)length; 102 | endPos += 2; 103 | } 104 | 105 | for (int i = 0; i < dataToAdd.Length; i++) 106 | { 107 | buffer2[endPos + i] = dataToAdd[i]; 108 | } 109 | 110 | // copy the reamining data if any 111 | for (int i = serverHello.ServerHelloLength; i < count; i++) 112 | { 113 | buffer2[offset + newByteCount + i] = buffer[offset + i]; 114 | } 115 | 116 | buffer = buffer2; 117 | count += newByteCount; 118 | } 119 | 120 | stream.Write(buffer, offset, count); 121 | } 122 | 123 | public override bool CanRead => stream.CanRead; 124 | 125 | public override bool CanSeek => stream.CanSeek; 126 | 127 | public override bool CanWrite => stream.CanWrite; 128 | 129 | public override long Length => stream.Length; 130 | 131 | public override long Position 132 | { 133 | get => stream.Position; 134 | set => stream.Position = value; 135 | } 136 | } 137 | } -------------------------------------------------------------------------------- /StreamExtended/Network/ClientHelloAlpnAdderStream.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.IO; 3 | using System.Threading; 4 | 5 | namespace StreamExtended.Network 6 | { 7 | public class ClientHelloAlpnAdderStream : Stream 8 | { 9 | private readonly CustomBufferedStream stream; 10 | private readonly IBufferPool bufferPool; 11 | 12 | private bool called; 13 | 14 | public ClientHelloAlpnAdderStream(CustomBufferedStream stream, IBufferPool bufferPool) 15 | { 16 | this.stream = stream; 17 | } 18 | 19 | public override void Flush() 20 | { 21 | stream.Flush(); 22 | } 23 | 24 | public override long Seek(long offset, SeekOrigin origin) 25 | { 26 | return stream.Seek(offset, origin); 27 | } 28 | 29 | public override void SetLength(long value) 30 | { 31 | stream.SetLength(value); 32 | } 33 | 34 | [DebuggerStepThrough] 35 | public override int Read(byte[] buffer, int offset, int count) 36 | { 37 | return stream.Read(buffer, offset, count); 38 | } 39 | 40 | public override void Write(byte[] buffer, int offset, int count) 41 | { 42 | if (called) 43 | { 44 | stream.Write(buffer, offset, count); 45 | return; 46 | } 47 | 48 | called = true; 49 | var ms = new MemoryStream(buffer, offset, count); 50 | 51 | //this can be non async, because reads from a memory stream 52 | var cts = new CancellationTokenSource(); 53 | var clientHello = SslTools.PeekClientHello(new CustomBufferedStream(ms, bufferPool, (int)ms.Length), bufferPool, cts.Token).Result; 54 | if (clientHello != null) 55 | { 56 | // 0x00 0x10: ALPN identifier 57 | // 0x00 0x0e: length of ALPN data 58 | // 0x00 0x0c: length of ALPN data again:) 59 | var dataToAdd = new byte[] 60 | { 61 | 0x0, 0x10, 0x0, 0xE, 0x0, 0xC, 62 | 2, (byte)'h', (byte)'2', 63 | 8, (byte)'h', (byte)'t', (byte)'t', (byte)'p', (byte)'/', (byte)'1', (byte)'.', (byte)'1' 64 | }; 65 | 66 | int newByteCount = clientHello.Extensions == null ? dataToAdd.Length + 2 : dataToAdd.Length; 67 | var buffer2 = new byte[buffer.Length + newByteCount]; 68 | 69 | for (int i = 0; i < buffer.Length; i++) 70 | { 71 | buffer2[i] = buffer[i]; 72 | } 73 | 74 | //this is a hacky solution, but works 75 | int length = (buffer[offset + 3] << 8) + buffer[offset + 4]; 76 | length += newByteCount; 77 | buffer2[offset + 3] = (byte)(length >> 8); 78 | buffer2[offset + 4] = (byte)length; 79 | 80 | length = (buffer[offset + 6] << 16) + (buffer[offset + 7] << 8) + buffer[offset + 8]; 81 | length += newByteCount; 82 | buffer2[offset + 6] = (byte)(length >> 16); 83 | buffer2[offset + 7] = (byte)(length >> 8); 84 | buffer2[offset + 8] = (byte)length; 85 | 86 | int pos = offset + clientHello.EntensionsStartPosition; 87 | int endPos = offset + clientHello.ClientHelloLength; 88 | if (clientHello.Extensions != null) 89 | { 90 | // update ALPN length 91 | length = (buffer[pos] << 8) + buffer[pos + 1]; 92 | length += newByteCount; 93 | buffer2[pos] = (byte)(length >> 8); 94 | buffer2[pos + 1] = (byte)length; 95 | } 96 | else 97 | { 98 | // add ALPN length 99 | length = dataToAdd.Length; 100 | buffer2[pos] = (byte)(length >> 8); 101 | buffer2[pos + 1] = (byte)length; 102 | endPos += 2; 103 | } 104 | 105 | for (int i = 0; i < dataToAdd.Length; i++) 106 | { 107 | buffer2[endPos + i] = dataToAdd[i]; 108 | } 109 | 110 | // copy the reamining data if any 111 | for (int i = clientHello.ClientHelloLength; i < count; i++) 112 | { 113 | buffer2[offset + newByteCount + i] = buffer[offset + i]; 114 | } 115 | 116 | buffer = buffer2; 117 | count += newByteCount; 118 | } 119 | 120 | stream.Write(buffer, offset, count); 121 | } 122 | 123 | public override bool CanRead => stream.CanRead; 124 | 125 | public override bool CanSeek => stream.CanSeek; 126 | 127 | public override bool CanWrite => stream.CanWrite; 128 | 129 | public override long Length => stream.Length; 130 | 131 | public override long Position 132 | { 133 | get => stream.Position; 134 | set => stream.Position = value; 135 | } 136 | } 137 | } -------------------------------------------------------------------------------- /libSyslogServer/Classes/Common.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace libSyslogServer 3 | { 4 | 5 | 6 | /// 7 | /// Commonly used static methods. 8 | /// 9 | public static class Common 10 | { 11 | 12 | 13 | public static string SerializeJson(object obj) 14 | { 15 | if (obj == null) return null; 16 | string json = Newtonsoft.Json.JsonConvert.SerializeObject( 17 | obj, 18 | Newtonsoft.Json.Formatting.Indented, 19 | new Newtonsoft.Json.JsonSerializerSettings 20 | { 21 | DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc 22 | }); 23 | 24 | return json; 25 | } 26 | 27 | public static T DeserializeJson(string json) 28 | { 29 | if (string.IsNullOrEmpty(json)) throw new System.ArgumentNullException(nameof(json)); 30 | 31 | try 32 | { 33 | return Newtonsoft.Json.JsonConvert.DeserializeObject(json); 34 | } 35 | catch (System.Exception) 36 | { 37 | System.Console.WriteLine(""); 38 | System.Console.WriteLine("Exception while deserializing:"); 39 | System.Console.WriteLine(json); 40 | System.Console.WriteLine(""); 41 | throw; 42 | } 43 | } 44 | 45 | public static T DeserializeJson(byte[] data) 46 | { 47 | if (data == null || data.Length < 1) throw new System.ArgumentNullException(nameof(data)); 48 | return DeserializeJson(System.Text.Encoding.UTF8.GetString(data)); 49 | } 50 | 51 | public static bool InputBoolean(string question, bool yesDefault) 52 | { 53 | System.Console.Write(question); 54 | 55 | if (yesDefault) System.Console.Write(" [Y/n]? "); 56 | else System.Console.Write(" [y/N]? "); 57 | 58 | string userInput = System.Console.ReadLine(); 59 | 60 | if (string.IsNullOrEmpty(userInput)) 61 | { 62 | if (yesDefault) return true; 63 | return false; 64 | } 65 | 66 | userInput = userInput.ToLower(); 67 | 68 | if (yesDefault) 69 | { 70 | if ( 71 | (System.String.Compare(userInput, "n") == 0) 72 | || (System.String.Compare(userInput, "no") == 0) 73 | ) 74 | { 75 | return false; 76 | } 77 | 78 | return true; 79 | } 80 | else 81 | { 82 | if ( 83 | (System.String.Compare(userInput, "y") == 0) 84 | || (System.String.Compare(userInput, "yes") == 0) 85 | ) 86 | { 87 | return true; 88 | } 89 | 90 | return false; 91 | } 92 | } 93 | 94 | public static string InputString(string question, string defaultAnswer, bool allowNull) 95 | { 96 | while (true) 97 | { 98 | System.Console.Write(question); 99 | 100 | if (!string.IsNullOrEmpty(defaultAnswer)) 101 | { 102 | System.Console.Write(" [" + defaultAnswer + "]"); 103 | } 104 | 105 | System.Console.Write(" "); 106 | 107 | string userInput = System.Console.ReadLine(); 108 | 109 | if (string.IsNullOrEmpty(userInput)) 110 | { 111 | if (!string.IsNullOrEmpty(defaultAnswer)) return defaultAnswer; 112 | if (allowNull) return null; 113 | else continue; 114 | } 115 | 116 | return userInput; 117 | } 118 | } 119 | 120 | public static int InputInteger(string question, int defaultAnswer, bool positiveOnly, bool allowZero) 121 | { 122 | while (true) 123 | { 124 | System.Console.Write(question); 125 | System.Console.Write(" [" + defaultAnswer + "] "); 126 | 127 | string userInput = System.Console.ReadLine(); 128 | 129 | if (string.IsNullOrEmpty(userInput)) 130 | { 131 | return defaultAnswer; 132 | } 133 | 134 | int ret = 0; 135 | if (!int.TryParse(userInput, out ret)) 136 | { 137 | System.Console.WriteLine("Please enter a valid integer."); 138 | continue; 139 | } 140 | 141 | if (ret == 0) 142 | { 143 | if (allowZero) 144 | { 145 | return 0; 146 | } 147 | } 148 | 149 | if (ret < 0) 150 | { 151 | if (positiveOnly) 152 | { 153 | System.Console.WriteLine("Please enter a value greater than zero."); 154 | continue; 155 | } 156 | } 157 | 158 | return ret; 159 | } 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /StreamExtended/Network/CustomBufferedPeekStream.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace StreamExtended.Network 5 | { 6 | internal class CustomBufferedPeekStream : ICustomStreamReader 7 | { 8 | private readonly IBufferPool bufferPool; 9 | private readonly ICustomStreamReader baseStream; 10 | 11 | internal int Position { get; private set; } 12 | 13 | internal CustomBufferedPeekStream(ICustomStreamReader baseStream, IBufferPool bufferPool, int startPosition = 0) 14 | { 15 | this.bufferPool = bufferPool; 16 | this.baseStream = baseStream; 17 | Position = startPosition; 18 | } 19 | 20 | int ICustomStreamReader.BufferSize => baseStream.BufferSize; 21 | 22 | /// 23 | /// Gets a value indicating whether data is available. 24 | /// 25 | bool ICustomStreamReader.DataAvailable => Available > 0; 26 | 27 | /// 28 | /// Gets the available data size. 29 | /// 30 | public int Available => baseStream.Available - Position; 31 | 32 | internal async Task EnsureBufferLength(int length, CancellationToken cancellationToken) 33 | { 34 | var val = await baseStream.PeekByteAsync(Position + length - 1, cancellationToken); 35 | return val != -1; 36 | } 37 | 38 | internal byte ReadByte() 39 | { 40 | return baseStream.PeekByteFromBuffer(Position++); 41 | } 42 | 43 | internal int ReadInt16() 44 | { 45 | int i1 = ReadByte(); 46 | int i2 = ReadByte(); 47 | return (i1 << 8) + i2; 48 | } 49 | 50 | internal int ReadInt24() 51 | { 52 | int i1 = ReadByte(); 53 | int i2 = ReadByte(); 54 | int i3 = ReadByte(); 55 | return (i1 << 16) + (i2 << 8) + i3; 56 | } 57 | 58 | internal byte[] ReadBytes(int length) 59 | { 60 | var buffer = new byte[length]; 61 | for (int i = 0; i < buffer.Length; i++) 62 | { 63 | buffer[i] = ReadByte(); 64 | } 65 | 66 | return buffer; 67 | } 68 | 69 | /// 70 | /// Fills the buffer asynchronous. 71 | /// 72 | /// 73 | Task ICustomStreamReader.FillBufferAsync(CancellationToken cancellationToken) 74 | { 75 | return baseStream.FillBufferAsync(cancellationToken); 76 | } 77 | 78 | /// 79 | /// Peeks a byte from buffer. 80 | /// 81 | /// The index. 82 | /// 83 | byte ICustomStreamReader.PeekByteFromBuffer(int index) 84 | { 85 | return baseStream.PeekByteFromBuffer(index); 86 | } 87 | 88 | /// 89 | /// Peeks bytes asynchronous. 90 | /// 91 | /// The index. 92 | /// The cancellation token. 93 | /// 94 | Task ICustomStreamReader.PeekBytesAsync(int index, int size, CancellationToken cancellationToken) 95 | { 96 | return baseStream.PeekBytesAsync(index, size, cancellationToken); 97 | } 98 | 99 | /// 100 | /// Peeks a byte asynchronous. 101 | /// 102 | /// The index. 103 | /// The cancellation token. 104 | /// 105 | Task ICustomStreamReader.PeekByteAsync(int index, CancellationToken cancellationToken) 106 | { 107 | return baseStream.PeekByteAsync(index, cancellationToken); 108 | } 109 | 110 | /// 111 | /// Reads a byte from buffer. 112 | /// 113 | /// 114 | /// Buffer is empty 115 | byte ICustomStreamReader.ReadByteFromBuffer() 116 | { 117 | return ReadByte(); 118 | } 119 | 120 | int ICustomStreamReader.Read(byte[] buffer, int offset, int count) 121 | { 122 | return baseStream.Read(buffer, offset, count); 123 | } 124 | 125 | /// 126 | /// Reads the asynchronous. 127 | /// 128 | /// The buffer. 129 | /// The offset. 130 | /// The count. 131 | /// The cancellation token. 132 | /// 133 | Task ICustomStreamReader.ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) 134 | { 135 | return baseStream.ReadAsync(buffer, offset, count, cancellationToken); 136 | } 137 | 138 | /// 139 | /// Read a line from the byte stream 140 | /// 141 | /// 142 | /// 143 | Task ICustomStreamReader.ReadLineAsync(CancellationToken cancellationToken) 144 | { 145 | return CustomBufferedStream.ReadLineInternalAsync(this, bufferPool, cancellationToken); 146 | } 147 | 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc -------------------------------------------------------------------------------- /NetCoreServer/WsServer.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Text; 3 | 4 | namespace NetCoreServer 5 | { 6 | /// 7 | /// WebSocket server 8 | /// 9 | /// WebSocket server is used to communicate with clients using WebSocket protocol. Thread-safe. 10 | public class WsServer : HttpServer, IWebSocket 11 | { 12 | internal readonly WebSocket WebSocket; 13 | 14 | /// 15 | /// Initialize WebSocket server with a given IP address and port number 16 | /// 17 | /// IP address 18 | /// Port number 19 | public WsServer(IPAddress address, int port) : base(address, port) { WebSocket = new WebSocket(this); } 20 | /// 21 | /// Initialize WebSocket server with a given IP address and port number 22 | /// 23 | /// IP address 24 | /// Port number 25 | public WsServer(string address, int port) : base(address, port) { WebSocket = new WebSocket(this); } 26 | /// 27 | /// Initialize WebSocket server with a given IP endpoint 28 | /// 29 | /// IP endpoint 30 | public WsServer(IPEndPoint endpoint) : base(endpoint) { WebSocket = new WebSocket(this); } 31 | 32 | public virtual bool CloseAll(int status) 33 | { 34 | lock (WebSocket.WsSendLock) 35 | { 36 | WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_CLOSE, false, null, 0, 0, status); 37 | if (!Multicast(WebSocket.WsSendBuffer.ToArray())) 38 | return false; 39 | 40 | return base.DisconnectAll(); 41 | } 42 | } 43 | 44 | public override bool Multicast(byte[] buffer, long offset, long size) 45 | { 46 | if (!IsStarted) 47 | return false; 48 | 49 | if (size == 0) 50 | return true; 51 | 52 | // Multicast data to all WebSocket sessions 53 | foreach (var session in Sessions.Values) 54 | { 55 | if (session is WsSession wsSession) 56 | { 57 | if (wsSession.WebSocket.WsHandshaked) 58 | wsSession.SendAsync(buffer, offset, size); 59 | } 60 | } 61 | 62 | return true; 63 | } 64 | 65 | #region WebSocket multicast text methods 66 | 67 | public bool MulticastText(byte[] buffer, long offset, long size) 68 | { 69 | lock (WebSocket.WsSendLock) 70 | { 71 | WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_TEXT, false, buffer, offset, size); 72 | return Multicast(WebSocket.WsSendBuffer.ToArray()); 73 | } 74 | } 75 | 76 | public bool MulticastText(string text) 77 | { 78 | lock (WebSocket.WsSendLock) 79 | { 80 | var data = Encoding.UTF8.GetBytes(text); 81 | WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_TEXT, false, data, 0, data.Length); 82 | return Multicast(WebSocket.WsSendBuffer.ToArray()); 83 | } 84 | } 85 | 86 | #endregion 87 | 88 | #region WebSocket multicast binary methods 89 | 90 | public bool MulticastBinary(byte[] buffer, long offset, long size) 91 | { 92 | lock (WebSocket.WsSendLock) 93 | { 94 | WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_BINARY, false, buffer, offset, size); 95 | return Multicast(WebSocket.WsSendBuffer.ToArray()); 96 | } 97 | } 98 | 99 | public bool MulticastBinary(string text) 100 | { 101 | lock (WebSocket.WsSendLock) 102 | { 103 | var data = Encoding.UTF8.GetBytes(text); 104 | WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_BINARY, false, data, 0, data.Length); 105 | return Multicast(WebSocket.WsSendBuffer.ToArray()); 106 | } 107 | } 108 | 109 | #endregion 110 | 111 | #region WebSocket multicast ping methods 112 | 113 | public bool SendPing(byte[] buffer, long offset, long size) 114 | { 115 | lock (WebSocket.WsSendLock) 116 | { 117 | WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_PING, false, buffer, offset, size); 118 | return Multicast(WebSocket.WsSendBuffer.ToArray()); 119 | } 120 | } 121 | 122 | public bool SendPing(string text) 123 | { 124 | lock (WebSocket.WsSendLock) 125 | { 126 | var data = Encoding.UTF8.GetBytes(text); 127 | WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_PING, false, data, 0, data.Length); 128 | return Multicast(WebSocket.WsSendBuffer.ToArray()); 129 | } 130 | } 131 | 132 | #endregion 133 | 134 | #region WebSocket multicast pong methods 135 | 136 | public bool SendPong(byte[] buffer, long offset, long size) 137 | { 138 | lock (WebSocket.WsSendLock) 139 | { 140 | WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_PONG, false, buffer, offset, size); 141 | return Multicast(WebSocket.WsSendBuffer.ToArray()); 142 | } 143 | } 144 | 145 | public bool SendPong(string text) 146 | { 147 | lock (WebSocket.WsSendLock) 148 | { 149 | var data = Encoding.UTF8.GetBytes(text); 150 | WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_PONG, false, data, 0, data.Length); 151 | return Multicast(WebSocket.WsSendBuffer.ToArray()); 152 | } 153 | } 154 | 155 | #endregion 156 | 157 | protected override TcpSession CreateSession() { return new WsSession(this); } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /SelfSignedCertificate/Helpers/NonBackdooredPrng.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SelfSignedCertificate 3 | { 4 | 5 | 6 | public abstract class NonBackdooredPrng : Org.BouncyCastle.Crypto.Prng.IRandomGenerator 7 | { 8 | public abstract void AddSeedMaterial(byte[] seed); 9 | public abstract void AddSeedMaterial(long seed); 10 | public abstract void NextBytes(byte[] bytes); 11 | public abstract void NextBytes(byte[] bytes, int start, int len); 12 | 13 | 14 | public static NonBackdooredPrng Create() 15 | { 16 | bool isWindows = 17 | System.Runtime.InteropServices.RuntimeInformation 18 | .IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows); 19 | 20 | if (isWindows) 21 | return new WindowsPrng(); 22 | 23 | return new PosixPrng(); 24 | } // End Function Create 25 | 26 | 27 | public static Org.BouncyCastle.Security.SecureRandom SecureRandom 28 | { 29 | get 30 | { 31 | return new Org.BouncyCastle.Security.SecureRandom( 32 | NonBackdooredPrng.Create() 33 | ); 34 | } 35 | } 36 | 37 | 38 | } // End Class NonBackdooredPrng 39 | 40 | 41 | 42 | public class WindowsPrng : NonBackdooredPrng 43 | { 44 | protected Org.BouncyCastle.Crypto.Prng.IRandomGenerator m_rnd; 45 | 46 | public WindowsPrng() 47 | { 48 | // this.m_rnd = new Org.BouncyCastle.Crypto.Prng.CryptoApiRandomGenerator(); 49 | 50 | const string digestName = "SHA256"; 51 | Org.BouncyCastle.Crypto.IDigest digest = Org.BouncyCastle.Security.DigestUtilities.GetDigest(digestName); 52 | if (digest == null) 53 | return; 54 | 55 | Org.BouncyCastle.Crypto.Prng.DigestRandomGenerator prng = 56 | new Org.BouncyCastle.Crypto.Prng.DigestRandomGenerator(digest); 57 | 58 | const bool autoSeed = true; 59 | if (autoSeed) 60 | { 61 | // prng.AddSeedMaterial(NextCounterValue()); 62 | // prng.AddSeedMaterial(GetNextBytes(Master, digest.GetDigestSize())); 63 | } 64 | 65 | this.m_rnd = prng; 66 | } 67 | 68 | 69 | /// Add more seed material to the generator. 70 | /// A byte array to be mixed into the generator's state. 71 | public override void AddSeedMaterial(byte[] seed) 72 | { 73 | this.m_rnd.AddSeedMaterial(seed); 74 | } // End Sub AddSeedMaterial 75 | 76 | 77 | /// Add more seed material to the generator. 78 | /// A long value to be mixed into the generator's state. 79 | public override void AddSeedMaterial(long seed) 80 | { 81 | this.m_rnd.AddSeedMaterial(seed); 82 | } // End Sub AddSeedMaterial 83 | 84 | 85 | /// Fill byte array with random values. 86 | /// Array to be filled. 87 | public override void NextBytes(byte[] bytes) 88 | { 89 | this.m_rnd.NextBytes(bytes); 90 | } // End Sub NextBytes 91 | 92 | 93 | /// Fill byte array with random values. 94 | /// Array to receive bytes. 95 | /// Index to start filling at. 96 | /// Length of segment to fill. 97 | public override void NextBytes(byte[] bytes, int start, int len) 98 | { 99 | this.m_rnd.NextBytes(bytes, start, len); 100 | } // End Sub NextBytes 101 | 102 | } // End Class WindowsPrng 103 | 104 | 105 | public class PosixPrng : NonBackdooredPrng 106 | { 107 | // Early boot on a very low entropy device. 108 | // The /dev/urandom device cannot guarantee 109 | // that it has received enough initial entropy, 110 | // while when using /dev/random that is guaranteed 111 | // (even if it may block). 112 | // therefore, use /dev/urandom 113 | 114 | // /dev/random // potentially blocking 115 | // /dev/urandom 116 | 117 | 118 | /// Add more seed material to the generator. 119 | /// A byte array to be mixed into the generator's state. 120 | public override void AddSeedMaterial(byte[] seed) 121 | { 122 | // throw new System.NotImplementedException(); 123 | } // End Sub AddSeedMaterial 124 | 125 | 126 | /// Add more seed material to the generator. 127 | /// A long value to be mixed into the generator's state. 128 | public override void AddSeedMaterial(long seed) 129 | { 130 | // throw new System.NotImplementedException(); 131 | } // End Sub AddSeedMaterial 132 | 133 | 134 | /// Fill byte array with random values. 135 | /// Array to be filled. 136 | public override void NextBytes(byte[] bytes) 137 | { 138 | using (System.IO.FileStream fs = 139 | new System.IO.FileStream( 140 | "/dev/urandom" 141 | , System.IO.FileMode.Open 142 | , System.IO.FileAccess.Read)) 143 | { 144 | fs.Read(bytes, 0, bytes.Length); 145 | } 146 | 147 | } // End Sub NextBytes 148 | 149 | 150 | /// Fill byte array with random values. 151 | /// Array to receive bytes. 152 | /// Index to start filling at. 153 | /// Length of segment to fill. 154 | public override void NextBytes(byte[] bytes, int start, int len) 155 | { 156 | using (System.IO.FileStream fs = 157 | new System.IO.FileStream( 158 | "/dev/urandom" 159 | , System.IO.FileMode.Open 160 | , System.IO.FileAccess.Read)) 161 | { 162 | fs.Read(bytes, start, len); 163 | } 164 | 165 | } // End Sub NextBytes 166 | 167 | 168 | } // End Class LinuxPrng 169 | 170 | 171 | } // End Namespace CoreCMS.JWT 172 | -------------------------------------------------------------------------------- /NetCoreServer/WssServer.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Text; 3 | 4 | namespace NetCoreServer 5 | { 6 | /// 7 | /// WebSocket secure server 8 | /// 9 | /// WebSocket secure server is used to communicate with clients using WebSocket protocol. Thread-safe. 10 | public class WssServer : HttpsServer, IWebSocket 11 | { 12 | internal readonly WebSocket WebSocket; 13 | 14 | /// 15 | /// Initialize WebSocket server with a given IP address and port number 16 | /// 17 | /// SSL context 18 | /// IP address 19 | /// Port number 20 | public WssServer(SslContext context, IPAddress address, int port) : base(context, address, port) { WebSocket = new WebSocket(this); } 21 | /// 22 | /// Initialize WebSocket server with a given IP address and port number 23 | /// 24 | /// SSL context 25 | /// IP address 26 | /// Port number 27 | public WssServer(SslContext context, string address, int port) : base(context, address, port) { WebSocket = new WebSocket(this); } 28 | /// 29 | /// Initialize WebSocket server with a given IP endpoint 30 | /// 31 | /// SSL context 32 | /// IP endpoint 33 | public WssServer(SslContext context, IPEndPoint endpoint) : base(context, endpoint) { WebSocket = new WebSocket(this); } 34 | 35 | public virtual bool CloseAll(int status) 36 | { 37 | lock (WebSocket.WsSendLock) 38 | { 39 | WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_CLOSE, false, null, 0, 0, status); 40 | if (!Multicast(WebSocket.WsSendBuffer.ToArray())) 41 | return false; 42 | 43 | return base.DisconnectAll(); 44 | } 45 | } 46 | 47 | public override bool Multicast(byte[] buffer, long offset, long size) 48 | { 49 | if (!IsStarted) 50 | return false; 51 | 52 | if (size == 0) 53 | return true; 54 | 55 | // Multicast data to all WebSocket sessions 56 | foreach (var session in Sessions.Values) 57 | { 58 | if (session is WssSession wssSession) 59 | { 60 | if (wssSession.WebSocket.WsHandshaked) 61 | wssSession.SendAsync(buffer, offset, size); 62 | } 63 | } 64 | 65 | return true; 66 | } 67 | 68 | #region WebSocket multicast text methods 69 | 70 | public bool MulticastText(byte[] buffer, long offset, long size) 71 | { 72 | lock (WebSocket.WsSendLock) 73 | { 74 | WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_TEXT, false, buffer, offset, size); 75 | return Multicast(WebSocket.WsSendBuffer.ToArray()); 76 | } 77 | } 78 | 79 | public bool MulticastText(string text) 80 | { 81 | lock (WebSocket.WsSendLock) 82 | { 83 | var data = Encoding.UTF8.GetBytes(text); 84 | WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_TEXT, false, data, 0, data.Length); 85 | return Multicast(WebSocket.WsSendBuffer.ToArray()); 86 | } 87 | } 88 | 89 | #endregion 90 | 91 | #region WebSocket multicast binary methods 92 | 93 | public bool MulticastBinary(byte[] buffer, long offset, long size) 94 | { 95 | lock (WebSocket.WsSendLock) 96 | { 97 | WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_BINARY, false, buffer, offset, size); 98 | return Multicast(WebSocket.WsSendBuffer.ToArray()); 99 | } 100 | } 101 | 102 | public bool MulticastBinary(string text) 103 | { 104 | lock (WebSocket.WsSendLock) 105 | { 106 | var data = Encoding.UTF8.GetBytes(text); 107 | WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_BINARY, false, data, 0, data.Length); 108 | return Multicast(WebSocket.WsSendBuffer.ToArray()); 109 | } 110 | } 111 | 112 | #endregion 113 | 114 | #region WebSocket multicast ping methods 115 | 116 | public bool SendPing(byte[] buffer, long offset, long size) 117 | { 118 | lock (WebSocket.WsSendLock) 119 | { 120 | WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_PING, false, buffer, offset, size); 121 | return Multicast(WebSocket.WsSendBuffer.ToArray()); 122 | } 123 | } 124 | 125 | public bool SendPing(string text) 126 | { 127 | lock (WebSocket.WsSendLock) 128 | { 129 | var data = Encoding.UTF8.GetBytes(text); 130 | WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_PING, false, data, 0, data.Length); 131 | return Multicast(WebSocket.WsSendBuffer.ToArray()); 132 | } 133 | } 134 | 135 | #endregion 136 | 137 | #region WebSocket multicast pong methods 138 | 139 | public bool SendPong(byte[] buffer, long offset, long size) 140 | { 141 | lock (WebSocket.WsSendLock) 142 | { 143 | WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_PONG, false, buffer, offset, size); 144 | return Multicast(WebSocket.WsSendBuffer.ToArray()); 145 | } 146 | } 147 | 148 | public bool SendPong(string text) 149 | { 150 | lock (WebSocket.WsSendLock) 151 | { 152 | var data = Encoding.UTF8.GetBytes(text); 153 | WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_PONG, false, data, 0, data.Length); 154 | return Multicast(WebSocket.WsSendBuffer.ToArray()); 155 | } 156 | } 157 | 158 | #endregion 159 | 160 | protected override SslSession CreateSession() { return new WssSession(this); } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /SyslogServer/Common/Rfc5424SyslogMessage.cs: -------------------------------------------------------------------------------- 1 | 2 | using System.Text.RegularExpressions; 3 | 4 | 5 | namespace SyslogServer 6 | { 7 | 8 | 9 | // https://stackoverflow.com/a/53617099/155077 10 | public class Rfc5424SyslogMessage 11 | { 12 | private static readonly string _SyslogMsgHeaderPattern = @"\<(?\d{1,3})\>(?[1-9]{0,2}) (?(\S|\w)+) (?-|(\S|\w){1,255}) (?-|(\S|\w){1,48}) (?-|(\S|\w){1,128}) (?-|(\S|\w){1,32})"; 13 | private static readonly string _SyslogMsgStructuredDataPattern = @"(?-|\[[^\[\=\x22\]\x20]{1,32}( ([^\[\=\x22\]\x20]{1,32}=\x22.+\x22))?\])"; 14 | private static readonly string _SyslogMsgMessagePattern = @"( (?.+))?"; 15 | private static Regex _Expression = new Regex($@"^{_SyslogMsgHeaderPattern} {_SyslogMsgStructuredDataPattern}{_SyslogMsgMessagePattern}$" 16 | , RegexOptions.None 17 | , new System.TimeSpan(0, 0, 5) 18 | ); 19 | 20 | 21 | // Rfc5424SyslogMessage.IsRfc5424SyslogMessage(message); 22 | public static bool IsRfc5424SyslogMessage(string syslogMessage) 23 | { 24 | Match match = _Expression.Match(syslogMessage); 25 | return match.Success; 26 | } 27 | 28 | 29 | public FacilityType Facility 30 | { 31 | get 32 | { 33 | return (FacilityType)System.Math.Floor((double)this.Prival / 8); 34 | // return FacilityType.User; // wenn nix 35 | } 36 | } 37 | 38 | public SeverityType Severity 39 | { 40 | get 41 | { 42 | return (SeverityType)(this.Prival % 8); 43 | // return SeverityType.Notice; // wenn nix 44 | } 45 | } 46 | 47 | 48 | public bool IsValid { get; private set; } 49 | 50 | public int Prival { get; private set; } 51 | public int Version { get; private set; } 52 | public System.DateTime TimeStamp { get; private set; } 53 | public string HostName { get; private set; } 54 | public string AppName { get; private set; } 55 | public string ProcId { get; private set; } 56 | public string MessageId { get; private set; } 57 | public string StructuredData { get; private set; } 58 | public string Message { get; private set; } 59 | public string RawMessage { get; private set; } 60 | public System.Exception Exception { get; private set; } 61 | public System.Exception EndpointException { get; private set; } 62 | 63 | public System.Guid PrimaryKey { get; private set; } 64 | public string SourceEndpoint { get; private set; } 65 | public string SourceIP { get; private set; } 66 | public string SourceHost { get; private set; } 67 | public System.DateTime MessageReceivedTime { get; private set; } 68 | 69 | 70 | public static Rfc5424SyslogMessage Invalid(string rawMessage, System.Exception ex) 71 | { 72 | return new Rfc5424SyslogMessage 73 | { 74 | RawMessage = rawMessage, 75 | IsValid = false, 76 | MessageReceivedTime = System.DateTime.UtcNow, 77 | Exception = ex 78 | }; 79 | } 80 | 81 | 82 | 83 | public void SetSourceEndpoint(System.Net.EndPoint remoteEndpoint) 84 | { 85 | try 86 | { 87 | this.PrimaryKey = System.Guid.NewGuid(); 88 | this.SourceEndpoint = remoteEndpoint.ToString(); 89 | System.Net.IPEndPoint ipEndPoint = remoteEndpoint as System.Net.IPEndPoint; 90 | this.SourceIP = ipEndPoint.Address.ToString(); 91 | this.SourceHost = System.Net.Dns.GetHostEntry(ipEndPoint.Address).HostName; 92 | } 93 | catch (System.Exception ex) 94 | { 95 | this.EndpointException = ex; 96 | } 97 | } 98 | 99 | public static Rfc5424SyslogMessage Invalid(string rawMessage) 100 | { 101 | return Invalid(rawMessage, null); 102 | } 103 | 104 | 105 | 106 | /// 107 | /// Parses a Syslog message in RFC 5424 format. 108 | /// 109 | /// 110 | /// 111 | /// 112 | /// 113 | public static Rfc5424SyslogMessage Parse(string rawMessage) 114 | { 115 | if (string.IsNullOrWhiteSpace(rawMessage)) 116 | { 117 | throw new System.ArgumentNullException("message"); 118 | } 119 | 120 | Match match = _Expression.Match(rawMessage); 121 | if (match.Success) 122 | { 123 | return new Rfc5424SyslogMessage 124 | { 125 | MessageReceivedTime = System.DateTime.UtcNow, 126 | Prival = System.Convert.ToInt32(match.Groups["PRIVAL"].Value), 127 | Version = System.Convert.ToInt32(match.Groups["VERSION"].Value), 128 | TimeStamp = System.Convert.ToDateTime(match.Groups["TIMESTAMP"].Value), 129 | HostName = match.Groups["HOSTNAME"].Value, 130 | AppName = match.Groups["APPNAME"].Value, 131 | ProcId = match.Groups["PROCID"].Value, 132 | MessageId = match.Groups["MSGID"].Value, 133 | StructuredData = match.Groups["STRUCTUREDDATA"].Value, 134 | Message = match.Groups["MESSAGE"].Value, 135 | RawMessage = rawMessage, 136 | IsValid = true 137 | }; 138 | } 139 | else 140 | { 141 | return Invalid(rawMessage); 142 | } 143 | } 144 | 145 | 146 | public override string ToString() 147 | { 148 | System.Text.StringBuilder message = 149 | new System.Text.StringBuilder($@"<{Prival:###}>{Version:##} {TimeStamp.ToString("yyyy-MM-ddTHH:mm:ss.fffK")} {HostName} {AppName} {ProcId} {MessageId} {StructuredData}"); 150 | 151 | if (!string.IsNullOrWhiteSpace(Message)) 152 | { 153 | message.Append($" {Message}"); 154 | } 155 | 156 | return message.ToString(); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /NetCoreServer/Buffer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Text; 4 | 5 | namespace NetCoreServer 6 | { 7 | /// 8 | /// Dynamic byte buffer 9 | /// 10 | public class Buffer 11 | { 12 | private byte[] _data; 13 | private long _size; 14 | private long _offset; 15 | 16 | /// 17 | /// Is the buffer empty? 18 | /// 19 | public bool IsEmpty => (_data == null) || (_size == 0); 20 | /// 21 | /// Bytes memory buffer 22 | /// 23 | public byte[] Data => _data; 24 | /// 25 | /// Bytes memory buffer capacity 26 | /// 27 | public long Capacity => _data.Length; 28 | /// 29 | /// Bytes memory buffer size 30 | /// 31 | public long Size => _size; 32 | /// 33 | /// Bytes memory buffer offset 34 | /// 35 | public long Offset => _offset; 36 | 37 | /// 38 | /// Buffer indexer operator 39 | /// 40 | public byte this[int index] => _data[index]; 41 | 42 | /// 43 | /// Initialize a new expandable buffer with zero capacity 44 | /// 45 | public Buffer() { _data = new byte[0]; _size = 0; _offset = 0; } 46 | /// 47 | /// Initialize a new expandable buffer with the given capacity 48 | /// 49 | public Buffer(long capacity) { _data = new byte[capacity]; _size = 0; _offset = 0; } 50 | /// 51 | /// Initialize a new expandable buffer with the given data 52 | /// 53 | public Buffer(byte[] data) { _data = data; _size = data.Length; _offset = 0; } 54 | 55 | #region Memory buffer methods 56 | 57 | /// 58 | /// Get string from the current buffer 59 | /// 60 | public override string ToString() 61 | { 62 | return ExtractString(0, _size); 63 | } 64 | 65 | // Clear the current buffer and its offset 66 | public void Clear() 67 | { 68 | _size = 0; 69 | _offset = 0; 70 | } 71 | 72 | /// 73 | /// Extract the string from buffer of the given offset and size 74 | /// 75 | public string ExtractString(long offset, long size) 76 | { 77 | Debug.Assert(((offset + size) <= Size), "Invalid offset & size!"); 78 | if ((offset + size) > Size) 79 | throw new ArgumentException("Invalid offset & size!", nameof(offset)); 80 | 81 | return Encoding.UTF8.GetString(_data, (int)offset, (int)size); 82 | } 83 | 84 | /// 85 | /// Remove the buffer of the given offset and size 86 | /// 87 | public void Remove(long offset, long size) 88 | { 89 | Debug.Assert(((offset + size) <= Size), "Invalid offset & size!"); 90 | if ((offset + size) > Size) 91 | throw new ArgumentException("Invalid offset & size!", nameof(offset)); 92 | 93 | Array.Copy(_data, offset + size, _data, offset, _size - size - offset); 94 | _size -= size; 95 | if (_offset >= (offset + size)) 96 | _offset -= size; 97 | else if (_offset >= offset) 98 | { 99 | _offset -= _offset - offset; 100 | if (_offset > Size) 101 | _offset = Size; 102 | } 103 | } 104 | 105 | /// 106 | /// Reserve the buffer of the given capacity 107 | /// 108 | public void Reserve(long capacity) 109 | { 110 | Debug.Assert((capacity >= 0), "Invalid reserve capacity!"); 111 | if (capacity < 0) 112 | throw new ArgumentException("Invalid reserve capacity!", nameof(capacity)); 113 | 114 | if (capacity > Capacity) 115 | { 116 | byte[] data = new byte[Math.Max(capacity, 2 * Capacity)]; 117 | Array.Copy(_data, 0, data, 0, _size); 118 | _data = data; 119 | } 120 | } 121 | 122 | // Resize the current buffer 123 | public void Resize(long size) 124 | { 125 | Reserve(size); 126 | _size = size; 127 | if (_offset > _size) 128 | _offset = _size; 129 | } 130 | 131 | // Shift the current buffer offset 132 | public void Shift(long offset) { _offset += offset; } 133 | // Unshift the current buffer offset 134 | public void Unshift(long offset) { _offset -= offset; } 135 | 136 | #endregion 137 | 138 | #region Buffer I/O methods 139 | 140 | /// 141 | /// Append the given buffer 142 | /// 143 | /// Buffer to append 144 | /// Count of append bytes 145 | public long Append(byte[] buffer) 146 | { 147 | Reserve(_size + buffer.Length); 148 | Array.Copy(buffer, 0, _data, _size, buffer.Length); 149 | _size += buffer.Length; 150 | return buffer.Length; 151 | } 152 | 153 | /// 154 | /// Append the given buffer fragment 155 | /// 156 | /// Buffer to append 157 | /// Buffer offset 158 | /// Buffer size 159 | /// Count of append bytes 160 | public long Append(byte[] buffer, long offset, long size) 161 | { 162 | Reserve(_size + size); 163 | Array.Copy(buffer, offset, _data, _size, size); 164 | _size += size; 165 | return size; 166 | } 167 | 168 | /// 169 | /// Append the given text in UTF-8 encoding 170 | /// 171 | /// Text to append 172 | /// Count of append bytes 173 | public long Append(string text) 174 | { 175 | Reserve(_size + Encoding.UTF8.GetMaxByteCount(text.Length)); 176 | long result = Encoding.UTF8.GetBytes(text, 0, text.Length, _data, (int)_size); 177 | _size += result; 178 | return result; 179 | } 180 | 181 | #endregion 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /SyslogServer/Common/MessageHandler.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SyslogServer 3 | { 4 | 5 | public enum ServerType 6 | { 7 | UDP,TCP, SSL_TLS 8 | } 9 | 10 | public abstract class MessageHandler 11 | { 12 | public virtual void OnReceived(System.Net.EndPoint endpoint, byte[] buffer, long offset, long size) 13 | { 14 | throw new System.NotImplementedException("OnReceived"); 15 | } 16 | 17 | 18 | public virtual void OnError(System.Net.Sockets.SocketError error) 19 | { 20 | System.Console.WriteLine($"server caught an error with code {error}"); 21 | } 22 | 23 | 24 | public static MessageHandler CreateInstance(int serverType, int port) 25 | { 26 | return new DefaultMessageHandler(); 27 | } 28 | 29 | } 30 | 31 | 32 | public class DefaultMessageHandler 33 | : MessageHandler 34 | { 35 | 36 | public int ServerPort = 0; 37 | public ServerType ServerType = ServerType.TCP; 38 | 39 | 40 | private static char[] trimChars = new char[] { ' ', '\t', '\f', '\v', '\r', '\n' }; 41 | 42 | 43 | 44 | private static bool IsNumber(string s) 45 | { 46 | foreach (char c in s) 47 | { 48 | if (!char.IsDigit(c)) 49 | return false; 50 | } 51 | 52 | return true; 53 | } 54 | 55 | 56 | public override void OnReceived(System.Net.EndPoint endpoint, byte[] buffer, long offset, long size) 57 | { 58 | bool octetCounting = false; 59 | bool isRfc5424 = false; 60 | bool isRfc3164 = false; 61 | string rawMessage = null; 62 | 63 | try 64 | { 65 | rawMessage = System.Text.Encoding.UTF8.GetString(buffer, (int)offset, (int)size); 66 | System.Console.WriteLine("Incoming: " + rawMessage); 67 | 68 | if (string.IsNullOrWhiteSpace(rawMessage)) 69 | return; 70 | 71 | string message = rawMessage.TrimStart(); 72 | 73 | 74 | if (IsNumber(message)) 75 | return; // Discard - this is just the length of a message 76 | 77 | 78 | // rfc_5424_octet_counting: "218 <134>1 2021-09-16T21:44:22.395060+02:00 DESKTOP-6CN7QMR TestSerilog 31308 - [meta MessageNumber="2" AProperty="0.8263707183424247"] TCP: This is test message 00002 79 | // rfc_5424_nontransparent_framing: "<134>1 2021-09-16T21:44:22.395060+02:00 DESKTOP-6CN7QMR TestSerilog 31308 - [meta MessageNumber="2" AProperty="0.8263707183424247"] TCP: This is test message 00002 80 | 81 | // rfc_3164_octet_counting: "218 <30>Oct 82 | // rfc_3164_nontransparent_framing: "<30>Oct 83 | 84 | // p = ((int)facility * 8) + (int)severity; 85 | // ==> severity = p % 8 86 | // ==> faciliy = p \ 8 87 | 88 | // Probe octet-framing and message-type 89 | // Let's do this WITHOUT regex - for speed ! 90 | int ind = message.IndexOf('<'); 91 | if (ind != 0) 92 | { 93 | if (ind != -1) 94 | { 95 | octetCounting = true; 96 | string octet = message.Substring(0, ind - 1); 97 | octet = octet.TrimEnd(trimChars); 98 | if (!IsNumber(octet)) 99 | { 100 | throw new System.IO.InvalidDataException("Invalid octet framing ! \r\nMessage: " + rawMessage); 101 | } 102 | 103 | message = message.Substring(ind); 104 | } 105 | else 106 | throw new System.IO.InvalidDataException(rawMessage); 107 | 108 | } 109 | 110 | int closeAngleBracketIndex = message.IndexOf('>'); 111 | if (closeAngleBracketIndex != -1) 112 | { 113 | closeAngleBracketIndex++; 114 | string messageContent = message.Substring(closeAngleBracketIndex); 115 | messageContent = messageContent.TrimStart(trimChars); 116 | System.Console.WriteLine(messageContent); 117 | 118 | if (messageContent.Length > 0) 119 | { 120 | if (char.IsDigit(messageContent[0])) 121 | { 122 | isRfc5424 = true; 123 | } 124 | else 125 | { 126 | isRfc3164 = true; 127 | } 128 | } 129 | else 130 | throw new System.IO.InvalidDataException(rawMessage); 131 | } 132 | else 133 | throw new System.IO.InvalidDataException(rawMessage); 134 | 135 | 136 | System.Console.WriteLine("Octet counting: {0}", octetCounting); 137 | 138 | if (isRfc5424) 139 | { 140 | System.Console.WriteLine("rfc_5424"); 141 | Rfc5424SyslogMessage msg5424 = Rfc5424SyslogMessage.Parse(message); 142 | msg5424.SetSourceEndpoint(endpoint); 143 | System.Console.WriteLine(msg5424); 144 | } 145 | else if (isRfc3164) 146 | { 147 | System.Console.WriteLine("rfc_3164"); 148 | Rfc3164SyslogMessage msg3164 = Rfc3164SyslogMessage.Parse(message); 149 | msg3164.RemoteIP = endpoint.ToString(); 150 | System.Console.WriteLine(msg3164); 151 | } 152 | 153 | } 154 | catch (System.Exception ex) 155 | { 156 | System.Console.WriteLine(ex.Message); 157 | System.Console.WriteLine(ex.StackTrace); 158 | 159 | // bool octetCounting = false; 160 | 161 | if (isRfc5424) 162 | { 163 | Rfc5424SyslogMessage msg5424 = Rfc5424SyslogMessage.Invalid(rawMessage, ex); 164 | } 165 | else if (isRfc3164) 166 | { 167 | Rfc3164SyslogMessage msg3164 = Rfc3164SyslogMessage.Invalid(rawMessage, ex); 168 | } 169 | else 170 | { 171 | 172 | } 173 | } 174 | 175 | } 176 | 177 | 178 | } 179 | } 180 | 181 | -------------------------------------------------------------------------------- /SyslogServer/TlsSyslogServer.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SyslogServer 3 | { 4 | 5 | public class SyslogTlsSession 6 | : NetCoreServer.SslSession 7 | { 8 | protected MessageHandler m_messageHandler; 9 | 10 | public SyslogTlsSession(NetCoreServer.SslServer server, MessageHandler handler) 11 | : base(server) 12 | { } 13 | 14 | protected override void OnConnected() 15 | { 16 | System.Console.WriteLine($"Syslog SSL session with Id {Id} connected!"); 17 | } // End Sub OnConnected 18 | 19 | 20 | protected override void OnHandshaked() 21 | { 22 | System.Console.WriteLine($"Syslog SSL session with Id {Id} handshaked!"); 23 | 24 | // Send invite message 25 | // string message = "Hello from SSL Syslog! Please send a message or '!' to disconnect the client!"; 26 | // Send(message); 27 | } // End Sub OnHandshaked 28 | 29 | 30 | protected override void OnDisconnected() 31 | { 32 | System.Console.WriteLine($"Syslog SSL session with Id {Id} disconnected!"); 33 | } // End Sub OnDisconnected 34 | 35 | 36 | protected override void OnReceived(byte[] buffer, long offset, long size) 37 | { 38 | // string message = System.Text.Encoding.UTF8.GetString(buffer, (int)offset, (int)size); 39 | // System.Console.WriteLine("Incoming: " + message); 40 | this.m_messageHandler.OnReceived(this.Socket.RemoteEndPoint, buffer, offset, size); 41 | 42 | // Multicast message to all connected sessions 43 | // Server.Multicast(message); 44 | 45 | // If the buffer starts with '!' the disconnect the current session 46 | // if (message == "!") Disconnect(); 47 | 48 | } // End Sub OnReceived 49 | 50 | 51 | protected override void OnError(System.Net.Sockets.SocketError error) 52 | { 53 | // System.Console.WriteLine($"Syslog SSL session caught an error with code {error}"); 54 | this.m_messageHandler.OnError(error); 55 | } // End Sub OnError 56 | 57 | 58 | } // End Class SyslogTlsSession 59 | 60 | 61 | public class TlsSyslogServer 62 | : NetCoreServer.SslServer 63 | { 64 | 65 | protected MessageHandler m_messageHandler; 66 | 67 | 68 | public TlsSyslogServer( 69 | NetCoreServer.SslContext context 70 | , System.Net.IPAddress address 71 | , int port 72 | , MessageHandler handler 73 | ) 74 | : base(context, address, port) 75 | { 76 | this.m_messageHandler = handler; 77 | } 78 | 79 | protected override NetCoreServer.SslSession CreateSession() 80 | { 81 | return new SyslogTlsSession(this, this.m_messageHandler); 82 | } // End Function CreateSession 83 | 84 | 85 | protected override void OnError(System.Net.Sockets.SocketError error) 86 | { 87 | System.Console.WriteLine($"Syslog SSL server caught an error with code {error}"); 88 | } 89 | 90 | public static bool AllowAnything( 91 | object sender 92 | , System.Security.Cryptography.X509Certificates.X509Certificate certificate 93 | , System.Security.Cryptography.X509Certificates.X509Chain chain 94 | , System.Net.Security.SslPolicyErrors sslPolicyErrors) 95 | { 96 | return true; 97 | } // End Function AllowAnything 98 | 99 | 100 | public static void Test() 101 | { 102 | System.Net.ServicePointManager.ServerCertificateValidationCallback = 103 | new System.Net.Security.RemoteCertificateValidationCallback(AllowAnything); 104 | 105 | // SSL server port 106 | int port = 6514; 107 | 108 | System.Console.WriteLine($"SSL server port: {port}"); 109 | 110 | System.Console.WriteLine(); 111 | 112 | 113 | string[] altNames = SelfSignedCertificate.SelfSigned.GetAlternativeNames(new string[0]); 114 | byte[] pfx = SelfSignedCertificate.SelfSigned.CreateSelfSignedCertificate(altNames, ""); 115 | 116 | System.Security.Cryptography.X509Certificates.X509Certificate2 cert = 117 | new System.Security.Cryptography.X509Certificates.X509Certificate2(pfx,"", 118 | System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.Exportable 119 | // https://github.com/dotnet/runtime/issues/23749 120 | // | System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.EphemeralKeySet // Error ! 121 | ); 122 | 123 | // Create and prepare a new SSL server context 124 | NetCoreServer.SslContext context = new NetCoreServer.SslContext( 125 | // System.Security.Authentication.SslProtocols.Tls 126 | // System.Security.Authentication.SslProtocols.Tls13 127 | System.Security.Authentication.SslProtocols.Tls12 128 | , cert 129 | ); 130 | 131 | // Create a new SSL Syslog server 132 | TlsSyslogServer server = 133 | new TlsSyslogServer(context, System.Net.IPAddress.Any, port, MessageHandler.CreateInstance(123, port)); 134 | 135 | // Start the server 136 | System.Console.Write("Server starting..."); 137 | server.Start(); 138 | System.Console.WriteLine("Done!"); 139 | 140 | System.Console.WriteLine("Press Enter to stop the server or '!' to restart the server..."); 141 | 142 | // Perform text input 143 | for (; ; ) 144 | { 145 | string line = System.Console.ReadLine(); 146 | if (string.IsNullOrEmpty(line)) 147 | break; 148 | 149 | // Restart the server 150 | if (line == "!") 151 | { 152 | System.Console.Write("Server restarting..."); 153 | server.Restart(); 154 | System.Console.WriteLine("Done!"); 155 | continue; 156 | } // End if (line == "!") 157 | 158 | // Multicast admin message to all sessions 159 | line = "(admin) " + line; 160 | server.Multicast(line); 161 | } // Next 162 | 163 | // Stop the server 164 | System.Console.Write("Server stopping..."); 165 | server.Stop(); 166 | System.Console.WriteLine("Done!"); 167 | } // End Sub Test 168 | 169 | 170 | } // End Class TlsSyslogServer 171 | 172 | 173 | } // End Namespace SyslogServer 174 | -------------------------------------------------------------------------------- /NetCoreServer/Utilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace NetCoreServer 5 | { 6 | /// 7 | /// Conversion metrics utilities 8 | /// 9 | public class Utilities 10 | { 11 | /// 12 | /// Generate data size string. Will return a pretty string of bytes, KiB, MiB, GiB, TiB based on the given bytes. 13 | /// 14 | /// Data size in bytes 15 | /// String with data size representation 16 | public static string GenerateDataSize(double b) 17 | { 18 | var sb = new StringBuilder(); 19 | 20 | long bytes = (long)b; 21 | long absBytes = Math.Abs(bytes); 22 | 23 | if (absBytes >= (1024L * 1024L * 1024L * 1024L)) 24 | { 25 | long tb = bytes / (1024L * 1024L * 1024L * 1024L); 26 | long gb = (bytes % (1024L * 1024L * 1024L * 1024L)) / (1024 * 1024 * 1024); 27 | sb.Append(tb); 28 | sb.Append('.'); 29 | sb.Append((gb < 100) ? "0" : ""); 30 | sb.Append((gb < 10) ? "0" : ""); 31 | sb.Append(gb); 32 | sb.Append(" TiB"); 33 | } 34 | else if (absBytes >= (1024 * 1024 * 1024)) 35 | { 36 | long gb = bytes / (1024 * 1024 * 1024); 37 | long mb = (bytes % (1024 * 1024 * 1024)) / (1024 * 1024); 38 | sb.Append(gb); 39 | sb.Append('.'); 40 | sb.Append((mb < 100) ? "0" : ""); 41 | sb.Append((mb < 10) ? "0" : ""); 42 | sb.Append(mb); 43 | sb.Append(" GiB"); 44 | } 45 | else if (absBytes >= (1024 * 1024)) 46 | { 47 | long mb = bytes / (1024 * 1024); 48 | long kb = (bytes % (1024 * 1024)) / 1024; 49 | sb.Append(mb); 50 | sb.Append('.'); 51 | sb.Append((kb < 100) ? "0" : ""); 52 | sb.Append((kb < 10) ? "0" : ""); 53 | sb.Append(kb); 54 | sb.Append(" MiB"); 55 | } 56 | else if (absBytes >= 1024) 57 | { 58 | long kb = bytes / 1024; 59 | bytes = bytes % 1024; 60 | sb.Append(kb); 61 | sb.Append('.'); 62 | sb.Append((bytes < 100) ? "0" : ""); 63 | sb.Append((bytes < 10) ? "0" : ""); 64 | sb.Append(bytes); 65 | sb.Append(" KiB"); 66 | } 67 | else 68 | { 69 | sb.Append(bytes); 70 | sb.Append(" bytes"); 71 | } 72 | 73 | return sb.ToString(); 74 | } 75 | 76 | /// 77 | /// Generate time period string. Will return a pretty string of ns, mcs, ms, s, m, h based on the given nanoseconds. 78 | /// 79 | /// Milliseconds 80 | /// String with time period representation 81 | public static string GenerateTimePeriod(double ms) 82 | { 83 | var sb = new StringBuilder(); 84 | 85 | long nanoseconds = (long) (ms * 1000.0 * 1000.0); 86 | long absNanoseconds = Math.Abs(nanoseconds); 87 | 88 | if (absNanoseconds >= (60 * 60 * 1000000000L)) 89 | { 90 | long hours = nanoseconds / (60 * 60 * 1000000000L); 91 | long minutes = ((nanoseconds % (60 * 60 * 1000000000L)) / 1000000000) / 60; 92 | long seconds = ((nanoseconds % (60 * 60 * 1000000000L)) / 1000000000) % 60; 93 | long milliseconds = ((nanoseconds % (60 * 60 * 1000000000L)) % 1000000000) / 1000000; 94 | sb.Append(hours); 95 | sb.Append(':'); 96 | sb.Append((minutes < 10) ? "0" : ""); 97 | sb.Append(minutes); 98 | sb.Append(':'); 99 | sb.Append((seconds < 10) ? "0" : ""); 100 | sb.Append(seconds); 101 | sb.Append('.'); 102 | sb.Append((milliseconds < 100) ? "0" : ""); 103 | sb.Append((milliseconds < 10) ? "0" : ""); 104 | sb.Append(milliseconds); 105 | sb.Append(" h"); 106 | } 107 | else if (absNanoseconds >= (60 * 1000000000L)) 108 | { 109 | long minutes = nanoseconds / (60 * 1000000000L); 110 | long seconds = (nanoseconds % (60 * 1000000000L)) / 1000000000; 111 | long milliseconds = ((nanoseconds % (60 * 1000000000L)) % 1000000000) / 1000000; 112 | sb.Append(minutes); 113 | sb.Append(':'); 114 | sb.Append((seconds < 10) ? "0" : ""); 115 | sb.Append(seconds); 116 | sb.Append('.'); 117 | sb.Append((milliseconds < 100) ? "0" : ""); 118 | sb.Append((milliseconds < 10) ? "0" : ""); 119 | sb.Append(milliseconds); 120 | sb.Append(" m"); 121 | } 122 | else if (absNanoseconds >= 1000000000) 123 | { 124 | long seconds = nanoseconds / 1000000000; 125 | long milliseconds = (nanoseconds % 1000000000) / 1000000; 126 | sb.Append(seconds); 127 | sb.Append('.'); 128 | sb.Append((milliseconds < 100) ? "0" : ""); 129 | sb.Append((milliseconds < 10) ? "0" : ""); 130 | sb.Append(milliseconds); 131 | sb.Append(" s"); 132 | } 133 | else if (absNanoseconds >= 1000000) 134 | { 135 | long milliseconds = nanoseconds / 1000000; 136 | long microseconds = (nanoseconds % 1000000) / 1000; 137 | sb.Append(milliseconds); 138 | sb.Append('.'); 139 | sb.Append((microseconds < 100) ? "0" : ""); 140 | sb.Append((microseconds < 10) ? "0" : ""); 141 | sb.Append(microseconds); 142 | sb.Append(" ms"); 143 | } 144 | else if (absNanoseconds >= 1000) 145 | { 146 | long microseconds = nanoseconds / 1000; 147 | nanoseconds = nanoseconds % 1000; 148 | sb.Append(microseconds); 149 | sb.Append('.'); 150 | sb.Append((nanoseconds < 100) ? "0" : ""); 151 | sb.Append((nanoseconds < 10) ? "0" : ""); 152 | sb.Append(nanoseconds); 153 | sb.Append(" mcs"); 154 | } 155 | else 156 | { 157 | sb.Append(nanoseconds); 158 | sb.Append(" ns"); 159 | } 160 | 161 | return sb.ToString(); 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /SyslogServer/Program.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SyslogServer 3 | { 4 | 5 | public class GeneralSyslogServer 6 | { 7 | 8 | protected UpdSyslogServer m_udpServer; 9 | protected TcpSyslogServer m_tcpServer; 10 | protected TlsSyslogServer m_tlsServer; 11 | 12 | 13 | 14 | public ServerType ServerType; 15 | public int Port; 16 | public System.Net.IPAddress ListenAddress; 17 | public System.Security.Authentication.SslProtocols Protocol; 18 | public System.Security.Cryptography.X509Certificates.X509Certificate2 Certificate; 19 | 20 | 21 | private static int GetDefaultPort(ServerType serverType) 22 | { 23 | if (serverType == ServerType.UDP) 24 | return 514; 25 | 26 | if (serverType == ServerType.TCP) 27 | return 1468; 28 | 29 | if (serverType == ServerType.SSL_TLS) 30 | return 6514; 31 | 32 | return -1; 33 | } 34 | 35 | 36 | public GeneralSyslogServer( 37 | ServerType serverType, 38 | int port, 39 | System.Net.IPAddress listenIP, 40 | System.Security.Authentication.SslProtocols protocol, 41 | System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) 42 | { 43 | this.ServerType = serverType; 44 | this.Port = port; 45 | this.ListenAddress = listenIP; 46 | this.Protocol = protocol; 47 | this.Certificate = certificate; 48 | } 49 | 50 | 51 | public GeneralSyslogServer( 52 | ServerType serverType, 53 | int port, 54 | System.Net.IPAddress listenIP 55 | ) 56 | : this(serverType, port, listenIP, System.Security.Authentication.SslProtocols.None, null) 57 | { 58 | } 59 | 60 | 61 | public GeneralSyslogServer( 62 | ServerType serverType, 63 | System.Net.IPAddress listenIP 64 | ) 65 | : this(serverType, GetDefaultPort(serverType), listenIP, System.Security.Authentication.SslProtocols.None, null) 66 | { 67 | } 68 | 69 | 70 | 71 | public GeneralSyslogServer( 72 | ServerType serverType, 73 | System.Net.IPAddress listenIP, 74 | System.Security.Authentication.SslProtocols protocol, 75 | System.Security.Cryptography.X509Certificates.X509Certificate2 certificate) 76 | : this(serverType, GetDefaultPort(serverType), listenIP, protocol, certificate) 77 | { 78 | } 79 | 80 | 81 | 82 | 83 | public bool Start() 84 | { 85 | // Create a new UDP echo server 86 | if (this.ServerType == ServerType.UDP) 87 | this.m_udpServer = new UpdSyslogServer(this.ListenAddress, this.Port, MessageHandler.CreateInstance(123, this.Port)); 88 | 89 | // Create a new TCP Syslog server 90 | if (this.ServerType == ServerType.TCP) 91 | this.m_tcpServer = new TcpSyslogServer(this.ListenAddress, this.Port, MessageHandler.CreateInstance(123, this.Port)); 92 | 93 | if (this.ServerType == ServerType.SSL_TLS) 94 | { 95 | 96 | if (this.Certificate != null && this.Protocol != System.Security.Authentication.SslProtocols.None) 97 | { 98 | // Create and prepare a new SSL server context 99 | NetCoreServer.SslContext context = new NetCoreServer.SslContext(this.Protocol, this.Certificate); 100 | // Create a new SSL Syslog server 101 | this.m_tlsServer = new TlsSyslogServer(context, this.ListenAddress, this.Port, MessageHandler.CreateInstance(123, this.Port)); 102 | } 103 | } 104 | 105 | 106 | 107 | if (this.m_udpServer == null && this.m_tcpServer == null && this.m_tlsServer == null) 108 | { 109 | // Can't start the server 110 | System.Console.Write("Server NOT starting..."); 111 | return false; 112 | } 113 | 114 | // Start the server 115 | System.Console.Write("Server starting..."); 116 | 117 | if (this.m_udpServer != null) 118 | this.m_udpServer.Start(); 119 | 120 | if (this.m_tcpServer != null) 121 | this.m_tcpServer.Start(); 122 | 123 | if (this.m_tlsServer != null) 124 | this.m_tlsServer.Start(); 125 | 126 | System.Console.WriteLine("Done!"); 127 | return true; 128 | } 129 | 130 | public void Run() 131 | { 132 | if (!this.Start()) 133 | return; 134 | 135 | System.Console.WriteLine("Press Enter to stop the server or '!' to restart the server..."); 136 | 137 | // Perform text input 138 | for (; ; ) 139 | { 140 | string line = System.Console.ReadLine(); 141 | if (string.IsNullOrEmpty(line)) 142 | break; 143 | 144 | // Restart the server 145 | if (line == "!") 146 | { 147 | System.Console.Write("Server restarting..."); 148 | 149 | if (this.m_udpServer != null) 150 | this.m_udpServer.Restart(); 151 | 152 | if (this.m_tcpServer != null) 153 | this.m_tcpServer.Restart(); 154 | 155 | if (this.m_tlsServer != null) 156 | this.m_tlsServer.Restart(); 157 | 158 | System.Console.WriteLine("Done!"); 159 | continue; 160 | } // End if (line == "!") 161 | 162 | } // Next 163 | 164 | // Stop the server 165 | System.Console.Write("Server stopping..."); 166 | 167 | if (this.m_udpServer != null) 168 | this.m_udpServer.Stop(); 169 | 170 | if (this.m_tcpServer != null) 171 | this.m_tcpServer.Stop(); 172 | 173 | if (this.m_tlsServer != null) 174 | this.m_tlsServer.Stop(); 175 | 176 | System.Console.WriteLine("Done!"); 177 | } 178 | 179 | } 180 | 181 | 182 | 183 | public class Program 184 | { 185 | 186 | 187 | public static void Main(string[] args) 188 | { 189 | // TODO: Use a performant TCP/UPD/TLS libary 190 | // https://github.com/chronoxor/NetCoreServer 191 | // https://chronoxor.github.io/NetCoreServer/ 192 | 193 | 194 | // var gsl = new GeneralSyslogServer(ServerType.SSL_TLS, System.Net.IPAddress.Any, System.Security.Authentication.SslProtocols.None, null); 195 | // var gsl = new GeneralSyslogServer(ServerType.UDP, System.Net.IPAddress.Any); 196 | var gsl = new GeneralSyslogServer(ServerType.TCP, System.Net.IPAddress.Any); 197 | 198 | gsl.Run(); 199 | 200 | // UpdSyslogServer.Test(); 201 | // TlsSyslogServer.Test(); 202 | // TcpSyslogServer.Test(); 203 | 204 | // libSyslogServer.SyslogServer.StartServer(); 205 | } // End Sub Main 206 | 207 | 208 | } // End Class Program 209 | 210 | 211 | } // End Namespace SyslogServer 212 | -------------------------------------------------------------------------------- /NetCoreServer/HttpSession.cs: -------------------------------------------------------------------------------- 1 | namespace NetCoreServer 2 | { 3 | /// 4 | /// HTTP session is used to receive/send HTTP requests/responses from the connected HTTP client. 5 | /// 6 | /// Thread-safe. 7 | public class HttpSession : TcpSession 8 | { 9 | public HttpSession(HttpServer server) : base(server) 10 | { 11 | Cache = server.Cache; 12 | Request = new HttpRequest(); 13 | Response = new HttpResponse(); 14 | } 15 | 16 | /// 17 | /// Get the static content cache 18 | /// 19 | public FileCache Cache { get; } 20 | 21 | /// 22 | /// Get the HTTP request 23 | /// 24 | protected HttpRequest Request { get; } 25 | 26 | /// 27 | /// Get the HTTP response 28 | /// 29 | public HttpResponse Response { get; } 30 | 31 | #region Send response / Send response body 32 | 33 | /// 34 | /// Send the current HTTP response (synchronous) 35 | /// 36 | /// Size of sent data 37 | public long SendResponse() { return SendResponse(Response); } 38 | /// 39 | /// Send the HTTP response (synchronous) 40 | /// 41 | /// HTTP response 42 | /// Size of sent data 43 | public long SendResponse(HttpResponse response) { return Send(response.Cache.Data, response.Cache.Offset, response.Cache.Size); } 44 | 45 | /// 46 | /// Send the HTTP response body (synchronous) 47 | /// 48 | /// HTTP response body 49 | /// Size of sent data 50 | public long SendResponseBody(string body) { return Send(body); } 51 | /// 52 | /// Send the HTTP response body (synchronous) 53 | /// 54 | /// HTTP response body buffer 55 | /// Size of sent data 56 | public long SendResponseBody(byte[] buffer) { return Send(buffer); } 57 | /// 58 | /// Send the HTTP response body (synchronous) 59 | /// 60 | /// HTTP response body buffer 61 | /// HTTP response body buffer offset 62 | /// HTTP response body size 63 | /// Size of sent data 64 | public long SendResponseBody(byte[] buffer, long offset, long size) { return Send(buffer, offset, size); } 65 | 66 | /// 67 | /// Send the current HTTP response (asynchronous) 68 | /// 69 | /// 'true' if the current HTTP response was successfully sent, 'false' if the session is not connected 70 | public bool SendResponseAsync() { return SendResponseAsync(Response); } 71 | /// 72 | /// Send the HTTP response (asynchronous) 73 | /// 74 | /// HTTP response 75 | /// 'true' if the current HTTP response was successfully sent, 'false' if the session is not connected 76 | public bool SendResponseAsync(HttpResponse response) { return SendAsync(response.Cache.Data, response.Cache.Offset, response.Cache.Size); } 77 | 78 | /// 79 | /// Send the HTTP response body (asynchronous) 80 | /// 81 | /// HTTP response body 82 | /// 'true' if the HTTP response body was successfully sent, 'false' if the session is not connected 83 | public bool SendResponseBodyAsync(string body) { return SendAsync(body); } 84 | /// 85 | /// Send the HTTP response body (asynchronous) 86 | /// 87 | /// HTTP response body buffer 88 | /// 'true' if the HTTP response body was successfully sent, 'false' if the session is not connected 89 | public bool SendResponseBodyAsync(byte[] buffer) { return SendAsync(buffer); } 90 | /// 91 | /// Send the HTTP response body (asynchronous) 92 | /// 93 | /// HTTP response body buffer 94 | /// HTTP response body buffer offset 95 | /// HTTP response body size 96 | /// 'true' if the HTTP response body was successfully sent, 'false' if the session is not connected 97 | public bool SendResponseBodyAsync(byte[] buffer, long offset, long size) { return SendAsync(buffer, offset, size); } 98 | 99 | #endregion 100 | 101 | #region Session handlers 102 | 103 | protected override void OnReceived(byte[] buffer, long offset, long size) 104 | { 105 | // Receive HTTP request header 106 | if (Request.IsPendingHeader()) 107 | { 108 | if (Request.ReceiveHeader(buffer, (int)offset, (int)size)) 109 | OnReceivedRequestHeader(Request); 110 | 111 | size = 0; 112 | } 113 | 114 | // Check for HTTP request error 115 | if (Request.IsErrorSet) 116 | { 117 | OnReceivedRequestError(Request, "Invalid HTTP request!"); 118 | Request.Clear(); 119 | Disconnect(); 120 | return; 121 | } 122 | 123 | // Receive HTTP request body 124 | if (Request.ReceiveBody(buffer, (int)offset, (int)size)) 125 | { 126 | OnReceivedRequestInternal(Request); 127 | Request.Clear(); 128 | return; 129 | } 130 | 131 | // Check for HTTP request error 132 | if (Request.IsErrorSet) 133 | { 134 | OnReceivedRequestError(Request, "Invalid HTTP request!"); 135 | Request.Clear(); 136 | Disconnect(); 137 | return; 138 | } 139 | } 140 | 141 | protected override void OnDisconnected() 142 | { 143 | // Receive HTTP request body 144 | if (Request.IsPendingBody()) 145 | { 146 | OnReceivedRequestInternal(Request); 147 | Request.Clear(); 148 | return; 149 | } 150 | } 151 | 152 | /// 153 | /// Handle HTTP request header received notification 154 | /// 155 | /// Notification is called when HTTP request header was received from the client. 156 | /// HTTP request 157 | protected virtual void OnReceivedRequestHeader(HttpRequest request) {} 158 | 159 | /// 160 | /// Handle HTTP request received notification 161 | /// 162 | /// Notification is called when HTTP request was received from the client. 163 | /// HTTP request 164 | protected virtual void OnReceivedRequest(HttpRequest request) {} 165 | 166 | /// 167 | /// Handle HTTP request error notification 168 | /// 169 | /// Notification is called when HTTP request error was received from the client. 170 | /// HTTP request 171 | /// HTTP request error 172 | protected virtual void OnReceivedRequestError(HttpRequest request, string error) {} 173 | 174 | #endregion 175 | 176 | private void OnReceivedRequestInternal(HttpRequest request) 177 | { 178 | // Try to get the cached response 179 | if (request.Method == "GET") 180 | { 181 | var response = Cache.Find(request.Url); 182 | if (response.Item1) 183 | { 184 | SendAsync(response.Item2); 185 | return; 186 | } 187 | } 188 | 189 | // Process the request 190 | OnReceivedRequest(request); 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /NetCoreServer/HttpsSession.cs: -------------------------------------------------------------------------------- 1 | namespace NetCoreServer 2 | { 3 | /// 4 | /// HTTPS session is used to receive/send HTTP requests/responses from the connected HTTPS client. 5 | /// 6 | /// Thread-safe. 7 | public class HttpsSession : SslSession 8 | { 9 | public HttpsSession(HttpsServer server) : base(server) 10 | { 11 | Cache = server.Cache; 12 | Request = new HttpRequest(); 13 | Response = new HttpResponse(); 14 | } 15 | 16 | /// 17 | /// Get the static content cache 18 | /// 19 | public FileCache Cache { get; } 20 | 21 | /// 22 | /// Get the HTTP request 23 | /// 24 | protected HttpRequest Request { get; } 25 | 26 | /// 27 | /// Get the HTTP response 28 | /// 29 | public HttpResponse Response { get; } 30 | 31 | #region Send response / Send response body 32 | 33 | /// 34 | /// Send the current HTTP response (synchronous) 35 | /// 36 | /// Size of sent data 37 | public long SendResponse() { return SendResponse(Response); } 38 | /// 39 | /// Send the HTTP response (synchronous) 40 | /// 41 | /// HTTP response 42 | /// Size of sent data 43 | public long SendResponse(HttpResponse response) { return Send(response.Cache.Data, response.Cache.Offset, response.Cache.Size); } 44 | 45 | /// 46 | /// Send the HTTP response body (synchronous) 47 | /// 48 | /// HTTP response body 49 | /// Size of sent data 50 | public long SendResponseBody(string body) { return Send(body); } 51 | /// 52 | /// Send the HTTP response body (synchronous) 53 | /// 54 | /// HTTP response body buffer 55 | /// Size of sent data 56 | public long SendResponseBody(byte[] buffer) { return Send(buffer); } 57 | /// 58 | /// Send the HTTP response body (synchronous) 59 | /// 60 | /// HTTP response body buffer 61 | /// HTTP response body buffer offset 62 | /// HTTP response body size 63 | /// Size of sent data 64 | public long SendResponseBody(byte[] buffer, long offset, long size) { return Send(buffer, offset, size); } 65 | 66 | /// 67 | /// Send the current HTTP response (asynchronous) 68 | /// 69 | /// 'true' if the current HTTP response was successfully sent, 'false' if the session is not connected 70 | public bool SendResponseAsync() { return SendResponseAsync(Response); } 71 | /// 72 | /// Send the HTTP response (asynchronous) 73 | /// 74 | /// HTTP response 75 | /// 'true' if the current HTTP response was successfully sent, 'false' if the session is not connected 76 | public bool SendResponseAsync(HttpResponse response) { return SendAsync(response.Cache.Data, response.Cache.Offset, response.Cache.Size); } 77 | 78 | /// 79 | /// Send the HTTP response body (asynchronous) 80 | /// 81 | /// HTTP response body 82 | /// 'true' if the HTTP response body was successfully sent, 'false' if the session is not connected 83 | public bool SendResponseBodyAsync(string body) { return SendAsync(body); } 84 | /// 85 | /// Send the HTTP response body (asynchronous) 86 | /// 87 | /// HTTP response body buffer 88 | /// 'true' if the HTTP response body was successfully sent, 'false' if the session is not connected 89 | public bool SendResponseBodyAsync(byte[] buffer) { return SendAsync(buffer); } 90 | /// 91 | /// Send the HTTP response body (asynchronous) 92 | /// 93 | /// HTTP response body buffer 94 | /// HTTP response body buffer offset 95 | /// HTTP response body size 96 | /// 'true' if the HTTP response body was successfully sent, 'false' if the session is not connected 97 | public bool SendResponseBodyAsync(byte[] buffer, long offset, long size) { return SendAsync(buffer, offset, size); } 98 | 99 | #endregion 100 | 101 | #region Session handlers 102 | 103 | protected override void OnReceived(byte[] buffer, long offset, long size) 104 | { 105 | // Receive HTTP request header 106 | if (Request.IsPendingHeader()) 107 | { 108 | if (Request.ReceiveHeader(buffer, (int)offset, (int)size)) 109 | OnReceivedRequestHeader(Request); 110 | 111 | size = 0; 112 | } 113 | 114 | // Check for HTTP request error 115 | if (Request.IsErrorSet) 116 | { 117 | OnReceivedRequestError(Request, "Invalid HTTP request!"); 118 | Request.Clear(); 119 | Disconnect(); 120 | return; 121 | } 122 | 123 | // Receive HTTP request body 124 | if (Request.ReceiveBody(buffer, (int)offset, (int)size)) 125 | { 126 | OnReceivedRequestInternal(Request); 127 | Request.Clear(); 128 | return; 129 | } 130 | 131 | // Check for HTTP request error 132 | if (Request.IsErrorSet) 133 | { 134 | OnReceivedRequestError(Request, "Invalid HTTP request!"); 135 | Request.Clear(); 136 | Disconnect(); 137 | return; 138 | } 139 | } 140 | 141 | protected override void OnDisconnected() 142 | { 143 | // Receive HTTP request body 144 | if (Request.IsPendingBody()) 145 | { 146 | OnReceivedRequestInternal(Request); 147 | Request.Clear(); 148 | return; 149 | } 150 | } 151 | 152 | /// 153 | /// Handle HTTP request header received notification 154 | /// 155 | /// Notification is called when HTTP request header was received from the client. 156 | /// HTTP request 157 | protected virtual void OnReceivedRequestHeader(HttpRequest request) { } 158 | 159 | /// 160 | /// Handle HTTP request received notification 161 | /// 162 | /// Notification is called when HTTP request was received from the client. 163 | /// HTTP request 164 | protected virtual void OnReceivedRequest(HttpRequest request) { } 165 | 166 | /// 167 | /// Handle HTTP request error notification 168 | /// 169 | /// Notification is called when HTTP request error was received from the client. 170 | /// HTTP request 171 | /// HTTP request error 172 | protected virtual void OnReceivedRequestError(HttpRequest request, string error) { } 173 | 174 | #endregion 175 | 176 | private void OnReceivedRequestInternal(HttpRequest request) 177 | { 178 | // Try to get the cached response 179 | if (request.Method == "GET") 180 | { 181 | var response = Cache.Find(request.Url); 182 | if (response.Item1) 183 | { 184 | SendAsync(response.Item2); 185 | return; 186 | } 187 | } 188 | 189 | // Process the request 190 | OnReceivedRequest(request); 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /SelfSignedCertificate/Helpers/CertificateInfo.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SelfSignedCertificate 3 | { 4 | 5 | 6 | public class CertificateInfo 7 | { 8 | 9 | public string CountryIso2Characters; 10 | public string StateOrProvince; 11 | public string LocalityOrCity; 12 | public string CompanyName; 13 | public string Division; 14 | public string DomainName; 15 | public string EMail; 16 | 17 | public System.DateTime ValidFrom; 18 | public System.DateTime ValidTo; 19 | 20 | // public PrivatePublicPemKeyPair SubjectKeyPair; 21 | // public PrivatePublicPemKeyPair IssuerKeyPair; 22 | 23 | public string[] AlternativeNames; 24 | 25 | 26 | public System.Collections.Generic.Dictionary NonCriticalExtensions; 27 | public System.Collections.Generic.Dictionary CriticalExtensions; 28 | 29 | 30 | 31 | public Org.BouncyCastle.Asn1.X509.X509Name Subject 32 | { 33 | get 34 | { 35 | return CreateSubject( 36 | this.CountryIso2Characters 37 | , this.StateOrProvince 38 | , this.LocalityOrCity 39 | , this.CompanyName 40 | , this.Division 41 | , this.DomainName 42 | , this.EMail 43 | ); 44 | } 45 | } // End Property Subject 46 | 47 | 48 | public Org.BouncyCastle.Asn1.DerSequence SubjectAlternativeNames 49 | { 50 | get 51 | { 52 | return CreateSubjectAlternativeNames(this.AlternativeNames); 53 | } 54 | } // End Property SubjectAlternativeNames 55 | 56 | 57 | public static Org.BouncyCastle.Asn1.DerSequence CreateSubjectAlternativeNames(string[] names) 58 | { 59 | Org.BouncyCastle.Asn1.Asn1Encodable[] alternativeNames = new Org.BouncyCastle.Asn1.Asn1Encodable[names.Length]; 60 | 61 | for (int i = 0; i < names.Length; ++i) 62 | { 63 | System.Net.IPAddress ipa; 64 | if (System.Net.IPAddress.TryParse(names[i], out ipa)) 65 | alternativeNames[i] = new Org.BouncyCastle.Asn1.X509.GeneralName(Org.BouncyCastle.Asn1.X509.GeneralName.IPAddress, names[i]); 66 | else 67 | alternativeNames[i] = new Org.BouncyCastle.Asn1.X509.GeneralName(Org.BouncyCastle.Asn1.X509.GeneralName.DnsName, names[i]); 68 | } // Next i 69 | 70 | Org.BouncyCastle.Asn1.DerSequence subjectAlternativeNames = new Org.BouncyCastle.Asn1.DerSequence(alternativeNames); 71 | return subjectAlternativeNames; 72 | } // End Function CreateSubjectAlternativeNames 73 | 74 | #if false 75 | private static void BuildAlternativeNameNetCoreVariant(System.Security.Cryptography.X509Certificates.X509Certificate2 cert) 76 | { 77 | // Certificate Policies 78 | // https://stackoverflow.com/questions/12147986/how-to-extract-the-authoritykeyidentifier-from-a-x509certificate2-in-net/12148637 79 | System.Security.Cryptography.X509Certificates.SubjectAlternativeNameBuilder sb = 80 | new System.Security.Cryptography.X509Certificates.SubjectAlternativeNameBuilder(); 81 | sb.AddDnsName("example.com"); 82 | sb.AddEmailAddress("webmaster@example.com"); 83 | sb.AddIpAddress(System.Net.IPAddress.Parse("127.0.0.1")); 84 | sb.AddUri(new System.Uri("https://www.google.com/bot.html")); 85 | sb.AddUserPrincipalName("domain\\username"); 86 | sb.Build(); 87 | System.Security.Cryptography.X509Certificates.X509Extension san = sb.Build(); 88 | cert.Extensions.Add(san); 89 | } // End Sub BuildAlternativeNameNetCoreVariant 90 | #endif 91 | 92 | // https://codereview.stackexchange.com/questions/84752/net-bouncycastle-csr-and-private-key-generation 93 | public static Org.BouncyCastle.Asn1.X509.X509Name CreateSubject( 94 | string countryIso2Characters 95 | , string stateOrProvince 96 | , string localityOrCity 97 | , string companyName 98 | , string division 99 | , string domainName 100 | , string email) 101 | { 102 | // https://people.eecs.berkeley.edu/~jonah/bc/org/bouncycastle/asn1/x509/X509Name.html 103 | KeyValuePairList attrs = 104 | new KeyValuePairList(); 105 | 106 | 107 | if (!string.IsNullOrEmpty(countryIso2Characters) && countryIso2Characters.Trim() != string.Empty) 108 | attrs.Add(Org.BouncyCastle.Asn1.X509.X509Name.C, countryIso2Characters); 109 | 110 | if (!string.IsNullOrEmpty(stateOrProvince) && stateOrProvince.Trim() != string.Empty) 111 | attrs.Add(Org.BouncyCastle.Asn1.X509.X509Name.ST, stateOrProvince); 112 | 113 | if (!string.IsNullOrEmpty(localityOrCity) && localityOrCity.Trim() != string.Empty) 114 | attrs.Add(Org.BouncyCastle.Asn1.X509.X509Name.L, localityOrCity); 115 | 116 | if (!string.IsNullOrEmpty(companyName) && companyName.Trim() != string.Empty) 117 | attrs.Add(Org.BouncyCastle.Asn1.X509.X509Name.O, companyName); 118 | 119 | if (!string.IsNullOrEmpty(division) && division.Trim() != string.Empty) 120 | attrs.Add(Org.BouncyCastle.Asn1.X509.X509Name.OU, division); 121 | 122 | // Must have ? 123 | if (!string.IsNullOrEmpty(domainName) && domainName.Trim() != string.Empty) 124 | attrs.Add(Org.BouncyCastle.Asn1.X509.X509Name.CN, domainName); 125 | 126 | if (!string.IsNullOrEmpty(email) && email.Trim() != string.Empty) 127 | { 128 | //attrs.Add(Org.BouncyCastle.Asn1.X509.X509Name.E, email); // email address in Verisign certificates 129 | attrs.Add(Org.BouncyCastle.Asn1.X509.X509Name.EmailAddress, email); // Email address (RSA PKCS#9 extension) 130 | } 131 | 132 | Org.BouncyCastle.Asn1.X509.X509Name subject = 133 | new Org.BouncyCastle.Asn1.X509.X509Name(attrs.Keys, attrs.Values); 134 | 135 | return subject; 136 | } // End Function CreateSubject 137 | 138 | 139 | public CertificateInfo() 140 | { 141 | this.NonCriticalExtensions = new System.Collections.Generic.Dictionary(System.StringComparer.OrdinalIgnoreCase); 142 | this.CriticalExtensions = new System.Collections.Generic.Dictionary(System.StringComparer.OrdinalIgnoreCase); 143 | } // End Constructor 144 | 145 | 146 | public CertificateInfo( 147 | string countryIso2Characters 148 | , string stateOrProvince 149 | , string localityOrCity 150 | , string companyName 151 | , string division 152 | , string domainName 153 | , string email 154 | , System.DateTime validFrom 155 | , System.DateTime validTo 156 | ) : this() 157 | { 158 | this.CountryIso2Characters = countryIso2Characters; 159 | this.StateOrProvince = stateOrProvince; 160 | this.LocalityOrCity = localityOrCity; 161 | this.CompanyName = companyName; 162 | this.Division = division; 163 | this.DomainName = domainName; 164 | this.EMail = email; 165 | this.ValidFrom = validFrom; 166 | this.ValidTo = validTo; 167 | } // End Constructor 168 | 169 | 170 | public void AddAlternativeNames(params string[] names) 171 | { 172 | this.AlternativeNames = names; 173 | } // End Sub AddAlternativeNames 174 | 175 | 176 | public void AddExtension( 177 | string oid 178 | , bool critical 179 | , Org.BouncyCastle.Asn1.Asn1Encodable extensionValue) 180 | { 181 | if (critical) 182 | this.CriticalExtensions.Add(oid, extensionValue); 183 | else 184 | this.NonCriticalExtensions.Add(oid, extensionValue); 185 | } // End Sub AddExtension 186 | 187 | 188 | } // End Class 189 | 190 | 191 | } // End Namespace 192 | --------------------------------------------------------------------------------