Форум АНТИЧАТ

Форум АНТИЧАТ (https://forum.antichat.xyz/index.php)
-   Избранное (https://forum.antichat.xyz/forumdisplay.php?f=89)
-   -   pproxy - прокси на PHP (https://forum.antichat.xyz/showthread.php?t=93318)

bons 24.11.2008 20:54

pproxy - прокси на PHP
 
Однажды мне понадобился прокси-сервер, работающий не как демон/служба а в контексте процесса веб-сервера. Подходящего не нашел поэтому написал свой, очень простой и достаточно юзабельный.

Описание

Состоит из двух частей. На удаленный веб-сервер заливается первая часть прокси, написанная на PHP - скрипт pproxy.php. На локалхосте запускается вторая часть прокси, реализованная на Perl (скрипт plocal.pl), которая прослушивает порт как HTTP-прокси. На этот локальный HTTP прокси настраивается, например, браузер.

Скрипты

pproxy.php - первая, удаленная часть прокси, на PHP:

PHP код:

<?php

//$secret = 'pproxypass';

if(isset($_POST['query']) && isset($_POST['host']))
{
    if(isset(
$secret) && ($_POST['secret'] != $secret))exit;
    
header('Content-type: application/octet-stream');
    @
set_time_limit(0);
    
$query base64_decode(str_replace(' ''+'$_POST['query']));
    list(
$host$port) = explode(':'base64_decode($_POST['host']));
    if(!
$port)$port 80;
    
$ip gethostbyname($host);
    if(
$fp = @fsockopen($ip$port$errno$errstr20))
    {
        
fwrite($fp$query);        
        while(!
feof($fp))
        {
            
$answer fread($fp1024);
            echo 
$answer;
        }
        
fclose($fp);
    }
    exit;
}
?>

plocal.pl - вторая, локальная часть прокси, на Perl:

Код:

use HTTP::Daemon;
use MIME::Base64 ();
use Getopt::Long;
use POSIX ":sys_wait_h";
use strict;

my $user_agent = 'Mozilla/5.0 (X11; U; Linux i686; ru; rv:1.9.0.4) Gecko/2008102920 Firefox/3.0.4';

my $errheader = "HTTP/1.1 200 OK\x0D\x0AContent-Type: text/plain\x0D\x0A\x0D\x0A";
my $errmsg1 = $errheader . 'Could not connect to server';
my $errmsg2 = $errheader . 'Could not connect to pproxy';

my ($pproxyhost, $pproxyport, $pproxyurl);
my ($tunnelhost, $tunnelport);
my ($pproxy, $bindport, $tunnel, $secret);
my ($destaddr, $destport, $desturl);

#Вывод справки
Usage() if @ARGV==0;

#Задание опций
GetOptions(
        "px=s"                => \$pproxy,
        "bp=s"        => \$bindport,
        "tpx=s"        => \$tunnel,
        "pwd=s"        => \$secret
    );
die "need pproxy.php url" unless $pproxy;


#Получение параметров
$pproxy =~ /http:\/\/([\w\.\-]+)(:\d*)?\/(.+)/;
$pproxyhost = $1; $pproxyport = substr($2, 1); $pproxyurl = '/' . $3;

defined($pproxyport) || ($pproxyport = 80);
defined($bindport) || ($bindport = 8008);

if(defined($tunnel))
{
        $tunnel =~ /http:\/\/([\w\.\-]+):(\d*)?/;
        $tunnelhost = $1; $tunnelport = $2;

        $destaddr = $tunnelhost;
        $destport = $tunnelport;
        $desturl = $pproxy;

        print "# tunnelhost = $tunnelhost\n";
        print "# tunnelport = $tunnelport\n";
}else{
        $destaddr = $pproxyhost;
        $destport = $pproxyport;
        $desturl = $pproxyurl;
}

print "# pproxyhost = $pproxyhost\n";
print "# pproxyport = $pproxyport\n";
print "# pproxyurl = $pproxyurl\n";
print "# bindport = $bindport\n\n";

$|=1;
my %children;
my $slave;

#Прием подключений
my $master = HTTP::Daemon->new(        LocalPort => $bindport,
                                LocalAddr => 'localhost',
                                Reuse=>1)
|| die "Can't start server ($@)";
&MainProc($slave) while $slave = $master->accept;

sub MainProc
{
                my $conn = shift;

                #Прием HTTP-запроса
                my $request = $conn->get_request();
               
                #Ответвление процесса(потока)
                my $pid = fork();
                unless(defined($pid))
                {
                        print "# Erorr couldn't fork\n";
                        close $conn;
                        return;
                }

                if($pid)
                {
                        close $conn;
                        $children{$pid}++;
                        foreach(keys %children )
                        {
                                my $kid = waitpid($_, &WNOHANG);
                                delete $children{$_} if($kid == -1 || $kid == $_);
                        }
                        return;
                }

                #Преобразование HTTP заголовка
                $request -> remove_header('Proxy-Connection');
                $request -> remove_header('Keep-Alive');
                $request -> header(Connection=>'close');
                my $host = $request -> header('Host');
                my $http = $request -> as_string();
                my $head_end = index ($http, "\x0A\x0A") + 2;
                my $head = substr ($http, 0, $head_end);
                my $post = substr ($http, $head_end);
                $head =~ s/\x0A/\x0D\x0A/g;
                $http = $head . $post;
                $http =~ s/http:\/\/$host//;
                my $time_start = time;
               
                #Отправка запроса прокси и прием результата
                SendToPProxy(        MIME::Base64::encode($host),
                                MIME::Base64::encode($http),
                                $conn
                                        );

                my $time_end = time;
               
                #Лог
                print &TranslateTimeHour($time_end), "  ", $host,
                        " (", &TranslateTime($time_end - $time_start),
                        ")\n";

                close $conn;
                exit;
}

sub SendToPProxy
{
        my $dest_host = shift;
        my $query_content = shift;
        my $clientsock = shift;
       
        my $proxysock = IO::Socket::INET->new(Proto=>'tcp',PeerAddr=>$destaddr,PeerPort=>$destport);
        unless($proxysock)
        {
                syswrite($clientsock, $errmsg2, length($errmsg2));
                return;
        }
       
        my $post_query;
        $post_query = 'secret=' . $secret . '&' if defined($secret);
        $post_query .= 'host=' . $dest_host . '&query=' . $query_content;
        my $postlen = length($post_query);

        my $request = "POST $desturl HTTP/1.0\x0D\x0A".
                        "Host: $pproxyhost:$pproxyport\x0D\x0A".
                        "Accept: */*\x0D\x0A".
                        "Content-Type: application/x-www-form-urlencoded\x0D\x0A".
                        "Content-Length: $postlen\x0D\x0A".
                        "User-Agent: $user_agent\x0D\x0A".
                        "Connection: close\x0D\x0A\x0D\x0A" . $post_query;

        syswrite($proxysock, $request, length($request));
        my ($result, $buffer, $response);
        my $contentstart = -1;
        my $count = 0;
        while(1)
        {
                $result = sysread($proxysock, $buffer, 1024);
                last if !defined($result) || !$result;

                if($contentstart == -1)
                {
                        $response .= $buffer;
                        last if length($response)>65535;
                        $contentstart = index($response,"\x0D\x0A\x0D\x0A");
                        next if $contentstart == -1;
                        $buffer = substr($response, $contentstart+4);
                }
                $count += length($buffer);
                syswrite($clientsock, $buffer, length($buffer));
        }
        syswrite($clientsock, $errmsg1, length($errmsg1)) if $count == 0;
        close $proxysock;
}

sub TranslateTime
{
        my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(shift);
        return sprintf "%02u:%02u", $min, $sec;
}

sub TranslateTimeHour
{
        my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(shift);
        return sprintf "%02u:%02u:%02u", $hour, $min, $sec;
}

sub Usage
{
        print "Usage: $0 -px proxy_url [-bp bindport] [-tpx tunnel_proxy] [-pwd secret]\n";
        print "Example: $0 -px http://site.com/proxy/proxy.php -bp 8080\n";
        print "        $0 -px http://site.com/proxy/proxy.php -pwd pproxypass\n";
        print "        $0 -px http://site.com/proxy/proxy.php -tpx http://localhost:8118\n";
        print "\nDefault bind port - 8008\n";
        exit;
}


Использование прокси

Допустим адрес pproxy.php будет http://site.com/proxy/pproxy.php
Тогда локальный скрипт запускается так:
Код:

perl plocal.pl -px http://site.com/proxy/pproxy.php
По умолчанию открывается порт 8008


Построение цепочки прокси

Для примера случай с двумя элементами цепочки.
Есть два веб-сервера с двумя скриптами pproxy:
http://site1.com/proxy/pproxy.php и
http://site2.com/proxy/pproxy.php

Локальный скрипт запускается два раза с такими параметрами:
Код:

perl plocal.pl -px http://site1.com/proxy/pproxy.php
perl plocal.pl -px http://site2.com/proxy/pproxy.php -tpx http://localhost:8008 -bp 8009

Браузер следует настроить на 8009 порт и трафик пойдет по такой цепочке:
localhost -> site1.com -> site2.com -> target

Думаю, несложно будет настроить и на более длинную цепочку.

Запуск через Tor

Если вы используете Tor вместе с Privoxy (по умолчанию на порту 8118)
тогда прокси запускается так:
Код:

perl plocal.pl -px http://site.com/proxy/pproxy.php -tpx http://localhost:8118
Соответственно http://site.com/proxy/pproxy.php - адрес PHP-прокси
К сожалению, через Tor некоторые сайты могут загружаться некорректно,
причины пока неизвестны.

Установка пароля

В pproxy.php раскомментировать строчку,
написать там свой пароль

PHP код:

$secret 'pproxypass'

в параметрах запуска plocal.pl указать его же.
Код:

perl plocal.pl -px http://site.com/proxy/pproxy.php -pwd pproxypass
Примечания

1. Если вы хотите в качестве элемента цепочки узел SOCKS, то Privoxy поможет вам (см пример с Tor)
2. pproxy.php очень компактен и легко может быть внедрен в код сайта,
Возможно это немного повысит ваш уровень анонимности.
3. Не стоит забывать что веб-серверы ведут логи и восстановить истинный источник нетрудно даже по цепочке;)
4. Альтернативный клиент(Delphi/Pascal) с открытым исходным кодом доступен тут http://dump.ru/file/3320224

Спасибо за совет: b3, AkyHa_MaTaTa

DVD_RW 27.11.2008 13:36

эм...а прокси хттп\с или сокс?

nerezus 27.11.2008 14:11

http
Перенес в избранное. кстати.

KemSucks 27.11.2008 23:50

может как нить на https прикрутить можно?

bons 28.11.2008 15:46

нет, HTTPS тут реализовать почти невозможно. HTTPS прокси - он как и сокс основан на постоянном соединении и непрерывном обмене данными. А здесь, как видишь, обмен данными сводится к отсылке HTTP-запроса браузера и получению результата.

jumperby 28.11.2008 22:57

А оградить это дело от чужих глаз можно как-то? логин:пасс какой-нибудь.
Связка ActivePerl + Opera 9.62 + Proxy, дает в результате тормоза, не в курсе как это можно исправить?
хттпс идет как я понял не через проксик, а через впн?

b3 29.11.2008 05:32

Цитата:

1. Понятно, что время соединения через такой прокси ограничено максимальным временем выполнения скрипта.
Обычно 30 секунд или 1 минута, это зависит от настроек в php.ini
допиши в начало кода РНР, строки:
Цитата:

set_time_limit(0);
ignore_user_abort();
Время выполнения - неограничено. Скрипт не останавливаеться после дисконекта клиента.

bons 29.11.2008 16:10

дописал set_time_limit(0);
добавил авторизацию в самом простом виде.

jumperby 30.11.2008 01:26

Подскажите пожалуйста, почему ActivePerl 5.10 так жестко начинает жрать память через минуту юзания проксика, заодно начинает жрать и браузер (опера и файрфокс, ведут себя одинаково). Как это исправить?
п.с. выкиньте с фиксом версию (сет тайм лимит + авторизация)

bons 30.11.2008 14:58

да, память и процессорное время оно ест немилосердно, это расплата за небольшой размер скрипта.
Если слабая машина то могу предложить
1. Снизь приоритет выполнения процесса, это можно сделать в диспетчере задач. (процесс perl.exe)
2. Отключи многопоточность, тогда запросы будут выполняться последовательно и не так загружать процессор.

закомментируй эти строчки
Код:

...
                #my $pid = fork();
                #unless(defined($pid))
                #{
                #        print "# Erorr couldn't fork\n";
                #        close $conn;
                #        return;
                #}

                #if($pid)
                #{
                #        close $conn;
                #        $children{$pid}++;
                #        foreach(keys %children )
                #        {
                #                my $kid = waitpid($_, &WNOHANG);
                #                delete $children{$_} if($kid == -1 || $kid == $_);
                #        }
                #        return;
                #}
...

и еще команду exit:

Код:

...
                close $conn;
                #exit;
}
...


c0m 12.12.2008 22:35

так, а если залью скрипт в индекс сайта, по идее тяжело будет отпределить кто через них лазел и куда заходил?

bons 13.12.2008 14:03

в логах апача будет сохранен факт обращения к скрипту с твоего айпи и скорее всего User_Agent перла (User-Agent впрочем можно сменить)
То, куда ты обращался через этот скрипт возможно будет сохранено в логах файрволла сервера, это зависит от его настроек.

jumperby 13.12.2008 14:27

Нашел себе счастье, спешу поделиться.
HTTPTunnel
Цитата:

Enables tunneling of network connections through restrictive HTTP proxies. Features: Portmapping, SOCKS4, SOCKS5, web-based admin interface, possibility to use standalone server (perl) or hosted server (PHP), optional authorization from LDAP or MySQL
На машине должен быть перл, на серваке можно через перл и обычный пхп. Все очень шустро работает, кучи возможностей:
Portmapping, SOCKS4, SOCKS5, веб-админка, авторизация с мускула или ldap.
http://sourceforge.net/projects/http-tunnel/
Все шустро работает и носки есть :) После ппрокси как-будто в летающую тарелку сел...
п.с. искал в чем трабл этого скрипта (ппрокси), вроде как в том что use много используется

