Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit9e8ebbf

Browse files
authored
backport(net8.0): http.sys on-demand TLS client hello retrieval (#62290)
* feat(HTTP.SYS): on-demand TLS client hello retrieval (#62209)* fix cherry-pick* setup sample* provide example* fix build error
1 parent7dd498b commit9e8ebbf

File tree

8 files changed

+175
-7
lines changed

8 files changed

+175
-7
lines changed

‎src/Servers/HttpSys/samples/TlsFeaturesObserve/Program.cs‎

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,17 @@ static IHostBuilder CreateHostBuilder(string[] args) =>
2626
options.Authentication.Schemes=AuthenticationSchemes.None;
2727
options.Authentication.AllowAnonymous=true;
2828

29-
varproperty=typeof(HttpSysOptions).GetProperty("TlsClientHelloBytesCallback",BindingFlags.NonPublic|BindingFlags.Instance);
30-
vardelegateType=property.PropertyType;// Get the exact delegate type
29+
// If you want to resolve a callback API, uncomment.
30+
// Recommended approach is to use the on-demand API to fetch TLS client hello bytes,
31+
// look into Startup.cs for details.
3132

32-
// Create a delegate of the correct type
33-
varcallbackDelegate=Delegate.CreateDelegate(delegateType,typeof(Holder).GetMethod(nameof(Holder.ProcessTlsClientHello),BindingFlags.Static|BindingFlags.Public));
33+
//var property = typeof(HttpSysOptions).GetProperty("TlsClientHelloBytesCallback", BindingFlags.NonPublic | BindingFlags.Instance);
34+
//vardelegateType =property.PropertyType; // Get the exact delegate type
3435

35-
property?.SetValue(options,callbackDelegate);
36+
//// Create a delegate of the correct type
37+
//var callbackDelegate = Delegate.CreateDelegate(delegateType, typeof(Holder).GetMethod(nameof(Holder.ProcessTlsClientHello), BindingFlags.Static | BindingFlags.Public));
38+
39+
//property?.SetValue(options, callbackDelegate);
3640
});
3741
});
3842

‎src/Servers/HttpSys/samples/TlsFeaturesObserve/Startup.cs‎

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
usingSystem;
5+
usingSystem.Buffers;
6+
usingSystem.Reflection;
57
usingMicrosoft.AspNetCore.Builder;
68
usingMicrosoft.AspNetCore.Connections.Features;
79
usingMicrosoft.AspNetCore.Hosting;
@@ -17,12 +19,52 @@ public class Startup
1719
{
1820
publicvoidConfigure(IApplicationBuilderapp)
1921
{
22+
// recommended approach to fetch TLS client hello bytes
23+
// is via on-demand API per request or by building own connection-lifecycle manager
2024
app.Run(async(HttpContextcontext)=>
2125
{
2226
context.Response.ContentType="text/plain";
2327

24-
vartlsFeature=context.Features.Get<IMyTlsFeature>();
25-
awaitcontext.Response.WriteAsync("TlsClientHello data: "+$"connectionId={tlsFeature?.ConnectionId}; length={tlsFeature?.TlsClientHelloLength}");
28+
varhttpSysAssembly=typeof(Microsoft.AspNetCore.Server.HttpSys.HttpSysOptions).Assembly;
29+
varhttpSysPropertyFeatureType=httpSysAssembly.GetType("Microsoft.AspNetCore.Server.HttpSys.IHttpSysRequestPropertyFeature");
30+
varhttpSysPropertyFeature=context.Features[httpSysPropertyFeatureType]!;
31+
32+
varmethod=httpSysPropertyFeature.GetType().GetMethod(
33+
"TryGetTlsClientHello",
34+
BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic
35+
);
36+
37+
// invoke first time to get required size
38+
byte[]bytes=Array.Empty<byte>();
39+
varparameters=newobject[]{bytes,0};
40+
varres=(bool)method.Invoke(httpSysPropertyFeature,parameters);
41+
42+
// fetching out parameter only works by looking into parameters array of objects
43+
varbytesReturned=(int)parameters[1];
44+
bytes=ArrayPool<byte>.Shared.Rent(bytesReturned);
45+
parameters=[bytes,0];// correct input now
46+
res=(bool)method.Invoke(httpSysPropertyFeature,parameters);
47+
48+
// to avoid CS4012 use a method which accepts a byte[] and length, where you can do Span<byte> slicing
49+
// error CS4012: Parameters or locals of type 'Span<byte>' cannot be declared in async methods or async lambda expressions.
50+
varmessage=ReadTlsClientHello(bytes,bytesReturned);
51+
awaitcontext.Response.WriteAsync(message);
52+
ArrayPool<byte>.Shared.Return(bytes);
2653
});
54+
55+
staticstringReadTlsClientHello(byte[]bytes,intbytesReturned)
56+
{
57+
vartlsClientHelloBytes=bytes.AsSpan(0,bytesReturned);
58+
return$"TlsClientHello bytes:{string.Join(" ",tlsClientHelloBytes.ToArray())}, length={bytesReturned}";
59+
}
60+
61+
// middleware compatible with callback API
62+
//app.Run(async (HttpContext context) =>
63+
//{
64+
// context.Response.ContentType = "text/plain";
65+
66+
// var tlsFeature = context.Features.Get<IMyTlsFeature>();
67+
// await context.Response.WriteAsync("TlsClientHello` data: " + $"connectionId={tlsFeature?.ConnectionId}; length={tlsFeature?.TlsClientHelloLength}");
68+
//});
2769
}
2870
}

