Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Лабораторная работа №8 Кэширующий http прокси-сервер и socks5 прокси-серверСодержание книги
Поиск на нашем сайте
Цель работы: написать консольные приложения- кэширующий HTTP прокси-сервер и SOCKS5 прокси-сервер; HTTP Кэширующий Прокси HTTP прокси реализуется в соответствии c HTTP протоколом и позволяет клиентам получать через себя доступ к веб серверам, таким образом, клиент по HTTP протоколу отправляет прокси- серверу те же HTTP запросы, какие бы он посылал на требуемый сервер. Задача прокси -сервера транслировать эти запросы на запрашиваемый сервер и осуществлять транспорт HTTP трафика через себя клиенту, при этом прокси сервер может изменять параметры HTTP запросов клиентов и ответа сервера и транслировать клиенту модифицированные объекты и заголовки ответа. Задача кэширующего прокси сервера сохранять проходящие через него страницы и объекты, и если клиенты запросили объект, который уже был сохранен на сервере, то сразу отправлять этот объект клиенту не скачивая его с веб сервера в случае если этот объект на веб сервере не менялся, при этом если объект на веб сервере обновился, информацию об этом необходимо получать с помощью заголовков if-modified-since, if-none-match. В языках программирования уже есть готовые компоненты для работы с HTTP сервером, которые позволяет переопределять HTTP обработчик запросов и получать доступ к передаваемым данным, полям запроса, анализировать их и делать новый запрос к серверу в качестве HTTP клиента. Рекомендуемая литература: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html http://ru.wikipedia.org/wiki/%D0%97%D0%B0%D0%B3%D0%BE%D0%BB%D0%BE%D0%B2%D0%BA%D0%B8_HTTP http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.ifmodifiedsince(v=vs.71).aspx http://msdn.microsoft.com/ru-ru/library/system.net.httpwebrequest(v=vs.90).aspx
Пример, C#: Класс HttpWebRequest предоставляет поддержку свойств и методов, определенных в классе WebRequest, а также дополнительных свойств и методов, позволяющих пользователю взаимодействовать непосредственно с серверами, используя протокол HTTP. Свойство HttpWebRequest.IfModifiedSince хранит информацию из заголовка if-modified-since. Пример использования: // Create a new 'Uri' object with the mentioned string. Uri myUri =new Uri("http://www.contoso.com"); // Create a new 'HttpWebRequest' object with the above 'Uri' object. HttpWebRequest myHttpWebRequest= (HttpWebRequest)WebRequest.Create(myUri); // Create a new 'DateTime' object. DateTime today= DateTime.Now; if (DateTime.Compare(today,myHttpWebRequest.IfModifiedSince)==0) { // Assign the response object of 'HttpWebRequest' to a 'HttpWebResponse' variable. HttpWebResponse myHttpWebResponse=(HttpWebResponse)myHttpWebRequest.GetResponse(); Console.WriteLine("Response headers \n{0}\n",myHttpWebResponse.Headers); Stream streamResponse=myHttpWebResponse.GetResponseStream(); StreamReader streamRead = new StreamReader(streamResponse); Char[] readBuff = new Char[256]; int count = streamRead.Read(readBuff, 0, 256); Console.WriteLine("\nThe contents of Html Page are: \n"); while (count > 0) { String outputData = new String(readBuff, 0, count); Console.Write(outputData); count = streamRead.Read(readBuff, 0, 256); } // Close the Stream object. streamResponse.Close(); streamRead.Close(); // Release the HttpWebResponse Resource. myHttpWebResponse.Close(); Console.WriteLine("\nPress 'Enter' key to continue................."); Console.Read(); } else { Console.WriteLine("\nThe page has been modified since "+today); } Для создания прокси -сервера используйте библиотеку System.Net.Sockets.
SOCKS5 прокси-сервер Сокс сервер просто пробрасывает через себя TCP и UDP трафик. В данном приложении необходимо реализовать проброску tcp, используя протокол SOCKS5. С его помощью можно решать самые разные задачи: организовывать защищенный доступ к службам, расположенным за межсетевым экраном (firewall), скрывать свой истинный IP- адрес во время работы с недружелюбными сетевыми ресурсами или реализовать универсальный прокси-сервер, поддерживающий любые протоколы прикладного уровня (HTTP, FTP, POP3/SMTP, ICQ и т.д.). Основная задача данного протокола – внедрить в «нормальный» процесс обмена данными некоего посредника, называемого SOCKS-сервером или SOCKS-прокси. Когда клиент (поддерживающее SOCKS приложение: web-браузер Mozilla, клиент ICQ Miranda IM и др., см. ниже) желает отправить какую-либо информацию по сети, он устанавливает соединение не с реальным адресатом, а с SOCKS-сервером, который, в свою очередь, пересылает данные по назначению, но уже от своего имени. С точки зрения «настоящего» сервера (например, web-узла, который пользователь желает просмотреть в Mozilla Firefox) SOCKS-прокси является самым обыкновенным клиентом. Таким образом, сущность (IP-адрес) истинного клиента оказывается скрытой от обслуживающего его сервера. Это весьма удобное обстоятельство таит в себе потенциальную опасность (можете спрятаться вы, но ведь могут и от вас), поэтому реально существующие SOCKS-сервера имеют развитые схемы контроля доступа (запрет входящих и исходящих соединений по заданному перечню адресов) и поддерживают авторизацию пользователей по паролю (см. ниже). Отметим, что коль скоро SOCKS работает на более низком по сравнению с прикладным (а именно, транспортном) уровне модели OSI, его поддержка не потребует никаких изменений в логике работы клиента, а тем более – сервера. Действительно, все что нужно – это модифицировать реализацию функций, отвечающих за создание сетевого подключения и отправку данных: connect(), bind(), send() и т.п. На практике это обычно достигается перехватом системных вызовов с их последующей подменой поддерживающими SOCKS пользовательскими аналогами. Никаких правок в исходном коде клиентских приложений, а тем более самого доступа к исходным текстам, как правило, не требуется. Эта мощная процедура известна как «соксификация» и будет подробно рассмотрена ниже. Теперь, когда мы получили общее представление о SOCKS, можно перейти к болеедетальному рассмотрению данного протокола. Спецификация SOCKS5 Протокол SOCKS5 подробно описан в RFC1928. Как уже отмечалось ранее, SOCKS5 является протоколом транспортного уровня. Его «соседи» – TCP и UDP непосредственно используются для передачи данных, поступающих с прикладного уровня (от пользовательских приложений), а значит, SOCKS-прокси должен уметь корректно работать с каждым из них. Отметим также, что протокол ICMP, используемый утилитами ping и traceroute, находится ниже транспортного уровня, а потому соксификации, к сожалению, не поддается. Прежде чем отправлять какие-либо данные, клиент должен пройти процедуру авторизации на SOCKS-сервере. Для этого он открывает TCP-соединение с портом 1080 (значение по умолчанию) SOCKS-сервера и отправляет по нему сообщение, содержащее кодовые номера поддерживаемых им методов аутентификации. SOCKS-сервер выбирает один из методов по своему усмотрению и сообщает его номер клиенту. аутентификация может отсутствовать (на практике это скорее всего означает, что SOCKS-сервер различает клиентов по их IP-адресам) или производиться на основании имени пользователя и пароля. В последнем случае возможно большое количество различных вариантов, от тривиального «Username/Password Authentication» (RFC 1929) предусматривающего передачу пароля в открытом виде до куда более безопасного CHAP (зашифрованный пароль, открытые данные) и GSSAPI (RFC 1961), которое может использоваться для полной криптографической защиты трафика. После успешной авторизации клиент получает возможность посылать запросы (команды), устанавливать исходящие соединения и даже принимать входящие. Установка исходящего TCP-соединения Для установки исходящего TCP-соединения, клиент отправляет SOCKS-серверу запрос «CONNECT», в котором указывает адрес и порт доставки. Для идентификации узла- получателя могут использоваться как IP-адреса (поддерживаются IPv4/IPv6), так и полноценные доменные имена. В последнем случае SOCKS-сервер берет на себя заботу по их разрешению, так что сеть, в которой работает клиент, в принципе может обходиться и без DNS-сервера. В ответном сообщении SOCKS-сервер сообщает код ошибки (как обычно, 0 обозначает, что операция прошла успешно), а также IP-адрес (BND.ADDR) и TCP-порт (BND.PORT), которые будут использоваться для фактической связи с запрошенным узлом. Поскольку SOCKS-сервера, как правило, имеют более одного сетевого интерфейса, данный IP-адрес может отличаться от того, с которым было установлено управляющее соединение. После этого клиент открывает новый TCP-сеанс с BND.ADDR:BND.PORT и осуществляет отправку данных. Исходящее TCP-соединение разрывается одновременно с закрытием управляющей сессии. Заметим, что запрос «CONNECT» может быть отклонен SOCKS- прокси, если адреса источника (клиента) или получателя (сервера) запрещены. Установка исходящего UDP-соединения В отличие от потокового протокола TCP, подразумевающего установку сеанса, протокол UDP является датаграммным, а потому несколько более сложным в обращении. Его поддержка появилась лишь в SOCKS5. Перед отправкой UDP-датаграмм клиент запрашивает у SOCKS-сервера UDP-ассоциацию, используя для этого команду «UDP ASSOCIATE». UDP-ассоциация – это своего рода виртуальный сеанс между клиентом и SOCKS-сервером. В исходящем запросе клиент указывает предполагаемые адрес и порт, которые будут выступать в качестве источника будущих UDP-датаграмм. Если на момент установки UDP-ассоциации эта информация еще не известна, клиент должен использовать комбинацию 0.0.0.0:0 (или, скажем, x.x.x.x:0, если неизвестен только номер порта). В ответном сообщении SOCKS-сервер указывает IP-адрес (BND.ADDR) и UDP-порт (BND.PORT), на которые следует направлять исходящие датаграммы. При этом адрес и порт их реального получателя указываются прямо в теле (можно сказать, что имеет место UDP-инкапсуляция). Эти параметры, наряду с адресом и портом отправителя используются для принятия решения о допустимости отправки датаграммы. это создает дополнительную нагрузку на SOCKS-сервер: правила фильтрации необходимо применять к каждой UDP-датаграмме, тогда как в случае TCP-соединения его легитимность оценивается один раз, в момент исполнения SOCKS-сервером команды «CONNECT». Согласно требованиям стандарта, SOCKS-сервер должен следить за тем, чтобы IP-адрес отправителя датаграммы совпадал с адресом узла, создавшего UDP-ассоциацию. UDP-ассоциация разрушается одновременно с закрытием управляющего TCP-сеанса, в рамках которого была послана команда «UDP ASSOCIATE». Многие из существующих SOCKS-серверов испытывают серьезные проблемы, если между ними и клиентом, запросившим UDP-ассоциацию, располагается межсетевой экран с функцией NAT (Network Address Translation). Причина этого кроется в изменении адреса и порта отправителя, которое происходит в тот момент, когда UDP-датаграмма пересекает межсетевой экран. Как следствие, сервер и ничего не подозревающее клиентское приложение начинают говорить на разных языках: предполагаемый адрес и порт источника, указанные в команде «UDP ASSOCIATE» перестают соответствовать реальным параметрам получаемых SOCKS-сервером датаграмм. В результате они оказываются отброшенными как не принадлежащие UDP-ассоциации. Проблему можно было бы решить, указав в качестве предполагаемого источника 0.0.0.0:0 (см. выше), что должно интерпретироваться SOCKS- сервером как «любая UDP-датаграмма, пришедшая с того же адреса, что и команда на создание ассоциации». К сожалению, большинство из реально существующих SOCKS-серверов трактуют стандарт более узко и не позволяют одновременно установить в ноль и предполагаемый адрес, и порт отправителя. Из протестированных автором реализаций описанный здесь «фокус с пробросом UDP через NAT» позволяет проделать лишь одна –Dante. Прием входящих соединений Эта достаточно оригинальная возможность может оказаться полезной в случаях, когда клиент и «настоящий» сервер в описанной выше схеме меняются местами, что может произойти, например, в протоколах типа FTP. В целях дальнейшего рассмотрения будем предполагать, что между “клиентом” (стороной, собирающейся принять входящее соединение) и “сервером” (стороной, инициирующей входящее соединение) уже установлен “прямой” канал связи при помощи команды “CONNECT”. Для открытия “обратного” канала “клиент” должен послать SOCKS-серверу команду “BIND”, указав в ее параметрах IP-адрес и порт, которые будут использоваться им для приема входящего соединения. В ответ на это SOCKS- сервер сообщает IP-адрес и порт, выделенные им для поддержания “обратного” канала. Предполагается, что «клиент» передаст эти параметры «серверу», используя средства, предоставляемые протоколами прикладного уровня (например, команду «PORT» протокола FTP). После того, как SOCKS-сервер примет (или отбросит) входящее соединение, он повторно уведомляет об этом «клиента», сообщая ему IP-адрес и порт, используемые “сервером”. Отметим, что прием входящих соединений может осуществлять лишь приложение, разработчики которого позаботились о поддержке SOCKS еще на этапе проектирования. В противном случае (если приложение работает с SOCKS-сервером через программу-соксификатор), оно не сможет предоставить корректную информацию об адресе ожидающего “обратной связи” сокета (т.е. сформирует неверную команду “PORT” в рассмотренном выше примере с FTP). «Цепочки» SOCKS Архитектура протокола SOCKS5 позволяет легко объединять SOCKS-сервера в каскады, или, как их еще называют «цепочки» («chains»). Примечательно, что все необходимые для этого действия могут быть произведены на стороне клиента. К «звеньям» цепочки предъявляется единственное требование: они должны «доверять» друг другу (т.е. допускать установку входящих и исходящих соединений). Если образующие каскад SOCKS-сервера не являются анонимными (т.е. используют схемы аутентификации Username/Password, CHAP или подобные), необходимо также, чтобы пользователь мог успешно пройти процедуру авторизации на каждом из них. Предположим, что у нас имеется набор из N SOCKS-серверов с именами socks1, socks2,...,socksN, удовлетворяющих всем вышеперечисленным требованиям. Тогда для создания каскада клиент может поступить следующим образом: В случае исходящего TCP-соединения: клиент подключается к socks1, проходит (при необходимости) процедуру авторизации и посылает команду «CONNECT», указав в качестве адреса доставки socks2. Выполняя этот запрос, socks1 создаст новое соединение с socks2 и будет исправно передавать всю идущую по нему информацию клиенту, при этом socks2 не будет даже догадываться, с кем он общается на самом деле. Далее процедура повторяется до тех пор, пока не будет установлено соединение между socks(N-1) и socksN. Последний сервер каскада подключается к непосредственно узлу, который интересует клиента. Передача данных происходит в обычном режиме: клиент отправляет пакет на сервер socks1, который, в свою очередь, передает его socks2,... и так до тех пор, пока не будет достигнут конечный узел. В случае исходящего UDP-соединения: клиент подключается к socks1, проходит процедуру авторизации и последовательно посылает две команды: “CONNECT” (адрес доставки – socks2) и “UDP ASSOCIATE”. Таким образом, создаются два новых соединения: виртуальный UDP-канал между клиентом и socks1, а также TCP-сессия между socks1 и socks2. Используя эту TCP-сессию, клиент (от имени socks1) посылает команду “UDP ASSOCIATE” на сервер socks2 (открывает UDP-канал между socks1 и socks2) и “CONNECT” на сервер socks3. Процедура продолжается до тех пор, пока между всеми SOCKS-серверами каскада не будут установлены виртуальные UDPканалы. Чтобы отослать какие-либо данные, клиент предварительно производит N-кратную инкапсуляцию UDP-датаграммы, указывая в качестве адреса доставки последовательно: socks1, socks2, socks3, socksN и адрес реального получателя, а затем отправляет ее на сервер socks1. Отметим, что на практике данный вариант каскадирования встречается крайне редко. Это связано с тем, что SOCKS-сервера, как и NAT Firewall'ы, могут изменить порт источника датаграммы, что приведет к проблемам, подробно описанным в разделе “Установка исходящего UDP-соединения”. Используя цепочки SOCKS-серверов, не требующих аутентификации, клиент может значительно повысить анонимность работы в Интернете (см. эпиграф). В Сети можно найти множество программ, реализующих описанные здесь схемы. Таковыми, например, являются SocksChain (http://www.ufasoft.com/socks/) для Windows или ProxyChains (http://proxychains.sourceforge.net) для Unix. Каскадирование SOCKS-серверов является также неотъемлемой частью некоторых соксификаторов, в первую очередь, FreeCap (http://www.freecap.ru/). SOCKS-сервера Теперь, когда принципы работы SOCKS-сервера нам хорошо знакомы, пора переходить от теории к практике. В мире существует большое количество программ, реализующих протокол SOCKS5. Они охватывают все популярные операционные системы (Unix, Windows,...) и способы распространения (freeware, shareware, open-source и т.д.). Здесь мы вкратце рассмотрим наиболее известные (или интересные с точки зрения автора) реализации. Начнем, пожалуй, с SOCKS5 Reference Implementation (http://www.socks.permeo.com/), выполненной компанией NEC и принадлежащей в настоящий момент фирме Permeo. Текущая версия имеет номер 1.0r11 и датирована августом 2000 года. Как легко догадаться по названию, этот сервер является справочной реализацией протокола и, вообще говоря, не предназначен для промышленного использования. Однако, по не очень понятным мне причинам, он был включен в порты FreeBSD, а посему является де-факто стандартом на данной платформе. Продукт имеет поддержку GSSAPI и распространяется в исходных текстах, но по несвободной лицензии. Коммерческое применение данного сервера запрещено. Dante, разработанный норвежской компанией Inferno Nettverk, особенно популярен среди сторонников Linux. Продукт развивается, хотя и не очень бурно (последняя версия, 1.1.15, датирована 31 января 2005 года) и вполне пригоден для практического применения. Как уже упоминалось ранее, Dante позволяет корректно работать с UDP-ассоциациями даже в том случае, если они проходят через NAT Firewall. Программа распространяется в исходных текстах по лицензии BSD. В состав Dante входит библиотека для прозрачной соксификации Unix-приложений (см. ниже) Antinat (http://antinat.sourceforge.net/) - динамично развивающийся, но еще не завершенный открытый продукт, работающий на платформах Unix и Windows. Поддерживает интересные схемы аутентификации, например CHAP или HMAC-MD5. Умеет работать с цепочками SOCKS-серверов. В комплект поставки входит библиотека для разработки пользовательских приложений, поддерживающих SOCKS. Распространяется в исходных текстах. Лицензирован по GPL. Среди других разработок следует упомянуть отечественный 3proxy (http://security.nnov.ru/soft/3proxy/), имеющий родную поддержку Unix и Win32, Delegate (http://www.delegate.org) и коммерческий SOCKS-сервер Hopster (http://www.hopster.com) для Microsoft Windows. Приведенный здесь список далеко не полон. Думается, что читателю не составит труда найти и другие сервера. Для этого можно воспользоваться поисковыми панелями репозитариев Freshmeat (http://www.freshmeat.net) или SourceForge (http://www.sf.net), а также поисковыми машинами общего назначения. Учитывая многообразие существующих SOCKS-серверов, мы не будем подробно останавливаться на процедуре их настройки. Вся необходимая для этого информация может быть найдена в сопроводительной документации к конкретному программному продукту. Как правило, SOCKS-сервера имеют один или несколько конфигурационных файлов, позволяющих указать предпочтительные схемы авторизации и, что более важно, ограничить доступ к сервису по списку IP-адресов. Не пренебрегайте этой возможностью! Неправильно настроенный SOCKS-прокси может быть использован злоумышленниками для рассылки спама через корпоративный почтовый сервер (с точки зрения которого SOCKS-прокси – это один из установленных в фирме компьютеров, поэтому отправка почты с его IP-адреса, скорее всего, не будет запрещена) и выполнения других антиобщественных действий. SOCKS-клиенты Некоторые настольные приложения обладают встроенной поддержкой SOCKS. В их число входят web-браузеры, построенные на коде Mozilla и ряд клиентов сетей мгновенного обмена сообщениями (Miranda IM, Mirabilis ICQ). Для включения поддержки SOCKS в этих программах достаточно указать необходимые параметры в файле или диалоге настроек. Но что делать в случае, если приложение не умеет работать с SOCKS напрямую (классическим примером является Microsoft Internet Explorer)? Существует несколько вариантов решения данной проблемы. Если исходные тексты приложения доступны, можно собрать их заново, используя готовые клиентские библиотеки, например, входящие в состав Dante или Antinat. Однако, как уже упоминалось выше, наличие “исходников” не является обязательным требованием. Откомпилированное приложение можно заставить работать с SOCKS «обманным путем» при помощи программ- соксификаторов, подменяющих стандартные функции для работы с сокетами их аналогами, поддерживающими SOCKS. Так, модифицированная функция connect(), устанавливающая соединение с заданным узлом на самом деле отсылает команду «CONNECT» на адрес указанного пользователем SOCKS-сервера, а «соксифицированная» функция sendto() выполняет инкапсуляцию UDP-датаграммы и отправляет ее, используя заранее установленную UDP-ассоциацию. Процедура подмены функций существенно зависит от типа операционной системы. Так, в мире Windows для этих целей применяются «вирусные» методики. Например, соксификатор может запускать указанный пользователем процесс в режиме «Suspend», после чего внедрять в его память код, загружающий специальную DLL-библиотеку, перехватывающую обращения к API-вызовам LoadLibrary/GetProcAddress, ответственным за подключение сторонних DLL и поиск в них экспортируемых функций. После этого соксификатор отслеживает момент загрузки wsock32.dll и подменяет запрашиваемые адреса функций Winsock указателями на их SOCKS-аналоги. В мире Unix, как это часто случается, все обстоит значительно проще. Динамический компоновщик ld.so использует специальную переменную окружения, LD_PRELOAD, а также файл /etc/ld.so.preload, чтобы определить список разделяемых библиотек, подлежащих предварительной загрузке до непосредственно запрашиваемых исполняемым файлом. Поскольку большая часть современных приложений использует динамическую компоновку, соксифицированные аналоги сетевых функций, оформленные в виде разделяемой библиотеки, перечисленной в LD_PRELOAD, будут найдены и использованы вместо стандартных вызовов, определенных в glibc. Этот метод, очевидно, не будет работать для приложений, использующих статическое связывание с glibc. Встречаться с таковыми (кроме низкоуровневых системных утилит) автору не приходилось. Кроме того, значение переменной LD_PRELOAD обрабатывается особым образом для исполняемых файлов, имеющих бит SUID. Среди клиентских (настольных) приложений они, как правило, не встречаются. Любая ли программа поддается соксификации описанными выше методами? Как легко видеть по ходу рассуждения, нет. Процедура внедрения кода Windows-соксификатора в чужой процесс может не сработать, если исполняемый файл имеет особую структуру, например, он сжат оригинальным образом или зашифрован. Кроме этого, пользовательские SOCKS-аналоги должны максимально точно повторять поведение (в том числе, эмулировать недокументированные возможности) родных функций операционной системы, что, согласитесь, не всегда легко достижимо. Чтобы не быть голословными, приведем примеры конкретных программ-соксификаторов. В среде Windows можно использовать SocksCap (http://www.socks.permeo.com/) от все той же фирмы Permeo (и с все той же неудобной лицензией, ограничивающей коммерческое применение) или открытый (распространяющийся по GPL) продукт FreeCap (http://www.freecap.ru), написанный нашим соотечественником Максимом Артемьевым. В последнем случае в вашем распоряжении окажутся также исходные тексты на языке Object Pascal (Delphi), с помощью которых вы сможете глубоко разобраться во всех обсуждаемых в статье вопросах, от процедуры внедрения в чужой процесс до точного формата SOCKS- сообщений. В Unix можно воспользоваться сценарием оболочки socksify, входящим в состав Dante. Наконец, рассмотрим особый случай - программы, обеспечивающие соксификацию всей системы целиком. В Windows этого можно достичь, подменив на диске файл wsock32.dll или более корректным образом, используя спецификацию Winsock Service Provider. В Unix достаточно добавить команду, устанавливающую значение переменной LD_PRELOAD в один из стартовых сценариев (например, rc.local) или внести изменения в файл /etc/ld.so.preload. Примером такого “общесистемного соксификатора” могут служить WideCap Максима Артемьева (пребывающий пока в состоянии бета-версии) или Permeo Security Driver. Вместо заключения Вот и подошло к концу наше повествование. Теперь, когда вы хорошо представляете себе, что такое SOCKS и с чем его едят, вам не составит труда придумать тысячу и одно применение данному протоколу. Например, на базе SOCKS-сервера можно организовать шлюз для доступа из локальной сети организации в Интернет. Это будет особенно удобно в случае, когда использование традиционного HTTP-прокси не может дать требуемых результатов (например, необходимо реализовать поддержку дополнительных протоколов, не связанных с web), а открывать полноценный доступ через NAT почему-либо представляется нецелесообразным. Предлагаемую схему можно инвертировать и возложить на SOCKS- сервер функции “стража”, допускающего выделенных пользователей (скажем, находящихся в командировке сотрудников службы технической поддержки) к внутренним ресурсам компании (корпоративной базе данных). Можно... да мало ли чего можно придумать, имея необходимые знания и смекалку? Дерзайте! Таблица 1. Некоторые методы SOCKS-аутентификации Код Название метода 0x00 No Authentication Required (Аутентификация отсутствует) 0x01 GSSAPI (RFC 1961) 0x02 Username/Password (RFC 1929) 0x03 Challenge-Handshake Authentication Method (CHAP) 0x05 Challenge-Handshake Authentication Method (CRAM) Рекомендуемая литература: http://ru.wikipedia.org/wiki/SOCKS http://msdn.microsoft.com/ru-ru/library/system.net.sockets.tcplistener.aspx http://msdn.microsoft.com/ru-ru/library/system.net.sockets.tcpclient.aspx http://rfc2.ru/1928.rfc
Пример, C#: Для реализации приложения используйте классы: TcpListener- Прослушивает подключения от TCP-клиентов сети. TcpClient - Предоставляет клиентские подключения для сетевых служб протокола TCP. TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), 1080); server.Start(); TcpClient client = server.AcceptTcpClient();//Ожидаем подключения от браузера
Проверяем наш браузер на то, с каким протоколом он работает:
byte SOCKS_VERSION = 5; byte SOCKS_NOAUTH = 0; byte SOCKS_REPLYSUCCESS = 0; byte SOCKS_IPV4ADDR = 1; byte SOCKS_DNSNAME = 3;
NetworkStream socksClientStream = socksClient.GetStream();
/* ***************************************************** * supported methods part * ***************************************************** */
byte[] authFields = new byte[2]; socksClientStream.Read(authFields, 0, 2); Console.WriteLine(string.Format ("authFields: version {0} methods {1}", authFields[0], authFields[1])); // read n supported methods byte[] methods = new byte[authFields[1]]; socksClientStream.Read(methods, 0, methods.Length); Console.WriteLine("socks client supports " + methods.Length.ToString() + " methods");
Устанавливаем версию SOCKS и метод без аутентификации:
/* ***************************************************** * method selection part * ***************************************************** */
// return version and auth method "no auth" byte[] selectedAuthMethod = { SOCKS_VERSION, SOCKS_NOAUTH }; socksClientStream.Write(selectedAuthMethod, 0, 2);
Затем устанавливаем соединение с удаленным сервером, к которому был направлен запрос от браузера:
/* ***************************************************** * server client connection part * ***************************************************** */
byte[] bintargetport = new byte[2]; socksClientStream.Read(bintargetport, 0, 2); byte[] tmp_byteorder = new byte[2]; tmp_byteorder[0] = bintargetport[1]; tmp_byteorder[1] = bintargetport[0]; target_port = (int)BitConverter.ToUInt16(tmp_byteorder, 0); Console.WriteLine("requesting " + connection_target + ":" + target_port.ToString());
serverClient = new TcpClient(connection_target, target_port);
/* ***************************************************** * reply part * ***************************************************** */
if (serverClient.Connected) { // reply successful audience byte[] reply = new byte[10]; //version reply[0] = SOCKS_VERSION; // replycode reply[1] = SOCKS_REPLYSUCCESS; //reserved and 0 reply[2] = 0; // addresstype reply[3] = 1; string ip = serverClient.Client.LocalEndPoint.ToString().Split(':')[0]; IPAddress ipaddr = IPAddress.Parse(ip); reply[4] = ipaddr.GetAddressBytes()[0]; reply[5] = ipaddr.GetAddressBytes()[1]; reply[6] = ipaddr.GetAddressBytes()[2]; reply[7] = ipaddr.GetAddressBytes()[3]; int port = int.Parse(serverClient.Client.LocalEndPoint.ToString().Split(':')[1]); // read unsigned integer in networkoctet order reply[8] = BitConverter.GetBytes((UInt16)port)[0]; reply[9] = BitConverter.GetBytes((UInt16)port)[1]; socksClientStream.Write(reply, 0, 10); Console.WriteLine("writing reply"); Etc.. }
Ниже приведен простейшего HTTP прокси сервера на Python.
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from SocketServer import ThreadingMixIn import urllib2, sys, re, httplib, urlparse
class ProxyHandler(BaseHTTPRequestHandler): server_version = '' sys_version = ''
def do_HEAD(self): print "HEAD" def log_message(self,format,*args): return def do_POST(self): print '-------------------------------' print "POST" requested_url = self.requestline.split()[1] parsed_url = urlparse.urlsplit(requested_url) cutted_url = urlparse.urlunsplit(('', '', parsed_url.path, parsed_url.query, '')) print parsed_url.hostname print parsed_url.path print parsed_url.query print requested_url port = 80 if None == parsed_url.port else parsed_url.port req_headers = {} for x in self.headers.items(): req_headers[x[0]] = x[1] print req_headers body = self.rfile.read(int(self.headers['content-length'])) print 'body -- ',body conn = httplib.HTTPConnection(parsed_url.hostname, port) conn.request('POST', cutted_url, body,headers = req_headers) response = conn.getresponse() print response.status self.send_response(response.status) #resp_headers = [] #if 'content-type' in response.msg.dict: # resp_headers.append(('content-type', response.msg.dict['content-type'])) #if 'date' in response.msg.dict: # resp_headers.append(('date', response.msg.dict['date'])) #if 'set-cookie' in response.msg.dict: # resp_headers.append(('set-cookie', response.msg.dict['set-cookie'])) #if 'location' in response.msg.dict: # resp_headers.append(('location', response.msg.dict['location'])) #for x in resp_headers: # self.send_header(x[0], x[1]) #self.end_headers() for x in response.msg.items(): self.send_header(x[0], x[1]) self.end_headers()
html = response.read() self.wfile.write(html) self.connection.close() conn.close() print 'End - post' return
def do_GET(self): print '--------------------------------------' print 'GET' requested_url = self.requestline.split()[1] parsed_url = urlparse.urlsplit(requested_url) cutted_url = urlparse.urlunsplit(('', '', parsed_url.path, parsed_url.query, '')) port = 80 if None == parsed_url.port else parsed_url.port #if 'accept' in self.headers.dict: # req_headers['accept'] = self.headers.dict['accept'] #if 'accept-charset' in self.headers.dict: # req_headers['accept-charset'] = self.headers.dict['accept-charset'] #if 'accept-language' in self.headers.dict: # req_headers['accept-language'] = self.headers.dict['accept-language'] #req_headers['cache-control'] = 'no-cache' #req_headers['pragma'] = 'no-cache' #req_headers['user-agent'] = 'Mozilla/5.0' #if 'referer' in self.headers.dict: # req_headers['referer'] = self.headers.dict['referer'] #if 'host' in self.headers.dict: # req_headers['host'] = self.headers.dict['host'] #for x in self.headers.dict.keys(): # if len(x) >= 6 and x[:6] == 'cookie': # req_headers[x] = self.headers.dict[x] #for x in self.headers.dict.keys(): # req_headers[x] = self.headers.headers[x]
req_headers = {} for x in self.headers.items(): req_headers[x[0]] = x[1] print req_headers
conn = httplib.HTTPConnection(parsed_url.hostname, port) conn.request('GET', cutted_url,'',req_headers) response = conn.getresponse() #if response.status not in (200, 302, 301, 204): # print requested_url + ' - ' + str(response.status) # print self.headers.headers # print response.msg.headers # self.send_response(response.status) # self.end_headers() # self.connection.close() # conn.close() # return
#resp_headers = [] #if 'content-type' in response.msg.dict: # resp_headers.append(('content-type', response.msg.dict['content-type'])) #if 'date' in response.msg.dict: # resp_headers.append(('date', response.msg.dict['date'])) #if 'set-cookie' in response.msg.dict: # resp_headers.append(('set-cookie', response.msg.dict['set-cookie'])) #if 'location' in response.msg.dict: # resp_headers.append(('location', response.msg.dict['location'])) self.name = '' self.send_response(response.status) print response.status print parsed_url.hostname, ':', port print response.msg.items() i=0 for x in response.msg.items(): i=i+1 if(x[0]!='transfer-encoding'): print '--- ', x[0],': ',x[1] if(x[0]!='connection'): self.send_header(x[0], x[1]) else: self.send_header('connection', 'close')
self.end_headers()
#self.wfile.write(response.status) #self.wfile.write(' content-type: text/html') html = response.read() self.wfile.write(html) #self.wfile.flush() #self.wfile.close() conn.close() self.connection.close() print 'END -- GET' return 0
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer): daemon_threads = True
if __name__ == '__main__': proxy = ThreadedHTTPServer(('localhost', 19277), ProxyHandler)
try: proxy.serve_forever() except KeyboardInterrupt: print 'End of server' proxy.server_close()
|
||||
Последнее изменение этой страницы: 2016-08-10; просмотров: 417; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.137.190.200 (0.012 с.) |