i-Worm.Fizzer 13.12.2008 15:22

Хостинг провайдер может как-то запалить, что я использую проксик, через него ?

Shaitan-Devil 13.12.2008 18:08

Цитата:

Сообщение от i-Worm.Fizzer
Хостинг провайдер может как-то запалить, что я использую проксик, через него ?

Да.Большинство хостингов отключают внешние соеденения,а те которые разрешают все быстро палят.

DIAgen 13.12.2008 18:49

Только есть маленький не достаток, пока perl не загрузит полность весь фаил он его не выдаст, и из-за этого такие тупки, кто знает к это можно исправить? (:

preda1or 13.12.2008 18:52

DIAgen могу предположить, только, сорри если не прав
в php есть команда flush...
в перле нет аналогов?

bons 13.12.2008 19:59

разве что переписать весь код, написаный с LWP на сокеты. Как время будет, может сделаю...

Isis 13.12.2008 23:02

Почему ssl & socks нельзя?
Curl можно использовать

bons 14.12.2008 00:55

SOCKS здесь вряд ли возможен исходя из самого механизма обращения к прокси.
То есть алгоритм такой:
1. perl-скрипт принимает HTTP-запрос и передает POST-параметром к php-скрипту
2. PHP-скрипт принимает POST-параметр и с помощью сокетов посылает целевому серверу, все что вернул ему сервер отправляет назад perl-скрипту

для реализации обычного HTTP-запроса этого достаточно. Но SOCKS предусматривает создание постоянного канала, то есть многократный прием и передачу данных. А как передавать данные скрипту второй раз? При таком подходе это невозможно!
HTTPS прокси или HTTP CONNECT прокси по сути тот же SOCKS-прокси, так как для подключения к HTTPS нужен сложный протокол - передача сертификатов и т.д.

Но если цель не создание полноценного HTTPS-прокси а например просто зайти через прокси на сайт, расположенный на https то все же можно например придумать следующий алгоритм:
1. perl-скрипт принимает запрос как HTTP-прокси (!) и передает его POST-параметром PHP-скрипту
2. PHP-скрипт с помощью curl посылает этот запрос целевому серверу и т.д.

в теории это возможно, но при этом возникает некоторые проблемы:
- как браузер будет сообщать perl-скрипту что целевой хост именно на https а не на http
- на сервере с PHP-прокси будет возникать проблемы c памятью - ведь PHP-скрипту необходмо сначала полностью принять результат и только потом он сможет передать результат и освободить занятую память. (подобно проблемам с памятью в текущей версии perl-скрипта)

Плюс еще не на каждом сервере curl доступен

P.S. Посмотрел исходник HTTPTunnel. Можно использовать IPC, тогда все проблемы сразу решаются...

nerezus 14.12.2008 13:43

Цитата:

Да.Большинство хостингов отключают внешние соеденения
Поправка: бесплатные хостинги.

Платные никогда не отключают, т.к. сабж нужен большинству пользователей, которых им не хотелось бы терять.

Shaitan-Devil 14.12.2008 17:13

Цитата:

Сообщение от nerezus
Поправка: бесплатные хостинги.

Платные никогда не отключают, т.к. сабж нужен большинству пользователей, которых им не хотелось бы терять.

Да.Но лучше всего ставить прокси на шелл.Т.к. в случае чего хостинги отдадут логи компетентным органам.

bons 14.12.2008 17:35

обновил plocal.pl, теперь LWP не используется.
Файл отдается клиенту по мере загрузки, т.е. памяти расходуется меньше.
Старую версию можно найти здесь: http://slil.ru/26436538

zic 25.12.2008 19:02

Здравствуйте, а не подскажете как можно оптимизировать клиентскую часть, а именно plocal.pl чтоб была возможность хождения через прокси?

Ситуация следующая на работе закрыты все порты кроме 80, 21, 443, Интернет раздаётся через ISA-2007.... вот и есть нужда её обойти... ранеше делали тунель через SSH но по некоторым причинам перестали использовать.... так вот ближе к теме как сделать так чтоб клиенская часть ходили через местную проксю? ибо хоть и есть default getwey но там только 21 порт открыт....

Очень расчитываю на вашу помощь.... за ранее спасибо!

bons 25.12.2008 22:39

могу посоветовать NTLM Authorization Proxy Server (написан на python)
например если настроить его на порт 5865 локалхоста то plocal надо запускать так:
perl plocal.pl -px http://site.com/pproxy.php -tpx http://localhost:5865

скачать:
http://sourceforge.net/projects/ntlmaps/
мануал по его настройке тут:
http://www.linux.com/base/ldp/howto/Web-Browsing-Behind-ISA-Server-HOWTO-4.html

zic 26.12.2008 12:29

Цитата:

Сообщение от bons
могу посоветовать NTLM Authorization Proxy Server (написан на python)
например если настроить его на порт 5865 локалхоста то plocal надо запускать так:
perl plocal.pl -px http://site.com/pproxy.php -tpx http://localhost:5865

скачать:
http://sourceforge.net/projects/ntlmaps/
мануал по его настройке тут:
http://www.linux.com/base/ldp/howto/Web-Browsing-Behind-ISA-Server-HOWTO-4.html

Огромное тебе спасибо, за твои старания, перенёс всё это на linux(CentOS 5.2) единственное пришлось обновить Phyton, всё заработало..... ещё раз огромное спасибо!!!


P.S. А вот не мог бы ты ещё вот с какой вещью помоч, на linux'е реальзовал всё отлично работает, а вот как теперь сделать чтобы XP WORKSTATIONS могли ходить через linux по 8008 порту....

Ну грубо говоря чтоб была такая схема comp1(XP)->comp2(Linux, proxy 172.29.5.100:8008)->target

Или к примеру QIP настроенный на proxy HTTP 172.29.5.100:8008

Просто такое подозрение что никсовая машина не пускает к себе....

bons 26.12.2008 20:02

Для того чтобы plocal.pl разрешал подключение к себе не только с локалхоста надо удалить в нем строчку, отмеченную красным:
Код:

...
my $master = HTTP::Daemon->new(        LocalPort => $bindport,
                                LocalAddr => 'localhost',
                                Reuse=>1)
|| die "Can't start server ($@)";
...

Но QIP запустить через pproxy не получится так как для него необходим HTTPS прокси, поддержки которого пока нет.

m0Hze 28.12.2008 04:40

Кто подскажет как заюзать HTTPTUnnel? Чтото ниразберусь я никак :)

