Index: Appender/SnmpTrapAppender.cs
===================================================================
--- Appender/SnmpTrapAppender.cs (revision 0)
+++ Appender/SnmpTrapAppender.cs (revision 0)
@@ -0,0 +1,1059 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Globalization;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+
+using log4net.Layout;
+using log4net.Core;
+using log4net.Util;
+using System.IO;
+using System.Web;
+
+namespace log4net.Appender
+{
+ ///
+ /// Simple SNMP V1 Trap Appender Implementation
+ ///
+ public class SnmpTrapAppender : AppenderSkeleton
+ {
+ #region Public Instance Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The default constructor initializes all fields to their default values.
+ ///
+ public SnmpTrapAppender()
+ {
+ initDateTime = DateTime.Now;
+ }
+
+ #endregion Public Instance Constructors
+
+ #region Public Instance Properties
+
+ ///
+ /// Gets or sets the IP address of the remote host or multicast group to which
+ /// the underlying should sent the logging event.
+ ///
+ ///
+ /// The IP address of the remote host or multicast group to which the logging event
+ /// will be sent.
+ ///
+ ///
+ ///
+ /// Multicast addresses are identified by IP class D addresses (in the range 224.0.0.0 to
+ /// 239.255.255.255). Multicast packets can pass across different networks through routers, so
+ /// it is possible to use multicasts in an Internet scenario as long as your network provider
+ /// supports multicasting.
+ ///
+ ///
+ /// Hosts that want to receive particular multicast messages must register their interest by joining
+ /// the multicast group. Multicast messages are not sent to networks where no host has joined
+ /// the multicast group. Class D IP addresses are used for multicast groups, to differentiate
+ /// them from normal host addresses, allowing nodes to easily detect if a message is of interest.
+ ///
+ ///
+ /// Static multicast addresses that are needed globally are assigned by IANA. A few examples are listed in the table below:
+ ///
+ ///
+ ///
+ ///
+ /// IP Address
+ /// Description
+ ///
+ /// -
+ /// 224.0.0.1
+ ///
+ ///
+ /// Sends a message to all system on the subnet.
+ ///
+ ///
+ ///
+ /// -
+ /// 224.0.0.2
+ ///
+ ///
+ /// Sends a message to all routers on the subnet.
+ ///
+ ///
+ ///
+ /// -
+ /// 224.0.0.12
+ ///
+ ///
+ /// The DHCP server answers messages on the IP address 224.0.0.12, but only on a subnet.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// A complete list of actually reserved multicast addresses and their owners in the ranges
+ /// defined by RFC 3171 can be found at the IANA web site.
+ ///
+ ///
+ /// The address range 239.0.0.0 to 239.255.255.255 is reserved for administrative scope-relative
+ /// addresses. These addresses can be reused with other local groups. Routers are typically
+ /// configured with filters to prevent multicast traffic in this range from flowing outside
+ /// of the local network.
+ ///
+ ///
+ public IPAddress RemoteAddress
+ {
+ get { return m_remoteAddress; }
+ set { m_remoteAddress = value; }
+ }
+
+ ///
+ /// Gets or sets the TCP port number of the remote host or multicast group to which
+ /// the underlying should sent the logging event.
+ ///
+ ///
+ /// An integer value in the range to
+ /// indicating the TCP port number of the remote host or multicast group to which the logging event
+ /// will be sent.
+ ///
+ ///
+ /// The underlying will send messages to this TCP port number
+ /// on the remote host or multicast group.
+ ///
+ /// The value specified is less than or greater than .
+ public int RemotePort
+ {
+ get { return m_remotePort; }
+ set
+ {
+ if (value < IPEndPoint.MinPort || value > IPEndPoint.MaxPort)
+ {
+ throw log4net.Util.SystemInfo.CreateArgumentOutOfRangeException("value", (object)value,
+ "The value specified is less than " +
+ IPEndPoint.MinPort.ToString(NumberFormatInfo.InvariantInfo) +
+ " or greater than " +
+ IPEndPoint.MaxPort.ToString(NumberFormatInfo.InvariantInfo) + ".");
+ }
+ else
+ {
+ m_remotePort = value;
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets the TCP port number from which the underlying will communicate.
+ ///
+ ///
+ /// An integer value in the range to
+ /// indicating the TCP port number from which the underlying will communicate.
+ ///
+ ///
+ ///
+ /// The underlying will bind to this port for sending messages.
+ ///
+ ///
+ /// Setting the value to 0 (the default) will cause the udp client not to bind to
+ /// a local port.
+ ///
+ ///
+ /// The value specified is less than or greater than .
+ public int LocalPort
+ {
+ get { return m_localPort; }
+ set
+ {
+ if (value != 0 && (value < IPEndPoint.MinPort || value > IPEndPoint.MaxPort))
+ {
+ throw log4net.Util.SystemInfo.CreateArgumentOutOfRangeException("value", (object)value,
+ "The value specified is less than " +
+ IPEndPoint.MinPort.ToString(NumberFormatInfo.InvariantInfo) +
+ " or greater than " +
+ IPEndPoint.MaxPort.ToString(NumberFormatInfo.InvariantInfo) + ".");
+ }
+ else
+ {
+ m_localPort = value;
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets used to write the packets.
+ ///
+ ///
+ /// The used to write the packets.
+ ///
+ ///
+ ///
+ /// The used to write the packets.
+ ///
+ ///
+ public Encoding Encoding
+ {
+ get { return m_encoding; }
+ set { m_encoding = value; }
+ }
+
+ ///
+ /// The OID for the enterprise you are sending a trap to
+ ///
+ public string EnterpriseOID
+ {
+ get { return enterpriseOID; }
+ set { enterpriseOID = value; }
+ }
+
+ ///
+ /// The OID associated with a message variable added to the trap PDU
+ ///
+ public string ApplicationTrapOID
+ {
+ get { return applicationTrapOID; }
+ set { applicationTrapOID = value; }
+ }
+
+ ///
+ /// Community String (password) for the remote listener
+ ///
+ public string CommunityString
+ {
+ get { return communityString; }
+ set { communityString = value; }
+ }
+
+ ///
+ /// One of the generic SNMP Trap Types
+ ///
+ public GenericStatus GenericTrapType
+ {
+ get { return genericTrapType; }
+ set { genericTrapType = value; }
+ }
+
+ ///
+ /// An Enterprise-Specific SNMP Trap Type (SpecificTrapType, SpecificTrapCode, SpecificCode, etc.)
+ ///
+ public int SpecificTrapType
+ {
+ get { return specificTrapType; }
+ set { specificTrapType = value; }
+ }
+
+ #endregion Public Instance Properties
+
+ #region Protected Instance Properties
+
+ ///
+ /// Gets or sets the underlying .
+ ///
+ ///
+ /// The underlying .
+ ///
+ ///
+ /// creates a to send logging events
+ /// over a network. Classes deriving from can use this
+ /// property to get or set this . Use the underlying
+ /// returned from if you require access beyond that which
+ /// provides.
+ ///
+ protected UdpClient Client
+ {
+ get { return this.m_client; }
+ set { this.m_client = value; }
+ }
+
+ ///
+ /// Gets or sets the cached remote endpoint to which the logging events should be sent.
+ ///
+ ///
+ /// The cached remote endpoint to which the logging events will be sent.
+ ///
+ ///
+ /// The method will initialize the remote endpoint
+ /// with the values of the and
+ /// properties.
+ ///
+ protected IPEndPoint RemoteEndPoint
+ {
+ get { return this.m_remoteEndPoint; }
+ set { this.m_remoteEndPoint = value; }
+ }
+
+ #endregion Protected Instance Properties
+
+ #region Implementation of IOptionHandler
+
+ ///
+ /// Initialize the appender based on the options set.
+ ///
+ ///
+ ///
+ /// This is part of the delayed object
+ /// activation scheme. The method must
+ /// be called on this object after the configuration properties have
+ /// been set. Until is called this
+ /// object is in an undefined state and must not be used.
+ ///
+ ///
+ /// If any of the configuration properties are modified then
+ /// must be called again.
+ ///
+ ///
+ /// The appender will be ignored if no was specified or
+ /// an invalid remote or local TCP port number was specified.
+ ///
+ ///
+ /// The required property was not specified.
+ /// The TCP port number assigned to or is less than or greater than .
+ public override void ActivateOptions()
+ {
+ base.ActivateOptions();
+
+ if (this.RemoteAddress == null)
+ {
+ throw new ArgumentNullException("The required property 'Address' was not specified.");
+ }
+ else if (this.RemotePort < IPEndPoint.MinPort || this.RemotePort > IPEndPoint.MaxPort)
+ {
+ throw log4net.Util.SystemInfo.CreateArgumentOutOfRangeException("this.RemotePort", (object)this.RemotePort,
+ "The RemotePort is less than " +
+ IPEndPoint.MinPort.ToString(NumberFormatInfo.InvariantInfo) +
+ " or greater than " +
+ IPEndPoint.MaxPort.ToString(NumberFormatInfo.InvariantInfo) + ".");
+ }
+ else if (this.LocalPort != 0 && (this.LocalPort < IPEndPoint.MinPort || this.LocalPort > IPEndPoint.MaxPort))
+ {
+ throw log4net.Util.SystemInfo.CreateArgumentOutOfRangeException("this.LocalPort", (object)this.LocalPort,
+ "The LocalPort is less than " +
+ IPEndPoint.MinPort.ToString(NumberFormatInfo.InvariantInfo) +
+ " or greater than " +
+ IPEndPoint.MaxPort.ToString(NumberFormatInfo.InvariantInfo) + ".");
+ }
+ else if (this.EnterpriseOID == null)
+ {
+ throw new ArgumentNullException("The required property 'EnterpriseOID' was not specified.");
+ }
+ else if (this.CommunityString == null)
+ {
+ throw new ArgumentNullException("The required property 'CommunityString' was not specified.");
+ }
+ else if (this.ApplicationTrapOID == null)
+ {
+ throw new ArgumentNullException("The required property 'ApplicationTrapOID' was not specified.");
+ }
+ else
+ {
+ this.RemoteEndPoint = new IPEndPoint(this.RemoteAddress, this.RemotePort);
+ this.InitializeClientConnection();
+ }
+ }
+
+ #endregion
+
+ #region Override implementation of AppenderSkeleton
+
+ ///
+ /// This method is called by the method.
+ ///
+ /// The event to log.
+ ///
+ ///
+ /// Sends the event using an SNMP trap.
+ ///
+ ///
+ /// Exceptions are passed to the .
+ ///
+ ///
+ protected override void Append(LoggingEvent loggingEvent)
+ {
+ try
+ {
+ SnmpTrapOptions options = new SnmpTrapOptions(this.GenericTrapType, this.SpecificTrapType, this.EnterpriseOID,
+ this.ApplicationTrapOID, this.CommunityString, this.GetUptimeTicks());
+
+ string message = RenderLoggingEvent(loggingEvent);
+
+ List byteList = SnmpTrap.GetTrapSequence(message, options);
+
+ this.Client.Send(byteList.ToArray(), byteList.Count, m_remoteEndPoint);
+ }
+ catch (Exception ex)
+ {
+ ErrorHandler.Error(
+ "Unable to send logging event to remote host " +
+ this.RemoteAddress.ToString() +
+ " on port " +
+ this.RemotePort + ".",
+ ex,
+ ErrorCode.WriteFailure);
+ }
+ }
+
+ ///
+ /// This appender requires a to be set.
+ ///
+ /// true
+ ///
+ ///
+ /// This appender requires a to be set.
+ ///
+ ///
+ override protected bool RequiresLayout
+ {
+ get { return true; }
+ }
+
+ ///
+ /// Closes the UDP connection and releases all resources associated with
+ /// this instance.
+ ///
+ ///
+ ///
+ /// Disables the underlying and releases all managed
+ /// and unmanaged resources associated with the .
+ ///
+ ///
+ override protected void OnClose()
+ {
+ base.OnClose();
+
+ if (this.Client != null)
+ {
+ this.Client.Close();
+ this.Client = null;
+ }
+ }
+
+ #endregion Override implementation of AppenderSkeleton
+
+ #region Protected Instance Methods
+
+ ///
+ /// Initializes the underlying connection.
+ ///
+ ///
+ ///
+ /// The underlying is initialized and binds to the
+ /// port number from which you intend to communicate.
+ ///
+ ///
+ /// Exceptions are passed to the .
+ ///
+ ///
+ protected virtual void InitializeClientConnection()
+ {
+ try
+ {
+ if (this.LocalPort == 0)
+ {
+ this.Client = new UdpClient();
+ }
+ else
+ {
+ this.Client = new UdpClient(this.LocalPort);
+ }
+ }
+ catch (Exception ex)
+ {
+ ErrorHandler.Error(
+ "Could not initialize the UdpClient connection on port " +
+ this.LocalPort.ToString(NumberFormatInfo.InvariantInfo) + ".",
+ ex,
+ ErrorCode.GenericFailure);
+
+ this.Client = null;
+ }
+ }
+
+ #endregion Protected Instance Methods
+
+ #region Private Instance Fields
+
+ ///
+ /// The IP address of the remote host or multicast group to which
+ /// the logging event will be sent.
+ ///
+ private IPAddress m_remoteAddress;
+
+ ///
+ /// The TCP port number of the remote host or multicast group to
+ /// which the logging event will be sent.
+ ///
+ private int m_remotePort;
+
+ ///
+ /// The cached remote endpoint to which the logging events will be sent.
+ ///
+ private IPEndPoint m_remoteEndPoint;
+
+ ///
+ /// The TCP port number from which the will communicate.
+ ///
+ private int m_localPort;
+
+ ///
+ /// The instance that will be used for sending the
+ /// logging events.
+ ///
+ private UdpClient m_client;
+
+ ///
+ /// The encoding to use for the packet.
+ ///
+ private Encoding m_encoding = Encoding.Default;
+
+ private string enterpriseOID;
+ private GenericStatus genericTrapType;
+ private int specificTrapType;
+ private string communityString;
+ private string applicationTrapOID;
+ private DateTime initDateTime;
+
+ #endregion Private Instance Fields
+
+ #region Private Instance Methods
+
+ ///
+ /// Get TimeTicks portion of Trap PDU. Based on the time this class was instantiated.
+ ///
+ ///
+ private long GetUptimeTicks()
+ {
+ return (DateTime.Now - initDateTime).Ticks;
+
+ //if (UseAppPoolUptime) // if asp.net, get application pool uptime
+ //{
+ // ProcessInfo processInfo = ProcessModelInfo.GetCurrentProcessInfo();
+ // return (DateTime.Now - processInfo.StartTime).Ticks;
+ //}
+ //else // use time since boot-up
+ //{
+ // // define a select query
+ // SelectQuery query =
+ // new SelectQuery("SELECT LastBootUpTime FROM Win32_OperatingSystem WHERE Primary='true'");
+
+ // // create a new management object searcher and pass it
+ // // the select query
+ // ManagementObjectSearcher searcher =
+ // new ManagementObjectSearcher(query);
+
+ // // get the datetime value and set the local boot
+ // // time variable to contain that value
+ // foreach (ManagementObject mo in searcher.Get())
+ // {
+ // dtBootTime = ManagementDateTimeConverter.ToDateTime(mo.Properties["LastBootUpTime"].Value.ToString());
+ // return (DateTime.Now - dtBootTime).Ticks
+ // }
+ //}
+ }
+
+ #endregion
+ }
+
+ #region Snmp Trap Generation
+
+ ///
+ /// Specific settings for an SNMP Trap
+ ///
+ public class SnmpTrapOptions
+ {
+ #region Public Properties
+
+ ///
+ /// Typically GenericStatus.EnterpriseSpecific
+ ///
+ public GenericStatus SnmpGenericStatus { get; set; }
+
+ ///
+ /// An Enterprise-Specific Status Type
+ ///
+ public int SnmpSpecificStatus { get; set; }
+
+ ///
+ /// The OID for the enterprise you are sending a trap to
+ ///
+ public byte[] EnterpriseOID { get; set; }
+
+ ///
+ /// The OID for your Application
+ ///
+ public byte[] ApplicationTrapOID { get; set; }
+
+ ///
+ /// Community String (password) for the remote listener
+ ///
+ public byte[] CommunityString { get; set; }
+
+ ///
+ /// Ticks since last trap, uptime in ticks, etc.
+ ///
+ public long TimeTicks { get; set; }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Instantiate an SnmpTrapOptions class for use with SnmpSendTrap.Send()
+ ///
+ /// i.e. GenericStatus.EnterpriseSpecific
+ /// The OID for the enterprise you are sending a trap to
+ /// The OID for your Application
+ /// Community String (password) for the remote listener
+ public SnmpTrapOptions(GenericStatus snmpGenericStatus, int snmpSpecificStatus, string enterpriseOID, string applicationTrapOID, string communityString, long timeTicks)
+ {
+ SnmpGenericStatus = snmpGenericStatus;
+ SnmpSpecificStatus = snmpSpecificStatus;
+ CommunityString = Encoding.Default.GetBytes(communityString);
+ EnterpriseOID = StringOidToByteArray(enterpriseOID);
+ ApplicationTrapOID = StringOidToByteArray(applicationTrapOID);
+ TimeTicks = timeTicks;
+ }
+
+ #endregion
+
+ #region OID Conversion
+ private static byte[] StringOidToByteArray(string oid)
+ {
+ string[] oidList = oid.Split('.');
+
+ if (oidList.Length < 2)
+ {
+ throw new ArgumentException(String.Format("Invalid OID string: {0}", oid));
+ }
+
+ ulong[] values = new ulong[oidList.Length];
+
+ for (int i = 0; i < oidList.Length; i++)
+ {
+ values[i] = Convert.ToUInt64(oidList[i]);
+ }
+ MemoryStream byteStream = new MemoryStream();
+ byteStream.WriteByte((byte)(values[0] * 40 + values[1]));
+
+ for (int i = 2; i < values.Length; i++)
+ {
+ EncodeValue(byteStream, values[i]);
+ }
+
+ return byteStream.ToArray();
+ }
+
+ ///
+ /// Encode single OID value.
+ ///
+ /// Output stream
+ /// Source value
+ private static void EncodeValue(Stream stream, ulong value)
+ {
+ for (int i = (BitPrecision(value) - 1) / 7; i > 0; i--)
+ {
+ stream.WriteByte((byte)(0x80 | ((value >> (i * 7)) & 0x7f)));
+ }
+
+ stream.WriteByte((byte)(value & 0x7f));
+ }
+
+ ///
+ /// Calculate how many bits is enough to hold ivalue.
+ ///
+ /// Source value
+ /// Number of bits
+ private static int BitPrecision(ulong value)
+ {
+ if (value == 0)
+ {
+ return 0;
+ }
+
+ int l = 0, h = 8 * 4; // 4: sizeof(ulong)
+ while (h - l > 1)
+ {
+ int t = (int)(l + h) / 2;
+ if ((value >> t) != 0)
+ l = t;
+ else
+ h = t;
+ }
+
+ return h;
+ }
+
+ #endregion
+ }
+
+ ///
+ /// Generates simple SNMP packet including Trap PDU
+ ///
+ static class SnmpTrap
+ {
+ // may be the result of the ASN.1 length encoding routine, however this has proven to be
+ // the most reliable out of all the c# snmp code I could review
+ private static int maxEncodableStringLength = 4000;
+
+ #region SNMP Trap Generation
+
+ ///
+ /// Generate SNMP Trap sequence including a message
+ ///
+ /// Your message
+ /// SnmpTrapOptions object
+ /// A generic list of bytes representing a complete snmp packet
+ public static List GetTrapSequence(string message, SnmpTrapOptions options)
+ {
+ List data = new List();
+
+ // build header
+ byte[] header = new byte[] { 0x02, 0x01, 0x00, ToByte(DataType.OctetString), Convert.ToByte(options.CommunityString.Length) };
+ // { "IntegerType", "Length", "SNMP Version 1 == 0", "String Type", "Length", "CommunityName"
+
+ data.AddRange(header);
+ data.AddRange(options.CommunityString);
+
+ // add trap PDU
+ AddTrapPDU(data, message, options);
+
+ // insert length-dependent bytes after generating sequence PDU, etc.
+ data.InsertRange(0, GetLengthInBytes(data.Count));
+ data.Insert(0, ToByte(DataType.Sequence));
+
+ return data;
+ }
+
+ #endregion
+
+ #region Byte Conversion Routines
+ // does not appear to work w/ length > 190
+ //private static List GetLengthInBytes(int length)
+ //{
+ // List data = new List();
+ // int len = length;
+
+ // data.Add((byte)(len & 0xFF));
+
+ // len >>= 8;
+ // while (len != 0)
+ // {
+ // data.Add((byte)(len & 0xFF));
+ // len >>= 8;
+ // }
+
+ // if (length >= 0x80 || data.Count > 1)
+ // {
+ // data.Insert(0, (byte)(data.Count | 0x80));
+ // }
+
+ // return data;
+ //}
+
+ // doesn't work at all
+ //private static List GetLengthInBytes(int length)
+ //{
+ // byte[] lengthInBytes = BitConverter.GetBytes(length);
+
+ // List data = new List();
+ // for (int i = 3; i >= 0; i--)
+ // {
+ // if (lengthInBytes[i] != 0)// || buf.Length > 0)
+ // {
+ // data.Add(lengthInBytes[i]);
+ // }
+ // }
+
+ // if (data.Count == 0)
+ // {
+ // // we are encoding a 0 value. Can't have a 0 byte length encoding
+ // data.Add((byte)0);
+ // }
+
+ // // check for short/long form encoding
+ // if (data.Count != 1 && (data[0] & HIGH_BIT) != 0)
+ // {
+ // // long form
+ // byte encHeader = (byte)data.Count;
+ // encHeader = (byte)(encHeader | HIGH_BIT);
+
+ // data.Insert(0, encHeader);
+ // }
+
+ // return data;
+ //}
+
+ // staight from ASN.1 implementation
+ private static List GetLengthInBytes(int length)
+ {
+ List lengthInBytes = new List();
+
+ if (length > 127)
+ {
+ if (length <= Byte.MaxValue)
+ {
+ lengthInBytes.Add(0x81);
+ lengthInBytes.Add(ToByte(length));
+ }
+ else if (length <= UInt16.MaxValue)
+ {
+ lengthInBytes.Add(0x82);
+ lengthInBytes.Add(ToByte(length >> 8));
+ lengthInBytes.Add(ToByte(length));
+ }
+ else if (length <= 0xFFFFFF) // 24 bits
+ {
+ lengthInBytes.Add(0x83);
+ lengthInBytes.Add(ToByte(length >> 16));
+ lengthInBytes.Add(ToByte(length >> 8));
+ lengthInBytes.Add(ToByte(length));
+ }
+ else // 32 bits (max)
+ {
+ lengthInBytes.Add(0x84);
+ lengthInBytes.Add(ToByte(length >> 24));
+ lengthInBytes.Add(ToByte(length >> 16));
+ lengthInBytes.Add(ToByte(length >> 8));
+ lengthInBytes.Add(ToByte(length));
+ }
+ }
+ else
+ {
+ lengthInBytes.Add(ToByte(length));
+ }
+
+ return lengthInBytes;
+ }
+
+ // using same routine as signed integer, works up to long.MaxValue
+ private static List GetLengthAndDataBytes(long value, DataType dataType)
+ {
+ List data = new List();
+ long val = value;
+ do
+ {
+ data.Insert(0, (byte)(val & 0xFF));
+ val >>= 8;
+ }
+ while (val != 0);
+
+ data.InsertRange(0, GetLengthInBytes(data.Count));
+ data.Insert(0, ToByte(DataType.TimeTicks));
+
+ return data;
+ }
+
+ private static byte ToByte(GenericStatus value)
+ {
+ return (byte)value;
+ }
+
+ private static byte ToByte(DataType value)
+ {
+ return (byte)value;
+ }
+
+ private static byte ToByte(int value)
+ {
+ return (byte)value;
+ }
+
+ private static byte[] ToBytes(string data)
+ {
+ return Encoding.Default.GetBytes(data);
+ }
+ #endregion
+
+ #region Trap PDU Generation
+ private static void AddTrapPDU(List data, string message, SnmpTrapOptions options)
+ {
+ List innerData = new List();
+
+ AddTrapSourceOID(innerData, options.EnterpriseOID);
+ AddTrapSourceIP(innerData);
+ AddTrapStatusGeneric(innerData, options.SnmpGenericStatus);
+ AddTrapStatusSpecific(innerData, ToByte(options.SnmpSpecificStatus));
+ AddTrapTimeTicks(innerData, options.TimeTicks);
+
+ // TODO: make extensible? Maybe not... appenders all seem to do the same thing, log a string
+ AddTrapVariables(innerData, message, options);
+
+ innerData.InsertRange(0, GetLengthInBytes(innerData.Count));
+ innerData.Insert(0, ToByte(DataType.TrapV1Pdu));
+
+ data.AddRange(innerData);
+ }
+
+ private static void AddTrapSourceOID(List data, byte[] enterpriseOID)
+ {
+ data.Add(ToByte(DataType.ObjectIdentifier));
+ data.AddRange(GetLengthInBytes(enterpriseOID.Length));
+ data.AddRange(enterpriseOID);
+ }
+
+ private static void AddTrapSourceIP(List data)
+ {
+ IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());
+ data.Add(ToByte(DataType.IPAddress));
+ data.Add((byte)4);
+
+ if (ips.Length > 0)
+ data.AddRange(ips[0].GetAddressBytes());
+ else
+ data.AddRange(new byte[4] { 0, 0, 0, 0 });
+ }
+
+ private static void AddTrapStatusGeneric(List data, GenericStatus genericStatus)
+ {
+ data.Add(ToByte(DataType.Integer32));
+ data.Add((byte)1);
+ data.Add(ToByte(genericStatus));
+ }
+
+ private static void AddTrapStatusSpecific(List data, byte specificStatus)
+ {
+ data.Add(ToByte(DataType.Integer32));
+ data.Add((byte)1);
+ data.Add(specificStatus);
+ }
+
+ private static void AddTrapTimeTicks(List data, long timeTicks)
+ {
+ data.AddRange(GetLengthAndDataBytes(timeTicks, DataType.TimeTicks));
+ }
+
+ private static void AddTrapVariables(List data, string message, SnmpTrapOptions options)
+ {
+ List innerData = new List();
+
+ // using the ASN1 length encoding routine above, there appears to be a threshold where
+ // WireShark, TrapListener and SysEdge cannot decode the Trap PDU
+ if (message.Length > maxEncodableStringLength)
+ {
+ message = String.Format("{0}...", message.Substring(0, maxEncodableStringLength - 3));
+ }
+
+ AddTrapMessage(innerData, message, options.ApplicationTrapOID);
+
+ innerData.InsertRange(0, GetLengthInBytes(innerData.Count));
+ innerData.Insert(0, ToByte(DataType.Sequence));
+
+ data.AddRange(innerData);
+ }
+
+ private static void AddTrapMessage(List data, string message, byte[] applicationTrapOID)
+ {
+ List innerData = new List();
+
+ innerData.Add(ToByte(DataType.ObjectIdentifier));
+ innerData.AddRange(GetLengthInBytes(applicationTrapOID.Length));
+ innerData.AddRange(applicationTrapOID);
+
+ innerData.Add(ToByte(DataType.OctetString));
+ innerData.AddRange(GetLengthInBytes(message.Length));
+ innerData.AddRange(ToBytes(message));
+
+ innerData.InsertRange(0, GetLengthInBytes(innerData.Count));
+ innerData.Insert(0, ToByte(DataType.Sequence));
+
+ data.AddRange(innerData);
+ }
+ #endregion
+ }
+
+ #endregion
+
+ #region Snmp/ASN.1 Enums
+ ///
+ /// The Generic SNMP Status of the Trap
+ ///
+ public enum GenericStatus
+ {
+ ///
+ /// 0 - The system is starting up for the first time
+ ///
+ ColdStart = 0,
+
+ ///
+ /// 1 - The system has rebooted
+ ///
+ WarmStart = 1,
+
+ ///
+ /// 2 - The link is down
+ ///
+ LinkDown = 2,
+
+ ///
+ /// 3 - The link is up
+ ///
+ LinkUp = 3,
+
+ ///
+ /// 4 - An authentication failure occured
+ ///
+ AuthenticationFailure = 4,
+
+ ///
+ /// 5
+ ///
+ EgpNeighborLoss = 5,
+
+ ///
+ /// 6
+ ///
+ EnterpriseSpecific = 6
+ }
+
+ ///
+ /// The ASN1 DataType of the variable element
+ ///
+ public enum DataType
+ {
+ ///
+ /// 0x02 - A single byte integer (0x00 - 0xFF)
+ ///
+ Integer32 = 0x02,
+
+ ///
+ /// 0x03 - String of character information encoded in up to 255 bytes
+ ///
+ DisplayString = 0x03,
+
+ ///
+ /// 0x04 - Stream of bytes up to 255 bytes long
+ ///
+ OctetString = 0x04,
+
+ ///
+ /// 0x06 - An SNMP OID, which can be up to 255 bytes long
+ ///
+ ObjectIdentifier = 0x06,
+
+ ///
+ /// 0x30 - A sequence of OID,Length,Data triplets where the maximum size can be up to 255 bytes
+ ///
+ Sequence = 0x30,
+
+ ///
+ /// 0x40 - A 4 byte octet string representing an IP
+ ///
+ IPAddress = 0x40,
+
+ ///
+ /// 0x43 - 1/100 seconds since some epoch as per MIB specification, 4 bytes long
+ ///
+ TimeTicks = 0x43,
+
+ ///
+ /// 0x45 - Network Address (IP) and is 4 bytes long
+ ///
+ NetworkAddress = 0x45,
+
+ ///
+ /// 0xa4 - Declaration of the main body of the trap
+ ///
+ TrapV1Pdu = 0xa4,
+ }
+
+ #endregion
+}
\ No newline at end of file
Index: log4net.vs2008.csproj
===================================================================
--- log4net.vs2008.csproj (revision 934970)
+++ log4net.vs2008.csproj (working copy)
@@ -153,6 +153,7 @@
Code
+
Code