Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Commit cbc001c

Browse files
committed
Record remote IPAddress and port
1. Record local IPAddress and port as well; 2. Frame will implement IHttpConnectionFeature.
1 parent 151b0f3 commit cbc001c

File tree

8 files changed

+149
-13
lines changed

8 files changed

+149
-13
lines changed

samples/SampleApp/Startup.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory, IAp
5252
context.Request.Path,
5353
context.Request.QueryString);
5454

55+
var connectionFeature = context.Features.Get<IHttpConnectionFeature>();
56+
if (connectionFeature != null)
57+
{
58+
Console.WriteLine($"Peer: {connectionFeature.RemoteIpAddress.ToString()} {connectionFeature.RemotePort}");
59+
Console.WriteLine($"Sock: {connectionFeature.LocalIpAddress.ToString()} {connectionFeature.LocalPort}");
60+
Console.WriteLine($"IsLocal: {connectionFeature.IsLocal}");
61+
}
62+
5563
context.Response.ContentLength = 11;
5664
context.Response.ContentType = "text/plain";
5765
await context.Response.WriteAsync("Hello world");

samples/SampleApp/project.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
}
1818
},
1919
"commands": {
20-
"web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.Kestrel --server.urls http://localhost:5000;https://localhost:5001",
21-
"kestrel": "Microsoft.AspNet.Server.Kestrel --server.urls http://localhost:5000;https://localhost:5001",
20+
"web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.Kestrel --server.urls http://*:5000;https://*:5001",
21+
"kestrel": "Microsoft.AspNet.Server.Kestrel --server.urls http://*:5000;https://*:5001",
2222
"run-socket": "Microsoft.AspNet.Server.Kestrel --server.urls http://unix:/tmp/kestrel-test.sock"
2323
}
2424
}

src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Net;
56
using System.Threading;
7+
using Microsoft.AspNet.Http.Features;
68
using Microsoft.AspNet.Server.Kestrel.Filter;
79
using Microsoft.AspNet.Server.Kestrel.Infrastructure;
810
using Microsoft.AspNet.Server.Kestrel.Networking;
@@ -28,6 +30,11 @@ public class Connection : ConnectionContext, IConnectionControl
2830
private readonly object _stateLock = new object();
2931
private ConnectionState _connectionState;
3032

33+
private IPAddress _remoteIPAddress;
34+
private IPAddress _localIPAddress;
35+
private int _remotePort;
36+
private int _localPort;
37+
3138
public Connection(ListenerContext context, UvStreamHandle socket) : base(context)
3239
{
3340
_socket = socket;
@@ -46,13 +53,20 @@ public void Start()
4653
// Start socket prior to applying the ConnectionFilter
4754
_socket.ReadStart(_allocCallback, _readCallback, this);
4855

56+
var tcpHandle = _socket as UvTcpHandle;
57+
if (tcpHandle != null)
58+
{
59+
tcpHandle.GetPeerName(out _remoteIPAddress, out _remotePort);
60+
tcpHandle.GetSockName(out _localIPAddress, out _localPort);
61+
}
62+
4963
// Don't initialize _frame until SocketInput and SocketOutput are set to their final values.
5064
if (ConnectionFilter == null)
5165
{
5266
SocketInput = _rawSocketInput;
5367
SocketOutput = _rawSocketOutput;
5468

55-
_frame = new Frame(this);
69+
_frame = CreateFrame();
5670
_frame.Start();
5771
}
5872
else
@@ -94,7 +108,7 @@ private void ApplyConnectionFilter()
94108
SocketInput = filteredStreamAdapter.SocketInput;
95109
SocketOutput = filteredStreamAdapter.SocketOutput;
96110

97-
_frame = new Frame(this);
111+
_frame = CreateFrame();
98112
_frame.Start();
99113
}
100114

@@ -142,6 +156,22 @@ private void OnRead(UvStreamHandle handle, int status)
142156
_rawSocketInput.IncomingComplete(readCount, error);
143157
}
144158

