C/C++ Миниатюрный сокс-сервер

Николай "GorluM" Андреев

Xakep, номер #054, стр. 054-088-1


(gorlum@real.xakep.ru)

Прокси. Это слово знакомо каждому интернетчику. Его знает любитель поругаться грязными словами в чатах, им пользуются мерзкие кардеры в поисках бесплатного стафа, и уж тем более проксю юзают всеми любимые скрипткидисы, им ведь тоже необходимо иметь анонимность в Сети. А иначе, какие же они "хакеры"? :)

На самом деле полезность прокси бывает разной. Например, у тебя в доме настроена локалка, а на одном из компьютеров есть выход в интернет. Чтобы все остальные компы тоже смогли выползать в инет, тебе придется выполнить одно из следующих действий: поставить NAT, что может сделать далеко не каждый, установить обычный роутер, но для этого понадобится выделить подсеть IP-адресов, и самый распространенный выход - установить проксю. Причем последний вариант - самый простой в реализации. Но ты можешь возразить, сказав, что не весь сетевой софт будет работать с проксей. Доля правды в этом есть, но фактически прокси поддерживает весь популярный софт: mIRC, ICQ, Opera, Internet Explorer. Вряд ли ты сможешь найти известное сетевое приложение без возможности использования прокси. Так что ты будешь устанавливать именно проксю, причем не какой-нибудь WinGate, а собственную реализацию. Да-да, собственную! Этот урок программирования на C/C++ посвящен написанию своего прокси-сервера.

В качестве протокола нашего сервера мы будем использовать socks4. Несложный в реализации и широко распространенный, он оптимален для нашей задачи. А задача наша - написать прозрачный (не анонимный) прокси-сервер, работающий с большинством сетевых приложений. Причем, весить он будет всего 2.5 Кб, что позволит использовать его в любых проектах.

Немного RFC

Идея программы проста до безобразия. Создаем порт-сервер и ждем подключений. При соединении принимаем некоторую информацию от клиента, содержащую версию протокола, ip и порт, на которые необходимо законнектиться. Инфу о пользователе и просьбы пройти аутентификацию мы просто пропускаем. Оно нам не нужно. Если вдруг понадобится, ты сам сможешь это реализовать, почитав RFC1928.

Итак, необходимую информацию мы записываем в структуру tag_SOCKS4_REQUEST:

typedef struct tag_SOCKS4_REQUEST{

unsigned char ucVersion;

unsigned char ucCommand;

WORD wDestPort;

DWORD dwDestIp;

} SOCKS4_REQUEST;

Заметь, IP принимается как DWORD (двойное слово, 4 байта). Чтобы использовать его в функции connect, нам придется преобразовать это значение в тип IN_ADDR. IN_ADDR - это те же 4 байта, только разделенные в структуре. Удобнее всего сделать преобразование с помощью функции memcpy (A,B,C), которая выполняет копирование куска памяти длиной C из A в B, где A - указатель "куда" копировать, а B - "откуда".

Игнорировать ненужную нам информацию, мы будем с помощью дополнительной функции. Она будет читать из порта по байтику до тех пор, пока не придет завершающий символ - '\0'. Вот эта функция:

void FlushRecvBufferUntil(SOCKET s, char condition){

int iReceiveRes; char cDummy;

do iReceiveRes = recv(s, &cDummy, sizeof(cDummy), 0);

while (iReceiveRes != SOCKET_ERROR && iReceiveRes != 0 && cDummy != condition);

Содержание  Вперед на стр. 054-088-2