From 17a69a0864a234729820998683c366e171df3d51 Mon Sep 17 00:00:00 2001 From: Max Gortman Date: Fri, 25 Nov 2016 01:42:31 -0800 Subject: [PATCH] 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 --- src/DotNetty.Codecs.Mqtt/MqttDecoder.cs | 2 +- src/DotNetty.Codecs.Mqtt/MqttEncoder.cs | 201 ++++++++++------ .../RedisBulkStringAggregator.cs | 18 +- .../Internal/MacAddressUtil.cs | 222 ++++++++++++++++++ .../Utilities/AbstractReferenceCounted.cs | 4 +- .../Utilities/ReferenceCountUtil.cs | 33 ++- src/DotNetty.Common/project.json | 3 +- src/DotNetty.Handlers/Tls/TlsHandler.cs | 4 +- .../Channels/DefaultChannelId.cs | 186 ++------------- src/DotNetty.Transport/project.json | 3 +- .../MqttCodecTests.cs | 2 +- 11 files changed, 415 insertions(+), 263 deletions(-) create mode 100644 src/DotNetty.Common/Internal/MacAddressUtil.cs diff --git a/src/DotNetty.Codecs.Mqtt/MqttDecoder.cs b/src/DotNetty.Codecs.Mqtt/MqttDecoder.cs index 617c82027..b2418db83 100644 --- a/src/DotNetty.Codecs.Mqtt/MqttDecoder.cs +++ b/src/DotNetty.Codecs.Mqtt/MqttDecoder.cs @@ -466,7 +466,7 @@ static string DecodeString(IByteBuffer buffer, ref int remainingLength, int minB 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; diff --git a/src/DotNetty.Codecs.Mqtt/MqttEncoder.cs b/src/DotNetty.Codecs.Mqtt/MqttEncoder.cs index dd6389c70..2e41bc6a6 100644 --- a/src/DotNetty.Codecs.Mqtt/MqttEncoder.cs +++ b/src/DotNetty.Codecs.Mqtt/MqttEncoder.cs @@ -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 @@ -124,44 +125,55 @@ static void EncodeConnectMessage(IByteBufferAllocator bufferAllocator, ConnectPa 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); + IByteBuffer buf = null; + try + { + buf = bufferAllocator.Buffer(fixedHeaderBufferSize + variablePartSize); + buf.WriteByte(CalculateFirstByteOfFixedHeader(packet)); + WriteVariableLengthInt(buf, variablePartSize); - buf.WriteShort(protocolNameBytes.Length); - buf.WriteBytes(protocolNameBytes); + buf.WriteShort(protocolNameBytes.Length); + buf.WriteBytes(protocolNameBytes); - buf.WriteByte(Util.ProtocolLevel); - buf.WriteByte(CalculateConnectFlagsByte(packet)); - buf.WriteShort(packet.KeepAliveInSeconds); + 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.WriteShort(willTopicBytes.Length); - buf.WriteBytes(willTopicBytes, 0, willTopicBytes.Length); - buf.WriteShort(willMessage.ReadableBytes); - if (willMessage.IsReadable()) + // 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) + if (packet.HasUsername) { - buf.WriteShort(passwordBytes.Length); - buf.WriteBytes(passwordBytes, 0, passwordBytes.Length); + 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); + output.Add(buf); + buf = null; + } + finally + { + buf?.SafeRelease(); + willMessage?.SafeRelease(); + } } static int CalculateConnectFlagsByte(ConnectPacket packet) @@ -193,20 +205,30 @@ static int CalculateConnectFlagsByte(ConnectPacket packet) static void EncodeConnAckMessage(IByteBufferAllocator bufferAllocator, ConnAckPacket message, List 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 + 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); + buffer = null; } - else + finally { - buffer.WriteByte(0); // 7 reserved 0-bits and SP = 0 + buffer?.SafeRelease(); } - buffer.WriteByte((byte)message.ReturnCode); - - output.Add(buffer); } static void EncodePublishMessage(IByteBufferAllocator bufferAllocator, PublishPacket packet, List output) @@ -223,17 +245,26 @@ static void EncodePublishMessage(IByteBufferAllocator bufferAllocator, PublishPa 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 @@ static void EncodePacketWithIdOnly(IByteBufferAllocator bufferAllocator, PacketW 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 output) @@ -262,6 +302,7 @@ static void EncodeSubscribeMessage(IByteBufferAllocator bufferAllocator, Subscri ThreadLocalObjectList encodedTopicFilters = ThreadLocalObjectList.NewInstance(); + IByteBuffer buf = null; try { foreach (SubscriptionRequest topic in packet.Requests) @@ -274,7 +315,7 @@ static void EncodeSubscribeMessage(IByteBufferAllocator bufferAllocator, Subscri 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 @@ static void EncodeSubscribeMessage(IByteBufferAllocator bufferAllocator, Subscri } output.Add(buf); + buf = null; } finally { + buf?.SafeRelease(); encodedTopicFilters.Return(); } } @@ -303,16 +346,26 @@ static void EncodeSubAckMessage(IByteBufferAllocator bufferAllocator, SubAckPack 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); + buf = null; - output.Add(buf); + } + finally + { + buf?.SafeRelease(); + } } static void EncodeUnsubscribeMessage(IByteBufferAllocator bufferAllocator, UnsubscribePacket packet, List output) @@ -322,6 +375,7 @@ static void EncodeUnsubscribeMessage(IByteBufferAllocator bufferAllocator, Unsub ThreadLocalObjectList encodedTopicFilters = ThreadLocalObjectList.NewInstance(); + IByteBuffer buf = null; try { foreach (string topic in packet.TopicFilters) @@ -334,7 +388,7 @@ static void EncodeUnsubscribeMessage(IByteBufferAllocator bufferAllocator, Unsub 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 @@ static void EncodeUnsubscribeMessage(IByteBufferAllocator bufferAllocator, Unsub } output.Add(buf); + buf = null; } finally { + buf?.SafeRelease(); encodedTopicFilters.Return(); } } static void EncodePacketWithFixedHeaderOnly(IByteBufferAllocator bufferAllocator, Packet packet, List 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) diff --git a/src/DotNetty.Codecs.Redis/RedisBulkStringAggregator.cs b/src/DotNetty.Codecs.Redis/RedisBulkStringAggregator.cs index 5d870c493..371791b3d 100644 --- a/src/DotNetty.Codecs.Redis/RedisBulkStringAggregator.cs +++ b/src/DotNetty.Codecs.Redis/RedisBulkStringAggregator.cs @@ -23,7 +23,8 @@ public sealed class RedisBulkStringAggregator : MessageToMessageDecoder= 2); @@ -66,8 +64,8 @@ public override bool AcceptInboundMessage(object message) var redisMessage = (IRedisMessage)message; return (IsContentMessage(redisMessage) - || IsStartMessage(redisMessage)) - && !IsAggregated(redisMessage); + || IsStartMessage(redisMessage)) + && !IsAggregated(redisMessage); } protected override void Decode(IChannelHandlerContext context, IRedisMessage message, List output) @@ -138,6 +136,7 @@ protected override void Decode(IChannelHandlerContext context, IRedisMessage mes throw new MessageAggregationException($"Unexpected message {message}"); } } + static void AppendPartialContent(CompositeByteBuffer content, IByteBuffer partialContent) { Contract.Requires(content != null); @@ -154,6 +153,7 @@ static void AppendPartialContent(CompositeByteBuffer content, IByteBuffer partia // 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 @@ static bool IsStartMessage(IRedisMessage message) { Contract.Requires(message != null); - return message is BulkStringHeaderRedisMessage + return message is BulkStringHeaderRedisMessage && !IsAggregated(message); } @@ -216,4 +216,4 @@ static bool IsAggregated(IRedisMessage message) return message is IFullBulkStringRedisMessage; } } -} +} \ No newline at end of file diff --git a/src/DotNetty.Common/Internal/MacAddressUtil.cs b/src/DotNetty.Common/Internal/MacAddressUtil.cs new file mode 100644 index 000000000..0f3d51a6b --- /dev/null +++ b/src/DotNetty.Common/Internal/MacAddressUtil.cs @@ -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. + /// byte array containing a MAC. null if no MAC can be found. + 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 ifaces = new Dictionary(); + 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; + } + + /// byte array of a MAC address. + /// hex formatted MAC address. + 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); + } + + /// positive - current is better, 0 - cannot tell from MAC addr, negative - candidate is better. + 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; + } + } + } + + /// positive - current is better, 0 - cannot tell, negative - candidate is better + 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; + } + } +} \ No newline at end of file diff --git a/src/DotNetty.Common/Utilities/AbstractReferenceCounted.cs b/src/DotNetty.Common/Utilities/AbstractReferenceCounted.cs index 019e240d2..3f7cadc26 100644 --- a/src/DotNetty.Common/Utilities/AbstractReferenceCounted.cs +++ b/src/DotNetty.Common/Utilities/AbstractReferenceCounted.cs @@ -11,7 +11,7 @@ public abstract class AbstractReferenceCounted : IReferenceCounted 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 @@ IReferenceCounted RetainCore(int increment) public abstract IReferenceCounted Touch(object hint); - public bool Release() => this.Release(1); + public bool Release() => this.ReleaseCore(1); public bool Release(int decrement) { diff --git a/src/DotNetty.Common/Utilities/ReferenceCountUtil.cs b/src/DotNetty.Common/Utilities/ReferenceCountUtil.cs index f65451f10..eaf90fa52 100644 --- a/src/DotNetty.Common/Utilities/ReferenceCountUtil.cs +++ b/src/DotNetty.Common/Utilities/ReferenceCountUtil.cs @@ -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(); + static readonly IInternalLogger Logger = InternalLoggerFactory.GetInstance(typeof(ReferenceCountUtil)); /// /// Try to call {@link ReferenceCounted#retain()} if the specified message implements {@link ReferenceCounted}. @@ -138,6 +138,30 @@ public static void SafeRelease(object msg, int decrement) } } + 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); + } + } + /// /// 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 @@ public static T ReleaseLater(T msg, int decrement) } 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()}"; } } \ No newline at end of file diff --git a/src/DotNetty.Common/project.json b/src/DotNetty.Common/project.json index 127219c37..a3e4cbbb7 100644 --- a/src/DotNetty.Common/project.json +++ b/src/DotNetty.Common/project.json @@ -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": {} diff --git a/src/DotNetty.Handlers/Tls/TlsHandler.cs b/src/DotNetty.Handlers/Tls/TlsHandler.cs index 1e2fcd8ef..e404735e1 100644 --- a/src/DotNetty.Handlers/Tls/TlsHandler.cs +++ b/src/DotNetty.Handlers/Tls/TlsHandler.cs @@ -447,7 +447,7 @@ void Unwrap(IChannelHandlerContext ctx, IByteBuffer packet, int offset, int leng } else { - outputBuffer.Release(); + outputBuffer.SafeRelease(); } } } @@ -579,7 +579,7 @@ void Wrap(IChannelHandlerContext context) } catch (Exception ex) { - ReferenceCountUtil.SafeRelease(buf); + buf.SafeRelease(); this.HandleFailure(ex); throw; } diff --git a/src/DotNetty.Transport/Channels/DefaultChannelId.cs b/src/DotNetty.Transport/Channels/DefaultChannelId.cs index 4cc39f34e..d56b85786 100644 --- a/src/DotNetty.Transport/Channels/DefaultChannelId.cs +++ b/src/DotNetty.Transport/Channels/DefaultChannelId.cs @@ -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 @@ static DefaultChannelId() 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 @@ public static DefaultChannelId NewInstance() static byte[] DefaultMachineId() { - // Find the best MAC address available. - byte[] notFound = { byte.MaxValue }; - byte[] bestMacAddr = notFound; - IPAddress bestIpAddr = IPAddress.Loopback; - var ifaces = new SortedDictionary(); - 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 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() { diff --git a/src/DotNetty.Transport/project.json b/src/DotNetty.Transport/project.json index c38220849..a325fffae 100644 --- a/src/DotNetty.Transport/project.json +++ b/src/DotNetty.Transport/project.json @@ -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": { } diff --git a/test/DotNetty.Codecs.Mqtt.Tests/MqttCodecTests.cs b/test/DotNetty.Codecs.Mqtt.Tests/MqttCodecTests.cs index ffa3f511b..d59e8ea3b 100644 --- a/test/DotNetty.Codecs.Mqtt.Tests/MqttCodecTests.cs +++ b/test/DotNetty.Codecs.Mqtt.Tests/MqttCodecTests.cs @@ -236,7 +236,7 @@ T RecodePacket(T packet, bool useServer, bool explodeForDecode) foreach (IByteBuffer message in output) { - MqttDecoder mqttDecoder = (useServer ? this.serverDecoder : this.clientDecoder); + MqttDecoder mqttDecoder = useServer ? this.serverDecoder : this.clientDecoder; if (explodeForDecode) { while (message.IsReadable())