diff --git a/FubarDev.FtpServer.sln b/FubarDev.FtpServer.sln
index fef86b6c..d162e188 100644
--- a/FubarDev.FtpServer.sln
+++ b/FubarDev.FtpServer.sln
@@ -172,6 +172,7 @@ Global
EndGlobalSection
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\FubarDev.FtpServer.Shared\FubarDev.FtpServer.Shared.projitems*{01ebbf94-7469-43a0-a618-07fb734b163f}*SharedItemsImports = 5
+ third-party\GnuSslStream\GnuSslStream.projitems*{01ebbf94-7469-43a0-a618-07fb734b163f}*SharedItemsImports = 5
src\FubarDev.FtpServer.Shared\FubarDev.FtpServer.Shared.projitems*{37ce4218-fac3-4201-82a0-54945698c6ad}*SharedItemsImports = 5
src\FubarDev.FtpServer.Shared\FubarDev.FtpServer.Shared.projitems*{474bd381-5b80-4580-bfb1-d41beb70c458}*SharedItemsImports = 5
third-party\ReadLine\ReadLine.projitems*{4bda38ca-a19d-4ba9-ac7e-e0b091b3770d}*SharedItemsImports = 5
diff --git a/samples/TestFtpServer/TestFtpServer.csproj b/samples/TestFtpServer/TestFtpServer.csproj
index aec832eb..3dfc89d3 100644
--- a/samples/TestFtpServer/TestFtpServer.csproj
+++ b/samples/TestFtpServer/TestFtpServer.csproj
@@ -7,6 +7,12 @@
ftpserver
false
+
+ $(DefineConstants)TRACE;
+
+
+ $(DefineConstants)TRACE;
+
diff --git a/samples/TestFtpServer/appsettings.json b/samples/TestFtpServer/appsettings.json
index 789dc0ed..2d78764f 100644
--- a/samples/TestFtpServer/appsettings.json
+++ b/samples/TestFtpServer/appsettings.json
@@ -1,7 +1,7 @@
{
"Serilog": {
- "Using": ["Serilog.Sinks.Console"],
- "MinimumLevel":{
+ "Using": [ "Serilog.Sinks.Console" ],
+ "MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Information",
@@ -9,18 +9,19 @@
"FubarDev.FtpServer.CommandHandlers.MlstCommandHandler": "Verbose"
}
},
- "WriteTo": [
+ "WriteTo": [
{
"Name": "Console",
"OutputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {ConnectionId} {Message:lj}{NewLine}{Exception}"
}
],
- "Enrich": ["FromLogContext"]
+ "Enrich": [ "FromLogContext" ]
},
/* Supported authentication types are: "custom","anonymous", "pam", "default".
* "default" means "not set" and is equal to "anonymous". */
- "authentication": "default",
+ // "authentication": "default",
+ "authentication": "custom",
/* Sets the user id and group id for file system operations when
* authentication is "pam" and "backend" is "unix". */
@@ -76,11 +77,11 @@
"ftps": {
/* Path to the X.509 certificate.
* It may either be a certificate or a PKCS#12-file with private key. */
- "certificate": null,
+ "certificate": "C:\\Users\\alex\\Desktop\\cert\\alexander.abc.pfx",
/* Path to private key for the certificate. */
"privateKey": null,
/* Password used to decrypt the PFX file. */
- "password": null,
+ "password": "123",
/* Use implicit AUTH TLS? */
"implicit": false
},
diff --git a/src/FubarDev.FtpServer.Abstractions/AccountManagement/IMembershipProviderAsync.cs b/src/FubarDev.FtpServer.Abstractions/AccountManagement/IMembershipProviderAsync.cs
index 7fcec967..fb9fbf30 100644
--- a/src/FubarDev.FtpServer.Abstractions/AccountManagement/IMembershipProviderAsync.cs
+++ b/src/FubarDev.FtpServer.Abstractions/AccountManagement/IMembershipProviderAsync.cs
@@ -23,10 +23,7 @@ public interface IMembershipProviderAsync : IMembershipProvider
/// The password.
/// The .
/// The result of the validation.
- Task ValidateUserAsync(
- string username,
- string password,
- CancellationToken cancellationToken = default);
+ Task ValidateUserAsync(string username, string password, CancellationToken cancellationToken = default);
///
/// Logout of the given .
@@ -34,8 +31,6 @@ Task ValidateUserAsync(
/// The principal to be logged out.
/// The .
/// The task.
- Task LogOutAsync(
- ClaimsPrincipal principal,
- CancellationToken cancellationToken = default);
+ Task LogOutAsync(ClaimsPrincipal principal, CancellationToken cancellationToken = default);
}
}
diff --git a/src/FubarDev.FtpServer.Abstractions/AccountManagement/MemberValidationStatus.cs b/src/FubarDev.FtpServer.Abstractions/AccountManagement/MemberValidationStatus.cs
index da696d96..852cf6fd 100644
--- a/src/FubarDev.FtpServer.Abstractions/AccountManagement/MemberValidationStatus.cs
+++ b/src/FubarDev.FtpServer.Abstractions/AccountManagement/MemberValidationStatus.cs
@@ -31,5 +31,10 @@ public enum MemberValidationStatus
/// User authenticated successfully.
///
AuthenticatedUser,
+
+ ///
+ /// The too many users
+ ///
+ TooManyUsers,
}
}
diff --git a/src/FubarDev.FtpServer.Abstractions/Commands/DefaultFtpCommandDispatcher.cs b/src/FubarDev.FtpServer.Abstractions/Commands/DefaultFtpCommandDispatcher.cs
index 1658ad0c..da9443bf 100644
--- a/src/FubarDev.FtpServer.Abstractions/Commands/DefaultFtpCommandDispatcher.cs
+++ b/src/FubarDev.FtpServer.Abstractions/Commands/DefaultFtpCommandDispatcher.cs
@@ -179,9 +179,8 @@ private async Task SendResponseAsync(IFtpResponse? response, CancellationToken c
}
var serverCommandFeature = _connection.Features.Get();
- await serverCommandFeature.ServerCommandWriter
- .WriteAsync(new SendResponseServerCommand(response), cancellationToken)
- .ConfigureAwait(false);
+ await serverCommandFeature.ServerCommandWriter.WriteAsync(new SendResponseServerCommand(response), cancellationToken).ConfigureAwait(false);
+
if (response.Code == 421)
{
// Critical Error: We have to close the connection!
diff --git a/src/FubarDev.FtpServer.Abstractions/Features/ISecureConnectionFeature.cs b/src/FubarDev.FtpServer.Abstractions/Features/ISecureConnectionFeature.cs
index 5e1469a4..c1c8a33b 100644
--- a/src/FubarDev.FtpServer.Abstractions/Features/ISecureConnectionFeature.cs
+++ b/src/FubarDev.FtpServer.Abstractions/Features/ISecureConnectionFeature.cs
@@ -28,5 +28,21 @@ public interface ISecureConnectionFeature
///
/// This doesn't apply to encrypted data streams.
CloseEncryptedStreamDelegate CloseEncryptedControlStream { get; set; }
+
+ ///
+ /// Checks the security.
+ ///
+ /// The error mess.
+ /// The connection.
+ /// IFtpResponse.
+ public IFtpResponse CheckSecurity(string errorMess, IFtpConnection connection);
+
+ ///
+ /// Gets or sets a value indicating whether this instance is secure.
+ ///
+ ///
+ /// true if this instance is secure; otherwise, false.
+ ///
+ public bool IsSecure { get; set; }
}
}
diff --git a/src/FubarDev.FtpServer.Abstractions/FubarDev.FtpServer.Abstractions.csproj b/src/FubarDev.FtpServer.Abstractions/FubarDev.FtpServer.Abstractions.csproj
index d58c1db1..2327fe0f 100644
--- a/src/FubarDev.FtpServer.Abstractions/FubarDev.FtpServer.Abstractions.csproj
+++ b/src/FubarDev.FtpServer.Abstractions/FubarDev.FtpServer.Abstractions.csproj
@@ -1,7 +1,7 @@
- netstandard2.0;netstandard2.1
+ netstandard2.0;netstandard2.1;net472
Interfaces for the portable FTP server
FubarDev.FtpServer
portable;FTP;server
diff --git a/src/FubarDev.FtpServer.Commands/CommandHandlers/AppeCommandHandler.cs b/src/FubarDev.FtpServer.Commands/CommandHandlers/AppeCommandHandler.cs
index 78ebcf92..38df843e 100644
--- a/src/FubarDev.FtpServer.Commands/CommandHandlers/AppeCommandHandler.cs
+++ b/src/FubarDev.FtpServer.Commands/CommandHandlers/AppeCommandHandler.cs
@@ -38,6 +38,15 @@ public AppeCommandHandler(
///
public override async Task Process(FtpCommand command, CancellationToken cancellationToken)
{
+ var secureConnectionFeature = Connection.Features.Get();
+
+ var isSecureResponse = secureConnectionFeature.CheckSecurity(T("Please use TLS connection"), Connection);
+
+ if (isSecureResponse != null)
+ {
+ return isSecureResponse;
+ }
+
var restartPosition = Connection.Features.Get()?.RestartPosition;
Connection.Features.Set(null);
diff --git a/src/FubarDev.FtpServer.Commands/CommandHandlers/AuthCommandHandler.cs b/src/FubarDev.FtpServer.Commands/CommandHandlers/AuthCommandHandler.cs
index 44320a62..7ba7847a 100644
--- a/src/FubarDev.FtpServer.Commands/CommandHandlers/AuthCommandHandler.cs
+++ b/src/FubarDev.FtpServer.Commands/CommandHandlers/AuthCommandHandler.cs
@@ -6,6 +6,7 @@
using System.Threading.Tasks;
using FubarDev.FtpServer.Commands;
+using FubarDev.FtpServer.Features;
using Microsoft.Extensions.DependencyInjection;
@@ -21,7 +22,13 @@ public class AuthCommandHandler : FtpCommandHandler
public override Task Process(FtpCommand command, CancellationToken cancellationToken)
{
var loginStateMachine = Connection.ConnectionServices.GetRequiredService();
- return loginStateMachine.ExecuteAsync(command, cancellationToken);
+
+ var res = loginStateMachine.ExecuteAsync(command, cancellationToken);
+
+ var secureConnectionFeature = Connection.Features.Get();
+ secureConnectionFeature.IsSecure = true;
+
+ return res;
}
}
}
diff --git a/src/FubarDev.FtpServer.Commands/CommandHandlers/DeleCommandHandler.cs b/src/FubarDev.FtpServer.Commands/CommandHandlers/DeleCommandHandler.cs
index dece32f2..5352e5be 100644
--- a/src/FubarDev.FtpServer.Commands/CommandHandlers/DeleCommandHandler.cs
+++ b/src/FubarDev.FtpServer.Commands/CommandHandlers/DeleCommandHandler.cs
@@ -37,6 +37,15 @@ public DeleCommandHandler(ILogger? logger = null)
///
public override async Task Process(FtpCommand command, CancellationToken cancellationToken)
{
+ var secureConnectionFeature = Connection.Features.Get();
+
+ var isSecureResponse = secureConnectionFeature.CheckSecurity(T("Please use TLS connection"), Connection);
+
+ if (isSecureResponse != null)
+ {
+ return isSecureResponse;
+ }
+
var path = command.Argument;
var fsFeature = Connection.Features.Get();
var currentPath = fsFeature.Path.Clone();
diff --git a/src/FubarDev.FtpServer.Commands/CommandHandlers/ListCommandHandler.cs b/src/FubarDev.FtpServer.Commands/CommandHandlers/ListCommandHandler.cs
index 7ea23bf0..d89812eb 100644
--- a/src/FubarDev.FtpServer.Commands/CommandHandlers/ListCommandHandler.cs
+++ b/src/FubarDev.FtpServer.Commands/CommandHandlers/ListCommandHandler.cs
@@ -49,6 +49,15 @@ public ListCommandHandler(
///
public override async Task Process(FtpCommand command, CancellationToken cancellationToken)
{
+ var secureConnectionFeature = Connection.Features.Get();
+
+ var isSecureResponse = secureConnectionFeature.CheckSecurity(T("Please use TLS connection"), Connection);
+
+ if (isSecureResponse != null)
+ {
+ return isSecureResponse;
+ }
+
await FtpContext.ServerCommandWriter
.WriteAsync(
new SendResponseServerCommand(new FtpResponse(150, T("Opening data connection."))),
diff --git a/src/FubarDev.FtpServer.Commands/CommandHandlers/MdtmCommandHandler.cs b/src/FubarDev.FtpServer.Commands/CommandHandlers/MdtmCommandHandler.cs
index d7b79b23..f0b0af42 100644
--- a/src/FubarDev.FtpServer.Commands/CommandHandlers/MdtmCommandHandler.cs
+++ b/src/FubarDev.FtpServer.Commands/CommandHandlers/MdtmCommandHandler.cs
@@ -24,6 +24,15 @@ public class MdtmCommandHandler : FtpCommandHandler
///
public override async Task Process(FtpCommand command, CancellationToken cancellationToken)
{
+ var secureConnectionFeature = Connection.Features.Get();
+
+ var isSecureResponse = secureConnectionFeature.CheckSecurity(T("Please use TLS connection"), Connection);
+
+ if (isSecureResponse != null)
+ {
+ return isSecureResponse;
+ }
+
var path = command.Argument;
var fsFeature = Connection.Features.Get();
var currentPath = fsFeature.Path.Clone();
diff --git a/src/FubarDev.FtpServer.Commands/CommandHandlers/MlstCommandHandler.cs b/src/FubarDev.FtpServer.Commands/CommandHandlers/MlstCommandHandler.cs
index dfca3785..23eba89f 100644
--- a/src/FubarDev.FtpServer.Commands/CommandHandlers/MlstCommandHandler.cs
+++ b/src/FubarDev.FtpServer.Commands/CommandHandlers/MlstCommandHandler.cs
@@ -90,6 +90,15 @@ internal static IMlstFactsFeature CreateMlstFactsFeature()
private async Task ProcessMlstAsync(FtpCommand command, CancellationToken cancellationToken)
{
+ var secureConnectionFeature = Connection.Features.Get();
+
+ var isSecureResponse = secureConnectionFeature.CheckSecurity(T("Please use TLS connection"), Connection);
+
+ if (isSecureResponse != null)
+ {
+ return isSecureResponse;
+ }
+
var argument = command.Argument;
var fsFeature = Connection.Features.Get();
var path = fsFeature.Path.Clone();
diff --git a/src/FubarDev.FtpServer.Commands/CommandHandlers/PassCommandHandler.cs b/src/FubarDev.FtpServer.Commands/CommandHandlers/PassCommandHandler.cs
index 5210b8a9..b1f18228 100644
--- a/src/FubarDev.FtpServer.Commands/CommandHandlers/PassCommandHandler.cs
+++ b/src/FubarDev.FtpServer.Commands/CommandHandlers/PassCommandHandler.cs
@@ -9,6 +9,7 @@
using System.Threading.Tasks;
using FubarDev.FtpServer.Commands;
+using FubarDev.FtpServer.Features;
using Microsoft.Extensions.DependencyInjection;
@@ -23,6 +24,15 @@ public class PassCommandHandler : FtpCommandHandler
///
public override Task Process(FtpCommand command, CancellationToken cancellationToken)
{
+ var secureConnectionFeature = Connection.Features.Get();
+
+ var isSecureResponse = secureConnectionFeature.CheckSecurity(T("Please use TLS connection"), Connection);
+
+ if (isSecureResponse != null)
+ {
+ return Task.FromResult(isSecureResponse);
+ }
+
var loginStateMachine = Connection.ConnectionServices.GetRequiredService();
return loginStateMachine.ExecuteAsync(command, cancellationToken);
}
diff --git a/src/FubarDev.FtpServer.Commands/CommandHandlers/RetrCommandHandler.cs b/src/FubarDev.FtpServer.Commands/CommandHandlers/RetrCommandHandler.cs
index a5b0df32..9a591479 100644
--- a/src/FubarDev.FtpServer.Commands/CommandHandlers/RetrCommandHandler.cs
+++ b/src/FubarDev.FtpServer.Commands/CommandHandlers/RetrCommandHandler.cs
@@ -42,6 +42,15 @@ public RetrCommandHandler(
///
public override async Task Process(FtpCommand command, CancellationToken cancellationToken)
{
+ var secureConnectionFeature = Connection.Features.Get();
+
+ var isSecureResponse = secureConnectionFeature.CheckSecurity(T("Please use TLS connection"), Connection);
+
+ if (isSecureResponse != null)
+ {
+ return isSecureResponse;
+ }
+
var restartPosition = Connection.Features.Get()?.RestartPosition;
Connection.Features.Set(null);
diff --git a/src/FubarDev.FtpServer.Commands/CommandHandlers/StorCommandHandler.cs b/src/FubarDev.FtpServer.Commands/CommandHandlers/StorCommandHandler.cs
index a1f2f016..74a33513 100644
--- a/src/FubarDev.FtpServer.Commands/CommandHandlers/StorCommandHandler.cs
+++ b/src/FubarDev.FtpServer.Commands/CommandHandlers/StorCommandHandler.cs
@@ -44,6 +44,15 @@ public StorCommandHandler(
///
public override async Task Process(FtpCommand command, CancellationToken cancellationToken)
{
+ var secureConnectionFeature = Connection.Features.Get();
+
+ var isSecureResponse = secureConnectionFeature.CheckSecurity(T("Please use TLS connection"), Connection);
+
+ if (isSecureResponse != null)
+ {
+ return isSecureResponse;
+ }
+
var restartPosition = Connection.Features.Get()?.RestartPosition;
Connection.Features.Set(null);
diff --git a/src/FubarDev.FtpServer.Commands/CommandHandlers/UserCommandHandler.cs b/src/FubarDev.FtpServer.Commands/CommandHandlers/UserCommandHandler.cs
index 25f26747..d2c4502a 100644
--- a/src/FubarDev.FtpServer.Commands/CommandHandlers/UserCommandHandler.cs
+++ b/src/FubarDev.FtpServer.Commands/CommandHandlers/UserCommandHandler.cs
@@ -9,6 +9,7 @@
using System.Threading.Tasks;
using FubarDev.FtpServer.Commands;
+using FubarDev.FtpServer.Features;
using Microsoft.Extensions.DependencyInjection;
@@ -23,6 +24,15 @@ public class UserCommandHandler : FtpCommandHandler
///
public override Task Process(FtpCommand command, CancellationToken cancellationToken)
{
+ var secureConnectionFeature = Connection.Features.Get();
+
+ var isSecureResponse = secureConnectionFeature.CheckSecurity(T("Please use TLS connection"), Connection);
+
+ if (isSecureResponse != null)
+ {
+ return Task.FromResult(isSecureResponse);
+ }
+
var loginStateMachine = Connection.ConnectionServices.GetRequiredService();
return loginStateMachine.ExecuteAsync(command, cancellationToken);
}
diff --git a/src/FubarDev.FtpServer/AuthTlsOptions.cs b/src/FubarDev.FtpServer/AuthTlsOptions.cs
index 2add3608..1250efdd 100644
--- a/src/FubarDev.FtpServer/AuthTlsOptions.cs
+++ b/src/FubarDev.FtpServer/AuthTlsOptions.cs
@@ -20,5 +20,13 @@ public class AuthTlsOptions
/// Gets or sets a value indicating whether implicit FTPS is used.
///
public bool ImplicitFtps { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether [only secure connection].
+ ///
+ ///
+ /// true if [only secure connection]; otherwise, false.
+ ///
+ public bool OnlySecureConnection { get; set; }
}
}
diff --git a/src/FubarDev.FtpServer/Authentication/DefaultSslStreamWrapperFactory.cs b/src/FubarDev.FtpServer/Authentication/DefaultSslStreamWrapperFactory.cs
index 30c1f999..4c820dfe 100644
--- a/src/FubarDev.FtpServer/Authentication/DefaultSslStreamWrapperFactory.cs
+++ b/src/FubarDev.FtpServer/Authentication/DefaultSslStreamWrapperFactory.cs
@@ -4,6 +4,7 @@
using System;
using System.IO;
+using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
@@ -39,6 +40,7 @@ public async Task WrapStreamAsync(
{
try
{
+ ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
_logger?.LogTrace("Create SSL stream");
var sslStream = CreateSslStream(unencryptedStream, keepOpen);
try
diff --git a/src/FubarDev.FtpServer/Authentication/SslStreamExt.cs b/src/FubarDev.FtpServer/Authentication/SslStreamExt.cs
new file mode 100644
index 00000000..fa657187
--- /dev/null
+++ b/src/FubarDev.FtpServer/Authentication/SslStreamExt.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net.Security;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Threading.Tasks;
+using System.Threading;
+
+namespace FubarDev.FtpServer.Authentication
+{
+ namespace System.Net.Security
+ {
+ public class NegotiateStream : AuthenticatedStream
+ {
+ public override Task FlushAsync(CancellationToken cancellationToken);
+ }
+ public class SslStream : AuthenticatedStream
+ {
+ public virtual void AuthenticateAsClient(string targetHost, X509CertificateCollection clientCertificates, bool checkCertificateRevocation);
+ public virtual Task AuthenticateAsClientAsync(string targetHost, X509CertificateCollection clientCertificates, bool checkCertificateRevocation);
+ public virtual void AuthenticateAsServer(X509Certificate serverCertificate, bool clientCertificateRequired, bool checkCertificateRevocation);
+ public virtual Task AuthenticateAsServerAsync(X509Certificate serverCertificate, bool clientCertificateRequired, bool checkCertificateRevocation);
+ public virtual IAsyncResult BeginAuthenticateAsClient(string targetHost, X509CertificateCollection clientCertificates, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState);
+ public virtual IAsyncResult BeginAuthenticateAsServer(X509Certificate serverCertificate, bool clientCertificateRequired, bool checkCertificateRevocation, AsyncCallback asyncCallback, object asyncState);
+ public override Task FlushAsync(CancellationToken cancellationToken);
+ public virtual Task ShutdownAsync();
+ }
+ }
+}
diff --git a/src/FubarDev.FtpServer/FtpConnection.cs b/src/FubarDev.FtpServer/FtpConnection.cs
index d86f6ac4..d123f764 100644
--- a/src/FubarDev.FtpServer/FtpConnection.cs
+++ b/src/FubarDev.FtpServer/FtpConnection.cs
@@ -1030,6 +1030,25 @@ private class SecureConnectionFeature : ISecureConnectionFeature
///
public CloseEncryptedStreamDelegate CloseEncryptedControlStream { get; set; } = ct => Task.CompletedTask;
+
+ ///
+ /// Gets or sets a value indicating whether this instance is secure.
+ ///
+ ///
+ /// true if this instance is secure; otherwise, false.
+ ///
+ public bool IsSecure { get; set; }
+
+ ///
+ /// Checks the security.
+ ///
+ /// FtpResponse if response not TLS.
+ public IFtpResponse CheckSecurity(string errorMess, IFtpConnection connection)
+ {
+ var authTlsOptions = connection.ConnectionServices.GetRequiredService>();
+
+ return authTlsOptions.Value?.OnlySecureConnection == true && !this.IsSecure ? new FtpResponse(550, errorMess) : null;
+ }
}
private class DuplexPipe : IDuplexPipe
diff --git a/src/FubarDev.FtpServer/FubarDev.FtpServer.csproj b/src/FubarDev.FtpServer/FubarDev.FtpServer.csproj
index 295ef36e..95cf125e 100644
--- a/src/FubarDev.FtpServer/FubarDev.FtpServer.csproj
+++ b/src/FubarDev.FtpServer/FubarDev.FtpServer.csproj
@@ -1,10 +1,43 @@
- netstandard2.0;netstandard2.1
+
+ netstandard2.1;net472;net471
TCP-based FTP server implementation
portable;FTP;server
+ True
+
+ $(DefineConstants)TRACE;USE_SYNC_SSL_STREAM;NETCOREAPP;USE_GNU_SSL_STREAM
+
+
+ $(DefineConstants)TRACE;USE_SYNC_SSL_STREAM;NETCOREAPP;USE_GNU_SSL_STREAM
+
+
+ $(DefineConstants)TRACE;USE_GNU_SSL_STREAM;USE_SYNC_SSL_STREAM;NETCOREAPP
+
+
+ $(DefineConstants)TRACE;USE_GNU_SSL_STREAM;USE_SYNC_SSL_STREAM;NETCOREAPP
+
+
+ $(DefineConstants)TRACE;USE_GNU_SSL_STREAM;USE_SYNC_SSL_STREAM;NET47
+
+
+ $(DefineConstants)TRACE;USE_GNU_SSL_STREAM;USE_SYNC_SSL_STREAM;NET47
+
+
+ $(DefineConstants)TRACE;USE_GNU_SSL_STREAM;USE_SYNC_SSL_STREAM;NET47
+
+
+ $(DefineConstants)TRACE;USE_GNU_SSL_STREAM;USE_SYNC_SSL_STREAM;NET47
+
+
+
+
+
+
+
+
@@ -13,4 +46,5 @@
+
diff --git a/src/FubarDev.FtpServer/Networking/PausableFtpService.cs b/src/FubarDev.FtpServer/Networking/PausableFtpService.cs
index 389af331..f9973f53 100644
--- a/src/FubarDev.FtpServer/Networking/PausableFtpService.cs
+++ b/src/FubarDev.FtpServer/Networking/PausableFtpService.cs
@@ -7,6 +7,8 @@
using System.Threading;
using System.Threading.Tasks;
+using Abc.FubarDev.FtpServer.Networking;
+
using Microsoft.Extensions.Logging;
namespace FubarDev.FtpServer.Networking
@@ -71,7 +73,7 @@ public virtual async Task StartAsync(CancellationToken cancellationToken)
throw new InvalidOperationException($"Status must be {FtpServiceStatus.ReadyToRun}, but was {Status}.");
}
- using var semaphore = new SemaphoreSlim(0, 1);
+ using var semaphore = new SemaphoreSlimExt(0, 1);
_jobPaused = new CancellationTokenSource();
_task = RunAsync(
new Progress(
diff --git a/src/FubarDev.FtpServer/Networking/SemaphoreSlimExt.cs b/src/FubarDev.FtpServer/Networking/SemaphoreSlimExt.cs
new file mode 100644
index 00000000..f7c8d4ff
--- /dev/null
+++ b/src/FubarDev.FtpServer/Networking/SemaphoreSlimExt.cs
@@ -0,0 +1,38 @@
+//
+// Copyright (c) Fubar Development Junker. All rights reserved.
+//
+
+using System.Threading;
+
+namespace Abc.FubarDev.FtpServer.Networking
+{
+ internal class SemaphoreSlimExt : SemaphoreSlim
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The initial number of requests for the semaphore that can be granted concurrently.
+ public SemaphoreSlimExt(int initialCount)
+ : base(initialCount)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The initial number of requests for the semaphore that can be granted concurrently.
+ /// The maximum number of requests for the semaphore that can be granted concurrently.
+ public SemaphoreSlimExt(int initialCount, int maxCount)
+ : base(initialCount, maxCount)
+ {
+ }
+
+ public bool IsDisposed { get; internal set; }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+ IsDisposed = true;
+ }
+ }
+}
diff --git a/third-party/GnuSslStream/ReflectUtil.cs b/third-party/GnuSslStream/ReflectUtil.cs
index c236287b..0824fd3c 100644
--- a/third-party/GnuSslStream/ReflectUtil.cs
+++ b/third-party/GnuSslStream/ReflectUtil.cs
@@ -12,8 +12,7 @@ internal static class ReflectUtil
public static object GetField(object obj, string fieldName)
{
var tp = obj.GetType();
- var info = GetAllFields(tp)
- .Where(f => f.Name == fieldName).SingleOrDefault();
+ var info = GetAllFields(tp)?.Where(f => f.Name == fieldName)?.FirstOrDefault();
return info?.GetValue(obj);
}
diff --git a/third-party/GnuSslStream/SslDirectCall.cs b/third-party/GnuSslStream/SslDirectCall.cs
index bc800a5a..f725f0e4 100644
--- a/third-party/GnuSslStream/SslDirectCall.cs
+++ b/third-party/GnuSslStream/SslDirectCall.cs
@@ -10,112 +10,126 @@ internal unsafe static class SslDirectCall
{
public static void CloseNotify(SslStream sslStream)
{
- if (sslStream.IsAuthenticated)
+ try
{
- bool isServer = sslStream.IsServer;
-
- byte[] result;
- int resultSz;
-
- int SCHANNEL_SHUTDOWN = 1;
- var workArray = BitConverter.GetBytes(SCHANNEL_SHUTDOWN);
+ if (sslStream.IsAuthenticated)
+ {
+ bool isServer = sslStream.IsServer;
- var nativeApiHelpers = GetNativeApiHelpers(sslStream);
- var (securityContextHandle, credentialsHandleHandle) = nativeApiHelpers.GetHandlesFunc();
+ byte[] result;
+ int resultSz;
- int bufferSize = 1;
- NativeApi.SecurityBufferDescriptor securityBufferDescriptor = new NativeApi.SecurityBufferDescriptor(bufferSize);
- NativeApi.SecurityBufferStruct[] unmanagedBuffer = new NativeApi.SecurityBufferStruct[bufferSize];
+ int SCHANNEL_SHUTDOWN = 1;
+ var workArray = BitConverter.GetBytes(SCHANNEL_SHUTDOWN);
- fixed (NativeApi.SecurityBufferStruct* ptr = unmanagedBuffer)
- fixed (void* workArrayPtr = workArray)
- {
- securityBufferDescriptor.UnmanagedPointer = (void*)ptr;
+ var nativeApiHelpers = GetNativeApiHelpers(sslStream);
+ var (securityContextHandle, credentialsHandleHandle) = nativeApiHelpers.GetHandlesFunc();
- unmanagedBuffer[0].token = (IntPtr)workArrayPtr;
- unmanagedBuffer[0].count = workArray.Length;
- unmanagedBuffer[0].type = NativeApi.BufferType.Token;
+ int bufferSize = 1;
+ NativeApi.SecurityBufferDescriptor securityBufferDescriptor = new NativeApi.SecurityBufferDescriptor(bufferSize);
+ NativeApi.SecurityBufferStruct[] unmanagedBuffer = new NativeApi.SecurityBufferStruct[bufferSize];
- NativeApi.SecurityStatus status;
- status = (NativeApi.SecurityStatus)NativeApi.ApplyControlToken(ref securityContextHandle, securityBufferDescriptor);
- if (status == NativeApi.SecurityStatus.OK)
+ fixed (NativeApi.SecurityBufferStruct* ptr = unmanagedBuffer)
+ fixed (void* workArrayPtr = workArray)
{
- unmanagedBuffer[0].token = IntPtr.Zero;
- unmanagedBuffer[0].count = 0;
- unmanagedBuffer[0].type = NativeApi.BufferType.Token;
-
- NativeApi.SSPIHandle contextHandleOut = default(NativeApi.SSPIHandle);
- NativeApi.ContextFlags outflags = NativeApi.ContextFlags.Zero;
- long ts = 0;
+ securityBufferDescriptor.UnmanagedPointer = (void*)ptr;
- var inflags = NativeApi.ContextFlags.SequenceDetect |
- NativeApi.ContextFlags.ReplayDetect |
- NativeApi.ContextFlags.Confidentiality |
- NativeApi.ContextFlags.AcceptExtendedError |
- NativeApi.ContextFlags.AllocateMemory |
- NativeApi.ContextFlags.InitStream;
+ unmanagedBuffer[0].token = (IntPtr)workArrayPtr;
+ unmanagedBuffer[0].count = workArray.Length;
+ unmanagedBuffer[0].type = NativeApi.BufferType.Token;
- if (isServer)
- {
- status = (NativeApi.SecurityStatus)NativeApi.AcceptSecurityContext(ref credentialsHandleHandle, ref securityContextHandle, null,
- inflags, NativeApi.Endianness.Native, ref contextHandleOut, securityBufferDescriptor, ref outflags, out ts);
- }
- else
- {
- status = (NativeApi.SecurityStatus)NativeApi.InitializeSecurityContextW(ref credentialsHandleHandle, ref securityContextHandle, null,
- inflags, 0, NativeApi.Endianness.Native, null, 0, ref contextHandleOut, securityBufferDescriptor, ref outflags, out ts);
- }
+ NativeApi.SecurityStatus status;
+ status = (NativeApi.SecurityStatus)NativeApi.ApplyControlToken(ref securityContextHandle, securityBufferDescriptor);
if (status == NativeApi.SecurityStatus.OK)
{
- byte[] resultArr = new byte[unmanagedBuffer[0].count];
- Marshal.Copy(unmanagedBuffer[0].token, resultArr, 0, resultArr.Length);
- Marshal.FreeCoTaskMem(unmanagedBuffer[0].token);
- result = resultArr;
- resultSz = resultArr.Length;
+ unmanagedBuffer[0].token = IntPtr.Zero;
+ unmanagedBuffer[0].count = 0;
+ unmanagedBuffer[0].type = NativeApi.BufferType.Token;
+
+ NativeApi.SSPIHandle contextHandleOut = default(NativeApi.SSPIHandle);
+ NativeApi.ContextFlags outflags = NativeApi.ContextFlags.Zero;
+ long ts = 0;
+
+ var inflags = NativeApi.ContextFlags.SequenceDetect |
+ NativeApi.ContextFlags.ReplayDetect |
+ NativeApi.ContextFlags.Confidentiality |
+ NativeApi.ContextFlags.AcceptExtendedError |
+ NativeApi.ContextFlags.AllocateMemory |
+ NativeApi.ContextFlags.InitStream;
+
+ if (isServer)
+ {
+ status = (NativeApi.SecurityStatus)NativeApi.AcceptSecurityContext(ref credentialsHandleHandle, ref securityContextHandle, null,
+ inflags, NativeApi.Endianness.Native, ref contextHandleOut, securityBufferDescriptor, ref outflags, out ts);
+ }
+ else
+ {
+ status = (NativeApi.SecurityStatus)NativeApi.InitializeSecurityContextW(ref credentialsHandleHandle, ref securityContextHandle, null,
+ inflags, 0, NativeApi.Endianness.Native, null, 0, ref contextHandleOut, securityBufferDescriptor, ref outflags, out ts);
+ }
+ if (status == NativeApi.SecurityStatus.OK)
+ {
+ byte[] resultArr = new byte[unmanagedBuffer[0].count];
+ Marshal.Copy(unmanagedBuffer[0].token, resultArr, 0, resultArr.Length);
+ Marshal.FreeCoTaskMem(unmanagedBuffer[0].token);
+ result = resultArr;
+ resultSz = resultArr.Length;
+ }
+ else
+ {
+ throw new InvalidOperationException(string.Format("AcceptSecurityContext/InitializeSecurityContextW returned [{0}] during CloseNotify.", status));
+ }
}
else
{
- throw new InvalidOperationException(string.Format("AcceptSecurityContext/InitializeSecurityContextW returned [{0}] during CloseNotify.", status));
+ throw new InvalidOperationException(string.Format("ApplyControlToken returned [{0}] during CloseNotify.", status));
}
}
- else
- {
- throw new InvalidOperationException(string.Format("ApplyControlToken returned [{0}] during CloseNotify.", status));
- }
- }
- var innerStream = nativeApiHelpers.GetInnerStreamFunc();
- innerStream.Write(result, 0, resultSz);
+ var innerStream = nativeApiHelpers.GetInnerStreamFunc();
+ innerStream.Write(result, 0, resultSz);
+ }
+ }
+ catch (Exception e)
+ {
+ throw e;
}
}
private static NativeApiHelpers GetNativeApiHelpers(SslStream sslStream)
{
- var sslState = ReflectUtil.GetField(sslStream, "_SslState");
- if (sslState != null)
+ try
{
- return new NativeApiHelpers()
+ var sslState = ReflectUtil.GetField(sslStream, "_SslState");
+ if (sslState != null)
{
- GetHandlesFunc = () => GetHandlesForDotNetFramework(sslState),
- GetInnerStreamFunc = () => (Stream)ReflectUtil.GetProperty(sslState, "InnerStream"),
- };
- }
+ return new NativeApiHelpers()
+ {
+ GetHandlesFunc = () => GetHandlesForDotNetFramework(sslState),
+ GetInnerStreamFunc = () => (Stream)ReflectUtil.GetProperty(sslState, "InnerStream"),
+ };
+ }
+
+ sslState = ReflectUtil.GetField(sslStream, "_sslState");
+ if (sslState != null)
+ {
+ return new NativeApiHelpers()
+ {
+ GetHandlesFunc = () => GetHandlesForNetCore10(sslStream),
+ GetInnerStreamFunc = () => (Stream)ReflectUtil.GetField(sslStream, "_innerStream"),
+ };
+ }
- sslState = ReflectUtil.GetField(sslStream, "_sslState");
- if (sslState != null)
- {
return new NativeApiHelpers()
{
- GetHandlesFunc = () => GetHandlesForNetCore10(sslStream),
+ GetHandlesFunc = () => GetHandlesForNetCore30(sslStream),
GetInnerStreamFunc = () => (Stream)ReflectUtil.GetField(sslStream, "_innerStream"),
};
}
-
- return new NativeApiHelpers()
+ catch (Exception e)
{
- GetHandlesFunc = () => GetHandlesForNetCore30(sslStream),
- GetInnerStreamFunc = () => (Stream)ReflectUtil.GetField(sslStream, "_innerStream"),
- };
+ throw e;
+ }
}
private static (NativeApi.SSPIHandle securityContextHandle, NativeApi.SSPIHandle credentialsHandleHandle)