FtpServer icon indicating copy to clipboard operation
FtpServer copied to clipboard

Recommended way to build for gnutls support?

Open kimboslice99 opened this issue 11 months ago • 5 comments

There seems to be most of whats needed already there, but with no docs on how to use it I am unsure what is needed.

  • Unsafe flag checked
  • defined USE_GNU_SSL_STREAM

Yet GnuSslStream remains undefined? I am not sure what is the suggested path to take to enable this?

kimboslice99 avatar Jan 23 '25 04:01 kimboslice99

You also have to reference the GnuSslStream shared project from the main FubarDev.FtpServer project. This is a bug that you'd be able to fix easily.

fubar-coder avatar Jan 23 '25 08:01 fubar-coder

Ok so I have referenced it in the main project

  <Import Project="../../third-party/GnuSslStream/GnuSslStream.projitems" Label="Shared" />

But now when I test I receive the following exception...

System.NullReferenceException: Object reference not set to an instance of an object.
   at System.Net.Security.ReflectUtil.GetField(Object obj, String fieldName) in FtpServer\third-party\GnuSslStream\ReflectUtil.cs:line 14
   at System.Net.Security.SslDirectCall.GetHandlesForNetCore30(SslStream sslStream) in FtpServer\third-party\GnuSslStream\SslDirectCall.cs:line 174
   at System.Net.Security.SslDirectCall.<>c__DisplayClass1_0.<GetNativeApiHelpers>b__4() in FtpServer\third-party\GnuSslStream\SslDirectCall.cs:line 116
   at System.Net.Security.SslDirectCall.CloseNotify(SslStream sslStream) in FtpServer\third-party\GnuSslStream\SslDirectCall.cs:line 24
   at System.Net.Security.GnuSslStream.Close() in FtpServer\third-party\GnuSslStream\GnuSslStream.cs:line 96
   at FubarDev.FtpServer.Authentication.DefaultSslStreamWrapperFactory.CloseStreamAsync(Stream sslStream, CancellationToken cancellationToken) in FtpServer\src\FubarDev.FtpServer\Authentication\DefaultSslStreamWrapperFactory.cs:line 89
   at FubarDev.FtpServer.ConnectionHandlers.SslStreamConnectionAdapter.StopAsync(CancellationToken cancellationToken) in FtpServer\src\FubarDev.FtpServer\ConnectionHandlers\SslStreamConnectionAdapter.cs:line 112
   at FubarDev.FtpServer.ServerCommandHandlers.CloseConnectionServerCommandHandler.ExecuteAsync(CloseConnectionServerCommand command, CancellationToken cancellationToken) in FtpServer\src\FubarDev.FtpServer\ServerCommandHandlers\CloseConnectionServerCommandHandler.cs:line 36
   at FubarDev.FtpServer.FtpConnection.SendResponsesAsync(ChannelReader`1 serverCommandReader, CancellationToken cancellationToken) in FtpServer\src\FubarDev.FtpServer\FtpConnection.cs:line 623

Testing with openssl s_client

echo quit | openssl s_client -connect 127.0.0.1:21 -starttls ftp

Am I missing some vital detail here? Does this fix only work on a specific .net version?

kimboslice99 avatar Jan 23 '25 15:01 kimboslice99

I wiped my local folder and did a fresh pull. The error I received this time is

System.InvalidOperationException: Sequence contains more than one element
   at System.Linq.ThrowHelper.ThrowMoreThanOneElementException()
   at System.Linq.Enumerable.TryGetSingle[TSource](IEnumerable1 source, Boolean& found)
   at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable1 source)
   at System.Net.Security.ReflectUtil.GetField(Object obj, String fieldName) in \FtpServer\third-party\GnuSslStream\ReflectUtil.cs:line 15
   at System.Net.Security.SslDirectCall.<>c__DisplayClass1_0.<GetNativeApiHelpers>b__5() in \FtpServer\third-party\GnuSslStream\SslDirectCall.cs:line 117
   at System.Net.Security.SslDirectCall.CloseNotify(SslStream sslStream) in \FtpServer\third-party\GnuSslStream\SslDirectCall.cs:line 87
   at System.Net.Security.GnuSslStream.Close() in \FtpServer\third-party\GnuSslStream\GnuSslStream.cs:line 96
   at System.IO.Stream.Dispose()
   at FubarDev.FtpServer.Authentication.DefaultSslStreamWrapperFactory.CloseStreamAsync(Stream sslStream, CancellationToken cancellationToken) in \FtpServer\src\FubarDev.FtpServer\Authentication\DefaultSslStreamWrapperFactory.cs:line 89
   at FubarDev.FtpServer.ConnectionHandlers.SslStreamConnectionAdapter.StopAsync(CancellationToken cancellationToken) in \FtpServer\src\FubarDev.FtpServer\ConnectionHandlers\SslStreamConnectionAdapter.cs:line 112
   at FubarDev.FtpServer.FtpConnection.StopAsync() in \FtpServer\src\FubarDev.FtpServer\FtpConnection.cs:line 412

I modified GetValue in ReflectionUtil. To inspect.

        public static object GetField(object obj, string fieldName)
        {
            var tp = obj.GetType();
            var fields = GetAllFields(tp).Where(f => f.Name == fieldName).ToList();

            if (fields.Count > 1)
            {
                Console.WriteLine($"Multiple fields found with name {fieldName}.");
            }

            foreach (var f in fields)
            {
                Console.WriteLine($"Field found: {f.Name}, DeclaringType: {f.DeclaringType}");
            }

            var field = fields.FirstOrDefault(f => f.DeclaringType == tp) ?? fields.FirstOrDefault();
            return field?.GetValue(obj);
        }
Multiple fields found with name _innerStream.
Field found: _innerStream, DeclaringType: System.Net.Security.SslStream
Field found: _innerStream, DeclaringType: System.Net.Security.AuthenticatedStream

Perhaps GetField() should be modified?

        public static object GetField(object obj, string fieldName)
        {
            var tp = obj.GetType();
            var fields = GetAllFields(tp).Where(f => f.Name == fieldName);
            var field = fields.FirstOrDefault(f => f.DeclaringType == tp) ?? fields.FirstOrDefault();
            return field?.GetValue(obj);
        }

FileZilla client connects with explicit tls and works fine now. I just dont understand why I have multiple _innerStream?

kimboslice99 avatar Jan 23 '25 16:01 kimboslice99

I remember that there was a change in the source files. However, I thought that this GNU TLS workaround wouldn't be necessary anymore due to the changes in .NET (Core) sometime before 5.0.

fubar-coder avatar Jan 24 '25 16:01 fubar-coder

I have modified DefaultSslStreamWrapperFactory.CloseStreamASync() to the following

        public Task CloseStreamAsync(Stream sslStream, CancellationToken cancellationToken)
        {
            if (sslStream is SslStream s)
            {
#if NET461 || NETSTANDARD2_0
                s.Close();
#elif NETSTANDARD2_1
                s.ShutdownAsync().Wait();
#else
                s.Dispose();
#endif
            }

            return Task.CompletedTask;
        }

I can now build and run without GnuSslStream and filezilla works well (and openssl s_client does not complain upon conn close either)

I am not well enough experienced with net standard to understand why you arent doing this already? Or perhaps just an oversight? I got the idea from a thread you started, actually

kimboslice99 avatar Jan 25 '25 03:01 kimboslice99