FIX: DefaultMachineId doesn't throw; MQTT: buffer release, better string decoding (#175)

Modifications
- algorithm of determining best machine's MAC modified to not throw
- added convenient IReferenceCounted.SafeRelease extension method
- added release for buffers in MQTT codec in case processing fails to ensure release (or proper data flow) of the buffer in all cases
- fixed the way string is decoded in MQTT codec
This commit is contained in:
Max Gortman 2016-11-25 01:42:31 -08:00 коммит произвёл GitHub
Родитель 44885964bd
Коммит 17a69a0864
11 изменённых файлов: 419 добавлений и 267 удалений

Просмотреть файл

@ -466,7 +466,7 @@ namespace DotNetty.Codecs.Mqtt
DecreaseRemainingLength(ref remainingLength, size);
string value = Encoding.UTF8.GetString(buffer.Array, buffer.ArrayOffset + buffer.ReaderIndex, size);
string value = buffer.ToString(buffer.ReaderIndex, size, Encoding.UTF8);
// todo: enforce string definition by MQTT spec
buffer.SetReaderIndex(buffer.ReaderIndex + size);
return value;

Просмотреть файл

@ -9,6 +9,7 @@ namespace DotNetty.Codecs.Mqtt
using DotNetty.Buffers;
using DotNetty.Codecs.Mqtt.Packets;
using DotNetty.Common;
using DotNetty.Common.Utilities;
using DotNetty.Transport.Channels;
public sealed class MqttEncoder : MessageToMessageEncoder<Packet>
@ -124,44 +125,55 @@ namespace DotNetty.Codecs.Mqtt
int variableHeaderBufferSize = StringSizeLength + protocolNameBytes.Length + 4;
int variablePartSize = variableHeaderBufferSize + payloadBufferSize;
int fixedHeaderBufferSize = 1 + MaxVariableLength;
IByteBuffer buf = bufferAllocator.Buffer(fixedHeaderBufferSize + variablePartSize);
buf.WriteByte(CalculateFirstByteOfFixedHeader(packet));
WriteVariableLengthInt(buf, variablePartSize);
buf.WriteShort(protocolNameBytes.Length);
buf.WriteBytes(protocolNameBytes);
buf.WriteByte(Util.ProtocolLevel);
buf.WriteByte(CalculateConnectFlagsByte(packet));
buf.WriteShort(packet.KeepAliveInSeconds);
// Payload
buf.WriteShort(clientIdBytes.Length);
buf.WriteBytes(clientIdBytes, 0, clientIdBytes.Length);
if (packet.HasWill)
IByteBuffer buf = null;
try
{
buf.WriteShort(willTopicBytes.Length);
buf.WriteBytes(willTopicBytes, 0, willTopicBytes.Length);
buf.WriteShort(willMessage.ReadableBytes);
if (willMessage.IsReadable())
buf = bufferAllocator.Buffer(fixedHeaderBufferSize + variablePartSize);
buf.WriteByte(CalculateFirstByteOfFixedHeader(packet));
WriteVariableLengthInt(buf, variablePartSize);
buf.WriteShort(protocolNameBytes.Length);
buf.WriteBytes(protocolNameBytes);
buf.WriteByte(Util.ProtocolLevel);
buf.WriteByte(CalculateConnectFlagsByte(packet));
buf.WriteShort(packet.KeepAliveInSeconds);
// Payload
buf.WriteShort(clientIdBytes.Length);
buf.WriteBytes(clientIdBytes, 0, clientIdBytes.Length);
if (packet.HasWill)
{
buf.WriteBytes(willMessage);
buf.WriteShort(willTopicBytes.Length);
buf.WriteBytes(willTopicBytes, 0, willTopicBytes.Length);
buf.WriteShort(willMessage.ReadableBytes);
if (willMessage.IsReadable())
{
buf.WriteBytes(willMessage);
}
willMessage.Release();
willMessage = null;
}
willMessage.Release();
if (packet.HasUsername)
{
buf.WriteShort(userNameBytes.Length);
buf.WriteBytes(userNameBytes, 0, userNameBytes.Length);
if (packet.HasPassword)
{
buf.WriteShort(passwordBytes.Length);
buf.WriteBytes(passwordBytes, 0, passwordBytes.Length);
}
}
output.Add(buf);
buf = null;
}
if (packet.HasUsername)
finally
{
buf.WriteShort(userNameBytes.Length);
buf.WriteBytes(userNameBytes, 0, userNameBytes.Length);
if (packet.HasPassword)
{
buf.WriteShort(passwordBytes.Length);
buf.WriteBytes(passwordBytes, 0, passwordBytes.Length);
}
buf?.SafeRelease();
willMessage?.SafeRelease();
}
output.Add(buf);
}
static int CalculateConnectFlagsByte(ConnectPacket packet)
@ -193,20 +205,30 @@ namespace DotNetty.Codecs.Mqtt
static void EncodeConnAckMessage(IByteBufferAllocator bufferAllocator, ConnAckPacket message, List<object> output)
{
IByteBuffer buffer = bufferAllocator.Buffer(4);
buffer.WriteByte(CalculateFirstByteOfFixedHeader(message));
buffer.WriteByte(2); // remaining length
if (message.SessionPresent)
IByteBuffer buffer = null;
try
{
buffer.WriteByte(1); // 7 reserved 0-bits and SP = 1
}
else
{
buffer.WriteByte(0); // 7 reserved 0-bits and SP = 0
}
buffer.WriteByte((byte)message.ReturnCode);
buffer = bufferAllocator.Buffer(4);
buffer.WriteByte(CalculateFirstByteOfFixedHeader(message));
buffer.WriteByte(2); // remaining length
if (message.SessionPresent)
{
buffer.WriteByte(1); // 7 reserved 0-bits and SP = 1
}
else
{
buffer.WriteByte(0); // 7 reserved 0-bits and SP = 0
}
buffer.WriteByte((byte)message.ReturnCode);
output.Add(buffer);
output.Add(buffer);
buffer = null;
}
finally
{
buffer?.SafeRelease();
}
}
static void EncodePublishMessage(IByteBufferAllocator bufferAllocator, PublishPacket packet, List<object> output)
@ -223,17 +245,26 @@ namespace DotNetty.Codecs.Mqtt
int variablePartSize = variableHeaderBufferSize + payloadBufferSize;
int fixedHeaderBufferSize = 1 + MaxVariableLength;
IByteBuffer buf = bufferAllocator.Buffer(fixedHeaderBufferSize + variablePartSize);
buf.WriteByte(CalculateFirstByteOfFixedHeader(packet));
WriteVariableLengthInt(buf, variablePartSize);
buf.WriteShort(topicNameBytes.Length);
buf.WriteBytes(topicNameBytes);
if (packet.QualityOfService > QualityOfService.AtMostOnce)
IByteBuffer buf = null;
try
{
buf.WriteShort(packet.PacketId);
}
buf = bufferAllocator.Buffer(fixedHeaderBufferSize + variablePartSize);
buf.WriteByte(CalculateFirstByteOfFixedHeader(packet));
WriteVariableLengthInt(buf, variablePartSize);
buf.WriteShort(topicNameBytes.Length);
buf.WriteBytes(topicNameBytes);
if (packet.QualityOfService > QualityOfService.AtMostOnce)
{
buf.WriteShort(packet.PacketId);
}
output.Add(buf);
output.Add(buf);
buf = null;
}
finally
{
buf?.SafeRelease();
}
if (payload.IsReadable())
{
@ -247,12 +278,21 @@ namespace DotNetty.Codecs.Mqtt
const int VariableHeaderBufferSize = PacketIdLength; // variable part only has a packet id
int fixedHeaderBufferSize = 1 + MaxVariableLength;
IByteBuffer buffer = bufferAllocator.Buffer(fixedHeaderBufferSize + VariableHeaderBufferSize);
buffer.WriteByte(CalculateFirstByteOfFixedHeader(packet));
WriteVariableLengthInt(buffer, VariableHeaderBufferSize);
buffer.WriteShort(msgId);
IByteBuffer buffer = null;
try
{
buffer = bufferAllocator.Buffer(fixedHeaderBufferSize + VariableHeaderBufferSize);
buffer.WriteByte(CalculateFirstByteOfFixedHeader(packet));
WriteVariableLengthInt(buffer, VariableHeaderBufferSize);
buffer.WriteShort(msgId);
output.Add(buffer);
output.Add(buffer);
buffer = null;
}
finally
{
buffer?.SafeRelease();
}
}
static void EncodeSubscribeMessage(IByteBufferAllocator bufferAllocator, SubscribePacket packet, List<object> output)
@ -262,6 +302,7 @@ namespace DotNetty.Codecs.Mqtt
ThreadLocalObjectList encodedTopicFilters = ThreadLocalObjectList.NewInstance();
IByteBuffer buf = null;
try
{
foreach (SubscriptionRequest topic in packet.Requests)
@ -274,7 +315,7 @@ namespace DotNetty.Codecs.Mqtt
int variablePartSize = VariableHeaderSize + payloadBufferSize;
int fixedHeaderBufferSize = 1 + MaxVariableLength;
IByteBuffer buf = bufferAllocator.Buffer(fixedHeaderBufferSize + variablePartSize);
buf = bufferAllocator.Buffer(fixedHeaderBufferSize + variablePartSize);
buf.WriteByte(CalculateFirstByteOfFixedHeader(packet));
WriteVariableLengthInt(buf, variablePartSize);
@ -291,9 +332,11 @@ namespace DotNetty.Codecs.Mqtt
}
output.Add(buf);
buf = null;
}
finally
{
buf?.SafeRelease();
encodedTopicFilters.Return();
}
}
@ -303,16 +346,26 @@ namespace DotNetty.Codecs.Mqtt
int payloadBufferSize = message.ReturnCodes.Count;
int variablePartSize = PacketIdLength + payloadBufferSize;
int fixedHeaderBufferSize = 1 + MaxVariableLength;
IByteBuffer buf = bufferAllocator.Buffer(fixedHeaderBufferSize + variablePartSize);
buf.WriteByte(CalculateFirstByteOfFixedHeader(message));
WriteVariableLengthInt(buf, variablePartSize);
buf.WriteShort(message.PacketId);
foreach (QualityOfService qos in message.ReturnCodes)
IByteBuffer buf = null;
try
{
buf.WriteByte((byte)qos);
}
buf = bufferAllocator.Buffer(fixedHeaderBufferSize + variablePartSize);
buf.WriteByte(CalculateFirstByteOfFixedHeader(message));
WriteVariableLengthInt(buf, variablePartSize);
buf.WriteShort(message.PacketId);
foreach (QualityOfService qos in message.ReturnCodes)
{
buf.WriteByte((byte)qos);
}
output.Add(buf);
output.Add(buf);
buf = null;
}
finally
{
buf?.SafeRelease();
}
}
static void EncodeUnsubscribeMessage(IByteBufferAllocator bufferAllocator, UnsubscribePacket packet, List<object> output)
@ -322,6 +375,7 @@ namespace DotNetty.Codecs.Mqtt
ThreadLocalObjectList encodedTopicFilters = ThreadLocalObjectList.NewInstance();
IByteBuffer buf = null;
try
{
foreach (string topic in packet.TopicFilters)
@ -334,7 +388,7 @@ namespace DotNetty.Codecs.Mqtt
int variablePartSize = VariableHeaderSize + payloadBufferSize;
int fixedHeaderBufferSize = 1 + MaxVariableLength;
IByteBuffer buf = bufferAllocator.Buffer(fixedHeaderBufferSize + variablePartSize);
buf = bufferAllocator.Buffer(fixedHeaderBufferSize + variablePartSize);
buf.WriteByte(CalculateFirstByteOfFixedHeader(packet));
WriteVariableLengthInt(buf, variablePartSize);
@ -350,20 +404,31 @@ namespace DotNetty.Codecs.Mqtt
}
output.Add(buf);
buf = null;
}
finally
{
buf?.SafeRelease();
encodedTopicFilters.Return();
}
}
static void EncodePacketWithFixedHeaderOnly(IByteBufferAllocator bufferAllocator, Packet packet, List<object> output)
{
IByteBuffer buffer = bufferAllocator.Buffer(2);
buffer.WriteByte(CalculateFirstByteOfFixedHeader(packet));
buffer.WriteByte(0);
IByteBuffer buffer = null;
try
{
buffer = bufferAllocator.Buffer(2);
buffer.WriteByte(CalculateFirstByteOfFixedHeader(packet));
buffer.WriteByte(0);
output.Add(buffer);
output.Add(buffer);
buffer = null;
}
finally
{
buffer?.SafeRelease();
}
}
static int CalculateFirstByteOfFixedHeader(Packet packet)

