Back to Home

Proxy on C# with FTP authentication

Universal proxy server on C# supports HTTP, HTTPS and FTP with automatic authentication. Intercepts USER/PASS commands, uses asynchronous tunneling. Full code and setup instructions for IT specialists.

Creating FTP proxy on C# with auto-authentication
Advertisement 728x90

Universal C# Proxy Server with FTP Authentication Support

This C# proxy server listens on a local port, detects traffic types, and forwards them with smart handling. For FTP, it automatically injects credentials by intercepting USER/PASS commands and swapping in your login details. It supports HTTP, HTTPS (via CONNECT), and FTP using only built-in TcpClient and NetworkStream—no external libraries needed.

Architecture and Initialization

The server uses TcpListener to monitor the loopback interface on your chosen port. FTP credentials are stored in a ConcurrentDictionary for thread-safe access.

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}");
    }
}

Launch it from Program.Main:

Google AdInline article slot
static async Task Main(string[] args)
{
    var proxy = new FullProxyServer(8888, "ftp.example.com", "my_username", "my_password");
    await proxy.StartAsync();
}

Request Type Detection

After receiving client data, the buffer is analyzed:

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);
}

This routes traffic based on protocol.

Handling HTTPS via CONNECT

For HTTPS, it sets up a direct tunnel:

Google AdInline article slot
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);

Bidirectional stream copying is fully async with CancellationTokenSource for clean shutdowns.

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();
}

FTP Proxy with Auto-Authentication

The star feature: intercepting and tweaking FTP commands. When the client sends USER, it swaps in your username. On the server's 331 (password needed) response, it fires off PASS.

private async Task FtpTunnelWithAuth(NetworkStream clientStream, NetworkStream ftpStream,
                                      string username, string password, string id)
{
    bool authenticated = false;
    
    // Intercept FTP server responses
    if (!authenticated && response.Contains("331")) // Password requested
    {
        string passCmd = $"PASS {password}\r\n";
        await ftpStream.WriteAsync(passBytes);
    }
    
    // Intercept client commands
    if (!authenticated && command.ToUpper().StartsWith("USER"))
    {
        // Swap in our username
        string userCmd = $"USER {username}\r\n";
        await ftpStream.WriteAsync(userBytes);
    }
}

Post-auth (230 response), all commands tunnel unchanged.

Google AdInline article slot

Client Configuration

  • Browser: Set HTTP/HTTPS/FTP proxy to 127.0.0.1:8888
  • FileZilla or similar: Generic Proxy, host 127.0.0.1, port 8888

Logs capture the flow:

[abc12345] FTP request
[abc12345] ->FTP: USER anonymous\r\n
[abc12345] FTP->: 331 User name okay, need password\r\n
[abc12345] Auto-sending password
[abc12345] FTP->: 230 User logged in\r\n
[abc12345] Authentication successful

Key Benefits

This lightweight server fits in a single file and leverages async/await for scalability. Perfect for:

  • Bypassing proxy restrictions in corporate networks
  • Automating FTP access without tweaking client settings
  • Traffic debugging with detailed logging
  • Extending to other protocols via command parsing

Key Takeaways

  • Auto-swaps USER/PASS for FTP without client awareness.
  • Async bidirectional tunneling with NetworkStream.
  • Thread-safe credential storage via ConcurrentDictionary.
  • Full HTTPS CONNECT and basic HTTP support.
  • Cross-platform on .NET 5+.

— Editorial Team

Advertisement 728x90

Read Next