Uniwersalny serwer proxy w C# z obsługą uwierzytelniania FTP
Serwer proxy w C# akceptuje połączenia na lokalnym porcie, określa typ ruchu i przekierowuje go z przetwarzaniem. Dla FTP implementuje się automatyczne podstawianie danych logowania: przechwytywanie poleceń USER/PASS i zamiana na określone login/hasło. Obsługiwane są HTTP, HTTPS (metoda CONNECT) i FTP bez zewnętrznych bibliotek, tylko TcpClient i NetworkStream.
Architektura i inicjalizacja
Serwer używa TcpListener do nasłuchiwania na Loopback na określonym porcie. Dane logowania FTP przechowywane są w ConcurrentDictionary dla bezpiecznego dostępu wielowątkowego.
public class FullProxyServer
{
private readonly TcpListener _listener;
private readonly ConcurrentDictionary<string, string> _ftpCredentials;
public FullProxyServer(int proxyPort, string ftpServer, string ftpUser, string ftpPassword)
{
_listener = new TcpListener(IPAddress.Loopback, proxyPort);
_ftpCredentials = new ConcurrentDictionary<string, string>();
_ftpCredentials.TryAdd(ftpServer, $"{ftpUser}:{ftpPassword}");
}
}
Uruchomienie w Program.Main:
static async Task Main(string[] args)
{
var proxy = new FullProxyServer(8888, "ftp.example.com", "my_username", "my_password");
await proxy.StartAsync();
}
Określenie typu żądania
Po przyjęciu danych od klienta analizowany jest bufor:
string request = Encoding.ASCII.GetString(buffer, 0, bytesRead);
if (request.StartsWith("CONNECT"))
{
await HandleConnectRequest(clientStream, request, id);
}
else if (request.Contains("FTP") || request.Contains("ftp"))
{
await HandleFtpRequest(clientStream, buffer, bytesRead, id);
}
else
{
await TunnelConnection(clientStream, buffer, bytesRead, id);
}
To pozwala kierować ruch według protokołu.
Obsługa HTTPS przez CONNECT
Dla HTTPS ustanawiany jest bezpośredni tunel:
var parts = connectLine.Split(' ');
string host = targetParts[0];
int port = int.Parse(targetParts[1]);
var targetClient = new TcpClient();
await targetClient.ConnectAsync(host, port);
string response = "HTTP/1.1 200 Connection Established\r\n\r\n";
await clientStream.WriteAsync(responseBytes);
await TunnelBidirectional(clientStream, targetStream, id);
Dwukierunkowe kopiowanie strumieni zaimplementowane asynchronicznie z CancellationTokenSource dla płynnego zamykania.
private async Task TunnelBidirectional(NetworkStream clientStream, NetworkStream targetStream, string id)
{
var cts = new CancellationTokenSource();
var clientToTarget = CopyDataAsync(clientStream, targetStream, id, "C->T", cts.Token);
var targetToClient = CopyDataAsync(targetStream, clientStream, id, "T->C", cts.Token);
await Task.WhenAny(clientToTarget, targetToClient);
cts.Cancel();
}
Proxy FTP z automatyczną autoryzacją
Kluczową funkcjonalnością jest przechwytywanie i modyfikacja poleceń FTP. Po otrzymaniu USER od klienta podstawiany jest określony username, na 331 (password required) – wysyłany jest PASS.
private async Task FtpTunnelWithAuth(NetworkStream clientStream, NetworkStream ftpStream,
string username, string password, string id)
{
bool authenticated = false;
// Przechwytujemy odpowiedzi serwera FTP
if (!authenticated && response.Contains("331")) // Żądanie hasła
{
string passCmd = $"PASS {password}\r\n";
await ftpStream.WriteAsync(passBytes);
}
// Przechwytujemy polecenia klienta
if (!authenticated && command.ToUpper().StartsWith("USER"))
{
// Podmieniamy login na nasz
string userCmd = $"USER {username}\r\n";
await ftpStream.WriteAsync(userBytes);
}
}
Późniejsze polecenia tunelowane są bez zmian po udanej autoryzacji (230).
Konfiguracja klientów
- Przeglądarka: Proxy HTTP/HTTPS/FTP — 127.0.0.1:8888
- FileZilla lub analog: Generic Proxy, host 127.0.0.1, port 8888
Logi pokazują proces:
[abc12345] FTP zapytanie
[abc12345] ->FTP: USER anonymous\r\n
[abc12345] FTP->: 331 User name okay, need password\r\n
[abc12345] Automatyczne wysłanie hasła
[abc12345] FTP->: 230 User logged in\r\n
[abc12345] Autoryzacja udana
Zalety implementacji
Serwer lekki, mieści się w jednym pliku, używa async/await do skalowalności. Nadaje się dla:
- Omijania ograniczeń proxy w sieciach korporacyjnych
- Automatyzacji dostępu do FTP bez zmiany konfiguracji klientów
- Debugowania ruchu z logowaniem
- Rozszerzenia o inne protokoły z analizą poleceń
Co jest ważne
- Automatyczne podstawianie USER/PASS dla FTP bez powiadamiania klienta.
- Asynchroniczne dwukierunkowe tunelowanie z NetworkStream.
- Bezpieczne przechowywanie danych logowania w ConcurrentDictionary.
- Obsługa HTTPS CONNECT i prostego HTTP.
- Wieloplatformowość na .NET 5+.
— Editorial Team
Brak komentarzy.