‎src/Servers/HttpSys/samples/TlsFeaturesObserve/TlsFeaturesObserve.csproj‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
55
<OutputType>Exe</OutputType>
66
<ServerGarbageCollection>true</ServerGarbageCollection>
7+
<LangVersion>latest</LangVersion>
78
</PropertyGroup>
89

910
<ItemGroup>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespaceMicrosoft.AspNetCore.Server.HttpSys;
5+
6+
/// <summary>
7+
/// Provides API to read HTTP_REQUEST_PROPERTY value from the HTTP.SYS request.
8+
/// <see href="https://learn.microsoft.com/windows/win32/api/http/ne-http-http_request_property"/>
9+
/// </summary>
10+
// internal for backport
11+
internalinterfaceIHttpSysRequestPropertyFeature
12+
{
13+
/// <summary>
14+
/// Reads the TLS client hello from HTTP.SYS
15+
/// </summary>
16+
/// <param name="tlsClientHelloBytesDestination">Where the raw bytes of the TLS Client Hello message are written.</param>
17+
/// <param name="bytesReturned">
18+
/// Returns the number of bytes written to <paramref name="tlsClientHelloBytesDestination"/>.
19+
/// Or can return the size of the buffer needed if <paramref name="tlsClientHelloBytesDestination"/> wasn't large enough.
20+
/// </param>
21+
/// <remarks>
22+
/// Works only if <c>HTTP_SERVICE_CONFIG_SSL_FLAG_ENABLE_CACHE_CLIENT_HELLO</c> flag is set on http.sys service configuration.
23+
/// See <see href="https://learn.microsoft.com/windows/win32/api/http/nf-http-httpsetserviceconfiguration"/>
24+
/// and <see href="https://learn.microsoft.com/windows/win32/api/http/ne-http-http_service_config_id"/>
25+
/// <br/><br/>
26+
/// If you don't want to guess the required <paramref name="tlsClientHelloBytesDestination"/> size before first invocation,
27+
/// you should first call with <paramref name="tlsClientHelloBytesDestination"/> set to empty size, so that you can retrieve the required buffer size from <paramref name="bytesReturned"/>,
28+
/// then allocate that amount of memory and retry the query.
29+
/// </remarks>
30+
/// <returns>
31+
/// True, if fetching TLS client hello was successful, false if <paramref name="tlsClientHelloBytesDestination"/> size is not large enough.
32+
/// If unsuccessful for other reason throws an exception.
33+
/// </returns>
34+
/// <exception cref="HttpSysException">Any HttpSys error except for ERROR_INSUFFICIENT_BUFFER or ERROR_MORE_DATA.</exception>
35+
/// <exception cref="InvalidOperationException">If HttpSys does not support querying the TLS Client Hello.</exception>
36+
// has byte[] (not Span<byte>) for reflection-based invocation
37+
boolTryGetTlsClientHello(byte[]tlsClientHelloBytesDestination,outintbytesReturned);
38+
}

‎src/Servers/HttpSys/src/RequestProcessing/RequestContext.FeatureCollection.cs‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ internal partial class RequestContext :
3636
IHttpResponseTrailersFeature,
3737
IHttpResetFeature,
3838
IHttpSysRequestDelegationFeature,
39+
IHttpSysRequestPropertyFeature,
3940
IConnectionLifetimeNotificationFeature
4041
{
4142
privateIFeatureCollection?_features;
@@ -751,4 +752,9 @@ void IConnectionLifetimeNotificationFeature.RequestClose()
751752
Response.Headers[HeaderNames.Connection]="close";
752753
}
753754
}
755+
756+
publicboolTryGetTlsClientHello(byte[]tlsClientHelloBytesDestination,outintbytesReturned)
757+
{
758+
returnTryGetTlsClientHelloMessageBytes(tlsClientHelloBytesDestination.AsSpan(),outbytesReturned);
759+
}
754760
}

‎src/Servers/HttpSys/src/RequestProcessing/RequestContext.cs‎

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,60 @@ internal void ForceCancelRequest()
239239
}
240240
}
241241