m0Hze 30.12.2008 04:01

В общем,смог запустить прокси,автору респектище гиганское!
Но вот вопрос: Я в уторренте прописываю прокси, localhost 8008бв порт для кача,че писать? Свой открытый порт,или порт сервера?Чтото совсем не качацо :(
Помогите пжалста..

Spyder 31.12.2008 04:59

если есть возможность - лучше переписать локальный скрипт на использование потоков, а не форков, производительность повысится

bons 01.01.2009 19:15

Цитата:

Но вот вопрос: Я в уторренте прописываю прокси, localhost 8008бв порт для кача,че писать? Свой открытый порт,или порт сервера?Чтото совсем не качацо
торрент-клиенту нужен HTTPS прокси

Цитата:

если есть возможность - лучше переписать локальный скрипт на использование потоков, а не форков, производительность повысится
для тестов делал версию с модулем threads но ощутимого прироста производительности это не дало плюс еще скрипт стал иногда вылетать. Тем более для windows например эмуляция fork сводится к созданию потока причем не факт что это медленнее или занимает больше ресурсов чем через threads
вот версия с потоками - http://slil.ru/26500515

DVD_RW 02.01.2009 16:04

а у мну траблы с кодировкой... %)

bons 03.01.2009 13:13

Цитата:

Сообщение от DVD_RW
а у мну траблы с кодировкой... %)

