Я хочу поделиться опытом создания универсального прокси-сервера на C#, который поддерживает не только HTTP/HTTPS, но и FTP-протокол с автоматической подстановкой учетных данных. Зачем это нужно? Представьте ситуацию: вам нужно работать с FTP-сервером через корпоративный прокси, но стандартные средства не поддерживают автоматическую аутентификацию. Или вы хотите иметь полный контроль над трафиком между клиентом и FTP-сервером. Мой прокси-сервер решает эти задачи.

Почему именно C# и FTP?

Выбор C# обусловлен несколькими факторами:

  • Мощная работа с сетевыми потоками (NetworkStreamTcpClient)

  • Простота реализации многопоточности с async/await

  • Кроссплатформенность (.NET Core/.NET 5+)

  • НУ И ПОТОМУ ЧТО Я НА НЁМ ПИШУ :)

FTP же был выбран потому, что это один из немногих протоколов, где проксирование требует не просто туннелирования, а анализа и подмены команд.

FTP подключение брал с сервера 1gb ru одного моего заказчика.

Архитектура решения

ой прокси-сервер работает на локальном порту (например, 8888) и обрабатывает три типа запросов:

  1. CONNECT метод — для HTTPS трафика

  2. FTP запросы — с автоматической аутентификацией

  3. Обычные 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:8888

  • HTTPS прокси: 127.0.0.1:8888

  • FTP прокси: 127.0.0.1:8888

Для FTP-клиента (например, FileZilla):

  • Тип прокси: Generic Proxy

  • Хост: 127.0.0.1

  • Порт: 8888

Как это работает на практике

Допустим, вы подключаетесь к FTP-серверу через обычный FTP-клиент:

  1. Клиент отправляет USER anonymous

  2. Прокси перехватывает команду и заменяет на USER my_username

  3. Сервер отвечает 331 Password required

  4. Прокси автоматически отправляет PASS my_password

  5. Сервер отвечает 230 Login successful

  6. Клиент даже не догадывается о подмене!

Отладка и мониторинг

Мой прокси выводит подробные логи всех операций:

[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-операций.

Плюсы решения:

  • Полный контроль над трафиком

  • Автоматическая аутентификация

  • Простота настройки

  • Кроссплатформенность

Кому нужно, вот проект на git

Комментарии (5)


  1. Milanova
    07.04.2026 20:58

    Ваш прокси как-то может помочь в обходе блокировок? Или ваш прокси не для этого.


    1. vova8977 Автор
      07.04.2026 20:58

      Это forward proxy, а не анонимизирующий

      Мой прокси - это прямой прокси-сервер (forward proxy), который:

      • Просто перенаправляет ваш трафик на целевой сервер

      • Не меняет ваш IP-адрес (подключается от вашего имени)

      • Не шифрует трафик (кроме HTTPS, но это стандартное шифрование)

      Мой прокси - это учебный проект и инструмент для отладки, а не средство обхода цензуры.


  1. Vilos
    07.04.2026 20:58

    Представьте ситуацию: вам нужно работать с FTP-сервером через корпоративный прокси, но стандартные средства не поддерживают автоматическую аутентификацию. Или вы хотите иметь полный контроль над трафиком между клиентом и FTP-сервером. Мой прокси-сервер решает эти задачи.

    Три раза перечитал этот фрагмент, но так и не понял что вы пытаетесь решить. Вообще вот никаких проблем не вижу в задаче. Либо вы неясно сформулировали проблематику. В чем проблема и что вы решаете??? Вы не можете аутентифицироваться на корпоративном прокси или на FTP сервере? Что значит полный контроль между клиентом и ftp сервером? а так вы не видите этот трафик? Короче неясна проблема и что вы пытаетесь сделать.

    ps. Увидел в комментариях что это "учебный проект" немного встало на свои места...почему бы об этом не сказать в статье?


    1. vova8977 Автор
      07.04.2026 20:58

      Я думал там по коду все понятно, в следующий раз буду писать детальнее


      1. Vilos
        07.04.2026 20:58

        Дак кто ж будет с вашим кодом разбираться, если даже до этого не дойдут читатели статьи!

        Будет это примерно так: "Таааак.....что тут за статья? ....ага....что-то про сети...а что он тут пытается сделать?....хм....что-то про прокси....а что там не так-то? Ну вот он пишет...ага...что-то с FTP не коннектится у него.....короче непонятное что он тут делает, пойду почитаю что-нибудь интересное"

        Ну и по комментариям смотрите: статья висит уже сутки, а всего 4 каммента....наверное это должно на мысли наводить, что что-то вы не то сделали.

        Уверяю, никто не будет погружаться в ваш код, если неясно, для чего весь ваш этот лунопарк.

        P.S. И кстати первый же комментарий подтверждает мою догадку, что ничего не понятно и @Milanova так же ничего не поняла как и я.

        pps И даааа.....в мире стомильёнов всяких прокси серверов.....например известный squid, чем он вам неугодил и пришлось писать свой?....он какой-то особенный или руки хотелось чем-то занять.