Socks Proxy на C#: асинхронные сокеты
https://papaproxy.net свежие валидные прокси socks5. papaproxy.net |
При разработке сетевых приложений нередко для упрощения архитектуры приложения используется асинхронная модель работы с сокетами. Работа с асинхронной моделью делится на 2 этапа: начало операции и ее завершение. В метод начала операции передается метод, который будет вызван после завершения операции (т.н. callback), а также объект, который будет доступен в callback-методе.
Например, метод:
socket.BeginReceive(buffer, offset, bytesToReceive, SocketFlags.None, ClientToRemote, socket);
после своего завершения вызовет метод ClientToRemote, из которого будет доступен пользовательский объект socket.
Callback-метод должен вызвать метод завершения операции. Для вышеприведенного примера это выглядит следующим образом:
static void ClientToRemote(IAsyncResult state) { Socket socket = (ConnectionState)state.AsyncState; int recievedBytes = socket.EndReceive(state); //Завершаем асинхронную операцию }
В данной статье мы разработаем Socks Proxy сервер V4 – V5 с использованием асинхронных сокетов.
Для упрощения поставленной задачи, реализуем лишь минимальный функционал нашего сервера:
- Cоединение с удаленным узлом по протоколу IPv4
- Проксирование информации от клиента к удаленному узлу и обратно
Такой функционал, как проброс порта, поддержку аутентификации по имени пользователя/паролю, соединение по протоколу IPv6, реализовывать не будем.
Алгоритм работы нашего сервера:
- Принимаем соединение
- Проверяем версию желаемого socks-сервера
- Версия 4:
- Проверяем тип запроса (поддерживаем только соединение с удаленным узлом)
- Устанавливаем соединение с удаленным узлом
- Читаем id клиента
- Проверка и выбор необходимого метода аутентификации (поддерживаем только стандартный метод аутентификации)
- Проверяем тип запроса (поддерживаем только соединение с удаленным узлом)
- Устанавливаем соединение с удаленным узлом
- Проксируем соединение
- Закрываем соединение
Версия 5:
Код класса с комментариями неочевидных моментов:
using System; using System.Net; using System.Net.Sockets; namespace Proxy { class SocksServer { //Класс хранящий состояние для каждого прокрируемого соединения (буферы), сокеты клиента //и удаленного узла, а также колбэк, используемый в вспомогательном методе //BeginReceive(ConnectionState connectionState, int numberOfBytesToReceive, AsyncCallback callback) class ConnectionState { public byte[] ClientReceiveBuffer; public int ClientReceivedBytes; public TcpClient Client; public TcpClient Remote; public byte[] RemoteReceiveBuffer; public AsyncCallback Callback; public ConnectionState(TcpClient tcpClient) { Client = tcpClient; } } delegate void AsyncCallback(ConnectionState state); TcpListener listener; //Старт сервера public void Start(int port) { listener = new TcpListener(IPAddress.Any, port); listener.Start(); listener.BeginAcceptTcpClient(EndAcceptTcpClient, listener); } //Остановка сервера public void Stop() { listener.Stop(); } //При входящем соединении static void EndAcceptTcpClient(IAsyncResult state) { TcpListener listener = (TcpListener)state.AsyncState; try { TcpClient client = listener.EndAcceptTcpClient(state); ConnectionState connectionState = new ConnectionState(client); BeginReceive(connectionState, 1, CheckVersion); } finally { listener.BeginAcceptTcpClient(EndAcceptTcpClient, listener); } } //Вспомогательный асинхронные метод, который начинает операцию получения от //connectionState.Client numberOfBytesToReceive байт static void BeginReceive(ConnectionState connectionState, int numberOfBytesToReceive , AsyncCallback callback) { connectionState.ClientReceiveBuffer = new byte[numberOfBytesToReceive]; connectionState.ClientReceivedBytes = 0; connectionState.Callback = callback; try { connectionState.Client.Client.BeginReceive(connectionState.ClientReceiveBuffer, 0, numberOfBytesToReceive, SocketFlags.None, EndRecieve, connectionState); } catch { } } //Вспомогательный асинхронные метод, который вызывается при получении информации от клиента. //Если кол-во принятых байтов равно numberOfBytesToReceive, переданному в BeginReceive, //то вызывается callback, в противном случае вызывается метод на получение от клиента //(numberOfBytesToReceive-полученныеБайты) байт static void EndRecieve(IAsyncResult state) { ConnectionState connectionState = (ConnectionState)state.AsyncState; int recievedBytes; try { recievedBytes = connectionState.Client.Client.EndReceive(state); } catch { return; } if (recievedBytes == 0) { connectionState.Client.Close(); } else { connectionState.ClientReceivedBytes += recievedBytes; if (connectionState.ClientReceivedBytes == connectionState.ClientReceiveBuffer.Length) { connectionState.Callback.Invoke(connectionState); } else { try { connectionState.Client.Client.BeginReceive(connectionState.ClientReceiveBuffer, connectionState.ClientReceivedBytes, connectionState.ClientReceiveBuffer.Length - connectionState.ClientReceivedBytes, SocketFlags.None, EndRecieve, connectionState); } catch { } } } } //Проверка версии желаемого socks сервера static void CheckVersion(ConnectionState state) { if (state.ClientReceiveBuffer[0] == 4) { BeginReceive(state, 1, SocksV4Request); } else if (state.ClientReceiveBuffer[0] == 5) { BeginReceive(state, 1, SocksV5GetingNumAuth); } else { state.Client.Close(); } } //Отправка ошибки от socks v4 сервера и разрыв соединения static void SocksV4Error(ConnectionState state) { try { state.Client.Client.Send(new byte[] { 0, 0x5b, 0, 0, 0, 0, 0, 0 }); } finally { Disconnect(state); } } //Проверка типа запроса static void SocksV4Request(ConnectionState state) { if (state.ClientReceiveBuffer[0] == 1) // Only TcpIp request enabled { BeginReceive(state, 6, SocksV4RequestIPPort); } else { SocksV4Error(state); } } //Установка соединения с удаленным узлом static void SocksV4RequestIPPort(ConnectionState state) { byte[] ip = new byte[4]; Array.Copy(state.ClientReceiveBuffer, 2, ip, 0, 4); state.Remote = new TcpClient(); try { state.Remote.Connect(new IPAddress(ip), state.ClientReceiveBuffer[0] * 256 + state.ClientReceiveBuffer[1]); } catch { SocksV4Error(state); return; } BeginReceive(state, 1, SocksV4UserID); } //Чтение id клиента static void SocksV4UserID(ConnectionState state) { if (state.ClientReceiveBuffer[0] == 0) { try { state.Client.Client.Send(new byte[] { 0, 0x5a, 0, 0, 0, 0, 0, 0 }); } catch { Disconnect(state); return; } state.RemoteReceiveBuffer = new byte[state.Remote.ReceiveBufferSize]; state.ClientReceiveBuffer = new byte[state.Client.ReceiveBufferSize]; try { state.Remote.Client.BeginReceive(state.RemoteReceiveBuffer, 0, state.RemoteReceiveBuffer.Length, SocketFlags.None, RemoteToClient, state); state.Client.Client.BeginReceive(state.ClientReceiveBuffer, 0, state.ClientReceiveBuffer.Length, SocketFlags.None, ClientToRemote, state); } catch { Disconnect(state); } } else { BeginReceive(state, 1, SocksV4UserID); } } //Отправка ошибки от socks v5 сервера и разрыв соединения static void SocksV5Error(ConnectionState state, byte error) { try { state.Client.Client.Send(new byte[] { 5, error }); } finally { Disconnect(state); } } //Чтение кол-ва желаемых методов аутентификации static void SocksV5GetingNumAuth(ConnectionState state) { if (state.ClientReceiveBuffer[0] == 0) { SocksV5Error(state, 0x07); } else { BeginReceive(state, state.ClientReceiveBuffer[0], SocksV5AuthChecking); } } //Проверка стандартного метода аутентификации static void SocksV5AuthChecking(ConnectionState state) { for (int i = 0; i < state.ClientReceiveBuffer.Length; i++) { if (state.ClientReceiveBuffer[i] == 0) { try { state.Client.Client.Send(new byte[] { 5, 0 }); // Authentication is not required } catch { state.Client.Close(); return; } BeginReceive(state, 4, SocksV5Request); return; } } SocksV5Error(state, 0xFF); // Necessary authentication is not received } //Проверка типа запроса static void SocksV5Request(ConnectionState state) { if (state.ClientReceiveBuffer[0] == 5 && state.ClientReceiveBuffer[2] == 0) { switch (state.ClientReceiveBuffer[1]) { case 0x01: if (state.ClientReceiveBuffer[3] == 0x01) { BeginReceive(state, 6, SocksV5RequestIPPort); } else { SocksV5Error(state, 0x08); //Address type is not supported } break; case 0x02: case 0x03: SocksV5Error(state, 0x05); //Denial of connection break; default: SocksV5Error(state, 0x07); //Command is not supported break; } } else { SocksV5Error(state, 0x07); //Command is not supported } } //Установка соединения с удаленным узлом static void SocksV5RequestIPPort(ConnectionState state) { byte[] ip = new byte[4]; Array.Copy(state.ClientReceiveBuffer, ip, 4); state.Remote = new TcpClient(); try { state.Remote.Connect(new IPAddress(ip), state.ClientReceiveBuffer[4] << 8 + state.ClientReceiveBuffer[5]); } catch { SocksV5Error(state, 0x04); //Host is not available return; } try { state.Client.<