Skip to content

Commit c830a01

Browse files
committed
Fixes, enhancements
Adds the ability to exclude headers from being excluded. Normally forbidden headers are stripped, but now the user can control this. Adds the ability to configure the proxy server in regards to dropping external proxy packets (originating from another proxy on the local machine). Adds the ability to provide a custom message handler for all proxy server upstream connections. This enables you to define an upstream proxy, etc. Fixes an issue where the host header is not set correctly on modification. Purges singlestons from the source code. Handlers are now instance members of proxy server classes via hidden IStartup instances.
1 parent 4396029 commit c830a01

13 files changed

+268
-107
lines changed

CitadelCore/CitadelCore.csproj

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,17 @@
1212
<PackageIconUrl />
1313
<RepositoryUrl>https://github.com/TechnikEmpire/CitadelCore</RepositoryUrl>
1414
<PackageTags>proxy filtering content-filtering transparent-proxy</PackageTags>
15-
<PackageReleaseNotes>Adds a replay API for external filtering.
16-
Inherits websocket implementation fixes by upgrading buggy third-party package.
17-
Adds stream "Closed" event for inspection streams.
18-
Fixes an issue where the InspectionStream object fed duplicate data back to the library user.</PackageReleaseNotes>
15+
<PackageReleaseNotes>Adds the ability to exclude headers from being excluded. Normally forbidden headers are stripped, but now the user can control this.
16+
Adds the ability to configure the proxy server in regards to dropping external proxy packets (originating from another proxy on the local machine).
17+
Adds the ability to provide a custom message handler for all proxy server upstream connections. This enables you to define an upstream proxy, etc.
18+
Fixes an issue where the host header is not set correctly on modification.
19+
Purges singlestons from the source code. Handlers are now instance members of proxy server classes via hidden IStartup instances.</PackageReleaseNotes>
1920
<Title>CitadelCore</Title>
2021
<Summary>Transparent filtering HTTP/S and Websocket/WebsocketSecure proxy.</Summary>
2122
<Description>Transparent filtering HTTP/S and Websocket/WebsocketSecure proxy.</Description>
22-
<Version>3.1.2</Version>
23-
<AssemblyVersion>3.1.2.0</AssemblyVersion>
24-
<FileVersion>3.1.2.0</FileVersion>
23+
<Version>3.4.1</Version>
24+
<AssemblyVersion>3.4.1.0</AssemblyVersion>
25+
<FileVersion>3.4.1.0</FileVersion>
2526
</PropertyGroup>
2627

2728
<ItemGroup Label="dotnet pack instructions">

CitadelCore/Diversion/IDiverter.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ bool IsRunning
2222
get;
2323
}
2424

25+
/// <summary>
26+
/// Gets or sets whether or not external proxies should be dropped during the packet
27+
/// diversion process.
28+
/// </summary>
29+
bool DropExternalProxies
30+
{
31+
get;
32+
set;
33+
}
34+
2535
/// <summary>
2636
/// Gets or sets the callback that the Diverter is to use when checking to see if an
2737
/// application behind a packet flow should have it's internet content pushed through the