попробуй дописать в pproxy.php строку:
Код:

<?php

//$secret = 'pproxypass';

if(isset($_POST['query']) && isset($_POST['host']))
{
    header('Content-type: application/octet-stream');
...

если это не поможет то дай пожалуйста больше информации о проблеме: как выглядит заголовок ответа веб-сервера с pproxy, в какой кодировке приходит страница, каким браузером пользуешься

m0Hze 03.01.2009 18:16

bons, можно тебя спросить,когда ты будеш свободен и смогеш сделать сокс4-5 сервер,или хттпс.В общем чтоб с торентов качать можно было 8) А то я убился уже весь..сам скрипт смтрел,но никогда не работал с проксями,поэтому боюсь чтонибудь намудитьтам....воть.

bons 03.01.2009 22:56

когда будет время напишу версию pproxy для SOCKS.
Но m0Hze, ты уверен что в твоем случае нужно именно туннелирование через HTTP? Ведь это необходимо если файрволл запрещает открыть порт для прослушивания на сервере, а иначе подойдет обычный прокси, например 3proxy.

m0Hze 03.01.2009 23:02

Знаеш в чем вся проблема?) Я не знаю как настроить этот гребаный 3прокси на Фряхе.Совсем не знаю.На офф сайте етсть инструкция для настройки под меломягкую,а про никсы - ни слова. :)
Если ты можеш объяснить как настроить - то я с удовольствием приму твои советы.
Но всеже для меня былобы безопаснее юхать тунелирование,так как меньшее палево на мой взгляд.Как никак,сервер Провайдера, :)))).....

