// Message factory type.
// Copyright (C) 2008-2010 Malcolm Crowe, Lex Li, and other contributors.
//
// 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.
/*
* Created by SharpDevelop.
* User: lextm
* Date: 2008/5/1
* Time: 11:17
*
* To change this template use Tools | Options | Coding | Edit Standard Headers.
*/
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using GSF.Net.Snmp.Security;
namespace GSF.Net.Snmp.Messaging
{
///
/// Factory that creates instances from byte format.
///
public static class MessageFactory
{
///
/// Creates instances from a string.
///
/// Byte string.
/// The registry.
///
public static IList ParseMessages(IEnumerable bytes, UserRegistry registry)
{
if (bytes == null)
{
throw new ArgumentNullException(nameof(bytes));
}
if (registry == null)
{
throw new ArgumentNullException(nameof(registry));
}
return ParseMessages(ByteTool.Convert(bytes), registry);
}
///
/// Creates instances from buffer.
///
/// Buffer.
/// The registry.
///
public static IList ParseMessages(byte[] buffer, UserRegistry registry)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
if (registry == null)
{
throw new ArgumentNullException(nameof(registry));
}
return ParseMessages(buffer, 0, buffer.Length, registry);
}
///
/// Creates instances from buffer.
///
/// Buffer.
/// The index.
/// The length.
/// The registry.
///
public static IList ParseMessages(byte[] buffer, int index, int length, UserRegistry registry)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
if (registry == null)
{
throw new ArgumentNullException(nameof(registry));
}
IList result = new List();
using (Stream stream = new MemoryStream(buffer, index, length, true))
{
int first;
while ((first = stream.ReadByte()) != -1)
{
ISnmpMessage message = ParseMessage(first, stream, registry);
if (message == null)
{
continue;
}
result.Add(message);
}
}
return result;
}
private static ISnmpMessage ParseMessage(int first, Stream stream, UserRegistry registry)
{
ISnmpData array = DataFactory.CreateSnmpData(first, stream);
if (array == null)
{
return null;
}
if (array.TypeCode != SnmpType.Sequence)
{
throw new SnmpException("not an SNMP message");
}
Sequence body = (Sequence)array;
if (body.Length != 3 && body.Length != 4)
{
throw new SnmpException("not an SNMP message");
}
VersionCode version = (VersionCode)((Integer32)body[0]).ToInt32();
Header header;
SecurityParameters parameters;
IPrivacyProvider privacy;
Scope scope;
if (body.Length == 3)
{
header = Header.Empty;
parameters = SecurityParameters.Create((OctetString)body[1]);
privacy = DefaultPrivacyProvider.DefaultPair;
scope = new Scope((ISnmpPdu)body[2]);
}
else
{
header = new Header(body[1]);
parameters = new SecurityParameters((OctetString)body[2]);
privacy = registry.Find(parameters.UserName);
if (privacy == null)
{
// handle decryption exception.
return new MalformedMessage(header.MessageId, parameters.UserName, body[3]);
}
SnmpType code = body[3].TypeCode;
if (code == SnmpType.Sequence)
{
// v3 not encrypted
scope = new Scope((Sequence)body[3]);
}
else if (code == SnmpType.OctetString)
{
// v3 encrypted
try
{
scope = new Scope((Sequence)privacy.Decrypt(body[3], parameters));
}
catch (DecryptionException)
{
// handle decryption exception.
return new MalformedMessage(header.MessageId, parameters.UserName, body[3]);
}
}
else
{
throw new SnmpException(string.Format(CultureInfo.InvariantCulture, "invalid v3 packets scoped data: {0}", code));
}
if (!privacy.VerifyHash(version, header, parameters, body[3], body.GetLengthBytes()))
{
parameters.IsInvalid = true;
}
}
SnmpType scopeCode = scope.Pdu.TypeCode;
try
{
switch (scopeCode)
{
case SnmpType.TrapV1Pdu:
return new TrapV1Message(body);
case SnmpType.TrapV2Pdu:
return new TrapV2Message(version, header, parameters, scope, privacy, body.GetLengthBytes());
case SnmpType.GetRequestPdu:
return new GetRequestMessage(version, header, parameters, scope, privacy, body.GetLengthBytes());
case SnmpType.ResponsePdu:
return new ResponseMessage(version, header, parameters, scope, privacy, false, body.GetLengthBytes());
case SnmpType.SetRequestPdu:
return new SetRequestMessage(version, header, parameters, scope, privacy, body.GetLengthBytes());
case SnmpType.GetNextRequestPdu:
return new GetNextRequestMessage(version, header, parameters, scope, privacy, body.GetLengthBytes());
case SnmpType.GetBulkRequestPdu:
return new GetBulkRequestMessage(version, header, parameters, scope, privacy, body.GetLengthBytes());
case SnmpType.ReportPdu:
return new ReportMessage(version, header, parameters, scope, privacy, body.GetLengthBytes());
case SnmpType.InformRequestPdu:
return new InformRequestMessage(version, header, parameters, scope, privacy, body.GetLengthBytes());
default:
throw new SnmpException(string.Format(CultureInfo.InvariantCulture, "unsupported pdu: {0}", scopeCode));
}
}
catch (Exception ex)
{
if (ex is SnmpException)
{
throw;
}
throw new SnmpException("message construction exception", ex);
}
}
}
}