CitadelCore/Extensions/HttpRequestExtensions.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using CitadelCore.Util;
1212
using Microsoft.AspNetCore.Http;
1313
using System;
14+
using System.Collections.Generic;
1415
using System.Collections.Specialized;
1516
using System.IO;
1617
using System.Threading;
@@ -33,17 +34,20 @@ internal static class HttpRequestExtensions
3334
/// <param name="headers">
3435
/// The headers.
3536
/// </param>
37+
/// <param name="exemptedHeaders">
38+
/// List of headers that are exempt from being removed if they are "forbidden" headers.
39+
/// </param>
3640
/// <returns>
3741
/// A collection of all headers that failed to be added.
3842
/// </returns>
39-
public static NameValueCollection PopulateHeaders(this HttpRequest message, NameValueCollection headers)
43+
public static NameValueCollection PopulateHeaders(this HttpRequest message, NameValueCollection headers, HashSet<string> exemptedHeaders)
4044
{
4145
// This will hold whatever headers we cannot successfully add here.
4246
var clonedCollection = new NameValueCollection(headers);
4347

4448
foreach (string key in headers)
45-
{
46-
if (ForbiddenHttpHeaders.IsForbidden(key))
49+
{
50+
if (!exemptedHeaders.Contains(key) && ForbiddenHttpHeaders.IsForbidden(key))
4751
{
4852
continue;
4953
}
@@ -81,7 +85,7 @@ public static async Task<bool> ApplyMessageInfo(this HttpRequest message, HttpMe
8185
{
8286
if (messageInfo.MessageType == MessageType.Request)
8387
{
84-
var failedHeaders = message.PopulateHeaders(messageInfo.Headers);
88+
var failedHeaders = message.PopulateHeaders(messageInfo.Headers, messageInfo.ExemptedHeaders);
8589

8690
#if VERBOSE_WARNINGS
8791
foreach (string key in failedHeaders)

CitadelCore/Extensions/HttpRequestMessageExtensions.cs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using CitadelCore.Net.Http;
1111
using CitadelCore.Util;
1212
using System;
13+
using System.Collections.Generic;
1314
using System.Collections.Specialized;
1415
using System.Net.Http;
1516
using System.Threading;
@@ -28,37 +29,43 @@ internal static class HttpRequestMessageExtensions
2829
/// <param name="headers">
2930
/// The headers.
3031
/// </param>
32+
/// <param name="exemptedHeaders">
33+
/// List of headers that are exempt from being removed if they are "forbidden" headers.
34+
/// </param>
3135
/// <returns>
3236
/// A collection of all headers that failed to be added.
3337
/// </returns>
34-
public static NameValueCollection PopulateHeaders(this HttpRequestMessage message, NameValueCollection headers)
38+
public static NameValueCollection PopulateHeaders(this HttpRequestMessage message, NameValueCollection headers, HashSet<string> exemptedHeaders)
3539
{
3640
// This will hold whatever headers we cannot successfully add here.
3741
var clonedCollection = new NameValueCollection(headers);
3842

3943
foreach (string key in headers)
4044
{
41-
if (ForbiddenHttpHeaders.IsForbidden(key))
45+
if (!exemptedHeaders.Contains(key) && ForbiddenHttpHeaders.IsForbidden(key))
4246
{
4347
continue;
4448
}
45-
49+
4650
if (message.Headers.TryAddWithoutValidation(key, headers.GetValues(key)))
47-
{
51+
{
4852
clonedCollection.Remove(key);
4953
}
5054
else
5155
{
5256
if (message.Content != null)
5357
{
5458
if (message.Content.Headers.TryAddWithoutValidation(key, headers.GetValues(key)))
55-
{
59+
{
5660
clonedCollection.Remove(key);
5761
}
5862
}
5963
}
6064
}
6165

66+
// Apparently, host won't set unless we use the typed accessor!
67+
message.Headers.Host = headers["Host"] ?? message.Headers.Host;
68+
6269
return clonedCollection;
6370
}
6471

@@ -84,7 +91,7 @@ public static bool ApplyMessageInfo(this HttpRequestMessage message, HttpMessage
8491
{
8592
if (messageInfo.MessageType == MessageType.Request)
8693
{
87-
var failedHeaders = message.PopulateHeaders(messageInfo.Headers);
94+
var failedHeaders = message.PopulateHeaders(messageInfo.Headers, messageInfo.ExemptedHeaders);
8895

8996
message.Method = messageInfo.Method;
9097
message.RequestUri = messageInfo.Url;
@@ -93,7 +100,7 @@ public static bool ApplyMessageInfo(this HttpRequestMessage message, HttpMessage
93100
{
94101
message.Content = new ByteArrayContent(messageInfo.Body.ToArray());
95102

96-
failedHeaders = message.PopulateHeaders(messageInfo.Headers);
103+
failedHeaders = message.PopulateHeaders(messageInfo.Headers, messageInfo.ExemptedHeaders);
97104

98105
#if VERBOSE_WARNINGS
99106
foreach (string key in failedHeaders)

CitadelCore/Extensions/HttpResponseExtensions.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using CitadelCore.Util;
1212
using Microsoft.AspNetCore.Http;
1313
using System;
14+
using System.Collections.Generic;
1415
using System.Collections.Specialized;
1516
using System.IO;
1617
using System.Threading;
@@ -33,17 +34,20 @@ internal static class HttpResponseExtensions
3334
/// <param name="headers">
3435
/// The headers.
3536
/// </param>
37+
/// <param name="exemptedHeaders">
38+
/// List of headers that are exempt from being removed if they are "forbidden" headers.
39+
/// </param>
3640
/// <returns>
3741
/// A collection of all headers that failed to be added.
3842
/// </returns>
39-
public static NameValueCollection PopulateHeaders(this HttpResponse message, NameValueCollection headers)
43+
public static NameValueCollection PopulateHeaders(this HttpResponse message, NameValueCollection headers, HashSet<string> exemptedHeaders)
4044
{
4145
// This will hold whatever headers we cannot successfully add here.
4246
var clonedCollection = new NameValueCollection(headers);
4347

4448
foreach (string key in headers)
4549
{
46-
if (ForbiddenHttpHeaders.IsForbidden(key))
50+
if (!exemptedHeaders.Contains(key) && ForbiddenHttpHeaders.IsForbidden(key))
4751
{
4852
continue;
4953
}
@@ -81,7 +85,7 @@ public static async Task<bool> ApplyMessageInfo(this HttpResponse message, HttpM
8185
{
8286
if (messageInfo.MessageType == MessageType.Response)
8387
{
84-
var failedHeaders = message.PopulateHeaders(messageInfo.Headers);
88+
var failedHeaders = message.PopulateHeaders(messageInfo.Headers, messageInfo.ExemptedHeaders);
8589
message.StatusCode = (int)messageInfo.StatusCode;
8690

8791
#if VERBOSE_WARNINGS

CitadelCore/Extensions/HttpResponseMessageExtensions.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using CitadelCore.Net.Http;
1111
using CitadelCore.Util;
1212
using System;
13+
using System.Collections.Generic;
1314
using System.Collections.Specialized;
1415
using System.Net.Http;
1516
using System.Threading;
@@ -28,10 +29,13 @@ internal static class HttpResponseMessageExtensions
2829
/// <param name="headers">
2930
/// The headers.
3031
/// </param>
32+
/// <param name="exemptedHeaders">
33+
/// List of headers that are exempt from being removed if they are "forbidden" headers.
34+
/// </param>
3135
/// <returns>
3236
/// A collection of all headers that failed to be added.
3337
/// </returns>
34-
public static NameValueCollection PopulateHeaders(this HttpResponseMessage message, NameValueCollection headers)
38+
public static NameValueCollection PopulateHeaders(this HttpResponseMessage message, NameValueCollection headers, HashSet<string> exemptedHeaders)
3539
{
3640
// This will hold whatever headers we cannot successfully add here.
3741
var clonedCollection = new NameValueCollection(headers);
@@ -84,15 +88,15 @@ public static bool ApplyMessageInfo(this HttpResponseMessage message, HttpMessag
8488
{
8589
if (messageInfo.MessageType == MessageType.Request)
8690
{
87-
var failedHeaders = message.PopulateHeaders(messageInfo.Headers);
91+
var failedHeaders = message.PopulateHeaders(messageInfo.Headers, messageInfo.ExemptedHeaders);
8892

8993
message.StatusCode = messageInfo.StatusCode;
9094

9195
if (messageInfo.BodyIsUserCreated && messageInfo.Body.Length > 0)
9296
{
9397
message.Content = new ByteArrayContent(messageInfo.Body.ToArray());
9498

95-
failedHeaders = message.PopulateHeaders(messageInfo.Headers);
99+
failedHeaders = message.PopulateHeaders(messageInfo.Headers, messageInfo.ExemptedHeaders);
96100

97101
#if VERBOSE_WARNINGS
98102
foreach (string key in failedHeaders)

CitadelCore/IO/ResponseReplay.cs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
*/
77

88
using CitadelCore.Net.Handlers;
9-
using CitadelCore.Net.Handlers.Replay;
109
using CitadelCore.Net.Http;
1110
using System;
1211
using System.Collections.Concurrent;
1312
using System.Collections.Generic;
1413
using System.Linq;
14+
using System.Net;
1515
using System.Threading;
1616

1717
namespace CitadelCore.IO
@@ -149,13 +149,21 @@ internal string ReplayUrl
149149
{
150150
get
151151
{
152-
return $"http://localhost:{ReplayResponseHandlerFactory.Default.V4HttpEndpoint.Port}/replay/{MessageInfo.MessageId.ToString()}";
152+
return $"http://localhost:{_serverHttpEndpoint.Port}/replay/{MessageInfo.MessageId.ToString()}";
153153
}
154154
}
155155

156+
/// <summary>
157+
/// The HTTP endpoint that the replay server can be reached at.
158+
/// </summary>
159+
private readonly IPEndPoint _serverHttpEndpoint;
160+
156161
/// <summary>
157162
/// Constructs a new ResponseReplay instance with the given parameters.
158163
/// </summary>
164+
/// <param name="serverHttpEndpoint">
165+
/// The HTTP endpoint that the replay server is bound to.
166+
/// </param>
159167
/// <param name="messageInfo">
160168
/// The message info.
161169
/// </param>
@@ -168,7 +176,7 @@ internal string ReplayUrl
168176
/// <exception cref="ArgumentException">
169177
/// If the cancellation token is null, this constructor will throw.
170178
/// </exception>
171-
public ResponseReplay(HttpMessageInfo messageInfo, CancellationToken cancellationToken)
179+
public ResponseReplay(IPEndPoint serverHttpEndpoint, HttpMessageInfo messageInfo, CancellationToken cancellationToken)
172180
{
173181
if (messageInfo == null || messageInfo.MessageType != MessageType.Response)
174182
{
@@ -180,6 +188,7 @@ public ResponseReplay(HttpMessageInfo messageInfo, CancellationToken cancellatio
180188
throw new ArgumentException("The cancellation token object must not be null.", nameof(cancellationToken));
181189
}
182190

191+
_serverHttpEndpoint = serverHttpEndpoint ?? throw new ArgumentException("The server endpoint cannot be null.", nameof(cancellationToken));
183192
MessageInfo = messageInfo;
184193
_cancellationToken = cancellationToken;
185194
}
@@ -232,8 +241,7 @@ internal bool TryReadBody(out List<byte[]> bytes)
232241

233242
do
234243
{
235-
byte[] buffer = null;
236-
success = _pendingBody.TryDequeue(out buffer);
244+
success = _pendingBody.TryDequeue(out byte[] buffer);
237245
if (success)
238246
{
239247
retVal.Add(buffer);

0 commit comments

Comments
 (0)