Просмотреть файл

@ -23,7 +23,8 @@ namespace DotNetty.Codecs.Redis
public RedisBulkStringAggregator()
: this(RedisConstants.MaximumMessageLength)
{ }
{
}
RedisBulkStringAggregator(int maximumContentLength)
{
@ -36,10 +37,7 @@ namespace DotNetty.Codecs.Redis
public int MaximumCumulationBufferComponents
{
get
{
return this.maximumCumulationBufferComponents;
}
get { return this.maximumCumulationBufferComponents; }
set
{
Contract.Requires(value >= 2);
@ -66,8 +64,8 @@ namespace DotNetty.Codecs.Redis
var redisMessage = (IRedisMessage)message;
return (IsContentMessage(redisMessage)
|| IsStartMessage(redisMessage))
&& !IsAggregated(redisMessage);
|| IsStartMessage(redisMessage))
&& !IsAggregated(redisMessage);
}
protected override void Decode(IChannelHandlerContext context, IRedisMessage message, List<object> output)
@ -138,6 +136,7 @@ namespace DotNetty.Codecs.Redis
throw new MessageAggregationException($"Unexpected message {message}");
}
}
static void AppendPartialContent(CompositeByteBuffer content, IByteBuffer partialContent)
{
Contract.Requires(content != null);
@ -154,6 +153,7 @@ namespace DotNetty.Codecs.Redis
// Note that WriterIndex must be manually increased
content.SetWriterIndex(content.WriterIndex + buffer.ReadableBytes);
}
void InvokeHandleOversizedMessage(IChannelHandlerContext context, BulkStringHeaderRedisMessage startMessage)
{
Contract.Requires(context != null);
@ -177,7 +177,7 @@ namespace DotNetty.Codecs.Redis
{
Contract.Requires(message != null);
return message is BulkStringHeaderRedisMessage
return message is BulkStringHeaderRedisMessage
&& !IsAggregated(message);
}
@ -216,4 +216,4 @@ namespace DotNetty.Codecs.Redis
return message is IFullBulkStringRedisMessage;
}
}
}
}

