Проксификаторы или как оно работает

от автора

К услугам программ-проксификаторов прибегали многие, но как они работают знают не все. Я расскажу об алгоритме положенному в их основу и практической реализации.

С чего началось

Чуть больше пяти лет назад я жил в студенческом общежитии и доступ к интернету был организован через прокси-сервер (далее прокси). С точки зрения рядовых пользователей это не очень удобно. Большинство программ не умеют работать с прокси, да и возможности последних сильно ограничены. Но как говорится «имеем то, что имеем». К счастью существуют программные продукты призванные подружить другие программы работать с прокси (далее проксификаторы). Одной из таких пользовался и я.

В то время, я и мой друг подсели на игру «Sword of the New World». Игра никак не дружилась с прокси. В основном проблема крылась в системе защиты игры. Были перепробованы доступные проксификаторы, но безрезультатно. Мой друг решил написать свой проксификатор и привлёк меня к этому процессу. О том как работают сокеты и прокси я представления имел и довольно быстро выдал алгоритм «как это должно работать». Друг, вооружившись моим алгоритмом и средой разработки Delphi, написал первую реализацию проксификатора, которая с успехом подружила игру с прокси.

Прошло время. Наша разработка как и исходники потерялись. И как на зло возникла потребность в проксификаторе (ну уж очень фирмы любят раздавать интернет через прокси для своих сотрудников). Делать нечего, пришлось писать с нуля. И так родился проект «azimuth-proxyfier», об устройстве которого и пойдет далее речь. (ссылка на исходники в конце статьи)

Все очень просто

Обратившись к документации по HTTP/1.1 протоколу мы обнаружим замечательный метод CONNECT, введённый для поддержки HTTPS (защищённых соединений с веб-сервером). Работает он следующим образом:

  1. прокси посылается запрос на подключение к ресурсу (удалённому сокету);
  2. если вам разрешено (авторизация, …) прокси пытается подключится к указанному ресурсу;
  3. если все ок, вам присылается положительный ответ. После чего по каналу идут данные между вами и удаленным ресурсом;

Пример диалога (запрос и ответ заканчиваются пустой строкой, а дальше raw данные):

CONNECT 205.188.11.33:443 HTTP/1.1 Connection: Keep-alive Host: 205.188.11.33:443  HTTP/1.1 200 Connection established  <RAWDATA> 

Прокси выступает в качестве моста. А этот канал и есть TCP канал между вашим сокетом и удаленным сокетом.
Все что нам надо:

  1. написать код посылающий запрос на прокси и обрабатывающий ответ. И все это должно стать обработчиком функции connect из библиотеки сокетов.
  2. мы как то должны загрузить наш код в адресное пространство приложения (которое хотим подружить с прокси) и заставить его работать.

Вариантов реализации выше изложенных пунктов множество. В моем случае целевая платформа Windows. В которой присутствует интересная способность системы загружать динамические библиотеки из каталога с приложением, а после из системного. Как правило приложения с сокетами работают через библиотеку ws2_32.dll или wsock32.dll (существует для совместимости с более ранней версией Windows). Вот для этих библиотек и были написаны их «адаптеры» с аналогичными именами и аналогичным набором функций. В действительности в системе присутствует ещё и более высокоуровневая библиотека для работы с сетью wininet.dll и несколько вариантов сокетов Но консерватизм штука сильная и большинство продолжает использовать сокеты Беркли (в Windows это Winsock 1).

В момент загрузки «адаптера», последний загружает «настоящую» библиотеку и начинает транслировать все вызовы функций, за исключением двух. Первая это connect. Наше приложение в действительности ничего не знает о существовании прокси. Оно пытается подключится по прямому IP адресу. И этот адрес передаёт в параметрах. Вот здесь мы и должны произвести всю работу. Наш код производит подключение по другому адресу (по адресу прокси), куда и передаётся настоящий адрес подключения. «Какая же вторая функция?» — спросите вы. Если адрес назначения вводится пользователем в программе (и в некоторых других случаях), то используются доменные имена (к примеру «www.example.org»). Но функция connect не умеет работать с доменными именами. Вот тут нужна функция gethostbyname (с её помощью и происходит преобразование доменных имён в IP адреса), обработку которой мы берём на себя. Здесь мы запоминаем запрошенный адрес в виде доменного имени и возвращаем фейковый адрес. В функции connect делаем обратное преобразование. Передавая запрос на прокси, мы можем указывать адрес как IP, так и доменной имя.

Для всех кто хочет ознакомиться с реализацией, почитать исходный код или просто воспользоваться готовым продуктом, добро пожаловать на страницу проекта http://code.google.com/p/azimuth-proxyfier/ (проект реализован на языке Си, распространяется по лицензии BSD).

Алгоритм справедлив для большинства операционных систем, необходимо разобраться только в способе внедрения кода.

Всем спасибо за внимание.

ссылка на оригинал статьи http://habrahabr.ru/post/176113/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *