-
Notifications
You must be signed in to change notification settings - Fork 19
/
FtpServer.cs
167 lines (150 loc) · 6.76 KB
/
FtpServer.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// <copyright file="FtpServer.cs" company="Zhaoquan Huang">
// Copyright (c) Zhaoquan Huang. All rights reserved
// </copyright>
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Zhaobang.FtpServer.Authenticate;
using Zhaobang.FtpServer.Connections;
using Zhaobang.FtpServer.File;
using Zhaobang.FtpServer.Trace;
namespace Zhaobang.FtpServer
{
/// <summary>
/// The class to run an FTP server.
/// </summary>
public sealed class FtpServer
{
private readonly IDataConnectionFactory dataConnFactory;
private readonly IAuthenticator authenticator;
private readonly IFileProviderFactory fileProviderFactory;
private readonly IControlConnectionSslFactory controlConnectionSslFactory;
private readonly FtpTracer tracer = new FtpTracer();
private IPEndPoint endPoint;
private TcpListener tcpListener;
/// <summary>
/// Initializes a new instance of the <see cref="FtpServer"/> class
/// with <see cref="SimpleFileProviderFactory"/>, <see cref="LocalDataConnectionFactory"/>,
/// and <see cref="AnonymousAuthenticator"/>.
/// </summary>
/// <param name="endPoint">The local end point to listen, usually 0.0.0.0:21.</param>
/// <param name="baseDirectory">The directory to provide files.</param>
public FtpServer(IPEndPoint endPoint, string baseDirectory)
: this(endPoint, new SimpleFileProviderFactory(baseDirectory), new LocalDataConnectionFactory(), new AnonymousAuthenticator())
{
}
/// <summary>
/// Initializes a new instance of the <see cref="FtpServer"/> class.
/// The server uses custom file, data connection, and authentication provider.
/// </summary>
/// <param name="endPoint">The local end point to listen, usually 0.0.0.0:21.</param>
/// <param name="fileProviderFactory">The <see cref="IFileProviderFactory"/> to use.</param>
/// <param name="dataConnFactory">The <see cref="IDataConnectionFactory"/> to use.</param>
/// <param name="authenticator">The <see cref="IAuthenticator"/> to use.</param>
public FtpServer(
IPEndPoint endPoint,
IFileProviderFactory fileProviderFactory,
IDataConnectionFactory dataConnFactory,
IAuthenticator authenticator)
: this(endPoint, fileProviderFactory, dataConnFactory, authenticator, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="FtpServer"/> class.
/// The server uses custom file, data connection, and authentication, and control connection SSL provider.
/// </summary>
/// <param name="endPoint">The local end point to listen, usually 0.0.0.0:21.</param>
/// <param name="fileProviderFactory">The <see cref="IFileProviderFactory"/> to use.</param>
/// <param name="dataConnFactory">The <see cref="IDataConnectionFactory"/> to use.</param>
/// <param name="authenticator">The <see cref="IAuthenticator"/> to use.</param>
/// <param name="controlConnectionSslFactory">The <see cref="IControlConnectionSslFactory"/> to upgrade control connection to SSL.</param>
public FtpServer(
IPEndPoint endPoint,
IFileProviderFactory fileProviderFactory,
IDataConnectionFactory dataConnFactory,
IAuthenticator authenticator,
IControlConnectionSslFactory controlConnectionSslFactory)
{
this.endPoint = endPoint;
tcpListener = new TcpListener(endPoint);
this.fileProviderFactory = fileProviderFactory;
this.dataConnFactory = dataConnFactory;
this.authenticator = authenticator;
this.controlConnectionSslFactory = controlConnectionSslFactory;
tracer.CommandInvoked += Tracer_CommandInvoked;
tracer.ReplyInvoked += Tracer_ReplyInvoked;
}
/// <summary>
/// Gets the instance of <see cref="FtpTracer"/> to trace FTP commands and replies.
/// </summary>
public FtpTracer Tracer => tracer;
/// <summary>
/// Gets the manager that provides <see cref="IDataConnectionFactory"/> for each user.
/// </summary>
internal IDataConnectionFactory DataConnector { get => dataConnFactory; }
/// <summary>
/// Gets the manager that authenticates user.
/// </summary>
internal IAuthenticator Authenticator { get => authenticator; }
/// <summary>
/// Gets the manager that provides <see cref="IFileProviderFactory"/> for each user.
/// </summary>
internal IFileProviderFactory FileManager { get => fileProviderFactory; }
/// <summary>
/// Gets the factory to upgrade control connection to an encrypted one. May be null.
/// </summary>
internal IControlConnectionSslFactory ControlConnectionSslFactory => controlConnectionSslFactory;
/// <summary>
/// Start the FTP server.
/// </summary>
/// <param name="cancellationToken">Token to stop the FTP server.</param>
/// <returns>The task that waits until the server stops.</returns>
public async Task RunAsync(CancellationToken cancellationToken)
{
try
{
tcpListener.Start();
cancellationToken.Register(() => tcpListener.Stop());
while (true)
{
TcpClient tcpClient;
try
{
tcpClient = await tcpListener.AcceptTcpClientAsync().WithCancellation(cancellationToken);
}
catch (OperationCanceledException)
{
return;
}
try
{
ControlConnection handler = new ControlConnection(this, tcpClient);
var result = handler.RunAsync(cancellationToken);
}
catch (Exception)
{
tcpClient.Dispose();
}
}
}
finally
{
tcpListener.Stop();
}
}
private static void Tracer_ReplyInvoked(string replyCode, IPEndPoint remoteAddress)
{
System.Diagnostics.Debug.WriteLine($"{remoteAddress}, reply, {replyCode}");
}
private static void Tracer_CommandInvoked(string command, IPEndPoint remoteAddress)
{
System.Diagnostics.Debug.WriteLine($"{remoteAddress}, command, {command}");
}
}
}