Просмотреть файл

@ -0,0 +1,222 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace DotNetty.Common.Internal
{
using System;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Collections.Generic;
using DotNetty.Common.Internal.Logging;
using DotNetty.Common.Utilities;
public static class MacAddressUtil
{
/// Length of a valid MAC address.
public const int MacAddressLength = 8;
static readonly byte[] NotFound = { byte.MaxValue };
static readonly IInternalLogger logger = InternalLoggerFactory.GetInstance(typeof(MacAddressUtil));
/// Obtains the best MAC address found on local network interfaces.
/// Generally speaking, an active network interface used on public
/// networks is better than a local network interface.
/// <returns>byte array containing a MAC. null if no MAC can be found.</returns>
public static byte[] GetBestAvailableMac()
{
// Find the best MAC address available.
byte[] bestMacAddr = NotFound;
IPAddress bestInetAddr = IPAddress.Loopback;
// Retrieve the list of available network interfaces.
Dictionary<NetworkInterface, IPAddress> ifaces = new Dictionary<NetworkInterface, IPAddress>();
try
{
foreach (var iface in NetworkInterface.GetAllNetworkInterfaces())
{
// Use the interface with proper INET addresses only.
var addrs = iface.GetIPProperties().UnicastAddresses;
if (addrs.Count > 0)
{
var addressInfo = addrs.First();
if (!IPAddress.IsLoopback(addressInfo.Address))
{
ifaces.Add(iface, addressInfo.Address);
}
}
}
}
catch (SocketException e)
{
logger.Warn("Failed to retrieve the list of available network interfaces", e);
}
foreach (var entry in ifaces)
{
NetworkInterface iface = entry.Key;
IPAddress inetAddr = entry.Value;
// todo: netty has a check for whether interface is virtual but it always returns false. There is no equivalent in .NET
byte[] macAddr = iface.GetPhysicalAddress().GetAddressBytes();
bool replace = false;
int res = CompareAddresses(bestMacAddr, macAddr);
if (res < 0)
{
// Found a better MAC address.
replace = true;
}
else if (res == 0)
{
// Two MAC addresses are of pretty much same quality.
res = CompareAddresses(bestInetAddr, inetAddr);
if (res < 0)
{
// Found a MAC address with better INET address.
replace = true;
}
else if (res == 0)
{
// Cannot tell the difference. Choose the longer one.
if (bestMacAddr.Length < macAddr.Length)
{
replace = true;
}
}
}
if (replace)
{
bestMacAddr = macAddr;
bestInetAddr = inetAddr;
}
}
if (bestMacAddr == NotFound)
{
return null;
}
switch (bestMacAddr.Length)
{
case 6: // EUI-48 - convert to EUI-64
var newAddr = new byte[MacAddressLength];
Array.Copy(bestMacAddr, 0, newAddr, 0, 3);
newAddr[3] = 0xFF;
newAddr[4] = 0xFE;
Array.Copy(bestMacAddr, 3, newAddr, 5, 3);
bestMacAddr = newAddr;
break;
default: // Unknown
bestMacAddr = bestMacAddr.Slice(0, Math.Min(bestMacAddr.Length, MacAddressLength));
break;
}
return bestMacAddr;
}
/// <param name="addr">byte array of a MAC address.</param>
/// <returns>hex formatted MAC address.</returns>
public static string FormatAddress(byte[] addr)
{
StringBuilder buf = new StringBuilder(24);
foreach (byte b in addr)
{
buf.Append((b & 0xFF).ToString("X2\\:"));
}
return buf.ToString(0, buf.Length - 1);
}
/// <returns>positive - current is better, 0 - cannot tell from MAC addr, negative - candidate is better.</returns>
static int CompareAddresses(byte[] current, byte[] candidate)
{
if (candidate == null)
{
return 1;
}
// Must be EUI-48 or longer.
if (candidate.Length < 6)
{
return 1;
}
// Must not be filled with only 0 and 1.
bool onlyZeroAndOne = true;
foreach (byte b in candidate)
{
if (b != 0 && b != 1)
{
onlyZeroAndOne = false;
break;
}
}
if (onlyZeroAndOne)
{
return 1;
}
// Must not be a multicast address
if ((candidate[0] & 1) != 0)
{
return 1;
}
// Prefer globally unique address.
if ((current[0] & 2) == 0)
{
if ((candidate[0] & 2) == 0)
{
// Both current and candidate are globally unique addresses.
return 0;
}
else
{
// Only current is globally unique.
return 1;
}
}
else
{
if ((candidate[0] & 2) == 0)
{
// Only candidate is globally unique.
return -1;
}
else
{
// Both current and candidate are non-unique.
return 0;
}
}
}
/// <returns>positive - current is better, 0 - cannot tell, negative - candidate is better</returns>
static int CompareAddresses(IPAddress current, IPAddress candidate) => ScoreAddress(current) - ScoreAddress(candidate);
static int ScoreAddress(IPAddress addr)
{
if (IPAddress.IsLoopback(addr))
{
return 0;
}
if (addr.IsIPv6Multicast)
{
return 1;
}
if (addr.IsIPv6LinkLocal)
{
return 2;
}
if (addr.IsIPv6SiteLocal)
{
return 3;
}
return 4;
}
}
}

