Skip to content

Commit

Permalink
Throw CoapOptionException when deserialising unsupported critical opt…
Browse files Browse the repository at this point in the history
…ions
  • Loading branch information
NZSmartie committed Aug 23, 2017
1 parent 98e879f commit 330ade3
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 9 deletions.
30 changes: 23 additions & 7 deletions CoAPNet/Message.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public static bool IsServerError(this CoapMessageCode code)
}
}

public class CoapMessageFormatException : Exception {
public class CoapMessageFormatException : CoapException {
public CoapMessageFormatException() :base() { }

public CoapMessageFormatException(string message) : base(message) { }
Expand Down Expand Up @@ -285,11 +285,11 @@ public byte[] Serialise()
public void Deserialise(byte[] data)
{
if (data == null)
throw new ArgumentNullException("data");
throw new ArgumentNullException(nameof(data));
if (data.Length < 4)
throw new CoapMessageFormatException("Message must be at least 4 bytes long");
if ((data[0] & 0xC0) != 0x40)
throw new CoapMessageFormatException("Only verison 1 of CoAP protocol is supported");
throw new CoapMessageFormatException($"Unexpected CoAP version ({data[0] & 0xC0:D}). Only verison 1 is supported");

var offset = 4;

Expand All @@ -305,13 +305,12 @@ public void Deserialise(byte[] data)
if (Code == CoapMessageCode.None && data.Length > 4)
throw new CoapMessageFormatException("Empty message must be 4 bytes long");

if (new int[] { 1, 6, 7 }.Contains(code / 100))
throw new CoapMessageFormatException("Message.Code can not use reserved classes");

offset += data[0] & 0x0F;
if ((data[0] & 0x0F) > 0)
_token = data.Skip(4).Take(data[0] & 0x0F).ToArray();

// Catch all the CoapOptionExceptions and throw them after all the options have been parsed.
var badOptions = new List<int>();
var optionDelta = 0;
for(var i = offset; i<data.Length; i++)
{
Expand Down Expand Up @@ -340,12 +339,29 @@ public void Deserialise(byte[] data)
dataLen = data[i++ + 1] << 8;
dataLen |= data[i++ + 1] + 269;
}
Options.Add(CoAPNet.Options.Factory.Create(optCode + optionDelta, data.Skip(i+1).Take(dataLen).ToArray()));

try
{
var option = CoAPNet.Options.Factory.Create(optCode + optionDelta,
data.Skip(i + 1).Take(dataLen).ToArray());
if (option != null)
Options.Add(option);
}
catch (CoapOptionException)
{
badOptions.Add(optCode + optionDelta);
}

i += dataLen;
optionDelta += optCode;
}

// Performing this check after parsing the options to allow the chance of reading the message token
if (new int[] {1, 6, 7}.Contains(code / 100))
throw new CoapMessageFormatException("Message.Code can not use reserved classes");

if (badOptions.Count > 0)
throw new CoapOptionException($"Unsupported critical option ({string.Join(", ", badOptions)})");
}

/// <summary>
Expand Down
9 changes: 9 additions & 0 deletions CoAPNet/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@

namespace CoAPNet
{
public class CoapOptionException : CoapException
{
public CoapOptionException() : base() { }

public CoapOptionException(string message) : base(message) { }

public CoapOptionException(string message, Exception innerException) : base(message, innerException) { }
}

public enum OptionType
{
/// <summary>
Expand Down
15 changes: 13 additions & 2 deletions CoAPNet/Options/Factory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,29 @@ public static void Register<T>() where T : CoapOption
public static void Register(Type type)
{
if(!type.GetTypeInfo().IsSubclassOf(typeof(CoapOption)))
throw new ArgumentException(string.Format("Type must be a subclass of {0}", typeof(CoapOption).FullName));
throw new ArgumentException($"Type must be a subclass of {nameof(CoapOption)}");

var option = (CoapOption)Activator.CreateInstance(type);
_options.Add(option.OptionNumber, type);
}

/// <summary>
/// Try to create an <see cref="CoapOption"/> from the option number and data.
/// </summary>
/// <param name="number"></param>
/// <param name="data"></param>
/// <returns><value>null</value> if the option is unsupported.</returns>
/// <exception cref="CoapOptionException">If the option number is unsuppported and is critical (See RFC 7252 Section 5.4.1)</exception>
public static CoapOption Create(int number, byte[] data = null)
{
// Let the exception get thrown if index is out of range
Type type = null;
if (!_options.TryGetValue(number, out type))
throw new ArgumentException(string.Format("Unsupported option number {0}", number));
{
if (number % 2 == 1)
throw new CoapOptionException($"Unsupported critical option ({number})", new ArgumentOutOfRangeException(nameof(number)));
return null;
}

var option = (CoapOption)Activator.CreateInstance(type);
if (data != null)
Expand Down

0 comments on commit 330ade3

Please sign in to comment.