ModernUO/Projects/Server/Kestrel/Transport.LIbuv/Internal/Networking/SockAddr.cs
2020-04-12 20:57:48 -07:00

114 lines
4.4 KiB
C#

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Net;
using System.Runtime.InteropServices;
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networking
{
[StructLayout(LayoutKind.Sequential)]
internal struct SockAddr
{
// this type represents native memory occupied by sockaddr struct
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms740496(v=vs.85).aspx
// although the c/c++ header defines it as a 2-byte short followed by a 14-byte array,
// the simplest way to reserve the same size in c# is with four nameless long values
private readonly long _field0;
private readonly long _field1;
private readonly long _field2;
private long _field3;
public SockAddr(long ignored) => _field0 = _field1 = _field2 = _field3 = 0;
public unsafe IPEndPoint GetIPEndPoint()
{
// The bytes are represented in network byte order.
//
// Example 1: [2001:4898:e0:391:b9ef:1124:9d3e:a354]:39179
//
// 0000 0000 0b99 0017 => The third and fourth bytes 990B is the actual port
// 9103 e000 9848 0120 => IPv6 address is represented in the 128bit field1 and field2.
// 54a3 3e9d 2411 efb9 Read these two 64-bit long from right to left byte by byte.
// 0000 0000 0000 0010 => Scope ID 0x10 (eg [::1%16]) the first 4 bytes of field3 in host byte order.
//
// Example 2: 10.135.34.141:39178 when adopt dual-stack sockets, IPv4 is mapped to IPv6
//
// 0000 0000 0a99 0017 => The port representation are the same
// 0000 0000 0000 0000
// 8d22 870a ffff 0000 => IPv4 occupies the last 32 bit: 0A.87.22.8d is the actual address.
// 0000 0000 0000 0000
//
// Example 3: 10.135.34.141:12804, not dual-stack sockets
//
// 8d22 870a fd31 0002 => sa_family == AF_INET (02)
// 0000 0000 0000 0000
// 0000 0000 0000 0000
// 0000 0000 0000 0000
//
// Example 4: 127.0.0.1:52798, on a Mac OS
//
// 0100 007F 3ECE 0210 => sa_family == AF_INET (02) Note that struct sockaddr on mac use
// 0000 0000 0000 0000 the second unint8 field for sa family type
// 0000 0000 0000 0000 http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/socket.h
// 0000 0000 0000 0000
//
// Reference:
// - Windows: https://msdn.microsoft.com/en-us/library/windows/desktop/ms740506(v=vs.85).aspx
// - Linux: https://github.com/torvalds/linux/blob/6a13feb9c82803e2b815eca72fa7a9f5561d7861/include/linux/socket.h
// - Linux (sin6_scope_id): https://github.com/torvalds/linux/blob/5924bbecd0267d87c24110cbe2041b5075173a25/net/sunrpc/addr.c#L82
// - Apple: http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/socket.h
// Quick calculate the port by mask the field and locate the byte 3 and byte 4
// and then shift them to correct place to form a int.
var port = ((int)(_field0 & 0x00FF0000) >> 8) | (int)((_field0 & 0xFF000000) >> 24);
int family = (int)_field0;
if (PlatformApis.IsDarwin)
// see explanation in example 4
family >>= 8;
family &= 0xFF;
if (family == 2)
{
// AF_INET => IPv4
return new IPEndPoint(new IPAddress((_field0 >> 32) & 0xFFFFFFFF), port);
}
else if (IsIPv4MappedToIPv6())
{
var ipv4bits = (_field2 >> 32) & 0x00000000FFFFFFFF;
return new IPEndPoint(new IPAddress(ipv4bits), port);
}
else
{
// otherwise IPv6
var bytes = new byte[16];
fixed (byte* b = bytes)
{
*(long*)b = _field1;
*(long*)(b + 8) = _field2;
}
return new IPEndPoint(new IPAddress(bytes, ScopeId), port);
}
}
public uint ScopeId
{
get => (uint)_field3;
set
{
_field3 &= unchecked ((long)0xFFFFFFFF00000000);
_field3 |= value;
}
}
private bool IsIPv4MappedToIPv6()
{
// If the IPAddress is an IPv4 mapped to IPv6, return the IPv4 representation instead.
// For example [::FFFF:127.0.0.1] will be transform to IPAddress of 127.0.0.1
if (_field1 != 0) return false;
return (_field2 & 0xFFFFFFFF) == 0xFFFF0000;
}
}
}