Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
FtpClientConnection.cs
1using System;
2using System.ComponentModel;
4using System.Security.Authentication;
5using System.Security.Cryptography.X509Certificates;
6using System.Text;
7using System.Threading.Tasks;
8using Waher.Events;
13using Waher.Security;
15
17{
22 {
23 private static readonly Random rnd = new Random();
24
25 private readonly Guid id = Guid.NewGuid();
26 private readonly UTF8Encoding encoding = new UTF8Encoding(false, false);
27 private FtpServer server;
28 private BinaryTcpClient client;
29 private readonly IFtpServerPersistenceLayer persistence;
30 private IAccount account = null;
31 private string authId = null;
32 private bool disposed = false;
34 private object tag = null;
35 private bool upgradeToTls = false;
36
45 params ISniffer[] Sniffers)
46 : base(false, Sniffers)
47 {
48 this.client = Client;
49 this.server = Server;
50 this.persistence = Persistence;
51
52 this.client.OnDisconnected += this.Client_OnDisconnected;
53 this.client.OnError += this.Client_OnError;
54 this.client.OnReceived += this.Client_OnReceived;
55 this.client.OnPaused += this.Client_OnPaused;
56 }
57
61 public bool Disposed => this.disposed;
62
66 public Guid ID => this.id;
67
71 public string AuthId => this.authId;
72
76 public IAccount Account => this.account;
77
81 public BinaryTcpClient Client => this.client;
82
86 public object Tag
87 {
88 get => this.tag;
89 set => this.tag = value;
90 }
91
95 public FtpServer Server => this.server;
96
100 public CaseInsensitiveString UserName => this.userName;
101
105 public string RemoteEndPoint => this.client.RemoteEndPoint;
106
110 public bool UpgradeToTls
111 {
112 get => this.upgradeToTls;
113 protected set => this.upgradeToTls = value;
114 }
115
119 public string Protocol => "FTP";
120
121 internal X509Certificate ClientCertificate => this.client.RemoteCertificate;
122 internal bool ClientCertificateValid => this.client.RemoteCertificateValid;
123
127 [Obsolete("Use the DisposeAsync() method.")]
128 public void Dispose()
129 {
130 this.DisposeAsync().Wait();
131 }
132
136 public async virtual Task DisposeAsync()
137 {
138 if (!this.disposed)
139 {
140 ISniffer[] Sniffers = this.Sniffers;
141 if (!(Sniffers is null) && !(this.server is null))
142 {
143 await this.RemoveRange(Sniffers, false);
144 await this.client.RemoveRange(Sniffers, false);
145 }
146
147 this.disposed = true;
148 this.server = null;
149
150 this.client?.DisposeWhenDone();
151 this.client = null;
152 }
153 }
154
155 private async Task<bool> Client_OnReceived(object Sender, bool ConstantBuffer, byte[] Buffer, int Offset, int Count)
156 {
157 try
158 {
159 return await this.ParseIncoming(ConstantBuffer, Buffer, Offset, Count);
160 }
161 catch (Exception ex)
162 {
163 if (!this.disposed)
164 {
165 this.Exception(ex);
166 await this.DisposeAsync();
167 }
168
169 return false;
170 }
171 }
172
178 private async Task Client_OnError(object Sender, Exception Exception)
179 {
180 await this.ErrorAndClose();
181 }
182
186 protected async virtual Task ErrorAndClose()
187 {
188 await this.DisposeAsync();
189 }
190
191 private Task Client_OnDisconnected(object Sender, EventArgs e)
192 {
193 return this.DisposeAsync();
194 }
195
204 protected abstract Task<bool> ParseIncoming(bool ConstantBuffer, byte[] Data, int Offset, int NrRead);
205
206 private async Task Client_OnPaused(object Sender, EventArgs e)
207 {
208 if (this.upgradeToTls)
209 {
210 this.upgradeToTls = false;
211
212 string RemoteEndPoint = this.client.RemoteEndPoint.RemovePortNumber();
213
215 {
216 try
217 {
218 object Bak = await this.BeforeUpgradeToTls();
219
220 await this.client.UpgradeToTlsAsServer(this.server.ServerCertificate, Crypto.SecureTls, ClientCertificates.Optional);
221
222 await this.AfterUpgradeToTls(Bak);
223
224 this.client.Continue();
225 }
226 catch (AuthenticationException ex)
227 {
228 await this.LoginFailure(ex, RemoteEndPoint);
229 }
230 catch (Win32Exception ex)
231 {
232 await this.LoginFailure(ex, RemoteEndPoint);
233 }
234 catch (Exception ex)
235 {
236 this.Exception(ex);
237 await this.ToError(null);
238 }
239 }
240 else
241 await this.ToError(null);
242 }
243 }
244
249 protected abstract Task<object> BeforeUpgradeToTls();
250
255 protected abstract Task AfterUpgradeToTls(object Item);
256
257 private async Task LoginFailure(Exception ex, string RemoteIpEndpoint)
258 {
259 Exception ex2 = Log.UnnestException(ex);
260 LoginAuditor.ReportTlsHackAttempt(RemoteIpEndpoint, "TLS handshake failed: " + ex2.Message, "FTP");
261
262 await this.ToError(null);
263 }
264
265 private async Task ToError(string ClosingCommand)
266 {
267 if (string.IsNullOrEmpty(ClosingCommand))
268 await this.ErrorAndClose();
269 else
270 {
271 await this.BeginWrite(ClosingCommand, async (Sender, e) =>
272 {
273 await this.ErrorAndClose();
274 }, null);
275 }
276 }
277
285 public Task<bool> BeginWrite(string Text, EventHandlerAsync<DeliveryEventArgs> Callback, object State)
286 {
287 if (this.disposed)
288 return Task.FromResult(false);
289
290 return this.client.SendAsync(true, this.encoding.GetBytes(Text), async (Sender, e) =>
291 {
292 this.TransmitText(Text);
293
294 if (!(Callback is null))
295 await Callback.Raise(this, e);
296 }, State);
297 }
298
302 public bool IsEncrypted => this.client.IsEncrypted;
303
309 {
310 this.userName = UserName;
311 this.authId = UserName + "@" + this.server.Domain;
312 }
313
318 public virtual Task SetAccount(IAccount Account)
319 {
320 this.account = Account;
321 this.userName = Account.UserName;
322
323 this.server.PersistenceLayer.AccountLogin(this.userName, this.client.RemoteEndPoint);
324
325 return Task.CompletedTask;
326 }
327
332 public abstract Task<bool> SaslErrorNotAuthorized();
333
338 public abstract Task<bool> SaslErrorAccountDisabled();
339
344 public abstract Task<bool> SaslErrorMalformedRequest();
345
351 public abstract Task<bool> SaslChallenge(string ChallengeBase64);
352
358 public abstract Task<bool> SaslSuccess(string ProofBase64);
359
364 public virtual bool CheckLive()
365 {
366 try
367 {
368 if (this.disposed)
369 return false;
370
371 if (!this.client.Connected)
372 return false;
373
374 // https://msdn.microsoft.com/en-us/library/system.net.sockets.socket.connected.aspx
375
376 bool BlockingBak = this.client.Client.Client.Blocking;
377 try
378 {
379 byte[] Temp = new byte[1];
380
381 this.client.Client.Client.Blocking = false;
382 this.client.Client.Client.Send(Temp, 0, 0);
383
384 return true;
385 }
386 catch (SocketException e)
387 {
388 if (e.NativeErrorCode.Equals(10035)) // WSAEWOULDBLOCK
389 return true;
390 else
391 return false;
392 }
393 finally
394 {
395 this.client.Client.Client.Blocking = BlockingBak;
396 }
397 }
398 catch (Exception)
399 {
400 return false;
401 }
402 }
403
407 public void ResetState()
408 {
409 this.ResetState(!(this.account is null));
410 }
411
416 public virtual void ResetState(bool Authenticated)
417 {
418 }
419
420 internal static int Next(int MaxValue)
421 {
422 lock (rnd)
423 {
424 return rnd.Next(MaxValue);
425 }
426 }
427
428 }
429}
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
static Exception UnnestException(Exception Exception)
Unnests an exception, to extract the relevant inner exception.
Definition: Log.cs:828
Implements a binary TCP Client, by encapsulating a TcpClient. It also makes the use of TcpClient safe...
bool Connected
If the connection is open.
bool RemoteCertificateValid
If the remote certificate is valid.
TcpClient Client
Underlying TcpClient object.
Task< bool > SendAsync(byte[] Packet)
Sends a binary packet.
string RemoteEndPoint
Remote End-point of connection. This corresponds to the IP Endpoint of the remote party in normal cas...
void DisposeWhenDone()
Disposes the client when done sending all data.
void Continue()
Continues reading from the socket, if paused in an event handler.
X509Certificate RemoteCertificate
Certificate used by the remote endpoint.
Task UpgradeToTlsAsServer(X509Certificate ServerCertificate)
Upgrades a server connection to TLS.
bool IsEncrypted
If connection is encrypted or not.
Simple base class for classes implementing communication protocols.
void TransmitText(string Text)
Called when text has been transmitted.
void Exception(Exception Exception)
Called to inform the viewer of an exception state.
ISniffer[] Sniffers
Registered sniffers.
Task RemoveRange(IEnumerable< ISniffer > Sniffers)
Removes a set of sniffers, if registered.
Abstract base class for FTP client connections.
bool UpgradeToTls
If the connection is to be upgraded to TLS.
abstract Task AfterUpgradeToTls(object Item)
Called after upgrading to TLS.
abstract Task< bool > SaslErrorNotAuthorized()
Is called when a failed authentication attempt has been made.
bool IsEncrypted
If the connection is encrypted.
abstract Task< object > BeforeUpgradeToTls()
Called before upgrading to TLS.
virtual async Task DisposeAsync()
Closes the connection and disposes of all resources.
IAccount Account
Account of authenticated user.
virtual Task SetAccount(IAccount Account)
Sets the account for the connection.
abstract Task< bool > SaslErrorAccountDisabled()
Is called when a an authentication attempt has been made using a disabled account.
Task< bool > BeginWrite(string Text, EventHandlerAsync< DeliveryEventArgs > Callback, object State)
Starts sending a text command to the client.
virtual void ResetState(bool Authenticated)
Resets the state of the connection.
string Protocol
String representing protocol being used.
abstract Task< bool > SaslErrorMalformedRequest()
Is called when a an authentication attempt has been made using a malformed request.
void SetUserIdentity(CaseInsensitiveString UserName)
Sets the identity of the user after successful authentication.
void ResetState()
Resets the state of the connection.
virtual bool CheckLive()
Checks if the connection is live.
abstract Task< bool > SaslChallenge(string ChallengeBase64)
Is called when a an authentication challenge has been received.
FtpClientConnection(BinaryTcpClient Client, FtpServer Server, IFtpServerPersistenceLayer Persistence, params ISniffer[] Sniffers)
Class managing a connection.
virtual async Task ErrorAndClose()
Closes the connection due to an error.
object Tag
Tag object. Can be used to maintain states between calls, for instance during authentication.
BinaryTcpClient Client
Underlying TCP connection.
abstract Task< bool > SaslSuccess(string ProofBase64)
Is called when a a successful authentication response has been received.
FtpServer Server
FTP Server serving the client.
abstract Task< bool > ParseIncoming(bool ConstantBuffer, byte[] Data, int Offset, int NrRead)
Parses incoming binary data.
Implements a simple FTP Server, as defined in:
Definition: FtpServer.cs:38
X509Certificate ServerCertificate
Server domain certificate.
Definition: FtpServer.cs:508
CaseInsensitiveString Domain
Domain name.
Definition: FtpServer.cs:503
Represents a case-insensitive string.
static readonly CaseInsensitiveString Empty
Empty case-insensitive string
Helper methods for encrypting and decrypting streams of data.
Definition: Crypto.cs:14
const SslProtocols SecureTls
TLS 1.2 & 1.3
Definition: Crypto.cs:18
Class that monitors login events, and help applications determine malicious intent....
Definition: LoginAuditor.cs:26
static bool CanStartTls(string RemoteEndPoint)
Checks if TLS negotiation can start, for a given endpoint. If the endpoint has tries a TLS hack attem...
static void ReportTlsHackAttempt(string RemoteEndPoint, string Message, string Protocol)
Reports a TLS hacking attempt from an endpoint. Can be used to deny TLS negotiation to proceed,...
Interface for asynchronously disposable objects.
Interface for SMTP user accounts.
Definition: IAccount.cs:11
CaseInsensitiveString UserName
User Name
Definition: IAccount.cs:24
void AccountLogin(CaseInsensitiveString UserName, string RemoteEndPoint)
Successful login to account registered.
Interface for server-side client connections.
Interface for sniffers. Sniffers can be added to ICommunicationLayer classes to eavesdrop on communic...
Definition: ISniffer.cs:10
ClientCertificates
Client Certificate Options