Просмотреть файл

@ -11,7 +11,7 @@ namespace DotNetty.Common.Utilities
public int ReferenceCount => this.referenceCount;
public IReferenceCounted Retain() => this.Retain(1);
public IReferenceCounted Retain() => this.RetainCore(1);
public IReferenceCounted Retain(int increment)
{
@ -46,7 +46,7 @@ namespace DotNetty.Common.Utilities
public abstract IReferenceCounted Touch(object hint);
public bool Release() => this.Release(1);
public bool Release() => this.ReleaseCore(1);
public bool Release(int decrement)
{

Просмотреть файл

@ -7,9 +7,9 @@ namespace DotNetty.Common.Utilities
using System.Threading;
using DotNetty.Common.Internal.Logging;
public sealed class ReferenceCountUtil
public static class ReferenceCountUtil
{
static readonly IInternalLogger Logger = InternalLoggerFactory.GetInstance<ReferenceCountUtil>();
static readonly IInternalLogger Logger = InternalLoggerFactory.GetInstance(typeof(ReferenceCountUtil));
/// <summary>
/// Try to call {@link ReferenceCounted#retain()} if the specified message implements {@link ReferenceCounted}.
@ -138,6 +138,30 @@ namespace DotNetty.Common.Utilities
}
}
public static void SafeRelease(this IReferenceCounted msg)
{
try
{
msg?.Release();
}
catch (Exception ex)
{
Logger.Warn("Failed to release a message: {}", msg, ex);
}
}
public static void SafeRelease(this IReferenceCounted msg, int decrement)
{
try
{
msg?.Release(decrement);
}
catch (Exception ex)
{
Logger.Warn("Failed to release a message: {} (decrement: {})", msg, decrement, ex);
}
}
/// <summary>
/// Schedules the specified object to be released when the caller thread terminates. Note that this operation is
/// intended to simplify reference counting of ephemeral objects during unit tests. Do not use it beyond the
@ -178,9 +202,6 @@ namespace DotNetty.Common.Utilities
}
static string FormatReleaseString(IReferenceCounted referenceCounted, int decrement)
{
return referenceCounted.GetType().Name + ".Release(" + decrement.ToString() + ") refCnt: "
+ referenceCounted.ReferenceCount.ToString();
}
=> $"{referenceCounted.GetType().Name}.Release({decrement.ToString()}) refCnt: {referenceCounted.ReferenceCount.ToString()}";
}
}