159+
private Frame CreateFrame()
160+
{
161+
var frame = new Frame(this);
162+
var httpConnectionFeature = frame as IHttpConnectionFeature;
163+
if (httpConnectionFeature != null)
164+
{
165+
httpConnectionFeature.RemoteIpAddress = _remoteIPAddress;
166+
httpConnectionFeature.RemotePort = _remotePort;
167+
httpConnectionFeature.LocalIpAddress = _localIPAddress;
168+
httpConnectionFeature.LocalPort = _localPort;
169+
httpConnectionFeature.IsLocal = IPAddress.IsLoopback(_remoteIPAddress);
170+
}
171+
172+
return frame;
173+
}
174+
145175
void IConnectionControl.Pause()
146176
{
147177
Log.ConnectionPause(_connectionId);

src/Microsoft.AspNet.Server.Kestrel/Http/Frame.FeatureCollection.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,19 @@
66
using System.Collections.Generic;
77
using System.IO;
88
using System.Linq;
9+
using System.Net;
910
using System.Threading.Tasks;
1011
using Microsoft.AspNet.Http;
1112
using Microsoft.AspNet.Http.Features;
1213
using Microsoft.Extensions.Primitives;
1314

1415
namespace Microsoft.AspNet.Server.Kestrel.Http
1516
{
16-
public partial class Frame : IFeatureCollection, IHttpRequestFeature, IHttpResponseFeature, IHttpUpgradeFeature
17+
public partial class Frame : IFeatureCollection,
18+
IHttpRequestFeature,
19+
IHttpResponseFeature,
20+
IHttpUpgradeFeature,
21+
IHttpConnectionFeature
1722
{
1823
// NOTE: When feature interfaces are added to or removed from this Frame class implementation,
1924
// then the list of `implementedFeatures` in the generated code project MUST also be updated.
@@ -246,6 +251,16 @@ bool IHttpUpgradeFeature.IsUpgradableRequest
246251

247252
int IFeatureCollection.Revision => _featureRevision;
248253

254+
IPAddress IHttpConnectionFeature.RemoteIpAddress { get; set; }
255+
256+
IPAddress IHttpConnectionFeature.LocalIpAddress { get; set; }
257+
258+
int IHttpConnectionFeature.RemotePort { get; set; }
259+
260+
int IHttpConnectionFeature.LocalPort { get; set; }
261+
262+
bool IHttpConnectionFeature.IsLocal { get; set; }
263+
249264
object IFeatureCollection.this[Type key]
250265
{
251266
get { return FastFeatureGet(key); }

src/Microsoft.AspNet.Server.Kestrel/Http/Frame.Generated.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@ private void FastReset()
4545
_currentIHttpRequestFeature = this;
4646
_currentIHttpResponseFeature = this;
4747
_currentIHttpUpgradeFeature = this;
48+
_currentIHttpConnectionFeature = this;
4849

4950
_currentIHttpRequestIdentifierFeature = null;
5051
_currentIServiceProvidersFeature = null;
5152
_currentIHttpRequestLifetimeFeature = null;
52-
_currentIHttpConnectionFeature = null;
5353
_currentIHttpAuthenticationFeature = null;
5454
_currentIQueryFeature = null;
5555
_currentIFormFeature = null;

src/Microsoft.AspNet.Server.Kestrel/Networking/Libuv.cs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public void Load(string dllToLoad)
4040
// TODO: fwlink?
4141
message += " Make sure libuv is installed and available as libuv.so.1";
4242
}
43-
43+
4444
throw new InvalidOperationException(message);
4545
}
4646

@@ -399,15 +399,35 @@ public int ip6_addr(string ip, int port, out sockaddr addr, out Exception error)
399399

400400
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
401401
public delegate void uv_walk_cb(IntPtr handle, IntPtr arg);
402+
402403
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
403404
unsafe protected delegate int uv_walk(UvLoopHandle loop, uv_walk_cb walk_cb, IntPtr arg);
405+
404406
protected uv_walk _uv_walk = default(uv_walk);
405407
unsafe public void walk(UvLoopHandle loop, uv_walk_cb walk_cb, IntPtr arg)
406408
{
407409
loop.Validate();
408410
_uv_walk(loop, walk_cb, arg);
409411
}
410412

413+
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
414+
public delegate int uv_tcp_getsockname(UvTcpHandle handle, out sockaddr addr, ref int namelen);
415+
protected uv_tcp_getsockname _uv_tcp_getsockname = default(uv_tcp_getsockname);
416+
public void tcp_getsockname(UvTcpHandle handle, out sockaddr addr, ref int namelen)
417+
{
418+
handle.Validate();
419+
Check(_uv_tcp_getsockname(handle, out addr, ref namelen));
420+
}
421+
422+
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
423+
public delegate int uv_tcp_getpeername(UvTcpHandle handle, out sockaddr addr, ref int namelen);
424+
protected uv_tcp_getpeername _uv_tcp_getpeername = default(uv_tcp_getpeername);
425+
public void tcp_getpeername(UvTcpHandle handle, out sockaddr addr, ref int namelen)
426+
{
427+
handle.Validate();
428+
Check(_uv_tcp_getpeername(handle, out addr, ref namelen));
429+
}
430+
411431
public uv_buf_t buf_init(IntPtr memory, int len)
412432
{
413433
return new uv_buf_t(memory, len, IsWindows);
@@ -420,10 +440,10 @@ public struct sockaddr
420440
// although the c/c++ header defines it as a 2-byte short followed by a 14-byte array,
421441
// the simplest way to reserve the same size in c# is with four nameless long values
422442

423-
private long _field0;
424-
private long _field1;
425-
private long _field2;
426-
private long _field3;
443+
public long _field0;
444+
public long _field1;
445+
public long _field2;
446+
public long _field3;
427447

428448
public sockaddr(long ignored) { _field3 = _field0 = _field1 = _field2 = _field3 = 0; }
429449
}

src/Microsoft.AspNet.Server.Kestrel/Networking/UvTcpHandle.cs

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Net;
6+
using System.Runtime.InteropServices;
67
using Microsoft.AspNet.Server.Kestrel.Infrastructure;
78

89
namespace Microsoft.AspNet.Server.Kestrel.Networking
@@ -17,7 +18,7 @@ public void Init(UvLoopHandle loop)
1718
{
1819
CreateMemory(
1920
loop.Libuv,
20-
loop.ThreadId,
21+
loop.ThreadId,
2122
loop.Libuv.handle_size(Libuv.HandleType.TCP));
2223

2324
_uv.tcp_init(loop, this);
@@ -26,7 +27,7 @@ public void Init(UvLoopHandle loop)
2627
public void Init(UvLoopHandle loop, Action<Action<IntPtr>, IntPtr> queueCloseHandle)
2728
{
2829
CreateHandle(
29-
loop.Libuv,
30+
loop.Libuv,
3031
loop.ThreadId,
3132
loop.Libuv.handle_size(Libuv.HandleType.TCP), queueCloseHandle);
3233

@@ -56,6 +57,24 @@ public void Bind(ServerAddress address)
5657
_uv.tcp_bind(this, ref addr, 0);
5758
}
5859

60+
public void GetPeerName(out IPAddress address, out int port)
61+
{
62+
Libuv.sockaddr socketAddress;
63+
int namelen = Marshal.SizeOf<Libuv.sockaddr>();
64+
_uv.tcp_getpeername(this, out socketAddress, ref namelen);
65+
66+
GetIPAddressAndPort(socketAddress, out address, out port);
67+
}
68+
69+
public void GetSockName(out IPAddress address, out int port)
70+
{
71+
Libuv.sockaddr socketAddress;
72+
int namelen = Marshal.SizeOf<Libuv.sockaddr>();
73+
_uv.tcp_getsockname(this, out socketAddress, ref namelen);
74+
75+
GetIPAddressAndPort(socketAddress, out address, out port);
76+
}
77+
5978
public void Open(IntPtr hSocket)
6079
{
6180
_uv.tcp_open(this, hSocket);
@@ -89,5 +108,48 @@ public static IPEndPoint CreateIPEndpoint(ServerAddress address)
89108

90109
return new IPEndPoint(ip, address.Port);
91110
}
111+
112+
private static void GetIPAddressAndPort(Libuv.sockaddr sockaddr, out IPAddress address, out int port)
113+
{
114+
// The bytes are represented in network byte order.
115+
//
116+
// Example 1: [2001:4898:e0:391:b9ef:1124:9d3e:a354]:39179
117+
//
118+
// 0000 0000 0b99 0017 => The third and fourth bytes 990B is the actual port
119+
// 9103 e000 9848 0120 => IPv6 address is represented in the 128bit field1 and field2.
120+
// 54a3 3e9d 2411 efb9 Read these two 64-bit long from right to left byte by byte.
121+
// 0000 0000 0000 0000
122+
//
123+
// Example 2: 10.135.34.141:39178
124+
//
125+
// 0000 0000 0a99 0017 => The port representation are the same
126+
// 0000 0000 0000 0000
127+
// 8d22 870a ffff 0000 => IPv4 occupies the last 32 bit: 0A.87.22.8d is the actual address.
128+
// 0000 0000 0000 0000
129+
//
130+
// IPAddress understands both IPv4 and IPv6.
131+
132+
var bytes1 = BitConverter.GetBytes(sockaddr._field1);
133+
var bytes2 = BitConverter.GetBytes(sockaddr._field2);
134+
135+
var bytes = new byte[16];
136+
for (int i = 0; i < 8; ++i)
137+
{
138+
bytes[i] = bytes1[i];
139+
bytes[i + 8] = bytes2[i];
140+
}
141+
142+
address = new IPAddress(bytes);
143+
144+
// If the IPAddress is an IPv4 mapped to IPv6, returns the IPv4 representation instead.
145+
if (address.IsIPv4MappedToIPv6)
146+
{
147+
address = address.MapToIPv4();
148+
}
149+
150+
// Quick calculate the port by mask the field and locate the byte 3 and byte 4
151+
// and then shift them to correct place to form a int.
152+
port = ((int)(sockaddr._field0 & 0x00FF0000) >> 8) | (int)((sockaddr._field0 & 0xFF000000) >> 24);
153+
}
92154
}
93155
}

tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/FrameFeatureCollection.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public static string GeneratedFile()
6666
typeof(IHttpRequestFeature),
6767
typeof(IHttpResponseFeature),
6868
typeof(IHttpUpgradeFeature),
69+
typeof(IHttpConnectionFeature)
6970
};
7071

7172
return $@"

0 commit comments

Comments
 (0)