--:[ Автор : Jinn, ZaeB.uS, ]]>http://zaeb.us]]> ]:--
o--:[ 1 | Введение ]:--o
Вот решил написать еще одну статью на тему соксов, некоторые личности скажут:"Вот понаписал статей, мог бы все в одной изложить!", объясню мою точку зрения на данную проблему: я считаю, что если бы весь этот материал(с первой статьи по текущую) содержался в одной статье, то статья бы получилась довольно большой и сложной для понимания! В этот раз рассмотрим метод не connect как в прошлых статьях, а bind. Если вы первый раз читаете про соксы и не знакомы с этим протоколом, то желательно прочитать мои статьи:"Организация работы php скрипта через socks5 сервер","PHP + socks5(авторизация по методу username/password)", либо RFC. Как и в прошлые разы перечислю на чем я все это гонял,ничего не изменилось=):
* Win XP SP2
* Opera 8.52
* Денвер
* 3proxy-0.5.3i(ЗАРАЗА)
o--:[ 2 | Общие принципы ]:--o
Для тех кто не знаком что это такое отправлю в гугл и коротко обьясню=) Bind используется для открытия порта, а по теме этой статьи видно что мы рассматриваем сокс => открывать порт будем на стороне socks5-сервера. А для чего это нужно? Это может использоваться в соединениях, которые требуют открытия порта на стороне клиента(мы ведь прячемся за сокс, т.е для удаленного сервера клиентом является сокс), хорошим примером требующим открытия порта на стороне клиента является ftp. Я думаю многие видели вопросы на форумах типа: "Ftp клиент не работает с http проксей", после долгой дискуссии выяснялось, что хлопчик пытается подключиться к фтп серверу через http проксю в активном режиме. И соответствующий ответ: "Юзай пассивный режим!". А что же это такое активный и пассивный режим? Если кто изучал ftp протокол те знаю, что в нем используется 2 вида соединения: клиент--->сервер по которому идут команды клиента и сервер--->клиент:альтернативный порт, который используется для передачи результата команды клиента(ls,get,put). Но вот проблема: с htpps проксями же такое не пройдет... Мы ведь не можем открыть порт на стороне https прокси, а для передачи данных его открыть нужно. Именно поэтому при использовании прокси сервера и активного режима вы не получите листинг директории,etc. Тогда приходиться юзать пассивный режим, там все открытия порта происходят на стороне ftp-сервера, а клиент из-за прокси просто к ним подключается и получает ответы на команды =) Но в соксах все по другому, тут есть механизм открытия порта на стороне сокс сервера и называется он bind. Т.е при использовании сокса не нужно переходить в пассивный режим(хотя возможно :-), но нужно тоже постараться чтобы открыть порт на соксе, а именно изучить данную небольшую статью =)
o--:[ 3 | Реализуем BIND ]:--o
Для чего это нужно разобрались, перейдем к практике и попробуем написать работающие приложение :-)
По RFC предполагается, что bind будет использован только как вторичное соединение, после первого первичного установленного с помощью метода CONNECT, но мы в наших экспериментах нарушим предписания RFC.
Пакеты для бинда порта мы должны слать такие же как и для коннекта, но метод должен быть не connect(01) а bind(02)=). Снова показывать структуру пакета я не буду, если ты ее не знаешь читай статьи [1],[2]. На connect сокс сервером возвращается один ответ, в котором указан статус(ok/error) подключения, с bind'ом все сложнее, соксом предоставляется 2 ответа: первый приходит сразу же после запроса, в нем так же указан статус успешности открытия порта для входящего соединения,а также ip и порт выделенные соксом для входящего соединения, эта информация, в случае надобности, может быть предоставлена серверу-приложению через первичное соединение как адрес для взаимодействия. Возможно что ip будет отличаться от ip сокс сервера, т.к иногда подобные сервера имеют несколько адресов. Второй пакет приходит после внешнего подключения к забинденому порту, там содержится ip и порт подключившегося хоста и статус подключения(коды те же, 0-ok,etc). Эти данные могут быть использованы для решения о дальнейшем взаимодействии(напрмиер ip подключившегося хоста содержится в черных списках, тогда соединение может быть разорвано....но это все на ваше усмотрение)
В принципе никакой сложности это вызывать не должно, после того как мы забиндим порт и получим первый ответ сокса, перед получением данных от подключившегося клиента мы должны будем получить второй пакет и проверять его статус, далее же, если все в порядке, мы сможем получать, отправлять данные как и прежде методами php(fread,fgets,fwrite,feof).
Теперь собственно обсудим что мы будем кодить. Мы не будем работать с ftp и вот почему: в php существуют ф-ции ftp_connect, ftp_put, etc. Но при подключении сначала к соксу а потом попытке передачи через данный сокет файла данными методами определенными для работы с ftp компилятор php верещит, что сокет не подходит => воспользоваться встроенными ф-циями работы с ftp сервером нам не удастся и придется изучать и писать свой класс для работы по протоколу ftp, а это не так то просто и не входит в мои планы....пока=) Но ведь наша цель просто открыть на соксе порт, и получить оттуда отправленные сервером данные, поэтому мы поступим вот как:
-- забиндим порт на соксе -- получим ответ сокса, со статусом открытия порта, ip и порт выделенный для входящих подключений [ Мы за сервер ] -- теперь поработает за сервер и откроем подлючение не используя сокса(хм..хотя никто вам не мешает это сделать) с помощью fsockopen и выделенным адресом для входящего подключения -- отправим туда произвольные данные -- закроем подключение [ сервер накрылся медным тазом ] -- теперь мы получим пакет от сокса, в котором указаны ip и порт подключившегося хоста(т.е нашего) -- опять получим данные, уже отправленные сервером(опять нами;-) -- закроем подлючение к соксу -- выведем все это пользователю
Этим примером мы проверим правильность понимания нами протокола, как мне кажется, данный пример полностью показывает все стороны bind'а, как клиентской стороны так и серверной :-)
Обсудим особенности нашего сегодняшнего кода.
Обычно сокс сервер формирует ответ с типом адреса 01, т.е ip адрес, а не так как мы 03 - доменное имя. Для перевода ip в привычный нам строковый вид, т.е с точками мы будем использовать функцию пхп long2ip, специального для этого предназначенную, но перед этим мы должны будем перевести адрес из hex'а в dec. Кстати существует функция ip2long, которая могла бы пригодиться нам в том случае если бы мы передавали соксу IPv4,но это просто на заметку :-)
Код изначально взят из моей статьи [2] и заточен именно для bind'а...
<? error_reporting(0); $socks_ip = "127.0.0.1"; // адрес scoks 5 прокси $socks_port = 1080; // порт сокса $socks_login = "jinn"; // логин для доступа к соксу $socks_pass = "jinn2"; // пароль $bind_host = "127.0.0.1"; $bind_port = 3232; $socks_server=fsockopen($socks_ip,$socks_port); // соединяемся с соксом if($socks_server) { echo'<meta http-equiv="Content-Language" content="ru"><meta http-equiv=Content-Type content="text/html; charset=windows-1251">'; $h=pack("H*",'05020002'); // привествие =) fwrite($socks_server,$h); $result=bin2hex(fread($socks_server,4)); // переводим в hex для дальнейшего сравнения if($result == '0500') // авторизация отсутствует { $auth_ok=1; } elseif($result == '0502') // метод username/password { $len_login = chr(strlen($socks_login)); //получаем спец символ соответсвующий ascii коду $len_pass = chr(strlen($socks_pass)); // получаем спец символ соответсвующий ascii коду $h=pack("H*","01").$len_login.$socks_login.$len_pass.$socks_pass; // формируем пакет с помощью pack() fwrite($socks_server,$h); $result=bin2hex(fread($socks_server,4)); // переводим из бинарного режима в hex // далее сверяем если код ответа 0, то авторизация успешна,выставляем об этом переменую,в ином случа выводим сообщение об ошибке if($result{3}!=0) { echo "<center><font color=red>Ошибка:аутентификация не прошла!</font></center>"; fclose($socks_server); } else { $auth_ok=1; } } else { echo "<center><font color=red>Ошибка:возможно это не socks5 или он не поддерживает данные(00,02) методы аутентификации!</font></center>"; } $list=""; // проверяем если авторизация успешна , то начинаем взаимодействие с соксом if($auth_ok==1) { $len_h=chr(strlen($bind_host)); $h=pack("H*","05020003").$len_h.$bind_host.pack("n",$bind_port); //формируем запрос fwrite($socks_server,$h); $result = bin2hex(fread($socks_server,1024)); $ver = substr($result,0,2); $status = substr($result,2,2); $in_ip = long2ip(hexdec(substr($result,8,8))); $in_port = hexdec(substr($result,16,4)); echo "Адрес для подключения:".$in_ip.":".$in_port."<br>"; // проверяем успешность подключения,выводи ошибки if($status == '00') { $head = "<br><br><center><font color='blue' size=4>HI!coded by ZaeB.uS</font></center>"; $in_socket=fsockopen($in_ip,$in_port); fwrite($in_socket,$head); fclose($in_socket); $result = bin2hex(fread($socks_server,1024)); $ver = substr($result,0,2); $status = substr($result,2,2); // тут статус входящего подключения $con_ip = long2ip(hexdec(substr($result,8,8))); // а кто это к нам подключился?ип подключившегося хоста $con_port = hexdec(substr($result,16,4)); // порт негодяя echo "Подключившийся хост:".$con_ip.":".$con_port."<br>"; echo "Полученные данные<br><br>"; // получаем данные while(!feof($socks_server)) { echo fread($socks_server,1024); } } // определяем ошибку! данные коды ответа взяты из RFC elseif ($status == 1) {echo "<center><font color=red>Ошибка: вина SOCKS-сервера</font></center>";} elseif ($status == 2) {echo "<center><font color=red>Ошибка: соединение запрещено набором правил</font></center>";} elseif ($status == 3) {echo "<center><font color=red>Ошибка: сеть недоступна</font></center>";} elseif ($status == 4) {echo "<center><font color=red>Ошибка: хост недоступен</font></center>";} elseif ($status == 5) {echo "<center><font color=red>Ошибка: отказ в соединении</font></center>";} elseif ($status == 6) {echo "<center><font color=red>Ошибка: истечение TTL</font></center>";} elseif ($status == 7) {echo "<center><font color=red>Ошибка: команда не поддерживается</font></center>";} elseif ($status == 8) {echo "<center><font color=red>Ошибка: тип адреса не поддерживается</font></center>";} else {echo "<center><font color=red>Ошибка: не определено!RFC в панике! :-)</font></center>";} } fclose($socks_server); } else { echo "<center><font color=red>SOCKS сервер недоступен!</font></center>"; } echo '<br><br><table border="0" cellspacing="1" width="100%" align="center" style="background-color:#000000;"><tr><td width="100%" align="center" style="background-color:#E8E8E8;"><font size=1 color="#292929" face="verdana">[ c0ded by <b>Jinn</b> | 304227033 | http://zaeb.us ] </font></td></tr></table>'; ?>
o--:[ 4 | End ]:--o
Да, статья получилась не слишком большой, и возможно вы не узнали из нее ничего нового, но все же я думаю что знать данную инфу нужно, ведь понимание сетевых протоколов является основным=) По прежнему, если возникли предложения или вопросы стучи мне в асю или пиши на мыло, постараюсь дать ответ...
o--:[ 5 | Материалы ]:--o
1.... Организация работы php скрипта через socks5 сервер, Jinn, ZaeB.uS, ]]>http://zaeb.us]]>
2.... PHP + socks5(авторизация по методу username/password), Jinn, ZaeB.uS, ]]>http://zaeb.us]]>
o--:[ Jinn | ZaeB.uS | ]]>http://zaeb.us]]> ]:--o