Просмотреть файл

@ -28,7 +28,8 @@
"netstandard1.3": {
"dependencies": {
"System.Threading.Thread": "4.0.0",
"System.Diagnostics.Contracts": "4.0.1"
"System.Diagnostics.Contracts": "4.0.1",
"System.Net.NetworkInformation": "4.1.0"
}
},
"net451": {}

Просмотреть файл

@ -447,7 +447,7 @@ namespace DotNetty.Handlers.Tls
}
else
{
outputBuffer.Release();
outputBuffer.SafeRelease();
}
}
}
@ -579,7 +579,7 @@ namespace DotNetty.Handlers.Tls
}
catch (Exception ex)
{
ReferenceCountUtil.SafeRelease(buf);
buf.SafeRelease();
this.HandleFailure(ex);
throw;
}

Просмотреть файл

@ -4,13 +4,8 @@
namespace DotNetty.Transport.Channels
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
@ -74,12 +69,21 @@ namespace DotNetty.Transport.Channels
if (MachineIdPattern.Match(customMachineId).Success)
{
machineId = ParseMachineId(customMachineId);
Logger.Debug("-Dio.netty.machineId: {} (user-set)", customMachineId);
}
else
{
Logger.Warn("-Dio.netty.machineId: {} (malformed)", customMachineId);
}
}
if (machineId == null)
{
machineId = DefaultMachineId();
if (Logger.DebugEnabled)
{
Logger.Debug("-Dio.netty.machineId: {} (auto-detected)", MacAddressUtil.FormatAddress(machineId));
}
}
MachineId = machineId;
}
@ -139,177 +143,17 @@ namespace DotNetty.Transport.Channels
static byte[] DefaultMachineId()
{
// Find the best MAC address available.
byte[] notFound = { byte.MaxValue };
byte[] bestMacAddr = notFound;
IPAddress bestIpAddr = IPAddress.Loopback;
var ifaces = new SortedDictionary<NetworkInterface, IPAddress>();
try
{
foreach (NetworkInterface iface in NetworkInterface.GetAllNetworkInterfaces())
{
UnicastIPAddressInformationCollection addrs = iface.GetIPProperties().UnicastAddresses;
UnicastIPAddressInformation addr = addrs.FirstOrDefault(a => !IPAddress.IsLoopback(a.Address));
if (addr != null)
{
ifaces.Add(iface, addr.Address);
}
}
}
catch (SocketException e)
{
Logger.Warn("Failed to retrieve the list of available network interfaces", e);
}
catch
{
// ignored
}
foreach (KeyValuePair<NetworkInterface, IPAddress> entry in ifaces)
{
NetworkInterface iface = entry.Key;
IPAddress addr = entry.Value;
//todo check if the iface is virtual(there is no equivvalent method in .Net like in java)
byte[] macAddr = iface.GetPhysicalAddress().GetAddressBytes();
bool replace = false;
int res = CompareAddresses(bestMacAddr, macAddr);
if (res < 0)
{
replace = true;
}
else if (res == 0)
{
res = CompareAddresses(bestIpAddr, addr);
if (res < 0)
{
replace = true;
}
else if (res == 0)
{
if (bestMacAddr.Length < macAddr.Length)
{
replace = true;
}
}
}
if (replace)
{
bestMacAddr = macAddr;
bestIpAddr = addr;
}
}
if (bestMacAddr == notFound)
{
bestMacAddr = new byte[MachineIdLen];
byte[] bestMacAddr = MacAddressUtil.GetBestAvailableMac();
if (bestMacAddr == null) {
bestMacAddr = new byte[MacAddressUtil.MacAddressLength];
ThreadLocalRandom.Value.NextBytes(bestMacAddr);
}
switch (bestMacAddr.Length)
{
case 6: // EUI-48 - convert to EUI-64
var newAddr = new byte[MachineIdLen];
Array.Copy(bestMacAddr, 0, newAddr, 0, 3);
newAddr[3] = 0xFF;
newAddr[4] = 0xFE;
Array.Copy(bestMacAddr, 3, newAddr, 5, 3);
bestMacAddr = newAddr;
break;
default: // Unknown
bestMacAddr = bestMacAddr.Take(MachineIdLen).ToArray();
break;
Logger.Warn(
"Failed to find a usable hardware address from the network interfaces; using random bytes: {}",
MacAddressUtil.FormatAddress(bestMacAddr));
}
return bestMacAddr;
}
static int CompareAddresses(byte[] current, byte[] candidate)
{
if (candidate == null)
{
return 1;
}
// Must be EUI-48 or longer.
if (candidate.Length < 6)
{
return 1;
}
// Must not be filled with only 0 and 1.
bool onlyZeroAndOne = true;
foreach (byte b in candidate)
{
if (b != 0 && b != 1)
{
onlyZeroAndOne = false;
break;
}
}
if (onlyZeroAndOne)
{
return 1;
}
// Must not be a multicast address
if ((candidate[0] & 1) != 0)
{
return 1;
}
// Prefer globally unique address.
if ((current[0] & 2) == 0)
{
if ((candidate[0] & 2) == 0)
{
// Both current and candidate are globally unique addresses.
return 0;
}
else
{
// Only current is globally unique.
return 1;
}
}
else
{
if ((candidate[0] & 2) == 0)
{
// Only candidate is globally unique.
return -1;
}
else
{
// Both current and candidate are non-unique.
return 0;
}
}
}
static int CompareAddresses(IPAddress current, IPAddress candidate) => ScoreAddress(current) - ScoreAddress(candidate);
static int ScoreAddress(IPAddress addr)
{
if (IPAddress.IsLoopback(addr))
{
return 0;
}
if (addr.IsIPv6Multicast)
{
return 1;
}
if (addr.IsIPv6LinkLocal)
{
return 2;
}
if (addr.IsIPv6SiteLocal)
{
return 3;
}
return 4;
}
string NewLongValue()
{

Просмотреть файл

@ -38,8 +38,7 @@
"System.Net.Primitives": "4.0.11",
"System.Diagnostics.StackTrace": "4.0.1",
"System.Net.NameResolution": "4.0.0",
"System.Diagnostics.Process": "4.1.0",
"System.Net.NetworkInformation": "4.1.0"
"System.Diagnostics.Process": "4.1.0"
}
},
"net451": { }

Просмотреть файл

@ -236,7 +236,7 @@ namespace DotNetty.Codecs.Mqtt.Tests
foreach (IByteBuffer message in output)
{
MqttDecoder mqttDecoder = (useServer ? this.serverDecoder : this.clientDecoder);
MqttDecoder mqttDecoder = useServer ? this.serverDecoder : this.clientDecoder;
if (explodeForDecode)
{
while (message.IsReadable())