bons 03.01.2009 23:21

в Сети полно манов по по этому случаю
допустим 3proxy-0.5.3k.tgz - архив с 3proxy

распаковываешь и компилишь:
Код:

tar xvf 3proxy-0.5.3k.tgz
make -f Makefile.unix
cd src

создаешь папке src файл proxy.cfg с содержимым
Код:

socks -p55554
запускаешь 3proxy фоновым процессом:
Код:

./3proxy proxy.cfg >/dev/null &
все файлы кроме 3proxy и proxy.cfg можно удалить, а эти лучше бы назвать по-другому

m0Hze 03.01.2009 23:37

Один глупый вопрос =)
Получиться сокс5 или 4? коннект по 55554 порту?

bons 03.01.2009 23:44

3proxy будет принимать подключения клиентов и 4-ой и 5-ой версий. По 55554 порту

Joker-jar 05.01.2009 18:25

Интересная тема. Тоже как-то делал такое. Прокси был на php, локальный туннель - на Delphi. Только у меня основной задачей было сжатие трафика. Использовал gzip. Для браузеров прозрачно, даже распаковывать не надо. В туннеле настраивалось качество картинок (если надо, прокси сжимал и их, понижая качество). Вместо swf подсовывалась заготовленная флэшка с надписью "BLOCKED BY PROXY". Да, еще прокси в хидерах передавал реальный и сжатый размер страницы, чтобы клиент мог вести статистику экономии. Это я так, может захочешь что-то подобное сделать


Время: 07:57