diff --git a/SyslogNet.Client/Facility.cs b/SyslogNet.Client/Facility.cs index 09bb2ed..31b7174 100644 --- a/SyslogNet.Client/Facility.cs +++ b/SyslogNet.Client/Facility.cs @@ -1,30 +1,35 @@ -namespace SyslogNet.Client + +namespace SyslogNet.Client { - public enum Facility - { - KernelMessages = 0, - UserLevelMessages = 1, - MailSystem = 2, - SystemDaemons = 3, - SecurityOrAuthorizationMessages1 = 4, - InternalMessages = 5, - LinePrinterSubsystem = 6, - NetworkNewsSubsystem = 7, - UUCPSubsystem = 8, - ClockDaemon1 = 9, - SecurityOrAuthorizationMessages2 = 10, - FTPDaemon = 11, - NTPSubsystem = 12, - LogAudit = 13, - LogAlert = 14, - ClockDaemon2 = 15, - LocalUse0 = 16, - LocalUse1 = 17, - LocalUse2 = 18, - LocalUse3 = 19, - LocalUse4 = 20, - LocalUse5 = 21, - LocalUse6 = 22, - LocalUse7 = 23 - } + + + public enum Facility + { + KernelMessages = 0, + UserLevelMessages = 1, + MailSystem = 2, + SystemDaemons = 3, + SecurityOrAuthorizationMessages1 = 4, + InternalMessages = 5, + LinePrinterSubsystem = 6, + NetworkNewsSubsystem = 7, + UUCPSubsystem = 8, + ClockDaemon1 = 9, + SecurityOrAuthorizationMessages2 = 10, + FTPDaemon = 11, + NTPSubsystem = 12, + LogAudit = 13, + LogAlert = 14, + ClockDaemon2 = 15, + LocalUse0 = 16, + LocalUse1 = 17, + LocalUse2 = 18, + LocalUse3 = 19, + LocalUse4 = 20, + LocalUse5 = 21, + LocalUse6 = 22, + LocalUse7 = 23 + } + + } \ No newline at end of file diff --git a/SyslogNet.Client/LinqHelper.cs b/SyslogNet.Client/LinqHelper.cs new file mode 100644 index 0000000..8a0cbcc --- /dev/null +++ b/SyslogNet.Client/LinqHelper.cs @@ -0,0 +1,424 @@ + +namespace SyslogNet.Client +{ + + public delegate object Getter_t(T obj); + public delegate void Setter_t(T obj, object value); + + + public class LinqHelper + { + + private static System.Reflection.MethodInfo m_FlexibleChangeType; + + + public static T ChangeType(object objVal) + { + System.Type targetType = typeof(T); + object obj = FlexibleChangeType(objVal, targetType); + return (T)obj; + } + + private static object FlexibleChangeType(object objVal, System.Type targetType) + { + bool typeIsNullable = (targetType.IsGenericType && object.ReferenceEquals(targetType.GetGenericTypeDefinition(), typeof(System.Nullable<>))); + bool typeCanBeAssignedNull = !targetType.IsValueType || typeIsNullable; + + if (objVal == null || object.ReferenceEquals(objVal, System.DBNull.Value)) + { + if (typeCanBeAssignedNull) + return null; + else + throw new System.ArgumentNullException("objVal ([DataSource] => SetProperty => FlexibleChangeType => you're trying to assign NULL to a type that NULL cannot be assigned to...)"); + } // End if (objVal == null || object.ReferenceEquals(objVal, System.DBNull.Value)) + + // Get base-type + System.Type tThisType = objVal.GetType(); + + if (typeIsNullable) + { + targetType = System.Nullable.GetUnderlyingType(targetType); + } // End if (typeIsNullable) + + + if (object.ReferenceEquals(tThisType, targetType)) + return objVal; + + // Convert Guid => string + if (object.ReferenceEquals(targetType, typeof(string)) && object.ReferenceEquals(tThisType, typeof(System.Guid))) + { + return objVal.ToString(); + } // End if (object.ReferenceEquals(targetType, typeof(string)) && object.ReferenceEquals(tThisType, typeof(System.Guid))) + + // Convert string => System.Net.IPAddress + if (object.ReferenceEquals(targetType, typeof(System.Net.IPAddress)) && object.ReferenceEquals(tThisType, typeof(string))) + { + return System.Net.IPAddress.Parse(objVal.ToString()); + } // End if (object.ReferenceEquals(targetType, typeof(System.Net.IPAddress)) && object.ReferenceEquals(tThisType, typeof(string))) + + // Convert string => TimeSpan + if (object.ReferenceEquals(targetType, typeof(System.TimeSpan)) && object.ReferenceEquals(tThisType, typeof(string))) + { + // https://stackoverflow.com/questions/11719055/why-does-timespan-parseexact-not-work + // This is grotesque... ParseExact ignores the 12/24 hour convention... + // return System.TimeSpan.ParseExact(objVal.ToString(), "HH':'mm':'ss", System.Globalization.CultureInfo.InvariantCulture); // Exception + // return System.TimeSpan.ParseExact(objVal.ToString(), "hh\\:mm\\:ss", System.Globalization.CultureInfo.InvariantCulture); // This works, bc of lowercase ? + // return System.TimeSpan.ParseExact(objVal.ToString(), "hh':'mm':'ss", System.Globalization.CultureInfo.InvariantCulture); // Yep, lowercase - no 24 notation... + +#if DOTNET_20 || DOTNET_35 + return System.TimeSpan.Parse(objVal.ToString()); +#else + return System.TimeSpan.Parse(objVal.ToString(), System.Globalization.CultureInfo.InvariantCulture); +#endif + + } // End if (object.ReferenceEquals(targetType, typeof(System.TimeSpan)) && object.ReferenceEquals(tThisType, typeof(string))) + + // Convert string => DateTime + if (object.ReferenceEquals(targetType, typeof(System.DateTime)) && object.ReferenceEquals(tThisType, typeof(string))) + { + return System.DateTime.Parse(objVal.ToString(), System.Globalization.CultureInfo.InvariantCulture); + } // End if (object.ReferenceEquals(targetType, typeof(System.DateTime)) && object.ReferenceEquals(tThisType, typeof(string))) + + // Convert string => Guid + if (object.ReferenceEquals(targetType, typeof(System.Guid)) && object.ReferenceEquals(tThisType, typeof(string))) + { + return new System.Guid(objVal.ToString()); + } // End else if (object.ReferenceEquals(targetType, typeof(System.Guid)) && object.ReferenceEquals(tThisType, typeof(string))) + + return System.Convert.ChangeType(objVal, targetType); + } // End Function FlexibleChangeType + + + + static LinqHelper() + { + m_FlexibleChangeType = typeof(LinqHelper) + .GetMethod("FlexibleChangeType", + System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic + | System.Reflection.BindingFlags.Public + ); + } // End static Constructor + + + +#if DOTNET_20 || DOTNET_35 + + public static Setter_t GetSetter(string fieldName) + { + System.Type t = typeof(T); + + System.Reflection.FieldInfo fi = t + .GetField(fieldName, System.Reflection.BindingFlags.IgnoreCase + | System.Reflection.BindingFlags.Instance + | System.Reflection.BindingFlags.Public); + + if (fi != null) + return delegate (T instance, object value) + { + fi.SetValue(instance, FlexibleChangeType(value, fi.FieldType)); + }; + + System.Reflection.PropertyInfo pi = t + .GetProperty(fieldName, System.Reflection.BindingFlags.IgnoreCase + | System.Reflection.BindingFlags.Instance + | System.Reflection.BindingFlags.Public); + + if (pi != null) + return delegate (T instance, object value) + { + pi.SetValue(instance, FlexibleChangeType(value, pi.PropertyType), null); + }; + + return null; + } + + + public static Getter_t GetGetter(string fieldName) + { + System.Type t = typeof(T); + + + System.Reflection.FieldInfo fi = t + .GetField(fieldName, System.Reflection.BindingFlags.IgnoreCase + | System.Reflection.BindingFlags.Instance + | System.Reflection.BindingFlags.Public); + + if (fi != null) + return delegate (T instance) + { + return fi.GetValue(instance); + }; + + + System.Reflection.PropertyInfo pi = t + .GetProperty(fieldName, System.Reflection.BindingFlags.IgnoreCase + | System.Reflection.BindingFlags.Instance + | System.Reflection.BindingFlags.Public); + + if (pi != null) + return delegate (T instance) + { + return pi.GetValue(instance, null); + }; + + return null; + } + + +#else + + // https://stackoverflow.com/questions/321650/how-do-i-set-a-field-value-in-an-c-sharp-expression-tree +#if false + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#endif + //public static System.Action GetSetter(string fieldName) + public static Setter_t GetSetter(string fieldName) + { + // Class in which to set value + System.Linq.Expressions.ParameterExpression targetExp = System.Linq.Expressions.Expression.Parameter(typeof(T), "target"); + + // Object's type: + System.Linq.Expressions.ParameterExpression valueExp = System.Linq.Expressions.Expression.Parameter(typeof(object), "value"); + + + // Expression.Property can be used here as well + System.Linq.Expressions.MemberExpression memberExp = null; + try + { + // memberExp = System.Linq.Expressions.Expression.Field(targetExp, fieldName); + // memberExp = System.Linq.Expressions.Expression.Property(targetExp, fieldName); + memberExp = System.Linq.Expressions.Expression.PropertyOrField(targetExp, fieldName); + } + catch (System.Exception ex) + { + return null; + } + + + // http://www.dotnet-tricks.com/Tutorial/linq/RJX7120714-Understanding-Expression-and-Expression-Trees.html + System.Linq.Expressions.ConstantExpression targetType = System.Linq.Expressions.Expression.Constant(memberExp.Type); + + // http://stackoverflow.com/questions/913325/how-do-i-make-a-linq-expression-to-call-a-method + System.Linq.Expressions.MethodCallExpression mce = System.Linq.Expressions.Expression.Call(m_FlexibleChangeType, valueExp, targetType); + + + //System.Linq.Expressions.UnaryExpression conversionExp = System.Linq.Expressions.Expression.Convert(valueExp, memberExp.Type); + System.Linq.Expressions.UnaryExpression conversionExp = System.Linq.Expressions.Expression.Convert(mce, memberExp.Type); + + + System.Linq.Expressions.BinaryExpression assignExp = + // System.Linq.Expressions.Expression.Assign(memberExp, valueExp); // Without conversion + System.Linq.Expressions.Expression.Assign(memberExp, conversionExp); + + // System.Action setter = System.Linq.Expressions.Expression + // System.Action setter = System.Linq.Expressions.Expression + // .Lambda>(assignExp, targetExp, valueExp).Compile(); + + Setter_t setter = System.Linq.Expressions.Expression + .Lambda>(assignExp, targetExp, valueExp).Compile(); + + return setter; + } // End Function GetGetter + + +#if false + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#endif + // public static System.Func GetGetter(string fieldName) + public static Getter_t GetGetter(string fieldName) + { + System.Linq.Expressions.ParameterExpression p = System.Linq.Expressions.Expression.Parameter(typeof(T)); + System.Linq.Expressions.MemberExpression prop = System.Linq.Expressions.Expression.PropertyOrField(p, fieldName); + System.Linq.Expressions.UnaryExpression con = System.Linq.Expressions.Expression.Convert(prop, typeof(object)); + + System.Linq.Expressions.Expression> exp = System.Linq.Expressions.Expression.Lambda>(con, p); + return exp.Compile(); + + //System.Linq.Expressions.LambdaExpression exp = System.Linq.Expressions.Expression.Lambda(con, p); + //// Getter_t getter = (Getter_t)exp.Compile(); + //System.Func getter = (System.Func)exp.Compile(); + //// Getter_t getter2 = (Getter_t)(arg1 => getter(arg1)); + //Getter_t getter2 = (Getter_t)delegate (T arg1) + //{ + // return getter(arg1); + //}; + + // return getter; + // return getter2; + } // End Function GetGetter + + +#endif + + + // ======================================================================================================================== + + + + public static System.Reflection.MemberInfo[] GetFieldsAndProperties(System.Type t) + { + System.Reflection.FieldInfo[] fis = t.GetFields(); + System.Reflection.PropertyInfo[] pis = t.GetProperties(); + System.Reflection.MemberInfo[] mis = new System.Reflection.MemberInfo[fis.Length + pis.Length]; + System.Array.Copy(fis, mis, fis.Length); + System.Array.Copy(pis, 0, mis, fis.Length, pis.Length); + + return mis; + } // End Function GetFieldsAndProperties + + + public static Getter_t[] GetGetters() + { + System.Reflection.MemberInfo[] mis = GetFieldsAndProperties(typeof(T)); + + string[] memberNames = new string[mis.Length]; + for (int i = 0; i < mis.Length; ++i) + { + memberNames[i] = mis[i].Name; + } + + return GetGetters(memberNames); + } // End Function GetGetters + + + public static System.Collections.Generic.Dictionary> GetGettersDictionary() + { + System.Collections.Generic.Dictionary> dict = + new System.Collections.Generic.Dictionary>(System.StringComparer.InvariantCultureIgnoreCase); + + System.Reflection.MemberInfo[] mis = GetFieldsAndProperties(typeof(T)); + + string[] memberNames = new string[mis.Length]; + for (int i = 0; i < mis.Length; ++i) + { + memberNames[i] = mis[i].Name; + } + + Getter_t[] getters = GetGetters(memberNames); + + for (int i = 0; i < memberNames.Length; ++i) + { + dict.Add(memberNames[i], getters[i]); + } + + return dict; + } // End Function GetGettersDictionary + + + public static System.Collections.Generic.Dictionary GetValuesDictionary(T obj) + { + System.Collections.Generic.Dictionary dict = + new System.Collections.Generic.Dictionary(System.StringComparer.InvariantCultureIgnoreCase); + + System.Reflection.MemberInfo[] mis = GetFieldsAndProperties(typeof(T)); + + string[] memberNames = new string[mis.Length]; + for (int i = 0; i < mis.Length; ++i) + { + memberNames[i] = mis[i].Name; + } + + Getter_t[] getters = GetGetters(memberNames); + + for (int i = 0; i < memberNames.Length; ++i) + { + dict.Add(memberNames[i], getters[i](obj)); + } + + return dict; + } + + public static System.Collections.Generic.Dictionary GetStringDictionary(T obj) + { + System.Collections.Generic.Dictionary dict = + new System.Collections.Generic.Dictionary(System.StringComparer.InvariantCultureIgnoreCase); + + System.Reflection.MemberInfo[] mis = GetFieldsAndProperties(typeof(T)); + + string[] memberNames = new string[mis.Length]; + for (int i = 0; i < mis.Length; ++i) + { + memberNames[i] = mis[i].Name; + } + + Getter_t[] getters = GetGetters(memberNames); + + for (int i = 0; i < memberNames.Length; ++i) + { + string val = ChangeType(getters[i](obj)); + dict.Add(memberNames[i], val); + } + + return dict; + } + + + public static Getter_t[] GetGetters(string[] fieldNames) + { + Getter_t[] iisLogGetters = new Getter_t[fieldNames.Length]; + + for (int i = 0; i < fieldNames.Length; ++i) + { + iisLogGetters[i] = GetGetter(fieldNames[i]); + } + + return iisLogGetters; + } // End Function GetGetters + + + public static Setter_t[] GetSetters() + { + System.Reflection.MemberInfo[] mis = GetFieldsAndProperties(typeof(T)); + + string[] memberNames = new string[mis.Length]; + for (int i = 0; i < mis.Length; ++i) + { + memberNames[i] = mis[i].Name; + } + + return GetSetters(memberNames); + } // End Function GetSetters + + + + public static System.Collections.Generic.Dictionary> GetSettersDictionary() + { + System.Collections.Generic.Dictionary> dict = + new System.Collections.Generic.Dictionary>(System.StringComparer.InvariantCultureIgnoreCase); + + System.Reflection.MemberInfo[] mis = GetFieldsAndProperties(typeof(T)); + + string[] memberNames = new string[mis.Length]; + for (int i = 0; i < mis.Length; ++i) + { + memberNames[i] = mis[i].Name; + } + + Setter_t[] setters = GetSetters(memberNames); + + for (int i = 0; i < memberNames.Length; ++i) + { + dict.Add(memberNames[i], setters[i]); + } + + return dict; + } // End Function GetSettersDictionary + + public static Setter_t[] GetSetters(string[] fieldNames) + { + // System.Action[] iisLogSetters = new System.Action[fieldNames.Length]; + Setter_t[] iisLogSetters = new Setter_t[fieldNames.Length]; + + for (int i = 0; i < fieldNames.Length; ++i) + { + iisLogSetters[i] = GetSetter(fieldNames[i]); + } + + return iisLogSetters; + } // End Function GetSetters + + + } // End Class LinqHelper + + +} // End Namespace MyLogParser \ No newline at end of file diff --git a/SyslogNet.Client/Serialization/ISyslogMessageSerializer.cs b/SyslogNet.Client/Serialization/ISyslogMessageSerializer.cs index 9ac828c..268c0dd 100644 --- a/SyslogNet.Client/Serialization/ISyslogMessageSerializer.cs +++ b/SyslogNet.Client/Serialization/ISyslogMessageSerializer.cs @@ -1,9 +1,12 @@ -using System.IO; namespace SyslogNet.Client.Serialization { - public interface ISyslogMessageSerializer - { - void Serialize(SyslogMessage message, Stream stream); - } + + + public interface ISyslogMessageSerializer + { + void Serialize(SyslogMessage message, System.IO.Stream stream); + } + + } \ No newline at end of file diff --git a/SyslogNet.Client/Serialization/SyslogLocalMessageSerializer.cs b/SyslogNet.Client/Serialization/SyslogLocalMessageSerializer.cs index 8f67f6b..20571c6 100644 --- a/SyslogNet.Client/Serialization/SyslogLocalMessageSerializer.cs +++ b/SyslogNet.Client/Serialization/SyslogLocalMessageSerializer.cs @@ -1,30 +1,41 @@ -using System; -using System.Text; -using System.IO; + +using System; + namespace SyslogNet.Client.Serialization { - public class SyslogLocalMessageSerializer : SyslogMessageSerializerBase, ISyslogMessageSerializer - { - public Encoding Encoding { get; set; } - - // Default constructor: produce no BOM in local syslog messages - public SyslogLocalMessageSerializer() : this(false) { ; } - - // Optionally produce a BOM in local syslog messages by passing true here - // (This can produce problems with some older syslog programs, so default is false) - public SyslogLocalMessageSerializer(bool useBOM) { - Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: useBOM); - } - - public void Serialize(SyslogMessage message, Stream stream) - { - // Local syslog serialization only cares about the Message string - if (!String.IsNullOrWhiteSpace(message.Message)) - { - byte[] streamBytes = Encoding.GetBytes(message.Message); - stream.Write(streamBytes, 0, streamBytes.Length); - } - } - } + + + public class SyslogLocalMessageSerializer + : SyslogMessageSerializerBase, ISyslogMessageSerializer + { + public System.Text.Encoding Encoding { get; set; } + + + // Default constructor: produce no BOM in local syslog messages + public SyslogLocalMessageSerializer() + : this(false) + { } + + // Optionally produce a BOM in local syslog messages by passing true here + // (This can produce problems with some older syslog programs, so default is false) + public SyslogLocalMessageSerializer(bool useBOM) + { + Encoding = new System.Text.UTF8Encoding(encoderShouldEmitUTF8Identifier: useBOM); + } + + public void Serialize(SyslogMessage message, System.IO.Stream stream) + { + // Local syslog serialization only cares about the Message string + if (!message.Message.IsNullOrWhiteSpace()) + { + byte[] streamBytes = Encoding.GetBytes(message.Message); + stream.Write(streamBytes, 0, streamBytes.Length); + } + } + + + } + + } diff --git a/SyslogNet.Client/Serialization/SyslogMessageSerializerBase.cs b/SyslogNet.Client/Serialization/SyslogMessageSerializerBase.cs index 58aa47b..ec3bd6d 100644 --- a/SyslogNet.Client/Serialization/SyslogMessageSerializerBase.cs +++ b/SyslogNet.Client/Serialization/SyslogMessageSerializerBase.cs @@ -1,10 +1,19 @@ + namespace SyslogNet.Client.Serialization { - public abstract class SyslogMessageSerializerBase - { - protected static int CalculatePriorityValue(Facility facility, Severity severity) - { - return ((int)facility * 8) + (int)severity; - } - } + + + public abstract class SyslogMessageSerializerBase + { + + + protected static int CalculatePriorityValue(Facility facility, Severity severity) + { + return ((int)facility * 8) + (int)severity; + } + + + } + + } \ No newline at end of file diff --git a/SyslogNet.Client/Serialization/SyslogMessageSerializerExtensions.cs b/SyslogNet.Client/Serialization/SyslogMessageSerializerExtensions.cs index af841ef..b4fc5ea 100644 --- a/SyslogNet.Client/Serialization/SyslogMessageSerializerExtensions.cs +++ b/SyslogNet.Client/Serialization/SyslogMessageSerializerExtensions.cs @@ -1,13 +1,16 @@ -using System.IO; namespace SyslogNet.Client.Serialization { + + public static class SyslogMessageSerializerExtensions { + + public static byte[] Serialize(this ISyslogMessageSerializer serializer, SyslogMessage message) { byte[] datagramBytes; - using (var stream = new MemoryStream()) + using (System.IO.MemoryStream stream = new System.IO.MemoryStream()) { serializer.Serialize(message, stream); @@ -16,7 +19,12 @@ public static byte[] Serialize(this ISyslogMessageSerializer serializer, SyslogM datagramBytes = new byte[stream.Length]; stream.Read(datagramBytes, 0, (int)stream.Length); } + return datagramBytes; - } + } + + } + + } \ No newline at end of file diff --git a/SyslogNet.Client/Serialization/SyslogRfc3164MessageSerializer.cs b/SyslogNet.Client/Serialization/SyslogRfc3164MessageSerializer.cs index 91c9a17..be12e2e 100644 --- a/SyslogNet.Client/Serialization/SyslogRfc3164MessageSerializer.cs +++ b/SyslogNet.Client/Serialization/SyslogRfc3164MessageSerializer.cs @@ -1,32 +1,62 @@ -using System; -using System.IO; -using System.Text; - + namespace SyslogNet.Client.Serialization { - public class SyslogRfc3164MessageSerializer : SyslogMessageSerializerBase, ISyslogMessageSerializer - { - public void Serialize(SyslogMessage message, Stream stream) - { - var priorityValue = CalculatePriorityValue(message.Facility, message.Severity); - - string timestamp = null; - if (message.DateTimeOffset.HasValue) - { - var dt = message.DateTimeOffset.Value; - var day = dt.Day < 10 ? " " + dt.Day : dt.Day.ToString(); // Yes, this is stupid but it's in the spec - timestamp = String.Concat(dt.ToString("MMM "), day, dt.ToString(" HH:mm:ss")); - } - - var headerBuilder = new StringBuilder(); - headerBuilder.Append("<").Append(priorityValue).Append(">"); - headerBuilder.Append(timestamp).Append(" "); - headerBuilder.Append(message.HostName).Append(" "); - headerBuilder.Append(message.AppName.IfNotNullOrWhitespace(x => x.EnsureMaxLength(32) + ":")); - headerBuilder.Append(message.Message ?? ""); - - byte[] asciiBytes = Encoding.ASCII.GetBytes(headerBuilder.ToString()); - stream.Write(asciiBytes, 0, asciiBytes.Length); - } - } + + + public class SyslogRfc3164MessageSerializer + : SyslogMessageSerializerBase, ISyslogMessageSerializer + { + + protected bool m_useUtf8; + + + public SyslogRfc3164MessageSerializer(bool useUtf8) + { + m_useUtf8 = useUtf8; + } + + + public SyslogRfc3164MessageSerializer() + : this(false) + { } + + + public void Serialize(SyslogMessage message, System.IO.Stream stream) + { + int priorityValue = CalculatePriorityValue(message.Facility, message.Severity); + + string timestamp = null; + if (message.DateTimeOffset.HasValue) + { + System.DateTimeOffset dt = message.DateTimeOffset.Value; + string day = dt.Day < 10 ? " " + dt.Day.ToString(System.Globalization.CultureInfo.InvariantCulture) : dt.Day.ToString(System.Globalization.CultureInfo.InvariantCulture); // Yes, this is stupid but it's in the spec + timestamp = string.Concat(dt.ToString("MMM' '", System.Globalization.CultureInfo.InvariantCulture), day, dt.ToString("' 'HH':'mm':'ss", System.Globalization.CultureInfo.InvariantCulture)); + } + + System.Text.StringBuilder headerBuilder = new System.Text.StringBuilder(); + headerBuilder.Append("<").Append(priorityValue).Append(">"); + headerBuilder.Append(timestamp).Append(" "); + headerBuilder.Append(message.HostName).Append(" "); + headerBuilder.Append(message.AppName.IfNotNullOrWhitespace(x => x.EnsureMaxLength(32) + ":")); + + + if (!this.m_useUtf8) + headerBuilder.Append(message.Message ?? ""); + + byte[] asciiBytes = System.Text.Encoding.ASCII.GetBytes(headerBuilder.ToString()); + stream.Write(asciiBytes, 0, asciiBytes.Length); + + if (this.m_useUtf8) + { + stream.Write(System.Text.Encoding.UTF8.GetPreamble(), 0, System.Text.Encoding.UTF8.GetPreamble().Length); + asciiBytes = System.Text.Encoding.UTF8.GetBytes(message.Message ?? ""); + stream.Write(asciiBytes, 0, asciiBytes.Length); + } + + } + + + } + + } \ No newline at end of file diff --git a/SyslogNet.Client/Serialization/SyslogRfc5424MessageSerializer.cs b/SyslogNet.Client/Serialization/SyslogRfc5424MessageSerializer.cs index 76f5be2..dc6ed47 100644 --- a/SyslogNet.Client/Serialization/SyslogRfc5424MessageSerializer.cs +++ b/SyslogNet.Client/Serialization/SyslogRfc5424MessageSerializer.cs @@ -1,145 +1,148 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; + +using System; using System.Linq; + namespace SyslogNet.Client.Serialization { - public class SyslogRfc5424MessageSerializer : SyslogMessageSerializerBase, ISyslogMessageSerializer - { - public const string NilValue = "-"; - public static readonly HashSet sdNameDisallowedChars = new HashSet() {' ', '=', ']', '"' }; - - private readonly char[] asciiCharsBuffer = new char[255]; - - public void Serialize(SyslogMessage message, Stream stream) - { - var priorityValue = CalculatePriorityValue(message.Facility, message.Severity); - - // Note: The .Net ISO 8601 "o" format string uses 7 decimal places for fractional second. Syslog spec only allows 6, hence the custom format string - var timestamp = message.DateTimeOffset.HasValue - ? message.DateTimeOffset.Value.ToString("yyyy-MM-ddTHH:mm:ss.ffffffK") - : null; - - var messageBuilder = new StringBuilder(); - messageBuilder.Append("<").Append(priorityValue).Append(">"); - messageBuilder.Append(message.Version); - messageBuilder.Append(" ").Append(timestamp.FormatSyslogField(NilValue)); - messageBuilder.Append(" ").Append(message.HostName.FormatSyslogAsciiField(NilValue, 255, asciiCharsBuffer)); - messageBuilder.Append(" ").Append(message.AppName.FormatSyslogAsciiField(NilValue, 48, asciiCharsBuffer)); - messageBuilder.Append(" ").Append(message.ProcId.FormatSyslogAsciiField(NilValue, 128, asciiCharsBuffer)); - messageBuilder.Append(" ").Append(message.MsgId.FormatSyslogAsciiField(NilValue, 32, asciiCharsBuffer)); - - writeStream(stream, Encoding.ASCII, messageBuilder.ToString()); - - var structuredData = message.StructuredDataElements?.ToList(); - if (structuredData != null && structuredData.Any()) - { - // Space - stream.WriteByte(32); + + + public class SyslogRfc5424MessageSerializer + : SyslogMessageSerializerBase, ISyslogMessageSerializer + { + public const string NilValue = "-"; + internal static readonly System.Collections.Generic.HashSet sdNameDisallowedChars = + new System.Collections.Generic.HashSet() { ' ', '=', ']', '"' }; + + private readonly char[] asciiCharsBuffer = new char[255]; + + public void Serialize(SyslogMessage message, System.IO.Stream stream) + { + int priorityValue = CalculatePriorityValue(message.Facility, message.Severity); + + // Note: The .Net ISO 8601 "o" format string uses 7 decimal places for fractional second. Syslog spec only allows 6, hence the custom format string + string timestamp = message.DateTimeOffset.HasValue + ? message.DateTimeOffset.Value.ToString("yyyy'-'MM'-'ddTHH':'mm':'ss'.'ffffffK", System.Globalization.CultureInfo.InvariantCulture) + : null; + + System.Text.StringBuilder messageBuilder = new System.Text.StringBuilder(); + messageBuilder.Append("<").Append(priorityValue).Append(">"); + messageBuilder.Append(message.Version); + messageBuilder.Append(" ").Append(timestamp.FormatSyslogField(NilValue)); + messageBuilder.Append(" ").Append(message.HostName.FormatSyslogAsciiField(NilValue, 255, asciiCharsBuffer)); + messageBuilder.Append(" ").Append(message.AppName.FormatSyslogAsciiField(NilValue, 48, asciiCharsBuffer)); + messageBuilder.Append(" ").Append(message.ProcId.FormatSyslogAsciiField(NilValue, 128, asciiCharsBuffer)); + messageBuilder.Append(" ").Append(message.MsgId.FormatSyslogAsciiField(NilValue, 32, asciiCharsBuffer)); + + writeStream(stream, System.Text.Encoding.ASCII, messageBuilder.ToString()); + + System.Collections.Generic.List structuredData = message.StructuredDataElements; + if (structuredData != null && structuredData.Count > 0) + { + // Space + stream.WriteByte(32); // Structured data foreach (StructuredDataElement sdElement in structuredData) - { - messageBuilder.Clear() - .Append("[") - .Append(sdElement.SdId.FormatSyslogSdnameField(asciiCharsBuffer)); - - writeStream(stream, Encoding.ASCII, messageBuilder.ToString()); - - foreach(System.Collections.Generic.KeyValuePair sdParam in sdElement.Parameters) - { - messageBuilder.Clear() - .Append(" ") - .Append(sdParam.Key.FormatSyslogSdnameField(asciiCharsBuffer)) - .Append("=") - .Append("\"") - .Append( - sdParam.Value != null ? - sdParam.Value - .Replace("\\", "\\\\") - .Replace("\"", "\\\"") - .Replace("]", "\\]") - : - String.Empty - ) - .Append("\""); - - writeStream(stream, Encoding.UTF8, messageBuilder.ToString()); - } - - // ] - stream.WriteByte(93); - } - } - else - { - writeStream(stream, Encoding.ASCII, " "); - writeStream(stream, Encoding.ASCII, NilValue); - } - - if (!String.IsNullOrWhiteSpace(message.Message)) - { - // Space - stream.WriteByte(32); - - stream.Write(Encoding.UTF8.GetPreamble(), 0, Encoding.UTF8.GetPreamble().Length); - writeStream(stream, Encoding.UTF8, message.Message); - } - } - - private void writeStream(Stream stream, Encoding encoding, String data) - { - byte[] streamBytes = encoding.GetBytes(data); - stream.Write(streamBytes, 0, streamBytes.Length); - } - } - - internal static class StringExtensions - { - public static string IfNotNullOrWhitespace(this string s, Func action) - { - return String.IsNullOrWhiteSpace(s) ? s : action(s); - } - - public static string FormatSyslogField(this string s, string replacementValue, int? maxLength = null) - { - return String.IsNullOrWhiteSpace(s) - ? replacementValue - : maxLength.HasValue ? EnsureMaxLength(s, maxLength.Value) : s; - } - - public static string EnsureMaxLength(this string s, int maxLength) - { - return String.IsNullOrWhiteSpace(s) - ? s - : s.Length > maxLength ? s.Substring(0, maxLength) : s; - } - - public static string FormatSyslogAsciiField(this string s, string replacementValue, int maxLength, char[] charBuffer, Boolean sdName = false) - { - s = FormatSyslogField(s, replacementValue, maxLength); - - int bufferIndex = 0; - for (int i = 0; i < s.Length; i++) - { - char c = s[i]; - if (c >= 33 && c <= 126) - { - if (!sdName || !SyslogRfc5424MessageSerializer.sdNameDisallowedChars.Contains(c)) - { - charBuffer[bufferIndex++] = c; - } - } - } - - return new string(charBuffer, 0, bufferIndex); - } - - public static string FormatSyslogSdnameField(this string s, char[] charBuffer) - { - return FormatSyslogAsciiField(s, SyslogRfc5424MessageSerializer.NilValue, 32, charBuffer, true); - } - } + { + messageBuilder.Clear() + .Append("[") + .Append(sdElement.SdId.FormatSyslogSdnameField(asciiCharsBuffer)); + + writeStream(stream, System.Text.Encoding.ASCII, messageBuilder.ToString()); + + foreach (System.Collections.Generic.KeyValuePair sdParam in sdElement.Parameters) + { + messageBuilder.Clear() + .Append(" ") + .Append(sdParam.Key.FormatSyslogSdnameField(asciiCharsBuffer)) + .Append("=") + .Append("\"") + .Append( + sdParam.Value != null ? + sdParam.Value + .Replace("\\", "\\\\") + .Replace("\"", "\\\"") + .Replace("]", "\\]") + : + String.Empty + ) + .Append("\""); + + writeStream(stream, System.Text.Encoding.UTF8, messageBuilder.ToString()); + } + + // ] + stream.WriteByte(93); + } + } + else + { + writeStream(stream, System.Text.Encoding.ASCII, " "); + writeStream(stream, System.Text.Encoding.ASCII, NilValue); + } + + if (!message.Message.IsNullOrWhiteSpace()) + { + // Space + stream.WriteByte(32); + + stream.Write(System.Text.Encoding.UTF8.GetPreamble(), 0, System.Text.Encoding.UTF8.GetPreamble().Length); + writeStream(stream, System.Text.Encoding.UTF8, message.Message); + } + } + + private void writeStream(System.IO.Stream stream, System.Text.Encoding encoding, string data) + { + byte[] streamBytes = encoding.GetBytes(data); + stream.Write(streamBytes, 0, streamBytes.Length); + } + } + + internal static class StringExtensions + { + public static string IfNotNullOrWhitespace(this string s, System.Func action) + { + return s.IsNullOrWhiteSpace() ? s : action(s); + } + + public static string FormatSyslogField(this string s, string replacementValue, int? maxLength = null) + { + return s.IsNullOrWhiteSpace() + ? replacementValue + : maxLength.HasValue ? EnsureMaxLength(s, maxLength.Value) : s; + } + + public static string EnsureMaxLength(this string s, int maxLength) + { + return s.IsNullOrWhiteSpace() + ? s + : s.Length > maxLength ? s.Substring(0, maxLength) : s; + } + + public static string FormatSyslogAsciiField(this string s, string replacementValue, int maxLength, char[] charBuffer, bool sdName = false) + { + s = FormatSyslogField(s, replacementValue, maxLength); + + int bufferIndex = 0; + for (int i = 0; i < s.Length; i++) + { + char c = s[i]; + if (c >= 33 && c <= 126) + { + if (!sdName || !SyslogRfc5424MessageSerializer.sdNameDisallowedChars.Contains(c)) + { + charBuffer[bufferIndex++] = c; + } + } + } + + return new string(charBuffer, 0, bufferIndex); + } + + public static string FormatSyslogSdnameField(this string s, char[] charBuffer) + { + return FormatSyslogAsciiField(s, SyslogRfc5424MessageSerializer.NilValue, 32, charBuffer, true); + } + } } diff --git a/SyslogNet.Client/Severity.cs b/SyslogNet.Client/Severity.cs index d4aa38e..73d44da 100644 --- a/SyslogNet.Client/Severity.cs +++ b/SyslogNet.Client/Severity.cs @@ -1,38 +1,42 @@ -namespace SyslogNet.Client + +namespace SyslogNet.Client { - public enum Severity - { - /// - /// System is unusable - /// - Emergency = 0, - /// - /// Action must be taken immediately - /// - Alert = 1, - /// - /// Critical conditions - /// - Critical = 2, - /// - /// Error conditions - /// - Error = 3, - /// - /// Warning conditions - /// - Warning = 4, - /// - /// Normal but significant condition - /// - Notice = 5, - /// - /// Informational messages - /// - Informational = 6, - /// - /// Debug-level messages - /// - Debug = 7 - } -} \ No newline at end of file + + public enum Severity + { + /// + /// System is unusable + /// + Emergency = 0, + /// + /// Action must be taken immediately + /// + Alert = 1, + /// + /// Critical conditions + /// + Critical = 2, + /// + /// Error conditions + /// + Error = 3, + /// + /// Warning conditions + /// + Warning = 4, + /// + /// Normal but significant condition + /// + Notice = 5, + /// + /// Informational messages + /// + Informational = 6, + /// + /// Debug-level messages + /// + Debug = 7 + } + + +} diff --git a/SyslogNet.Client/StructuredDataElement.cs b/SyslogNet.Client/StructuredDataElement.cs index af8a211..8d39eb8 100644 --- a/SyslogNet.Client/StructuredDataElement.cs +++ b/SyslogNet.Client/StructuredDataElement.cs @@ -1,29 +1,41 @@ -using System.Collections.Generic; - + namespace SyslogNet.Client { - public class StructuredDataElement - { - // RFC 5424 specifies that you must provide a private enterprise number. If none specified, using example number reserved for documentation (see RFC) - public const string DefaultPrivateEnterpriseNumber = "32473"; - - private readonly string sdId; - private readonly Dictionary parameters; - - public StructuredDataElement(string sdId, Dictionary parameters) - { - this.sdId = sdId.Contains("@") ? sdId : sdId + "@" + DefaultPrivateEnterpriseNumber; - this.parameters = parameters; - } - - public string SdId - { - get { return sdId; } - } - - public Dictionary Parameters - { - get { return parameters; } - } - } -} \ No newline at end of file + + + public class StructuredDataElement + { + + + // RFC 5424 specifies that you must provide a private enterprise number. + // If none specified, using example number reserved for documentation (see RFC) + public const string DefaultPrivateEnterpriseNumber = "32473"; + + + private readonly string sdId; + private readonly System.Collections.Generic.Dictionary parameters; + + + public StructuredDataElement(string sdId, System.Collections.Generic.Dictionary parameters) + { + this.sdId = sdId.Contains("@") ? sdId : sdId + "@" + DefaultPrivateEnterpriseNumber; + this.parameters = parameters; + } + + + public string SdId + { + get { return sdId; } + } + + + public System.Collections.Generic.Dictionary Parameters + { + get { return parameters; } + } + + + } + + +} diff --git a/SyslogNet.Client/SyslogMessage.cs b/SyslogNet.Client/SyslogMessage.cs index fdcedd6..97878c7 100644 --- a/SyslogNet.Client/SyslogMessage.cs +++ b/SyslogNet.Client/SyslogMessage.cs @@ -1,99 +1,138 @@ -using System; -using System.Collections.Generic; - + namespace SyslogNet.Client { - public class SyslogMessage - { - public static Facility DefaultFacility = Facility.UserLevelMessages; - public static Severity DefaultSeverity = Severity.Informational; - - /// - /// Convenience overload for sending local syslog messages with default facility (UserLevelMessages) - /// - public SyslogMessage( - Severity severity, - string appName, - string message) - : this(DefaultFacility, severity, appName, message) - { - } - - /// - /// Constructor for use when sending local syslog messages - /// - public SyslogMessage( - Facility facility, - Severity severity, - string appName, - string message) - { - Facility = facility; - Severity = severity; - AppName = appName; - Message = message; - } - - /// - /// Constructor for use when sending RFC 3164 messages - /// - public SyslogMessage( - DateTimeOffset? dateTimeOffset, - Facility facility, - Severity severity, - string hostName, - string appName, - string message) - { - DateTimeOffset = dateTimeOffset; - Facility = facility; - Severity = severity; - HostName = hostName; - AppName = appName; - Message = message; - } - - /// - /// Constructor for use when sending RFC 5424 messages - /// - public SyslogMessage( - DateTimeOffset? dateTimeOffset, - Facility facility, - Severity severity, - string hostName, - string appName, - string procId, - string msgId, - string message, - params StructuredDataElement[] structuredDataElements) - : this(dateTimeOffset, facility, severity, hostName, appName, message) - { - ProcId = procId; - MsgId = msgId; - StructuredDataElements = structuredDataElements; - } - - public int Version - { - get { return 1; } - } - - public Facility Facility { get; set; } - - public Severity Severity { get; set; } - - public DateTimeOffset? DateTimeOffset { get; set; } - - public string HostName { get; set; } - - public string AppName { get; set; } - - public string ProcId { get; set; } - - public string MsgId { get; set; } - - public string Message { get; set; } - - public IEnumerable StructuredDataElements { get; set; } - } + + + public class SyslogMessage + { + public static Facility DefaultFacility = Facility.UserLevelMessages; + public static Severity DefaultSeverity = Severity.Informational; + + + + public int Version + { + get { return 1; } + } + + public Facility Facility { get; set; } + + public Severity Severity { get; set; } + + public System.DateTimeOffset? DateTimeOffset { get; set; } + + public string HostName { get; set; } + + public string AppName { get; set; } + + public string ProcId { get; set; } + + public string MsgId { get; set; } + + public string Message { get; set; } + + + public System.Collections.Generic.List StructuredDataElements + { + get; set; + } + + + /// + /// Convenience overload for sending local syslog messages with default facility (UserLevelMessages) + /// + public SyslogMessage( + Severity severity, + string appName, + string message) + : this(DefaultFacility, severity, appName, message) + { + } + + /// + /// Constructor for use when sending local syslog messages + /// + public SyslogMessage( + Facility facility, + Severity severity, + string appName, + string message) + { + Facility = facility; + Severity = severity; + AppName = appName; + Message = message; + } + + /// + /// Constructor for use when sending RFC 3164 messages + /// + public SyslogMessage( + System.DateTimeOffset? dateTimeOffset, + Facility facility, + Severity severity, + string hostName, + string appName, + string message) + { + DateTimeOffset = dateTimeOffset; + Facility = facility; + Severity = severity; + HostName = hostName; + AppName = appName; + Message = message; + } + + /// + /// Constructor for use when sending RFC 5424 messages + /// + public SyslogMessage( + System.DateTimeOffset? dateTimeOffset, + Facility facility, + Severity severity, + string hostName, + string appName, + string procId, + string msgId, + string message, + System.Collections.Generic.List structuredDataElements) + : this(dateTimeOffset, facility, severity, hostName, appName, message) + { + ProcId = procId; + MsgId = msgId; + StructuredDataElements = structuredDataElements; + } + + + /// + /// Constructor for use when sending RFC 5424 messages + /// + public SyslogMessage( + System.DateTimeOffset? dateTimeOffset, + Facility facility, + Severity severity, + string hostName, + string appName, + string procId, + string msgId, + string message, + params StructuredDataElement[] structuredDataElements) + : this(dateTimeOffset, facility, severity, hostName, appName, procId, msgId, message + , ((structuredDataElements != null && structuredDataElements.Length > 0) ? new System.Collections.Generic.List(structuredDataElements) : null) + ) + { } + + + public void Send(SyslogOptions options) + { + using (Transport.ISyslogMessageSender sender = options.Sender) + { + sender.Send(this, options.Serializer); + } + } + + + } + + } diff --git a/SyslogNet.Client/SyslogNet.Client.csproj b/SyslogNet.Client/SyslogNet.Client.csproj index 61b9051..06ccb59 100644 --- a/SyslogNet.Client/SyslogNet.Client.csproj +++ b/SyslogNet.Client/SyslogNet.Client.csproj @@ -8,7 +8,11 @@ https://github.com/emertechie/SyslogNet https://github.com/emertechie/SyslogNet SyslogNet.Client - net40;netstandard2.0 + + + net20;net35;net40;net45;net451;net452;net46;net461;net462;net47;net472;net48;netstandard2.0 0.3.3 1.0.3.3 1.0.3.3 @@ -19,4 +23,16 @@ pdbonly - \ No newline at end of file + + + DOTNET_20 + + + + DOTNET_35 + + + + DOTNET_40 + + diff --git a/SyslogNet.Client/SyslogNetException.cs b/SyslogNet.Client/SyslogNetException.cs index d9b972f..03f6aa1 100644 --- a/SyslogNet.Client/SyslogNetException.cs +++ b/SyslogNet.Client/SyslogNetException.cs @@ -1,27 +1,34 @@ -using System; -using System.Runtime.Serialization; - + namespace SyslogNet.Client { - [Serializable] - public class SyslogNetException : Exception + + + [System.Serializable] + public class SyslogNetException + : System.Exception { + + public SyslogNetException() - { - } - - public SyslogNetException(string message) : base(message) - { - } - - public SyslogNetException(string message, Exception inner) : base(message, inner) - { - } - - protected SyslogNetException( - SerializationInfo info, - StreamingContext context) : base(info, context) - { - } + { } + + + public SyslogNetException(string message) + : base(message) + { } + + + public SyslogNetException(string message, System.Exception inner) + : base(message, inner) + { } + + + protected SyslogNetException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) + : base(info, context) + { } + + } -} \ No newline at end of file + + +} diff --git a/SyslogNet.Client/SyslogOptions.cs b/SyslogNet.Client/SyslogOptions.cs new file mode 100644 index 0000000..52b9cd8 --- /dev/null +++ b/SyslogNet.Client/SyslogOptions.cs @@ -0,0 +1,179 @@ + +namespace SyslogNet.Client +{ + + + [System.Flags] + public enum SslProtocols + { + None = 0, // Allows the operating system to choose the best protocol to use + Ssl2 = 12, // SSL 2.0 + Ssl3 = 48, // SSL 3.0 + Tls = 192, // TLS 1.0, RFC 2246 + Default = 240, // Negotiate between SSL 3.0 or TLS 1.0 + Tls11 = 768, // RFC 4346 + Tls12 = 3072, // RFC 5246 + Tls13 = 12288 // RFC 8446 + } + + + public enum NetworkProtocols + { + UPD, + TCP, + TLS + } + + + public enum SyslogVersions + { + Rfc5424, + Rfc3164, // obsolete - for backwards compatiblity + Local + } + + + // Package CommandLineParser + public class SyslogOptions + { + + // [CommandLine.Option('h', "hostName", Required = false, HelpText = "The host name. If not set, defaults to the NetBIOS name of the local machine")] + public string LocalHostName { get; set; } + + // [CommandLine.Option('a', "appName", Required = false, HelpText = "The application name")] + public string AppName { get; set; } + + // [CommandLine.Option('p', "procId", Required = false, HelpText = "The process identifier")] + public string ProcId { get; set; } + + // [CommandLine.Option('t', "msgType", Required = false, HelpText = "The message type (called msgId in spec)")] + public string MsgType { get; set; } + + // [CommandLine.Option('m', "msg", Required = false, HelpText = "The message")] + public string Message { get; set; } + + // [CommandLine.Option('s', "syslogServer", Required = true, HelpText = "Host name of the syslog server")] + public string SyslogServerHostname { get; set; } + + // [CommandLine.Option('r', "syslogPort", Required = true, HelpText = "The syslog server port")] + public int SyslogServerPort { get; set; } + + // [CommandLine.Option('v', "version", Required = false, Default = "5424", HelpText = "The version of syslog protocol to use. Possible values are '3164' and '5424' (from corresponding RFC documents) or 'local' to send messages to a local syslog (only on Linux or OS X). Default is '5424'")] + public SyslogVersions SyslogVersion { get; set; } + + // [CommandLine.Option('o', "protocol", Required = false, Default = "tcp", HelpText = "The network protocol to use. Possible values are 'tcp' or 'udp' to send to a remote syslog server, or 'local' to send to a local syslog over Unix sockets (only on Linux or OS X). Default is 'tcp'. Note: TCP always uses SSL connection.")] + + + protected NetworkProtocols m_networkProtocol; + + + public NetworkProtocols NetworkProtocol + { + get { return this.m_networkProtocol; } + set + { + this.m_networkProtocol = value; + this.InferNetworkProtocol(); + } + } + + public SslProtocols SslProtocol { get; set; } + + public bool UseUtf8 { get; set; } + + + + + // [CommandLine.Option('c', "cert", Required = false, HelpText = "Optional path to a CA certificate used to verify Syslog server certificate when using TCP protocol")] + public string CACertPath { get; set; } + + public SyslogOptions() + { + this.UseUtf8 = true; + this.AppName = System.AppDomain.CurrentDomain.FriendlyName; + this.ProcId = System.Diagnostics.Process.GetCurrentProcess().Id.ToString(System.Globalization.CultureInfo.InvariantCulture); + this.LocalHostName = System.Environment.MachineName; + + this.SyslogVersion = SyslogVersions.Rfc5424; + this.SyslogServerHostname = "127.0.0.1"; + + this.NetworkProtocol = NetworkProtocols.UPD; + this.InferNetworkProtocol(); + this.InferDefaultPort(); + } + + + public void InferNetworkProtocol() + { + if (this.NetworkProtocol == NetworkProtocols.TLS) + { + if (this.SslProtocol == SslProtocols.None) + this.SslProtocol = SyslogNet.Client.SslProtocols.Tls; + } + else + { + this.SslProtocol = SyslogNet.Client.SslProtocols.None; + } + } + + + public void InferDefaultPort() + { + switch (this.NetworkProtocol) + { + case NetworkProtocols.TCP: + this.SyslogServerPort = 1468; // TCP + break; + case NetworkProtocols.TLS: + this.SyslogServerPort = 6514; // TLS + break; + default: + this.SyslogServerPort = 514; // UDP + break; + } + } + + + internal SyslogNet.Client.Serialization.ISyslogMessageSerializer Serializer + { + get + { + switch (this.SyslogVersion) + { + + + case SyslogVersions.Rfc5424: + return new SyslogNet.Client.Serialization.SyslogRfc5424MessageSerializer(); + case SyslogVersions.Rfc3164: + return new SyslogNet.Client.Serialization.SyslogRfc3164MessageSerializer(this.UseUtf8); + default: + return new SyslogNet.Client.Serialization.SyslogLocalMessageSerializer(); + } + + } + } + + + internal SyslogNet.Client.Transport.ISyslogMessageSender Sender + { + get + { + switch (this.NetworkProtocol) + { + case NetworkProtocols.UPD: + return new SyslogNet.Client.Transport.SyslogUdpSender(this.SyslogServerHostname, this.SyslogServerPort); + case NetworkProtocols.TCP: + return new SyslogNet.Client.Transport.SyslogTcpSender(this.SyslogServerHostname, this.SyslogServerPort); + case NetworkProtocols.TLS: + return new SyslogNet.Client.Transport.SyslogEncryptedTcpSender(this.SyslogServerHostname, this.SyslogServerPort, this.SslProtocol, -1, true); + default: + return new SyslogNet.Client.Transport.SyslogLocalSender(); + } + } + } + + + } + + +} diff --git a/SyslogNet.Client/Transport/ISyslogMessageSender.cs b/SyslogNet.Client/Transport/ISyslogMessageSender.cs index 5c1d58c..0c8dca3 100644 --- a/SyslogNet.Client/Transport/ISyslogMessageSender.cs +++ b/SyslogNet.Client/Transport/ISyslogMessageSender.cs @@ -1,13 +1,18 @@ -using System; -using System.Collections.Generic; + using SyslogNet.Client.Serialization; + namespace SyslogNet.Client.Transport { - public interface ISyslogMessageSender : IDisposable - { - void Reconnect(); - void Send(SyslogMessage message, ISyslogMessageSerializer serializer); - void Send(IEnumerable messages, ISyslogMessageSerializer serializer); - } + + + public interface ISyslogMessageSender + : System.IDisposable + { + void Reconnect(); + void Send(SyslogMessage message, ISyslogMessageSerializer serializer); + void Send(System.Collections.Generic.IEnumerable messages, ISyslogMessageSerializer serializer); + } + + } \ No newline at end of file diff --git a/SyslogNet.Client/Transport/SyslogEncryptedTcpSender.cs b/SyslogNet.Client/Transport/SyslogEncryptedTcpSender.cs index c06e840..7473228 100644 --- a/SyslogNet.Client/Transport/SyslogEncryptedTcpSender.cs +++ b/SyslogNet.Client/Transport/SyslogEncryptedTcpSender.cs @@ -1,76 +1,101 @@ -using System; -using System.Net.Security; -using System.Security; -using System.Security.Cryptography.X509Certificates; -using System.Threading; - + namespace SyslogNet.Client.Transport { - public class SyslogEncryptedTcpSender : SyslogTcpSender - { - protected int IOTimeout; - public Boolean IgnoreTLSChainErrors { get; private set; } - - protected MessageTransfer _messageTransfer; - public override MessageTransfer messageTransfer - { - get { return _messageTransfer; } - set - { - if (!value.Equals(MessageTransfer.OctetCounting) && transportStream is SslStream) - { - throw new SyslogTransportException("Non-Transparent-Framing can not be used with TLS transport"); - } - - _messageTransfer = value; - } - } - - public SyslogEncryptedTcpSender(string hostname, int port, int timeout = Timeout.Infinite, bool ignoreChainErrors = false) : base(hostname, port) - { - IOTimeout = timeout; - IgnoreTLSChainErrors = ignoreChainErrors; - startTLS(); - } - - public override void Reconnect() - { - base.Reconnect(); - startTLS(); - } - - private void startTLS() - { - transportStream = new SslStream(tcpClient.GetStream(), false, new RemoteCertificateValidationCallback(ValidateServerCertificate)) - { - ReadTimeout = IOTimeout, - WriteTimeout = IOTimeout - }; - - // According to RFC 5425 we MUST support TLS 1.2, but this protocol version only implemented in framework 4.5 and Windows Vista+... - ((SslStream)transportStream).AuthenticateAsClient( - hostname, - null, - System.Security.Authentication.SslProtocols.Tls, - false - ); - - if (!((SslStream)transportStream).IsEncrypted) - throw new SecurityException("Could not establish an encrypted connection"); - - messageTransfer = MessageTransfer.OctetCounting; - } - - private bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) - { - if (sslPolicyErrors == SslPolicyErrors.None || (IgnoreTLSChainErrors && sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors)) - return true; - - CertificateErrorHandler(String.Format("Certificate error: {0}", sslPolicyErrors)); - return false; - } - - // Quick and nasty way to avoid logging framework dependency - public static Action CertificateErrorHandler = err => { }; - } + + + public class SyslogEncryptedTcpSender + : SyslogTcpSender + { + + protected int IOTimeout; + protected SslProtocols _sslProtocol; + + public bool IgnoreTLSChainErrors { get; private set; } + + protected MessageTransfer _messageTransfer; + + + public override MessageTransfer messageTransfer + { + get { return _messageTransfer; } + set + { + if (!value.Equals(MessageTransfer.OctetCounting) && transportStream is System.Net.Security.SslStream) + { + throw new SyslogTransportException("Non-Transparent-Framing can not be used with TLS transport"); + } + + _messageTransfer = value; + } + } + + + public SyslogEncryptedTcpSender( + string hostname + , int port + , SslProtocols sslProtocol + , int timeout = System.Threading.Timeout.Infinite + , bool ignoreChainErrors = false) + : base(hostname, port) + { + IOTimeout = timeout; + IgnoreTLSChainErrors = ignoreChainErrors; + startTLS(); + } + + + public override void Reconnect() + { + base.Reconnect(); + startTLS(); + } + + + private void startTLS() + { + transportStream = new System.Net.Security.SslStream(tcpClient.GetStream(), false + , new System.Net.Security.RemoteCertificateValidationCallback(ValidateServerCertificate)) + { + ReadTimeout = IOTimeout, + WriteTimeout = IOTimeout + }; + + // According to RFC 5425 we MUST support TLS 1.2, but this protocol version only implemented in framework 4.5 and Windows Vista+... + ((System.Net.Security.SslStream)transportStream).AuthenticateAsClient( + hostname, + null, + (System.Security.Authentication.SslProtocols)_sslProtocol, + false + ); + + + + if (!((System.Net.Security.SslStream)transportStream).IsEncrypted) + throw new System.Security.SecurityException("Could not establish an encrypted connection"); + + messageTransfer = MessageTransfer.OctetCounting; + } + + + private bool ValidateServerCertificate( + object sender, + System.Security.Cryptography.X509Certificates.X509Certificate certificate, + System.Security.Cryptography.X509Certificates.X509Chain chain, + System.Net.Security.SslPolicyErrors sslPolicyErrors) + { + if (sslPolicyErrors == System.Net.Security.SslPolicyErrors.None || + (IgnoreTLSChainErrors && sslPolicyErrors == System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors)) + return true; + + CertificateErrorHandler(string.Format("Certificate error: {0}", sslPolicyErrors)); + return false; + } + + // Quick and nasty way to avoid logging framework dependency + public static System.Action CertificateErrorHandler = err => { }; + + + } + + } diff --git a/SyslogNet.Client/Transport/SyslogLocalSender.cs b/SyslogNet.Client/Transport/SyslogLocalSender.cs index 644235b..7b6b2e9 100644 --- a/SyslogNet.Client/Transport/SyslogLocalSender.cs +++ b/SyslogNet.Client/Transport/SyslogLocalSender.cs @@ -1,135 +1,160 @@ -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; + using SyslogNet.Client.Serialization; + namespace SyslogNet.Client.Transport { - public class SyslogLocalSender : ISyslogMessageSender, IDisposable - { - // Will not work on Windows, as this relies on Unix system calls - public SyslogLocalSender() - { - PlatformID platform = Environment.OSVersion.Platform; - if (!(platform == PlatformID.MacOSX || platform == PlatformID.Unix)) { - throw new SyslogNetException("SyslogLocalSender is only available on Unix-like systems (e.g., Linux, BSD, OS X)"); - } - } - - [DllImport("libc", ExactSpelling=true)] - // Because openlog() makes a copy of the char *ident that it gets passed, we have to - // make sure it gets marshalled into unmanaged memory. Hence the IntPtr ident parameter. - protected static extern void openlog(IntPtr ident, int option, int facility); - - [DllImport("libc", CharSet=CharSet.Ansi, ExactSpelling=true, CallingConvention=CallingConvention.Cdecl)] - protected static extern void syslog(int priority, string fmt, byte[] msg); - - [DllImport("libc", ExactSpelling=true)] - protected static extern void closelog(); - - public static ISyslogMessageSerializer defaultSerializer = new SyslogLocalMessageSerializer(); - - public static int CalculatePriorityValue(Facility? facility, Severity? severity) - { - return ((int)facility << 3) | (int)severity; - } - - public void Reconnect() - { - // Not needed; glibc syslog() is effectively "connectionless" - } - - protected IntPtr MarshalIdent(string ident) - { - return (ident == null) ? IntPtr.Zero : Marshal.StringToHGlobalAnsi(ident); - } - - protected void DisposeOfIdent(IntPtr identPtr) - { - if (identPtr != IntPtr.Zero) - Marshal.FreeHGlobal(identPtr); - } - - protected ISyslogMessageSerializer EnsureValidSerializer(ISyslogMessageSerializer serializer) { - return serializer ?? defaultSerializer; - } - - protected void SendToSyslog(SyslogMessage message, ISyslogMessageSerializer serializer) - { - int priority = CalculatePriorityValue(message.Facility, message.Severity); - serializer = EnsureValidSerializer(serializer); - byte[] data = serializer.Serialize(message); - syslog(priority, "%s", data); - } - - public void Send(SyslogMessage message, ISyslogMessageSerializer serializer) - { - IntPtr ident = IntPtr.Zero; - try - { - ident = MarshalIdent(message.AppName); - openlog(ident, (int)SyslogOptions.LogPid, CalculatePriorityValue(message.Facility, 0)); - SendToSyslog(message, serializer); - } - finally - { - closelog(); - DisposeOfIdent(ident); - } - } - - public void Send(IEnumerable messages, ISyslogMessageSerializer serializer) - { - // Slightly tricky, since we need to get the appName out of the first message before - // looping, so we can't just use foreach(). Using an explicit iterator works, though. - IntPtr ident = IntPtr.Zero; - using (IEnumerator iterator = messages.GetEnumerator()) - { - try - { - if (iterator.MoveNext()) - { - SyslogMessage message = iterator.Current; - ident = MarshalIdent(message.AppName); - openlog(ident, (int)SyslogOptions.LogPid, CalculatePriorityValue(message.Facility, 0)); - SendToSyslog(message, serializer); - } - while (iterator.MoveNext()) - { - SendToSyslog(iterator.Current, serializer); - } - } - finally - { - closelog(); - DisposeOfIdent(ident); - } - } - } - - // Convenience overloads since there's only one possible serializer for local syslog messages - public void Send(SyslogMessage message) - { - Send(message, defaultSerializer); - } - public void Send(IEnumerable messages) - { - Send(messages, defaultSerializer); - } - - public void Dispose() - { - // No action needed as we don't keep any state - } - - [Flags] - protected enum SyslogOptions : int { - LogPid = 1, - LogToConsoleIfErrorSendingToSyslog = 2, - DelayOpenUntilFirstSyslogCall = 4, - DontDelayOpen = 8, - DEPRECATED_DontWaitForConsoleForks = 16, - AlsoLogToStderr = 32 - } - } + + + public class SyslogLocalSender + : ISyslogMessageSender, System.IDisposable + { + + + // Will not work on Windows, as this relies on Unix system calls + public SyslogLocalSender() + { + System.PlatformID platform = System.Environment.OSVersion.Platform; + if (!(platform == System.PlatformID.MacOSX || platform == System.PlatformID.Unix)) + { + throw new SyslogNetException("SyslogLocalSender is only available on Unix-like systems (e.g., Linux, BSD, OS X)"); + } + } + + + [System.Runtime.InteropServices.DllImport("libc", ExactSpelling = true)] + // Because openlog() makes a copy of the char *ident that it gets passed, we have to + // make sure it gets marshalled into unmanaged memory. Hence the IntPtr ident parameter. + protected static extern void openlog(System.IntPtr ident, int option, int facility); + + [System.Runtime.InteropServices.DllImport("libc", CharSet = System.Runtime.InteropServices.CharSet.Ansi, ExactSpelling = true, CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl)] + protected static extern void syslog(int priority, string fmt, byte[] msg); + + [System.Runtime.InteropServices.DllImport("libc", ExactSpelling = true)] + protected static extern void closelog(); + + public static ISyslogMessageSerializer defaultSerializer = new SyslogLocalMessageSerializer(); + + + public static int CalculatePriorityValue(Facility? facility, Severity? severity) + { + return ((int)facility << 3) | (int)severity; + } + + + public void Reconnect() + { + // Not needed; glibc syslog() is effectively "connectionless" + } + + + protected System.IntPtr MarshalIdent(string ident) + { + return (ident == null) ? System.IntPtr.Zero : System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(ident); + } + + + protected void DisposeOfIdent(System.IntPtr identPtr) + { + if (identPtr != System.IntPtr.Zero) + System.Runtime.InteropServices.Marshal.FreeHGlobal(identPtr); + } + + + protected ISyslogMessageSerializer EnsureValidSerializer(ISyslogMessageSerializer serializer) + { + return serializer ?? defaultSerializer; + } + + + protected void SendToSyslog(SyslogMessage message, ISyslogMessageSerializer serializer) + { + int priority = CalculatePriorityValue(message.Facility, message.Severity); + serializer = EnsureValidSerializer(serializer); + byte[] data = serializer.Serialize(message); + syslog(priority, "%s", data); + } + + + public void Send(SyslogMessage message, ISyslogMessageSerializer serializer) + { + System.IntPtr ident = System.IntPtr.Zero; + try + { + ident = MarshalIdent(message.AppName); + openlog(ident, (int)SyslogOptions.LogPid, CalculatePriorityValue(message.Facility, 0)); + SendToSyslog(message, serializer); + } + finally + { + closelog(); + DisposeOfIdent(ident); + } + } + + + public void Send(System.Collections.Generic.IEnumerable messages, ISyslogMessageSerializer serializer) + { + // Slightly tricky, since we need to get the appName out of the first message before + // looping, so we can't just use foreach(). Using an explicit iterator works, though. + System.IntPtr ident = System.IntPtr.Zero; + using (System.Collections.Generic.IEnumerator iterator = messages.GetEnumerator()) + { + try + { + if (iterator.MoveNext()) + { + SyslogMessage message = iterator.Current; + ident = MarshalIdent(message.AppName); + openlog(ident, (int)SyslogOptions.LogPid, CalculatePriorityValue(message.Facility, 0)); + SendToSyslog(message, serializer); + } + while (iterator.MoveNext()) + { + SendToSyslog(iterator.Current, serializer); + } + } + finally + { + closelog(); + DisposeOfIdent(ident); + } + } + } + + + // Convenience overloads since there's only one possible serializer for local syslog messages + public void Send(SyslogMessage message) + { + Send(message, defaultSerializer); + } + + + public void Send(System.Collections.Generic.IEnumerable messages) + { + Send(messages, defaultSerializer); + } + + + public void Dispose() + { + // No action needed as we don't keep any state + } + + + [System.Flags] + protected enum SyslogOptions : int + { + LogPid = 1, + LogToConsoleIfErrorSendingToSyslog = 2, + DelayOpenUntilFirstSyslogCall = 4, + DontDelayOpen = 8, + DEPRECATED_DontWaitForConsoleForks = 16, + AlsoLogToStderr = 32 + } + + + } + + } diff --git a/SyslogNet.Client/Transport/SyslogTcpSender.cs b/SyslogNet.Client/Transport/SyslogTcpSender.cs index 59ccbfc..067e53a 100644 --- a/SyslogNet.Client/Transport/SyslogTcpSender.cs +++ b/SyslogNet.Client/Transport/SyslogTcpSender.cs @@ -1,129 +1,143 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net.Sockets; -using System.Text; -using System.Threading; + using SyslogNet.Client.Serialization; + namespace SyslogNet.Client.Transport { - public enum MessageTransfer { - OctetCounting = 0, - NonTransparentFraming = 1 - } - - public class SyslogTcpSender : ISyslogMessageSender, IDisposable - { - protected String hostname; - protected int port; - - protected TcpClient tcpClient = null; - protected Stream transportStream = null; - - public virtual MessageTransfer messageTransfer { get; set; } - public byte trailer { get; set; } - - public SyslogTcpSender(string hostname, int port, bool shouldAutoConnect = true) - { - this.hostname = hostname; - this.port = port; - - if (shouldAutoConnect) - { - Connect(); - } - - messageTransfer = MessageTransfer.OctetCounting; - trailer = 10; // LF - } - - public void Connect() - { - try - { - tcpClient = new TcpClient(hostname, port); - transportStream = tcpClient.GetStream(); - } - catch - { - Disconnect(); - throw; - } - } - - public virtual void Reconnect() - { - Disconnect(); - Connect(); - } - - public void Disconnect() - { - if (transportStream != null) - { - transportStream.Close(); - transportStream = null; - } - - if (tcpClient != null) - { - tcpClient.Close(); - tcpClient = null; - } - } - - public void Send(SyslogMessage message, ISyslogMessageSerializer serializer) - { - Send(message, serializer, true); - } - - protected void Send(SyslogMessage message, ISyslogMessageSerializer serializer, bool flush = true) - { - if(transportStream == null) - { - throw new IOException("No transport stream exists"); - } - - using (MemoryStream memoryStream = new MemoryStream()) - { - var datagramBytes = serializer.Serialize(message); - - if (messageTransfer.Equals(MessageTransfer.OctetCounting)) - { - byte[] messageLength = Encoding.ASCII.GetBytes(datagramBytes.Length.ToString()); - memoryStream.Write(messageLength, 0, messageLength.Length); - memoryStream.WriteByte(32); // Space - } - - memoryStream.Write(datagramBytes, 0, datagramBytes.Length); - - if (messageTransfer.Equals(MessageTransfer.NonTransparentFraming)) - { - memoryStream.WriteByte(trailer); // LF - } - - transportStream.Write(memoryStream.GetBuffer(), 0, (int)memoryStream.Length); - } - - if (flush && !(transportStream is NetworkStream)) - transportStream.Flush(); - } - - public void Send(IEnumerable messages, ISyslogMessageSerializer serializer) - { - foreach (SyslogMessage message in messages) - { - Send(message, serializer, false); - } - - if (!(transportStream is NetworkStream)) - transportStream.Flush(); - } - - public void Dispose() - { - Disconnect(); - } - } -} \ No newline at end of file + + + public enum MessageTransfer + { + OctetCounting = 0, + NonTransparentFraming = 1 + } + + + public class SyslogTcpSender + : ISyslogMessageSender, System.IDisposable + { + protected string hostname; + protected int port; + + protected System.Net.Sockets.TcpClient tcpClient = null; + protected System.IO.Stream transportStream = null; + + public virtual MessageTransfer messageTransfer { get; set; } + public byte trailer { get; set; } + + + public SyslogTcpSender(string hostname, int port, bool shouldAutoConnect = true) + { + this.hostname = hostname; + this.port = port; + + if (shouldAutoConnect) + { + Connect(); + } + + //messageTransfer = MessageTransfer.OctetCounting; + messageTransfer = MessageTransfer.NonTransparentFraming; + trailer = 10; // LF + } + + + public void Connect() + { + try + { + tcpClient = new System.Net.Sockets.TcpClient(hostname, port); + transportStream = tcpClient.GetStream(); + } + catch + { + Disconnect(); + throw; + } + } + + + public virtual void Reconnect() + { + Disconnect(); + Connect(); + } + + + public void Disconnect() + { + if (transportStream != null) + { + transportStream.Close(); + transportStream = null; + } + + if (tcpClient != null) + { + tcpClient.Close(); + tcpClient = null; + } + } + + + public void Send(SyslogMessage message, ISyslogMessageSerializer serializer) + { + Send(message, serializer, true); + } + + + protected void Send(SyslogMessage message, ISyslogMessageSerializer serializer, bool flush = true) + { + if (transportStream == null) + { + throw new System.IO.IOException("No transport stream exists"); + } + + using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream()) + { + byte[] datagramBytes = serializer.Serialize(message); + + if (messageTransfer.Equals(MessageTransfer.OctetCounting)) + { + byte[] messageLength = System.Text.Encoding.ASCII.GetBytes(datagramBytes.Length.ToString(System.Globalization.CultureInfo.InvariantCulture)); + memoryStream.Write(messageLength, 0, messageLength.Length); + memoryStream.WriteByte(32); // Space + } + + memoryStream.Write(datagramBytes, 0, datagramBytes.Length); + + if (messageTransfer.Equals(MessageTransfer.NonTransparentFraming)) + { + memoryStream.WriteByte(trailer); // LF + } + + transportStream.Write(memoryStream.GetBuffer(), 0, (int)memoryStream.Length); + } + + if (flush && !(transportStream is System.Net.Sockets.NetworkStream)) + transportStream.Flush(); + } + + + public void Send(System.Collections.Generic.IEnumerable messages, ISyslogMessageSerializer serializer) + { + foreach (SyslogMessage message in messages) + { + Send(message, serializer, false); + } + + if (!(transportStream is System.Net.Sockets.NetworkStream)) + transportStream.Flush(); + } + + + public void Dispose() + { + Disconnect(); + } + + + } + + +} diff --git a/SyslogNet.Client/Transport/SyslogTransportException.cs b/SyslogNet.Client/Transport/SyslogTransportException.cs index 06266eb..982360c 100644 --- a/SyslogNet.Client/Transport/SyslogTransportException.cs +++ b/SyslogNet.Client/Transport/SyslogTransportException.cs @@ -1,15 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - + namespace SyslogNet.Client.Transport { - class SyslogTransportException : Exception - { - public SyslogTransportException(string message) : base(message) - { - } - } + + public class SyslogTransportException + : System.Exception + { + public SyslogTransportException(string message) + : base(message) + { + + } + } + + } diff --git a/SyslogNet.Client/Transport/SyslogUdpSender.cs b/SyslogNet.Client/Transport/SyslogUdpSender.cs index 1e9b922..f29edaf 100644 --- a/SyslogNet.Client/Transport/SyslogUdpSender.cs +++ b/SyslogNet.Client/Transport/SyslogUdpSender.cs @@ -1,27 +1,32 @@ -using System; -using System.Collections.Generic; -using System.Net.Sockets; -using System.Text; + using SyslogNet.Client.Serialization; + namespace SyslogNet.Client.Transport { - public class SyslogUdpSender : ISyslogMessageSender, IDisposable + + + public class SyslogUdpSender + : ISyslogMessageSender, System.IDisposable { - private readonly UdpClient udpClient; + + private readonly System.Net.Sockets.UdpClient udpClient; + public SyslogUdpSender(string hostname, int port) { - udpClient = new UdpClient(hostname, port); + udpClient = new System.Net.Sockets.UdpClient(hostname, port); } + public void Send(SyslogMessage message, ISyslogMessageSerializer serializer) { byte[] datagramBytes = serializer.Serialize(message); udpClient.Send(datagramBytes, datagramBytes.Length); } - public void Send(IEnumerable messages, ISyslogMessageSerializer serializer) + + public void Send(System.Collections.Generic.IEnumerable messages, ISyslogMessageSerializer serializer) { foreach(SyslogMessage message in messages) { @@ -29,11 +34,20 @@ public void Send(IEnumerable messages, ISyslogMessageSerializer s } } - public void Reconnect() { /* UDP is connectionless */ } + + public void Reconnect() + { + // UDP is connectionless + } + public void Dispose() { udpClient.Close(); } + + } + + } \ No newline at end of file diff --git a/SyslogNet.Client/_FrameworkFix/DOTNET_20/ExtAttr.cs b/SyslogNet.Client/_FrameworkFix/DOTNET_20/ExtAttr.cs new file mode 100644 index 0000000..e5ae8a7 --- /dev/null +++ b/SyslogNet.Client/_FrameworkFix/DOTNET_20/ExtAttr.cs @@ -0,0 +1,36 @@ + +#if DOTNET_20 + +// 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: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// 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 +// +// Authors: +// Alejandro Serrano "Serras" (trupill@yahoo.es) +// + + +namespace System.Runtime.CompilerServices +{ + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)] + internal sealed class ExtensionAttribute + : Attribute + { } +} + +#endif // DOTNET_20 diff --git a/SyslogNet.Client/_FrameworkFix/DOTNET_20/Funcs.cs b/SyslogNet.Client/_FrameworkFix/DOTNET_20/Funcs.cs new file mode 100644 index 0000000..69dde61 --- /dev/null +++ b/SyslogNet.Client/_FrameworkFix/DOTNET_20/Funcs.cs @@ -0,0 +1,58 @@ + +#if DOTNET_20 + +// +// System.Func.cs +// +// Authors: +// Alejandro Serrano "Serras" (trupill@yahoo.es) +// Marek Safar (marek.safar@gmail.com) +// + +// 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: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// 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 TArg 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 +// + +using System.Runtime.CompilerServices; + +namespace System { + + + //[TypeForwardedFrom (Consts.AssemblySystemCore_3_5)] + internal delegate TResult Func (); + + //[TypeForwardedFrom (Consts.AssemblySystemCore_3_5)] + internal delegate TResult Func (T arg1); + + //[TypeForwardedFrom (Consts.AssemblySystemCore_3_5)] + internal delegate TResult Func (T1 arg1, T2 arg2); + + //[TypeForwardedFrom (Consts.AssemblySystemCore_3_5)] + internal delegate TResult Func (T1 arg1, T2 arg2, T3 arg3); + + //[TypeForwardedFrom (Consts.AssemblySystemCore_3_5)] + internal delegate TResult Func (T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + internal delegate TResult Func (T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); + internal delegate TResult Func (T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6); + internal delegate TResult Func (T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7); + internal delegate TResult Func (T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8); + +} + +#endif diff --git a/SyslogNet.Client/_FrameworkFix/DOTNET_20/HashSet.cs b/SyslogNet.Client/_FrameworkFix/DOTNET_20/HashSet.cs new file mode 100644 index 0000000..2b01506 --- /dev/null +++ b/SyslogNet.Client/_FrameworkFix/DOTNET_20/HashSet.cs @@ -0,0 +1,765 @@ + +#if DOTNET_20 + +// +// HashSet.cs +// +// Authors: +// Jb Evain +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// 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: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// 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. +// + + +using System.Runtime.Serialization; +using System.Security.Permissions; +using System.Diagnostics; + +// HashSet is basically implemented as a reduction of Dictionary + +namespace System.Collections.Generic +{ + + [Serializable, HostProtection(SecurityAction.LinkDemand, MayLeakOnAbort = true)] + [DebuggerDisplay("Count={Count}")] + //[DebuggerTypeProxy (typeof (CollectionDebuggerView<,>))] + internal class HashSet : ICollection, ISerializable, IDeserializationCallback + { + const int INITIAL_SIZE = 10; + const float DEFAULT_LOAD_FACTOR = (90f / 100); + const int NO_SLOT = -1; + const int HASH_FLAG = -2147483648; + + struct Link + { + public int HashCode; + public int Next; + } + + // The hash table contains indices into the "links" array + int[] table; + + Link[] links; + T[] slots; + + // The number of slots in "links" and "slots" that + // are in use (i.e. filled with data) or have been used and marked as + // "empty" later on. + int touched; + + // The index of the first slot in the "empty slots chain". + // "Remove ()" prepends the cleared slots to the empty chain. + // "Add ()" fills the first slot in the empty slots chain with the + // added item (or increases "touched" if the chain itself is empty). + int empty_slot; + + // The number of items in this set. + int count; + + // The number of items the set can hold without + // resizing the hash table and the slots arrays. + int threshold; + + IEqualityComparer comparer; + SerializationInfo si; + + // The number of changes made to this set. Used by enumerators + // to detect changes and invalidate themselves. + int generation; + + public int Count + { + get { return count; } + } + + public HashSet() + { + Init(INITIAL_SIZE, null); + } + + public HashSet(IEqualityComparer comparer) + { + Init(INITIAL_SIZE, comparer); + } + + public HashSet(IEnumerable collection) : this(collection, null) + { + } + + public HashSet(IEnumerable collection, IEqualityComparer comparer) + { + if (collection == null) + throw new ArgumentNullException("collection"); + + int capacity = 0; + ICollection col = collection as ICollection; + if (col != null) + capacity = col.Count; + + Init(capacity, comparer); + foreach (T item in collection) + Add(item); + } + + protected HashSet(SerializationInfo info, StreamingContext context) + { + si = info; + } + + void Init(int capacity, IEqualityComparer comparer) + { + if (capacity < 0) + throw new ArgumentOutOfRangeException("capacity"); + + this.comparer = comparer ?? EqualityComparer.Default; + if (capacity == 0) + capacity = INITIAL_SIZE; + + /* Modify capacity so 'capacity' elements can be added without resizing */ + capacity = (int)(capacity / DEFAULT_LOAD_FACTOR) + 1; + + InitArrays(capacity); + generation = 0; + } + + void InitArrays(int size) + { + table = new int[size]; + + links = new Link[size]; + empty_slot = NO_SLOT; + + slots = new T[size]; + touched = 0; + + threshold = (int)(table.Length * DEFAULT_LOAD_FACTOR); + if (threshold == 0 && table.Length > 0) + threshold = 1; + } + + bool SlotsContainsAt(int index, int hash, T item) + { + int current = table[index] - 1; + while (current != NO_SLOT) + { + Link link = links[current]; + if (link.HashCode == hash && ((hash == HASH_FLAG && (item == null || null == slots[current])) ? (item == null && null == slots[current]) : comparer.Equals(item, slots[current]))) + return true; + + current = link.Next; + } + + return false; + } + + public void CopyTo(T[] array) + { + CopyTo(array, 0, count); + } + + public void CopyTo(T[] array, int index) + { + CopyTo(array, index, count); + } + + public void CopyTo(T[] array, int index, int count) + { + if (array == null) + throw new ArgumentNullException("array"); + if (index < 0) + throw new ArgumentOutOfRangeException("index"); + if (index > array.Length) + throw new ArgumentException("index larger than largest valid index of array"); + if (array.Length - index < count) + throw new ArgumentException("Destination array cannot hold the requested elements!"); + + for (int i = 0, items = 0; i < touched && items < count; i++) + { + if (GetLinkHashCode(i) != 0) + array[index++] = slots[i]; + } + } + + void Resize() + { + int newSize = PrimeHelper.ToPrime((table.Length << 1) | 1); + + // allocate new hash table and link slots array + int[] newTable = new int[newSize]; + Link[] newLinks = new Link[newSize]; + + for (int i = 0; i < table.Length; i++) + { + int current = table[i] - 1; + while (current != NO_SLOT) + { + int hashCode = newLinks[current].HashCode = GetItemHashCode(slots[current]); + int index = (hashCode & int.MaxValue) % newSize; + newLinks[current].Next = newTable[index] - 1; + newTable[index] = current + 1; + current = links[current].Next; + } + } + + table = newTable; + links = newLinks; + + // allocate new data slots, copy data + T[] newSlots = new T[newSize]; + Array.Copy(slots, 0, newSlots, 0, touched); + slots = newSlots; + + threshold = (int)(newSize * DEFAULT_LOAD_FACTOR); + } + + int GetLinkHashCode(int index) + { + return links[index].HashCode & HASH_FLAG; + } + + int GetItemHashCode(T item) + { + if (item == null) + return HASH_FLAG; + return comparer.GetHashCode(item) | HASH_FLAG; + } + + public bool Add(T item) + { + int hashCode = GetItemHashCode(item); + int index = (hashCode & int.MaxValue) % table.Length; + + if (SlotsContainsAt(index, hashCode, item)) + return false; + + if (++count > threshold) + { + Resize(); + index = (hashCode & int.MaxValue) % table.Length; + } + + // find an empty slot + int current = empty_slot; + if (current == NO_SLOT) + current = touched++; + else + empty_slot = links[current].Next; + + // store the hash code of the added item, + // prepend the added item to its linked list, + // update the hash table + links[current].HashCode = hashCode; + links[current].Next = table[index] - 1; + table[index] = current + 1; + + // store item + slots[current] = item; + + generation++; + + return true; + } + + public IEqualityComparer Comparer + { + get { return comparer; } + } + + public void Clear() + { + count = 0; + + Array.Clear(table, 0, table.Length); + Array.Clear(slots, 0, slots.Length); + Array.Clear(links, 0, links.Length); + + // empty the "empty slots chain" + empty_slot = NO_SLOT; + + touched = 0; + generation++; + } + + public bool Contains(T item) + { + int hashCode = GetItemHashCode(item); + int index = (hashCode & int.MaxValue) % table.Length; + + return SlotsContainsAt(index, hashCode, item); + } + + public bool Remove(T item) + { + // get first item of linked list corresponding to given key + int hashCode = GetItemHashCode(item); + int index = (hashCode & int.MaxValue) % table.Length; + int current = table[index] - 1; + + // if there is no linked list, return false + if (current == NO_SLOT) + return false; + + // walk linked list until right slot (and its predecessor) is + // found or end is reached + int prev = NO_SLOT; + do + { + Link link = links[current]; + if (link.HashCode == hashCode && ((hashCode == HASH_FLAG && (item == null || null == slots[current])) ? (item == null && null == slots[current]) : comparer.Equals(slots[current], item))) + break; + + prev = current; + current = link.Next; + } while (current != NO_SLOT); + + // if we reached the end of the chain, return false + if (current == NO_SLOT) + return false; + + count--; + + // remove slot from linked list + // is slot at beginning of linked list? + if (prev == NO_SLOT) + table[index] = links[current].Next + 1; + else + links[prev].Next = links[current].Next; + + // mark slot as empty and prepend it to "empty slots chain" + links[current].Next = empty_slot; + empty_slot = current; + + // clear slot + links[current].HashCode = 0; + slots[current] = default(T); + + generation++; + + return true; + } + + + public int RemoveWhere(Predicate predicate) + { + if (predicate == null) + throw new ArgumentNullException("predicate"); + + int counter = 0; + + List candidates = new List(); + + foreach (T item in this) + if (predicate(item)) + candidates.Add(item); + + foreach (T item in candidates) + Remove(item); + + return candidates.Count; + } + + + public void TrimExcess() + { + Resize(); + } + + + // set operations + public void IntersectWith(IEnumerable other) + { + if (other == null) + throw new ArgumentNullException("other"); + + HashSet other_set = ToSet(other); + + RemoveWhere(item => !other_set.Contains(item)); + } + + + public void ExceptWith(IEnumerable other) + { + if (other == null) + throw new ArgumentNullException("other"); + + foreach (T item in other) + Remove(item); + } + + + public bool Overlaps(IEnumerable other) + { + if (other == null) + throw new ArgumentNullException("other"); + + foreach (T item in other) + if (Contains(item)) + return true; + + return false; + } + + public bool SetEquals(IEnumerable other) + { + if (other == null) + throw new ArgumentNullException("other"); + + HashSet other_set = ToSet(other); + + if (count != other_set.Count) + return false; + + foreach (T item in this) + if (!other_set.Contains(item)) + return false; + + return true; + } + + public void SymmetricExceptWith(IEnumerable other) + { + if (other == null) + throw new ArgumentNullException("other"); + + foreach (T item in ToSet(other)) + if (!Add(item)) + Remove(item); + } + + HashSet ToSet(IEnumerable enumerable) + { + HashSet set = enumerable as HashSet; + if (set == null || !Comparer.Equals(set.Comparer)) + set = new HashSet(enumerable); + + return set; + } + + public void UnionWith(IEnumerable other) + { + if (other == null) + throw new ArgumentNullException("other"); + + foreach (T item in other) + Add(item); + } + + bool CheckIsSubsetOf(HashSet other) + { + if (other == null) + throw new ArgumentNullException("other"); + + foreach (T item in this) + if (!other.Contains(item)) + return false; + + return true; + } + + public bool IsSubsetOf(IEnumerable other) + { + if (other == null) + throw new ArgumentNullException("other"); + + if (count == 0) + return true; + + HashSet other_set = ToSet(other); + + if (count > other_set.Count) + return false; + + return CheckIsSubsetOf(other_set); + } + + public bool IsProperSubsetOf(IEnumerable other) + { + if (other == null) + throw new ArgumentNullException("other"); + + if (count == 0) + return true; + + HashSet other_set = ToSet(other); + + if (count >= other_set.Count) + return false; + + return CheckIsSubsetOf(other_set); + } + + bool CheckIsSupersetOf(HashSet other) + { + if (other == null) + throw new ArgumentNullException("other"); + + foreach (T item in other) + if (!Contains(item)) + return false; + + return true; + } + + public bool IsSupersetOf(IEnumerable other) + { + if (other == null) + throw new ArgumentNullException("other"); + + HashSet other_set = ToSet(other); + + if (count < other_set.Count) + return false; + + return CheckIsSupersetOf(other_set); + } + + + public bool IsProperSupersetOf(IEnumerable other) + { + if (other == null) + throw new ArgumentNullException("other"); + + HashSet other_set = ToSet(other); + + if (count <= other_set.Count) + return false; + + return CheckIsSupersetOf(other_set); + } + + + [MonoTODO] + public static IEqualityComparer> CreateSetComparer() + { + throw new NotImplementedException(); + } + + + [MonoTODO] + [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] + public virtual void GetObjectData(SerializationInfo info, StreamingContext context) + { + throw new NotImplementedException(); + } + + + [MonoTODO] + public virtual void OnDeserialization(object sender) + { + if (si == null) + return; + + throw new NotImplementedException(); + } + + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(this); + } + + + bool ICollection.IsReadOnly + { + get { return false; } + } + + + void ICollection.CopyTo(T[] array, int index) + { + CopyTo(array, index); + } + + + void ICollection.Add(T item) + { + Add(item); + } + + + IEnumerator IEnumerable.GetEnumerator() + { + return new Enumerator(this); + } + + + public Enumerator GetEnumerator() + { + return new Enumerator(this); + } + + + [Serializable] + public struct Enumerator : IEnumerator, IDisposable + { + HashSet hashset; + int next; + int stamp; + + T current; + + internal Enumerator(HashSet hashset) + : this() + { + this.hashset = hashset; + this.stamp = hashset.generation; + } + + public bool MoveNext() + { + CheckState(); + + if (next < 0) + return false; + + while (next < hashset.touched) + { + int cur = next++; + if (hashset.GetLinkHashCode(cur) != 0) + { + current = hashset.slots[cur]; + return true; + } + } + + next = NO_SLOT; + return false; + } + + public T Current + { + get { return current; } + } + + object IEnumerator.Current + { + get + { + CheckState(); + if (next <= 0) + throw new InvalidOperationException("Current is not valid"); + return current; + } + } + + void IEnumerator.Reset() + { + CheckState(); + next = 0; + } + + public void Dispose() + { + hashset = null; + } + + void CheckState() + { + if (hashset == null) + throw new ObjectDisposedException(null); + if (hashset.generation != stamp) + throw new InvalidOperationException("HashSet have been modified while it was iterated over"); + } + } + + // borrowed from System.Collections.HashTable + static class PrimeHelper + { + + static readonly int[] primes_table = { + 11, + 19, + 37, + 73, + 109, + 163, + 251, + 367, + 557, + 823, + 1237, + 1861, + 2777, + 4177, + 6247, + 9371, + 14057, + 21089, + 31627, + 47431, + 71143, + 106721, + 160073, + 240101, + 360163, + 540217, + 810343, + 1215497, + 1823231, + 2734867, + 4102283, + 6153409, + 9230113, + 13845163 + }; + + static bool TestPrime(int x) + { + if ((x & 1) != 0) + { + int top = (int)Math.Sqrt(x); + + for (int n = 3; n < top; n += 2) + { + if ((x % n) == 0) + return false; + } + + return true; + } + + // There is only one even prime - 2. + return x == 2; + } + + static int CalcPrime(int x) + { + for (int i = (x & (~1)) - 1; i < Int32.MaxValue; i += 2) + if (TestPrime(i)) + return i; + + return x; + } + + public static int ToPrime(int x) + { + for (int i = 0; i < primes_table.Length; i++) + if (x <= primes_table[i]) + return primes_table[i]; + + return CalcPrime(x); + } + } + } +} + +#endif diff --git a/SyslogNet.Client/_FrameworkFix/DOTNET_20/MonoTODOAttribute.cs b/SyslogNet.Client/_FrameworkFix/DOTNET_20/MonoTODOAttribute.cs new file mode 100644 index 0000000..3d89375 --- /dev/null +++ b/SyslogNet.Client/_FrameworkFix/DOTNET_20/MonoTODOAttribute.cs @@ -0,0 +1,127 @@ + +#if DOTNET_20 + +// +// MonoTODOAttribute.cs +// +// Authors: +// Ravi Pratap (ravi@ximian.com) +// Eyal Alaluf +// +// (C) Ximian, Inc. http://www.ximian.com +// + +// +// Copyright (C) 2004 Novell, Inc (http://www.novell.com) +// Copyright (C) 2006 Mainsoft, Inc (http://www.mainsoft.com) +// +// 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: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// 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. +// + +namespace System { + + [AttributeUsage (AttributeTargets.All, AllowMultiple=true)] + internal class MonoTODOAttribute + : Attribute + { + + string comment; + + public MonoTODOAttribute () + { + } + + public MonoTODOAttribute (string comment) + { + this.comment = comment; + } + + public virtual string Comment { + get { return comment; } + } + } + + [AttributeUsage (AttributeTargets.All, AllowMultiple=true)] + internal class MonoDocumentationNoteAttribute : MonoTODOAttribute { + + public MonoDocumentationNoteAttribute (string comment) + : base (comment) + { + } + + public override string Comment { + get { return base.Comment; } + } + } + + [AttributeUsage (AttributeTargets.All, AllowMultiple=true)] + internal class MonoExtensionAttribute : MonoTODOAttribute { + + public MonoExtensionAttribute (string comment) + : base (comment) + { + } + + public override string Comment { + get { return base.Comment; } + } + } + + [AttributeUsage (AttributeTargets.All, AllowMultiple=true)] + internal class MonoInternalNoteAttribute : MonoTODOAttribute { + + public MonoInternalNoteAttribute (string comment) + : base (comment) + { + } + + public override string Comment { + get { return base.Comment; } + } + } + + [AttributeUsage (AttributeTargets.All, AllowMultiple=true)] + internal class MonoLimitationAttribute : MonoTODOAttribute { + + public MonoLimitationAttribute (string comment) + : base (comment) + { + } + + public override string Comment { + get { return base.Comment; } + } + } + + [AttributeUsage (AttributeTargets.All, AllowMultiple=true)] + internal class MonoNotSupportedAttribute : MonoTODOAttribute { + + public MonoNotSupportedAttribute (string comment) + : base (comment) + { + } + + public override string Comment { + get { return base.Comment; } + } + } +} + +#endif diff --git a/SyslogNet.Client/_FrameworkFix/DOTNET_20/StringBuilderExtensions.cs b/SyslogNet.Client/_FrameworkFix/DOTNET_20/StringBuilderExtensions.cs new file mode 100644 index 0000000..3dec57b --- /dev/null +++ b/SyslogNet.Client/_FrameworkFix/DOTNET_20/StringBuilderExtensions.cs @@ -0,0 +1,19 @@ + +#if DOTNET_20 || DOTNET_35 + +namespace System.Linq +{ + + + internal static class StringBuilderExtensions + { + + internal static System.Text.StringBuilder Clear(this System.Text.StringBuilder sb) + { + sb.Length = 0; + return sb; + } + } +} + +#endif diff --git a/SyslogNet.Client/_FrameworkFix/DOTNET_20/SyslogClientStringExtensions.cs b/SyslogNet.Client/_FrameworkFix/DOTNET_20/SyslogClientStringExtensions.cs new file mode 100644 index 0000000..19086a7 --- /dev/null +++ b/SyslogNet.Client/_FrameworkFix/DOTNET_20/SyslogClientStringExtensions.cs @@ -0,0 +1,22 @@ + +namespace System +{ + + + internal static class SyslogClientStringExtensions + { + + + internal static bool IsNullOrWhiteSpace(this string s) + { + if (s == null) + return true; + + return (s.Trim() == string.Empty); + } + + + } + + +} diff --git a/SyslogNet.sln b/SyslogNet.sln index 5f71c7c..0e7e60e 100644 --- a/SyslogNet.sln +++ b/SyslogNet.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27703.2047 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30804.86 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SyslogNet.Client", "SyslogNet.Client\SyslogNet.Client.csproj", "{F94B72CD-DA67-436E-9244-FA79DF140E0A}" EndProject @@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SyslogNet.Client.Tests", "S EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TesterApp", "TesterApp\TesterApp.csproj", "{9D7482EB-EA3F-4047-9E79-C80EC8E18FED}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApplication", "TestApplication\TestApplication.csproj", "{920421CB-566C-466C-9325-DD91F8F53801}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +29,10 @@ Global {9D7482EB-EA3F-4047-9E79-C80EC8E18FED}.Debug|Any CPU.Build.0 = Debug|Any CPU {9D7482EB-EA3F-4047-9E79-C80EC8E18FED}.Release|Any CPU.ActiveCfg = Release|Any CPU {9D7482EB-EA3F-4047-9E79-C80EC8E18FED}.Release|Any CPU.Build.0 = Release|Any CPU + {920421CB-566C-466C-9325-DD91F8F53801}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {920421CB-566C-466C-9325-DD91F8F53801}.Debug|Any CPU.Build.0 = Debug|Any CPU + {920421CB-566C-466C-9325-DD91F8F53801}.Release|Any CPU.ActiveCfg = Release|Any CPU + {920421CB-566C-466C-9325-DD91F8F53801}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/TestApplication/Program.cs b/TestApplication/Program.cs new file mode 100644 index 0000000..61b0a52 --- /dev/null +++ b/TestApplication/Program.cs @@ -0,0 +1,157 @@ + +namespace TestApplication +{ + + public class MyStructuredData + { + public string Hello = "World"; + public int SomeNumber = 123; + public System.DateTime SomeTime = System.DateTime.Now; + + private int SomePriveNumber = 456; + private string SomePrivateString = "I'm Private !"; + private System.DateTime SomeOtherTime = System.DateTime.UtcNow; + + + public string HelloNull = null; + public int? SomeNumberNull = null; + public System.DateTime? SomeTimeNull = null; + public System.DateTime? SomeTimeNotNull = System.DateTime.UtcNow; + + } + + + + public class Program + { + + + private static SyslogNet.Client.SyslogMessage CreateSyslogMessage( + SyslogNet.Client.SyslogOptions options + , string message) + { + // https://www.syslog-ng.com/technical-documents/doc/syslog-ng-open-source-edition/3.16/administration-guide/option-description-log-msg-size + // Description: Maximum length of a message in bytes. + // This length includes the entire message + // (the data structure and individual fields). + // The maximal value that can be set is 268435456 bytes(256MB). + // For messages using the IETF-syslog message format(RFC5424), + // the maximal size of the value of an SDATA field is 64kB. + // In most cases, it is not recommended to set log-msg-size() + // higher than 10 MiB. + + // https://stackoverflow.com/questions/3310875/find-syslog-max-message-length + // Keep in mind syslog is a protocol, + // which means it sets minimums and makes recommendations. + // I can't find a source to this, but I believe + // the minimum length that should be supported is 1k, + // with 64k being recommended. + + + System.Collections.Generic.Dictionary sd = new System.Collections.Generic.Dictionary(System.StringComparer.InvariantCultureIgnoreCase); + sd["Hello"] = "World"; + sd["Привет"] = "мир"; + sd["你好"] = "世界"; + sd["nixda"] = "[]"; + sd["foo[]bar"] = "{}"; + + + SyslogNet.Client.StructuredDataElement sde = new SyslogNet.Client.StructuredDataElement("sdld", sd); + + + // Each implementation is free to do what they want, + // i.e. if you wanted a 16MB maximum and were writing + // a syslog server, you're free to do that. + // I'm not sure why you would, but you could. + // As far as I know, there is no standard programatic way + // of ascertaining this, so keeping messages at + // just under 1k would be ideal for portability. + return new SyslogNet.Client.SyslogMessage( + System.DateTimeOffset.Now, + SyslogNet.Client.Facility.UserLevelMessages, + SyslogNet.Client.Severity.Error, + options.LocalHostName, + options.AppName, + options.ProcId, + options.MsgType, + message ?? (options.Message ?? + "Test message at " + + System.DateTime.UtcNow.ToString("dddd, dd.MM.yyyy HH:mm:ss.fff", System.Globalization.CultureInfo.InvariantCulture)) + , sde + ); + } // End Function CreateSyslogMessage + + + + public static bool AllowAnything( + object sender + , System.Security.Cryptography.X509Certificates.X509Certificate certificate + , System.Security.Cryptography.X509Certificates.X509Chain chain + , System.Net.Security.SslPolicyErrors sslPolicyErrors) + { + return true; + } // End Function AllowAnything + + + public static void Main(string[] args) + { + MyStructuredData msd = new MyStructuredData(); + + System.Collections.Generic.Dictionary dict = SyslogNet.Client.LinqHelper.GetStringDictionary(msd); + System.Console.WriteLine(dict); + + + + System.Reflection.MemberInfo[] tnP = SyslogNet.Client.LinqHelper.GetFieldsAndProperties(msd.GetType()); + SyslogNet.Client.Getter_t[] gettrs = SyslogNet.Client.LinqHelper.GetGetters(); + SyslogNet.Client.Setter_t[] settrs = SyslogNet.Client.LinqHelper.GetSetters(); + + object o = gettrs[6](msd); + System.Console.WriteLine(o); + + System.DateTime? dt = SyslogNet.Client.LinqHelper.ChangeType(gettrs[5](msd)); + System.Console.WriteLine(dt); + + settrs[6](msd, dt); + + + + + + // System.Net.ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(AllowAnything); + + // SyslogNet.Client.SyslogOptions options = null; + // delegate (SyslogNet.Client.SyslogOptions a) { options = a; } + // CommandLine.Parser.Default.ParseArguments(args).WithParsed(opt => options = opt); + // CommandLine.ParserResultExtensions.WithParsed(CommandLine.Parser.Default.ParseArguments(args), opt => options = opt); + + SyslogNet.Client.SyslogOptions options = new SyslogNet.Client.SyslogOptions(); + // options.SyslogVersion = SyslogNet.Client.SyslogVersions.Rfc3164; + options.SyslogVersion = SyslogNet.Client.SyslogVersions.Rfc5424; + options.NetworkProtocol = SyslogNet.Client.NetworkProtocols.TCP; + // options.NetworkProtocol = SyslogNet.Client.NetworkProtocols.UPD; + // options.SyslogServerPort = 515; // Visual Syslog + options.InferDefaultPort(); + + System.Console.WriteLine(options); + + string logMessage = "Test message 112 äöüÄÖÜß 你好世界 Привет мир"; + // logMessage = "test123"; + + SyslogNet.Client.SyslogMessage msg1 = CreateSyslogMessage(options, logMessage); + msg1.Send(options); + + System.Console.WriteLine("Sent " + msg1.Message); + + + + + System.Console.WriteLine(" --- Press any key to continue --- "); + System.Console.ReadKey(); + } // End Sub Main + + + } // End Class Program + + +} // End Namespace TestApplication diff --git a/TestApplication/TestApplication.csproj b/TestApplication/TestApplication.csproj new file mode 100644 index 0000000..8c73005 --- /dev/null +++ b/TestApplication/TestApplication.csproj @@ -0,0 +1,16 @@ + + + + Exe + net5.0 + + + + + + + + + + + diff --git a/TesterApp/Program.cs b/TesterApp/Program.cs index 07545be..cc61fa1 100644 --- a/TesterApp/Program.cs +++ b/TesterApp/Program.cs @@ -59,7 +59,7 @@ public static void Main(string[] args) : (ISyslogMessageSerializer)new SyslogLocalMessageSerializer(); ISyslogMessageSender sender = options.NetworkProtocol == "tcp" - ? (ISyslogMessageSender)new SyslogEncryptedTcpSender(options.SyslogServerHostname, options.SyslogServerPort) + ? (ISyslogMessageSender)new SyslogEncryptedTcpSender(options.SyslogServerHostname, options.SyslogServerPort, SslProtocols.Tls12) : options.NetworkProtocol == "udp" ? (ISyslogMessageSender)new SyslogUdpSender(options.SyslogServerHostname, options.SyslogServerPort) : (ISyslogMessageSender)new SyslogLocalSender();