Я хочу поделиться опытом создания универсального прокси-сервера на C#, который поддерживает не только HTTP/HTTPS, но и FTP-протокол с автоматической подстановкой учетных данных. Зачем это нужно? Представьте ситуацию: вам нужно работать с FTP-сервером через корпоративный прокси, но стандартные средства не поддерживают автоматическую аутентификацию. Или вы хотите иметь полный контроль над трафиком между клиентом и FTP-сервером. Мой прокси-сервер решает эти задачи.
Почему именно C# и FTP?
Выбор C# обусловлен несколькими факторами:
Мощная работа с сетевыми потоками (
NetworkStream,TcpClient)Простота реализации многопоточности с
async/awaitКроссплатформенность (.NET Core/.NET 5+)
НУ И ПОТОМУ ЧТО Я НА НЁМ ПИШУ :)
FTP же был выбран потому, что это один из немногих протоколов, где проксирование требует не просто туннелирования, а анализа и подмены команд.
FTP подключение брал с сервера 1gb ru одного моего заказчика.
Архитектура решения
ой прокси-сервер работает на локальном порту (например, 8888) и обрабатывает три типа запросов:
CONNECT метод — для HTTPS трафика
FTP запросы — с автоматической аутентификацией
Обычные HTTP запросы — прямое туннелирование
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}"); } }
Основные компоненты
1. Определение типа запроса
Первый шаг — понять, с каким запросом мы имеем дело:
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); }
2. Обработка HTTPS (CONNECT метод)
Для HTTPS мы просто устанавливаем туннель между клиентом и целевым сервером:
private async Task HandleConnectRequest(NetworkStream clientStream, string request, string id) { 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); }
3. Самое интересное: FTP с подстановкой пароля
Сердце моего решения — автоматическая аутентификация на FTP-сервере:
private async Task FtpTunnelWithAuth(NetworkStream clientStream, NetworkStream ftpStream, string username, string password, string id) { bool authenticated = false; // Перехватываем ответы FTP-сервера if (!authenticated && response.Contains("331")) // Запрос пароля { string passCmd = $"PASS {password}\r\n"; await ftpStream.WriteAsync(passBytes); } // Перехватываем команды клиента if (!authenticated && command.ToUpper().StartsWith("USER")) { // Подменяем логин на наш string userCmd = $"USER {username}\r\n"; await ftpStream.WriteAsync(userBytes); } }
4. Двунаправленное туннелирование
Для эффективной передачи данных я использую асинхронное копирование потоков:
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(); }
Настройка и запуск
Вот как настроить и запустить сервер:
class Program { static async Task Main(string[] args) { var proxy = new FullProxyServer( proxyPort: 8888, ftpServer: "ftp.example.com", ftpUser: "my_username", ftpPassword: "my_password" ); await proxy.StartAsync(); } }
Настройка клиентских приложений

Для браузера:
HTTP прокси:
127.0.0.1:8888HTTPS прокси:
127.0.0.1:8888FTP прокси:
127.0.0.1:8888
Для FTP-клиента (например, FileZilla):
Тип прокси:
Generic ProxyХост:
127.0.0.1Порт:
8888
Как это работает на практике
Допустим, вы подключаетесь к FTP-серверу через обычный FTP-клиент:
Клиент отправляет
USER anonymousПрокси перехватывает команду и заменяет на
USER my_usernameСервер отвечает
331 Password requiredПрокси автоматически отправляет
PASS my_passwordСервер отвечает
230 Login successfulКлиент даже не догадывается о подмене!
Отладка и мониторинг
Мой прокси выводит подробные логи всех операций:
[abc12345] FTP запрос [abc12345] ->FTP: USER anonymous\r\n [abc12345] FTP->: 331 User name okay, need password\r\n [abc12345] Автоматическая отправка пароля [abc12345] FTP->: 230 User logged in\r\n [abc12345] Аутентификация успешна
Заключение
В итоге у меня получился легковесный, но мощный прокси-сервер на C#, который:
Поддерживает HTTP, HTTPS и FTP
Автоматически подставляет учетные данные для FTP
Легко расширяется под новые протоколы
Работает асинхронно и эффективно использует ресурсы
Весь код помещается в один файл, его легко модифицировать под свои нужды. Я использую этот прокси для обхода ограничений корпоративной сети и для автоматизации FTP-операций.
Плюсы решения:
Полный контроль над трафиком
Автоматическая аутентификация
Простота настройки
Кроссплатформенность