242+
/// <summary>
243+
/// Attempts to get the client hello message bytes from the http.sys.
244+
/// If successful writes the bytes into <paramref name="destination"/>, and shows how many bytes were written in <paramref name="bytesReturned"/>.
245+
/// If not successful because <paramref name="destination"/> is not large enough, returns false and shows a size of <paramref name="destination"/> required in <paramref name="bytesReturned"/>.
246+
/// If not successful for other reason - throws exception with message/errorCode.
247+
/// </summary>
248+
internalunsafeboolTryGetTlsClientHelloMessageBytes(
249+
Span<byte>destination,
250+
outintbytesReturned)
251+
{
252+
bytesReturned=default;
253+
if(!HttpApi.SupportsClientHello)
254+
{
255+
// not supported, so we just return and don't invoke the callback
256+
thrownewInvalidOperationException("Windows HTTP Server API does not support HTTP_FEATURE_ID.HttpFeatureCacheTlsClientHello or HttpQueryRequestProperty. See HTTP_FEATURE_ID for details.");
257+
}
258+
259+
uintstatusCode;
260+
varrequestId=PinsReleased?Request.RequestId:RequestId;
261+
262+
uintbytesReturnedValue=0;
263+
uint*bytesReturnedPointer=&bytesReturnedValue;
264+
265+
fixed(byte*pBuffer=destination)
266+
{
267+
statusCode=HttpApi.HttpGetRequestProperty(
268+
requestQueueHandle:Server.RequestQueue.Handle,
269+
requestId,
270+
propertyId:(HTTP_REQUEST_PROPERTY)11/* HTTP_REQUEST_PROPERTY.HttpRequestPropertyTlsClientHello */,
271+
qualifier:null,
272+
qualifierSize:0,
273+
output:pBuffer,
274+
outputSize:(uint)destination.Length,
275+
bytesReturned:bytesReturnedPointer,
276+
overlapped:IntPtr.Zero);
277+
278+
bytesReturned=checked((int)bytesReturnedValue);
279+
280+
if(statusCodeisErrorCodes.ERROR_SUCCESS)
281+
{
282+
returntrue;
283+
}
284+
285+
// if buffer supplied is too small, `bytesReturned` has proper size
286+
if(statusCodeisErrorCodes.ERROR_MORE_DATA orErrorCodes.ERROR_INSUFFICIENT_BUFFER)
287+
{
288+
returnfalse;
289+
}
290+
}
291+
292+
Log.TlsClientHelloRetrieveError(Logger,requestId,statusCode);
293+
thrownewHttpSysException((int)statusCode);
294+
}
295+
242296
/// <summary>
243297
/// Attempts to get the client hello message bytes from HTTP.sys and calls the user provided callback.
244298
/// If not successful, will return false.

‎src/Servers/HttpSys/src/StandardFeatureCollection.cs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ internal sealed class StandardFeatureCollection : IFeatureCollection
2727
{typeof(IHttpBodyControlFeature),_identityFunc},
2828
{typeof(IHttpSysRequestInfoFeature),_identityFunc},
2929
{typeof(IHttpSysRequestTimingFeature),_identityFunc},
30+
{typeof(IHttpSysRequestPropertyFeature),_identityFunc},
3031
{typeof(IHttpResponseTrailersFeature), ctx=>ctx.GetResponseTrailersFeature()},
3132
{typeof(IHttpResetFeature), ctx=>ctx.GetResetFeature()},
3233
{typeof(IConnectionLifetimeNotificationFeature), ctx=>ctx.GetConnectionLifetimeNotificationFeature()},

‎src/Servers/HttpSys/test/FunctionalTests/HttpsTests.cs‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,28 @@ public async Task Https_ITlsHandshakeFeature_MatchesIHttpSysExtensionInfoFeature
234234
}
235235
}
236236

237+
[ConditionalFact]
238+
[MinimumOSVersion(OperatingSystems.Windows,WindowsVersions.Win10_20H2)]
239+
publicasyncTaskHttps_SetsIHttpSysRequestPropertyFeature()
240+
{
241+
using(Utilities.CreateDynamicHttpsServer(outvaraddress,async httpContext=>
242+
{
243+
try
244+
{
245+
varrequestPropertyFeature=httpContext.Features.Get<IHttpSysRequestPropertyFeature>();
246+
Assert.NotNull(requestPropertyFeature);
247+
}
248+
catch(Exceptionex)
249+
{
250+
awaithttpContext.Response.WriteAsync(ex.ToString());
251+
}
252+
},LoggerFactory))
253+
{
254+
stringresponse=awaitSendRequestAsync(address);
255+
Assert.Equal(string.Empty,response);
256+
}
257+
}
258+
237259
[ConditionalFact]
238260
[MinimumOSVersion(OperatingSystems.Windows,WindowsVersions.Win10_20H2)]
239261
publicasyncTaskHttps_SetsIHttpSysRequestTimingFeature()

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp