С чего началось
Чуть больше пяти лет назад я жил в студенческом общежитии и доступ к интернету был организован через прокси-сервер (далее прокси). С точки зрения рядовых пользователей это не очень удобно. Большинство программ не умеют работать с прокси, да и возможности последних сильно ограничены. Но как говорится «имеем то, что имеем». К счастью существуют программные продукты призванные подружить другие программы работать с прокси (далее проксификаторы). Одной из таких пользовался и я.
В то время, я и мой друг подсели на игру «Sword of the New World». Игра никак не дружилась с прокси. В основном проблема крылась в системе защиты игры. Были перепробованы доступные проксификаторы, но безрезультатно. Мой друг решил написать свой проксификатор и привлёк меня к этому процессу. О том как работают сокеты и прокси я представления имел и довольно быстро выдал алгоритм «как это должно работать». Друг, вооружившись моим алгоритмом и средой разработки Delphi, написал первую реализацию проксификатора, которая с успехом подружила игру с прокси.
Прошло время. Наша разработка как и исходники потерялись. И как на зло возникла потребность в проксификаторе (ну уж очень фирмы любят раздавать интернет через прокси для своих сотрудников). Делать нечего, пришлось писать с нуля. И так родился проект «azimuth-proxyfier», об устройстве которого и пойдет далее речь. (ссылка на исходники в конце статьи)
Все очень просто
Обратившись к документации по HTTP/1.1 протоколу мы обнаружим замечательный метод CONNECT, введённый для поддержки HTTPS (защищённых соединений с веб-сервером). Работает он следующим образом:
- прокси посылается запрос на подключение к ресурсу (удалённому сокету);
- если вам разрешено (авторизация, …) прокси пытается подключится к указанному ресурсу;
- если все ок, вам присылается положительный ответ. После чего по каналу идут данные между вами и удаленным ресурсом;
Пример диалога (запрос и ответ заканчиваются пустой строкой, а дальше 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 канал между вашим сокетом и удаленным сокетом.
Все что нам надо:
- написать код посылающий запрос на прокси и обрабатывающий ответ. И все это должно стать обработчиком функции connect из библиотеки сокетов.
- мы как то должны загрузить наш код в адресное пространство приложения (которое хотим подружить с прокси) и заставить его работать.
Вариантов реализации выше изложенных пунктов множество. В моем случае целевая платформа 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/
Добавить комментарий