Думаю, ты знаешь что из себя представляет TCP logger и как он жизненно необходим в твоем ремесле 🙂 Поэтому перейду сразу к делу - к написанию программы.
Принцип действия программы следующий - мы как бы вклиниваемся между клиентом
(твоим браузером) и прокси-сервером. Когда все пакеты будут проходить через логгер, то ты будешь подключаться клиентом только к созданному логером серверу,
сам же логгер, если не усложнять программу до неприличия, коннектиться только к одному серваку
и записывает все проходящие данные.
В общем, примерная схема этой цепочки такая -
Браузер --- TCP logger --- Прокси
Сразу хочу сказать, что в нижеприведенной проге я больше внимания уделял самой сути программы, а не всяческим декоративным атрибутам вроде окошек и
менюшек 🙂 Программу мы будем писать на Visual C++, который ничем не уступает столь иногда незаслуженно популярному Delphi. Для начала создадим проект - (File - New - Projects - MFC
AppWizard(exe)). Почему мы выбрали именно MFC - объясню позже. В данном случае нам не важны внешние проявления типа разноцветных кнопок, а потребуются некоторые функции библиотеки MFC. Далее выбираем пункт Dialog
Based (не принципиально, но для простоты лучше этот). Далее жмем Finish, и вот мы имеем основу 🙂
Если ты все сделал правильно то начало главного файла должно иметь такой вид:
#include "stdafx.h"
#include "winsock2.h" //для работы с сокетами
// не забудь скопировать этот файл в каталог проекта
#include "dd.h" // подключаемые файлы для данного проекта
#include "ddDlg.h" //названия зависит от его имени.
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
int s; //глобальные переменные, дескрипторы сокетов.
int s1;
int s2;
UINT Function1( LPVOID pParam)// функция другого потока, см ниже
///////////////////////////////
// CDdApp
BEGIN_MESSAGE_MAP(CDdApp, CWinApp)
//{{AFX_MSG_MAP(CDdApp)
// NOTE - the ClassWizard will add and remove mapping macros here.
// DO NOT EDIT what you see in these blocks of generated code!
//}}AFX_MSG
ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()
///////////////////////////////
// CDdApp construction
CDdApp::CDdApp()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}
///////////////////////////////
// The one and only CDdApp object
CDdApp theApp;
// CDdApp initialization
BOOL CDdApp::InitInstance()
{
/////////здесь должен быт наш код
AfxEnableControlContainer();
Основная процедура - BOOL CDdApp. Наша задача - втиснуть сам код логгера в проект так, чтобы он выполнялся без лишнего геморроя. Самое простое засунуть его перед самой инициализацией формы - т.е. перед выполнением функции
AfxEnableControlContainer(). Теперь можно приступать к написанию кода.
Для начала нужно проинициализировать процесс библиотеки wsock32.dll для работы с сокетами. Что это такое ты думаю знаешь из целого ряда статей с этого сайта, поэтому не буду повторяться в xxx какой-то раз 🙂
(И еще для этого wsock32.dll нужно подключить к проекту в Project
Properties/Link). Инициализация в программе достигается
вызовом функции WSAStartup
WSADATA WsaData;
int err = WSAStartup (0x0101, &WsaData);
if (err == SOCKET_ERROR)
{
MessageBox(0,"Error Startup!",0,0);//вывод сообщения об ошибке
инициализации
return 1;
}
Далее мы создаем сервер логгера, к которому будет подключаться клиент.
Сначала создадим сокет:
s=socket(AF_INET,SOCK_STREAM,0);
Потом задаем параметры для сокета (сервера), сначала объявив структуру SOCKADDR_IN ser1, а затем заполняем параметры для сервера:
SOCKADDR_IN ser1;
ser1.sin_family = AF_INET;
ser1.sin_port = htons(88);//port
ser1.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
Здесь sin_port=htons(88) - как ты наверно догадался, порт нашего сервера,
inet_addr() - соответственно адрес сервака. Лучше использовать адрес
localhost'a.
Для подключения сокета к среде сети нужно выполнить системный вызов bind, который "привязывает" структуру ser1 к сокету s.
bind( s, (LPSOCKADDR)&ser1, sizeof(ser1));
Cервер создан :).
Теперь будем создавать клиента, который будет подключаться к прокси. Аналогично
создаем сокет:
s1=socket(AF_INET,SOCK_STREAM,0);
Но в структуре будут некоторые отличия:
SOCKADDR_IN cl1;
cl1.sin_family = AF_INET;
cl1.sin_port = htons(8000);//port
cl1.sin_addr.S_un.S_addr =INADDR_ANY;//Adress
В данном случае в поле sin_addr стоит INADDR_ANY, чтобы после коннекта привязать сокет к локальному узлу сети.
SOCKADDR_IN sc2;
sc2.sin_family = AF_INET;
sc2.sin_port = htons(80);//порт прокси
sc2.sin_addr.S_un.S_addr =inet_addr("xxx.xxx.xxx.xxx");//адрес прокси
В этой структуре мы задаем параметры сервера, к которому будет коннектиться логгер.
Теперь подключаемся:
int erc;//переменная для кода ошибки подключения
do
{
erc=connect(s1, (struct sockaddr *)&sc2, sizeof(struct sockaddr));
}
while (erc!=0)
Цикл - подключаемся пока не будет коннекта.
Теперь, когда мы подключились к прокси, нужно создать сервер, чтобы к нему мог подключиться клиент, в роли которого выступает твой браузер.
Вся фишка в том, что если бы клиент коннектился бы сразу к прокси, то он каждый раз бы создавал новое соединение, на что уходило бы лишнее время. У нас же коннект логгера с прокси-сервером постоянный и не зависит от состоянии клиента юзера, поэтому соединение логгера с проксей устанавливается раз и навсегда :))
Соединение же клиента с логгером нужно устанавливать каждый раз заново, когда браузер посылает запрос на какой-либо адрес.
//Слушаем порт все время, фиг с ним, с выходом 🙂
while (1)
{
listen(s,1);
//"cлушаем" сервер, ждем коннекта.
SOCKADDR_IN ser2;
int serlen=sizeof(ser2);
s2= accept(s,(struct sockaddr*)&ser2, &serlen);
Когда есть запрос, то подключаем клиента к серваку вызовом
accept. В структуре ser2 - параметры установившегося соединения.
s2 - сокет сервера, через который он обменивается данными с клиентом.
После установления связи приступаем к самой важной части - чтением данных из сокета клиента и пересылкой их в сокет прокси, с записью данных в файл конечно!
int nn,rr;
int yy;
char Buf[100000];//буфер, в которой читаются
(и из которого пишутся) данные
FILE *f;
char c;
while ((nn=recv(s2,Buf,sizeof(Buf),0))!=-1)
//пока нету ошибки читаем данные функцией recv (Код ошибки "-1")
{
if (nn>0)
//в переменной nn - кол-во прочитанных байт.
//Если что-то прочитали - то запишем.
{
f=fopen("c:\\clt.txt","aw+");
//каждый раз открываем файл и пишем в его конец.
for (yy=0;yy
{
c=Buf[yy];
fprintf(f,"%c",c);
}
// конечно этот кусок кода, пишущий данные в файл,
//можно усовершенствовать. Я рассматриваю самый простой
//способ, уходящий корнями еще в старый добрый C :)))
fclose(f);
//думаю догадался - закрываем файл.
rr=send(s1,Buf,nn,0);
//посылаем данные прокси.
//nn - кол-во пересылаемых байт. 0 -флаг.
//rr - кол-во отосланных байт, -1 - когда ошибка
}
}
}
Если бы был нормальный выход, то в конце нужно было бы
закрыть сокеты:
shutdown(s,0);
closesocket(s);
shutdown(s1,0);
closesocket(s1);
shutdown(s2,0);
closesocket(s2);
В общем, одну часть программы мы сделали - выполнили передачу данных от клиента к прокси. Но самое интересное - впереди 🙂 Думаю ты знаешь, что передача данных должна идти в две стороны - от клиента к прокси и от прокси к клиенту. Самое прикольное, что они должны идти параллельно. Вот для этого мы и юзаем MFC :). Нам нужно, чтобы параллельно с вышеописанным процессом передачи данных от клиента к прокси шел и
обратный процесс. Для этого мы запустим его параллельно с уже идущим. Такую возможность дает функция
AfxBeginThread - "начать поток". Поэтому вставим эту функцию сразу после подключения логгера к прокси:
AfxBeginThread(*Function1,NULL);
//*Functuion1 - указатель на нужную функцию.
Опишем ее - для этого перед основной функцией TheApp:
UINT Function1( LPVOID pParam);
А в самом конце программы собственно должна находиться сама функция -
UINT Function1( LPVOID pParam)
{
// эта функция аналогична уже описанной, читающей передающий данные
//от клиента к прокси.
char Buf1[100000];
int nn1;
int rr1;
int yy1;
FILE *f1;
char c1;
while (1)
//опять-таки читаем все время, так как коннект у логгера с прокси постоянный,
//а сервер шлет пакеты только при запросе клиента.
{
nn1=recv(s1,Buf1,sizeof(Buf1),0);
rr1=send(s2,Buf1,nn1,0);
if (nn1>0)
{
f1=fopen("c:\\serv.txt","aw+");
for(yy1=0;yy1
c1=Buf1[yy1];
fprintf(f1,"%c",c1);
}
fclose(f1);
}
}
return 0;
//код завершения
}
И не забудь - переменные дескрипоторов сокетов
s, s1 и s2 должны быть глобальные, так как доступ к ним необходим
из обеих функций. Поэтому объяви их вне функций, можешь сразу после includ'ов.
Как юзать? Элементарно - в браузер вбивается IPшник и порт логгера, как обычный прокси.
После всего вышеперечисленного программу можно считать законченной.
Она конечно далека от совершенства, в ней не предусмотрены мелочи типа выхода
(а Ctrl-Alt-Del тебе на что 🙂 ), да и кроме ведения лога можно добавить ей новые функции, но это уже тема для новой отдельной статьи.