diff --git a/.github/workflows/nuget.yml b/.github/workflows/nuget.yml
index 011138e..8172fc8 100644
--- a/.github/workflows/nuget.yml
+++ b/.github/workflows/nuget.yml
@@ -57,6 +57,14 @@ jobs:
run: dotnet nuget push Bunkum.Protocols.Http/bin/Release/Bunkum.Protocols.Http.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_TOKEN}
env:
NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }}
+ - name: Upload Bunkum.Protocols.Https
+ run: dotnet nuget push Bunkum.Protocols.Https/bin/Release/Bunkum.Protocols.Https.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_TOKEN}
+ env:
+ NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }}
+ - name: Upload Bunkum.Protocols.TlsSupport
+ run: dotnet nuget push Bunkum.Protocols.TlsSupport/bin/Release/Bunkum.Protocols.TlsSupport.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_TOKEN}
+ env:
+ NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }}
- name: Upload Bunkum.Protocols.Gopher
run: dotnet nuget push Bunkum.Protocols.Gopher/bin/Release/Bunkum.Protocols.Gopher.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_TOKEN}
env:
diff --git a/Bunkum.Protocols.Gemini/Bunkum.Protocols.Gemini.csproj b/Bunkum.Protocols.Gemini/Bunkum.Protocols.Gemini.csproj
index 01b8349..8820305 100644
--- a/Bunkum.Protocols.Gemini/Bunkum.Protocols.Gemini.csproj
+++ b/Bunkum.Protocols.Gemini/Bunkum.Protocols.Gemini.csproj
@@ -36,6 +36,7 @@
+
diff --git a/Bunkum.Protocols.Gemini/BunkumGeminiServer.cs b/Bunkum.Protocols.Gemini/BunkumGeminiServer.cs
index 5bcc1be..1d35c75 100644
--- a/Bunkum.Protocols.Gemini/BunkumGeminiServer.cs
+++ b/Bunkum.Protocols.Gemini/BunkumGeminiServer.cs
@@ -3,6 +3,7 @@
using Bunkum.Core.Configuration;
using Bunkum.Listener;
using Bunkum.Protocols.Gemini.Socket;
+using Bunkum.Protocols.TlsSupport;
using NotEnoughLogs;
using NotEnoughLogs.Sinks;
@@ -11,6 +12,7 @@ namespace Bunkum.Protocols.Gemini;
public class BunkumGeminiServer : BunkumServer
{
private readonly X509Certificate2 _cert;
+ private readonly SslConfiguration _sslConfiguration;
///
/// Create a new BunkumGeminiServer
@@ -21,14 +23,14 @@ public class BunkumGeminiServer : BunkumServer
public BunkumGeminiServer(SslConfiguration? sslConfiguration = null, LoggerConfiguration? configuration = null, List? sinks = null) : base(configuration, sinks)
{
//If the SSL configuration is not specified, load the config from JSON
- sslConfiguration ??= Config.LoadFromJsonFile("geminissl.json", this.Logger);
+ this._sslConfiguration = sslConfiguration ?? Config.LoadFromJsonFile("geminissl.json", this.Logger);
- this._cert = new X509Certificate2(File.ReadAllBytes(sslConfiguration.SslCertificate), sslConfiguration.CertificatePassword);
+ this._cert = new X509Certificate2(File.ReadAllBytes(sslConfiguration.SslCertificate), this._sslConfiguration.CertificatePassword);
}
protected override BunkumListener CreateDefaultListener(Uri listenEndpoint, bool useForwardedIp, Logger logger)
{
- return new SocketGeminiListener(this._cert, listenEndpoint, logger);
+ return new SocketGeminiListener(this._cert, this._sslConfiguration, listenEndpoint, logger);
}
protected override string ProtocolUriName => "gemini";
}
\ No newline at end of file
diff --git a/Bunkum.Protocols.Gemini/Socket/SocketGeminiListener.cs b/Bunkum.Protocols.Gemini/Socket/SocketGeminiListener.cs
index cd87786..bbc6809 100644
--- a/Bunkum.Protocols.Gemini/Socket/SocketGeminiListener.cs
+++ b/Bunkum.Protocols.Gemini/Socket/SocketGeminiListener.cs
@@ -2,7 +2,7 @@
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
-using System.Security.Authentication;
+using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
@@ -11,6 +11,7 @@
using Bunkum.Listener.Extensions;
using Bunkum.Listener.Protocol;
using Bunkum.Listener.Request;
+using Bunkum.Protocols.TlsSupport;
using NotEnoughLogs;
namespace Bunkum.Protocols.Gemini.Socket;
@@ -20,14 +21,16 @@ public partial class SocketGeminiListener : BunkumGeminiListener
private System.Net.Sockets.Socket? _socket;
private readonly Uri _listenEndpoint;
private readonly X509Certificate2 _cert;
+ private readonly SslConfiguration _sslConfiguration;
[GeneratedRegex("^[a-zA-Z]+$")]
private static partial Regex LettersRegex();
- public SocketGeminiListener(X509Certificate2 cert, Uri listenEndpoint, Logger logger) : base(logger)
+ public SocketGeminiListener(X509Certificate2 cert, SslConfiguration sslConfiguration, Uri listenEndpoint, Logger logger) : base(logger)
{
this._listenEndpoint = listenEndpoint;
this._cert = cert;
+ this._sslConfiguration = sslConfiguration;
this.Logger.LogInfo(ListenerCategory.Startup, "Internal Gemini server is listening at URL {0}", listenEndpoint);
}
@@ -88,13 +91,19 @@ private async Task ReadRequestIntoContext(System.Net.Sockets.So
{
SslStream stream = new(rawStream);
- await stream.AuthenticateAsServerAsync(new SslServerAuthenticationOptions
+ SslServerAuthenticationOptions authOptions = new()
{
- EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13,
+ EnabledSslProtocols = this._sslConfiguration.EnabledSslProtocols,
ServerCertificate = this._cert,
ClientCertificateRequired = true,
- RemoteCertificateValidationCallback = (sender, certificate, chain, errors) => true,
- });
+ RemoteCertificateValidationCallback = (_, _, _, _) => true,
+ };
+
+ // If the cipher suite set is enabled, and we are not on windows, enable the selected cipher suites
+ if (this._sslConfiguration.EnabledCipherSuites != null && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ authOptions.CipherSuitesPolicy = new CipherSuitesPolicy(this._sslConfiguration.EnabledCipherSuites);
+
+ await stream.AuthenticateAsServerAsync(authOptions);
Uri uri = new(GetPath(stream));
diff --git a/Bunkum.Protocols.Gemini/SslConfiguration.cs b/Bunkum.Protocols.Gemini/SslConfiguration.cs
deleted file mode 100644
index 512e933..0000000
--- a/Bunkum.Protocols.Gemini/SslConfiguration.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using Bunkum.Core.Configuration;
-
-namespace Bunkum.Protocols.Gemini;
-
-public class SslConfiguration : Config
-{
- public override int CurrentConfigVersion => 1;
- public override int Version { get; set; }
-
- protected override void Migrate(int oldVer, dynamic oldConfig)
- {}
-
- public string SslCertificate { get; set; } = "cert.pfx";
- public string? CertificatePassword { get; set; } = "password here or null";
-}
\ No newline at end of file
diff --git a/Bunkum.Protocols.Http/BunkumHttpServer.cs b/Bunkum.Protocols.Http/BunkumHttpServer.cs
index 83cf6a9..de394b2 100644
--- a/Bunkum.Protocols.Http/BunkumHttpServer.cs
+++ b/Bunkum.Protocols.Http/BunkumHttpServer.cs
@@ -1,4 +1,6 @@
+using System.Security.Cryptography.X509Certificates;
using Bunkum.Core;
+using Bunkum.Core.Configuration;
using Bunkum.Listener;
using NotEnoughLogs;
using NotEnoughLogs.Sinks;
diff --git a/Bunkum.Protocols.Http/Socket/SocketHttpListener.cs b/Bunkum.Protocols.Http/Socket/SocketHttpListener.cs
index 8220791..e41ea5f 100644
--- a/Bunkum.Protocols.Http/Socket/SocketHttpListener.cs
+++ b/Bunkum.Protocols.Http/Socket/SocketHttpListener.cs
@@ -1,6 +1,10 @@
using System.Diagnostics;
using System.Net;
+using System.Net.Security;
using System.Net.Sockets;
+using System.Runtime.InteropServices;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
using System.Text.RegularExpressions;
using System.Web;
using Bunkum.Listener;
@@ -16,14 +20,27 @@ public partial class SocketHttpListener : BunkumHttpListener
private System.Net.Sockets.Socket? _socket;
private readonly Uri _listenEndpoint;
private readonly bool _useForwardedIp;
-
+ private readonly X509Certificate2? _cert;
+ private readonly SslProtocols _enabledSslProtocols;
+ private readonly TlsCipherSuite[]? _enabledCipherSuites;
+
[GeneratedRegex("^[a-zA-Z]+$")]
private static partial Regex LettersRegex();
- public SocketHttpListener(Uri listenEndpoint, bool useForwardedIp, Logger logger) : base(logger)
+ public SocketHttpListener(
+ Uri listenEndpoint,
+ bool useForwardedIp,
+ Logger logger,
+ X509Certificate2? certificate = null,
+ SslProtocols enabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13,
+ TlsCipherSuite[]? enabledCipherSuites = null)
+ : base(logger)
{
this._listenEndpoint = listenEndpoint;
this._useForwardedIp = useForwardedIp;
+ this._cert = certificate;
+ this._enabledCipherSuites = enabledCipherSuites;
+ this._enabledSslProtocols = enabledSslProtocols;
this.Logger.LogInfo(ListenerCategory.Startup, "Internal HTTP server is listening at URL " + listenEndpoint);
}
@@ -75,8 +92,31 @@ public override void StartListening()
}
}
- private ListenerContext ReadRequestIntoContext(System.Net.Sockets.Socket client, Stream stream)
+ private ListenerContext ReadRequestIntoContext(System.Net.Sockets.Socket client, Stream rawStream)
{
+ Stream stream = rawStream;
+ SslStream? sslStream = null;
+ if (this._cert != null)
+ {
+ sslStream = new SslStream(rawStream);
+
+ SslServerAuthenticationOptions authOptions = new()
+ {
+ EnabledSslProtocols = this._enabledSslProtocols,
+ ServerCertificate = this._cert,
+ ClientCertificateRequired = false,
+ RemoteCertificateValidationCallback = (_, _, _, _) => true,
+ };
+
+ // If the cipher suite set is enabled, and we are not on windows, enable the selected cipher suites
+ if (this._enabledCipherSuites != null && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ authOptions.CipherSuitesPolicy = new CipherSuitesPolicy(this._enabledCipherSuites);
+
+ sslStream.AuthenticateAsServer(authOptions);
+
+ stream = sslStream;
+ }
+
Span method = stackalloc char[RequestLineMethodLimit];
Span path = stackalloc char[RequestLinePathLimit];
Span version = stackalloc char[RequestLineVersionLimit];
@@ -199,6 +239,10 @@ private ListenerContext ReadRequestIntoContext(System.Net.Sockets.Socket client,
}
context.InputStream = inputStream;
+ // If this an SSL connection, set the remote certificate
+ if (sslStream != null)
+ context.RemoteCertificate = sslStream.RemoteCertificate;
+
return context;
}
diff --git a/Bunkum.Protocols.Http/Socket/SocketHttpListenerContext.cs b/Bunkum.Protocols.Http/Socket/SocketHttpListenerContext.cs
index 09e15e0..b8bd7a2 100644
--- a/Bunkum.Protocols.Http/Socket/SocketHttpListenerContext.cs
+++ b/Bunkum.Protocols.Http/Socket/SocketHttpListenerContext.cs
@@ -11,10 +11,13 @@ public SocketHttpListenerContext(System.Net.Sockets.Socket socket, Stream stream
protected override async Task SendResponseInternal(HttpStatusCode code, ArraySegment? data = null)
{
- // this is dumb and stupid
- this.ResponseHeaders.Add("Server", "Bunkum");
- this.ResponseHeaders.Add("Connection", "close");
- this.ResponseHeaders.Add("Date", DateTime.UtcNow.ToString("ddd, dd MMM yyyy HH:mm:ss 'GMT'"));
+ // These are "TryAdd" and not "Add" since if you are proxying requests,
+ // both of these should be set to the headers sent by the proxied server, and not be generated by Bunkum directly
+ this.ResponseHeaders.TryAdd("Server", "Bunkum");
+ this.ResponseHeaders.TryAdd("Date", DateTime.UtcNow.ToString("ddd, dd MMM yyyy HH:mm:ss 'GMT'"));
+ // This is an unconditional set because Bunkum does not support keep-alive connections,
+ // theres currently no reason for anything other than Bunkum to set this value
+ this.ResponseHeaders["Connection"] = "close";
List response = new() { $"HTTP/1.1 {code.GetHashCode()} {code.ToString()}" }; // TODO: spaced code names ("Not Found" instead of "NotFound")
foreach ((string? key, string? value) in this.ResponseHeaders)
diff --git a/Bunkum.Protocols.Https/Bunkum.Protocols.Https.csproj b/Bunkum.Protocols.Https/Bunkum.Protocols.Https.csproj
new file mode 100644
index 0000000..5c7ff46
--- /dev/null
+++ b/Bunkum.Protocols.Https/Bunkum.Protocols.Https.csproj
@@ -0,0 +1,14 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
diff --git a/Bunkum.Protocols.Https/BunkumHttpsServer.cs b/Bunkum.Protocols.Https/BunkumHttpsServer.cs
new file mode 100644
index 0000000..eb17778
--- /dev/null
+++ b/Bunkum.Protocols.Https/BunkumHttpsServer.cs
@@ -0,0 +1,33 @@
+using System.Security.Cryptography.X509Certificates;
+using Bunkum.Core;
+using Bunkum.Core.Configuration;
+using Bunkum.Listener;
+using Bunkum.Protocols.TlsSupport;
+using NotEnoughLogs;
+using NotEnoughLogs.Sinks;
+
+namespace Bunkum.Protocols.Https;
+
+public class BunkumHttpsServer : BunkumServer
+{
+ private readonly X509Certificate2? _cert;
+ private readonly SslConfiguration _sslConfiguration;
+
+ public BunkumHttpsServer(LoggerConfiguration? configuration = null, List? sinks = null,
+ SslConfiguration? sslConfiguration = null) : base(configuration, sinks)
+ {
+ //If the SSL configuration is not specified, load the config from JSON
+ this._sslConfiguration = sslConfiguration ?? Config.LoadFromJsonFile("ssl.json", this.Logger);
+
+ this._cert = new X509Certificate2(File.ReadAllBytes(this._sslConfiguration.SslCertificate), this._sslConfiguration.CertificatePassword);
+ }
+
+ ///
+ protected override BunkumListener CreateDefaultListener(Uri listenEndpoint, bool useForwardedIp, Logger logger)
+ {
+ return new Http.Socket.SocketHttpListener(listenEndpoint, useForwardedIp, logger, this._cert, this._sslConfiguration.EnabledSslProtocols, this._sslConfiguration.EnabledCipherSuites);
+ }
+
+ ///
+ protected override string ProtocolUriName => "https";
+}
\ No newline at end of file
diff --git a/Bunkum.Protocols.TlsSupport/Bunkum.Protocols.TlsSupport.csproj b/Bunkum.Protocols.TlsSupport/Bunkum.Protocols.TlsSupport.csproj
new file mode 100644
index 0000000..91c48da
--- /dev/null
+++ b/Bunkum.Protocols.TlsSupport/Bunkum.Protocols.TlsSupport.csproj
@@ -0,0 +1,13 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/Bunkum.Protocols.TlsSupport/SslConfiguration.cs b/Bunkum.Protocols.TlsSupport/SslConfiguration.cs
new file mode 100644
index 0000000..7eaecf8
--- /dev/null
+++ b/Bunkum.Protocols.TlsSupport/SslConfiguration.cs
@@ -0,0 +1,53 @@
+using System.Net.Security;
+using System.Security.Authentication;
+using Bunkum.Core.Configuration;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+
+namespace Bunkum.Protocols.TlsSupport;
+
+public class SslConfiguration : Config
+{
+ public override int CurrentConfigVersion => 1;
+ public override int Version { get; set; }
+
+ protected override void Migrate(int oldVer, dynamic oldConfig)
+ {}
+
+ ///
+ /// The path to the certificate
+ ///
+ public string SslCertificate { get; set; } = "cert.pfx";
+ ///
+ /// The password for the certificate, null if none
+ ///
+ public string? CertificatePassword { get; set; }
+
+ ///
+ /// The SSL protocols which are enabled. If null, enables TLS1.3 and TLS1.2
+ ///
+ [JsonProperty("EnabledSslProtocols", ItemConverterType = typeof(StringEnumConverter))]
+ private SslProtocols[]? _EnabledSslProtocols { get; set; }
+ ///
+ /// The cipher suites which are enabled. If null, lets the system decide
+ ///
+ [JsonProperty(ItemConverterType = typeof(StringEnumConverter))]
+ public TlsCipherSuite[]? EnabledCipherSuites { get; set; }
+
+ [JsonIgnore]
+ public SslProtocols EnabledSslProtocols
+ {
+ get
+ {
+ SslProtocols protocols = SslProtocols.None;
+
+ if (this._EnabledSslProtocols == null)
+ protocols = SslProtocols.Tls12 | SslProtocols.Tls13;
+ else
+ protocols = this._EnabledSslProtocols
+ .Aggregate(protocols, (current, protocol) => current | protocol);
+
+ return protocols;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Bunkum.sln b/Bunkum.sln
index fc30410..bcdc1ed 100644
--- a/Bunkum.sln
+++ b/Bunkum.sln
@@ -69,6 +69,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Database", "Database", "{ED
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DataStore", "DataStore", "{D09D7F3C-B198-40D8-B7E0-CC2CFFC75CDD}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bunkum.Protocols.Https", "Bunkum.Protocols.Https\Bunkum.Protocols.Https.csproj", "{FEDB1C3F-5549-4B44-94CE-54FFE590A35A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bunkum.Protocols.TlsSupport", "Bunkum.Protocols.TlsSupport\Bunkum.Protocols.TlsSupport.csproj", "{B7B1AAD8-2E00-439E-A587-7434E51C1E9E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -174,6 +178,14 @@ Global
{23BC7994-0E93-4FB3-8D31-16EC3BE1198B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{23BC7994-0E93-4FB3-8D31-16EC3BE1198B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{23BC7994-0E93-4FB3-8D31-16EC3BE1198B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FEDB1C3F-5549-4B44-94CE-54FFE590A35A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FEDB1C3F-5549-4B44-94CE-54FFE590A35A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FEDB1C3F-5549-4B44-94CE-54FFE590A35A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FEDB1C3F-5549-4B44-94CE-54FFE590A35A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B7B1AAD8-2E00-439E-A587-7434E51C1E9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B7B1AAD8-2E00-439E-A587-7434E51C1E9E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B7B1AAD8-2E00-439E-A587-7434E51C1E9E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B7B1AAD8-2E00-439E-A587-7434E51C1E9E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{B6B9F1DD-7E85-402E-88A7-EF6B88E6CE9A} = {77922B8B-F6FE-47D6-8742-6BA528BC8D7C}
@@ -200,5 +212,7 @@ Global
{DEE9CFC0-9529-451F-9633-AF5B49498AFB} = {EDBF1652-710C-4CF9-8890-AF3B46FDA8E1}
{D09D7F3C-B198-40D8-B7E0-CC2CFFC75CDD} = {77922B8B-F6FE-47D6-8742-6BA528BC8D7C}
{993BD323-2E2C-4E79-9252-F92B8CEB834D} = {D09D7F3C-B198-40D8-B7E0-CC2CFFC75CDD}
+ {FEDB1C3F-5549-4B44-94CE-54FFE590A35A} = {BBAA9024-6FEC-4C68-9F96-83D2055EC6D2}
+ {B7B1AAD8-2E00-439E-A587-7434E51C1E9E} = {BBAA9024-6FEC-4C68-9F96-83D2055EC6D2}
EndGlobalSection
EndGlobal