зеркало из https://github.com/Azure/DotNetty.git
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:
Родитель
44885964bd
Коммит
17a69a0864
|
@ -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())
|
||||
|
|
Загрузка…
Ссылка в новой задаче