Underground InformatioN Center [&articles] | |
[network & security news] [RSS & Twitter] [articles, programing info] [books] [links, soft & more...] [soft archive] | [home] |
Введение самая лучшая документация для программиста - это исходный текст программы
Для начала определимся с константами. Какой локальный порт будем прослушивать: #define IN_PORT 3128 Удаленный IP адрес. #define OUT_IP "192.168.0.1" Порт к которому будем подключаться. #define OUT_PORT 3128 Решим, какое максимальное количество соединений мы будем поддерживать. #define MAXCONN 1000 Объявим глобальные переменные. Это будет буфер для принятых данных. char buf[MAX_DATA]; Слушающий сокет, на который будут коннектиться клиенты. SOCKET hListenSockTCP; Массив дескрипторов сокетов, полученных при соединении нашей программой с удаленным сервисом в ответ на подключение со стороны клиента. Дескриптор сокета с клиентской стороны и будет индексом. Например: к нашей программе подключается клиент. После выполнения строки "currentsock = accept(hListenSockTCP,NULL,NULL);" в переменной currentsock типа SOCKET будет возращен дескриптор сокета. Он, например, может быть числом 5,6 и т.д., поэтому, сам дескриптор можно использовать в качестве индекса в массиве. Теперь в ответ на "пятое" соединение (в случае, когда currentsock=5) соединяемся с прокси, на который мы делаем перенаправление, и полученный дескриптор сохраняем sockets[5]. Это равносильно строке "sockets[5]=connect (sockets[nofsock], ". Как вы должны понимать, это не самый лучший метод. Но зато он самый простой и нам пока подойдет. SOCKET sockets[MAXCONN];Начнем. * Инициализация среды перед использованием WinSock: WSADATA stWSADataTCPIP; if(WSAStartup(0x0101, &stWSADataTCPIP)) MessageBox(hwndMain, "WSAStartup error !","NET ERROR!!!",0); * Заполним массив дескрипторов сокетов нулями (на всякий случай). ZeroMemory(sockets,sizeof(sockets)); * Зарегистрируем класс и создадим окно. Получим hwndMain - дескриптор окна. * Создадим сокет. hListenSockTCP = socket (AF_INET,SOCK_STREAM,0); * Заполним структуру SOCKADDR_IN, указав тип протокола(family) и порт, к которому будем "биндиться", и "привязываем" сокет. SOCKADDR_IN myaddrTCP; myaddrTCP.sin_family = AF_INET; myaddrTCP.sin_addr.s_addr = htonl (INADDR_ANY); myaddrTCP.sin_port = htons (IN_PORT); bind( hListenSockTCP,(LPSOCKADDR)&myaddrTCP, sizeof(struct sockaddr) ); * Запускаем сокет "на прослушку". listen (hListenSockTCP, SOMAXCONN)); * Привязываем события FD_ACCEPT, FD_READ, FD_CLOSE сокета к главному окну программы. WSAAsyncSelect (hListenSockTCP,hwndMain,WM_ASYNC_CLIENTEVENT, FD_ACCEPT|FD_READ|FD_CLOSE); Это значит, что при попытке клиента подключиться к прослушиваемому сокету окну с дескриптором hwndMain будет передаваться сообщение WM_ASYNC_CLIENTEVENT. Напомню, что функция обработки сообщений окна выглядит так - "LRESULT CALLBACK MainWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)". В переменной wParam будет передан дескриптор сокета, в котором произошло событие. А какое именно - узнаем из lParam. Но кроме кода события в lParam еще находится код ошибки. Для извлечения их этого 4-х байтного числа (DWORD) двух слов (WORD) существуют два макроопределения - WSAGETSELECTERROR(lParam) и WSAGETSELECTEVENT(lParam). * Процедура обработки сообщений. ............. case WM_ASYNC_CLIENTEVENT: Сообщения о событиях подключенных к клиенту сокетов... currentsock = wParam; именно так узнаем, какое событие с сокетом произошло WSAEvent = WSAGETSELECTEVENT (lParam); switch (WSAEvent) { Это сообщение приходит тогда, когда к нам хотят подключиться. case FD_ACCEPT: Разрешаем подключение клиента, и пытаемся теперь подключиться к нашему удаленному прокси. ConnectToProxy(accept(hListenSockTCP,NULL,NULL)); Если это не удалось, закрываем соединение, которое только что мы позволили установить с нами клиенту. Второй параметр - SD_SEND (у меня - просто единица). Этим мы позволяем соединению спокойно закрыться. После этой команды с сокетом произойдет событие "FD_CLOSE". shutdown(currentsock,1); return 0; case FD_CLOSE : Клиент по какой-либо причине хочет прервать соединение. Глушим соединение с уд. прокси, которое мы установили в ответ на это соединение. shutdown(sockets[currentsock],1); и закрываем сокет. closesocket(currentsock); return 0; case FD_READ: На сокет пришли данные. Берем от клиента, посылаем на сервер. i=recv(currentsock, buf, MAX_DATA, 0); send(sockets[currentsock], buf, i, 0); и отправляем... return 0; } break;
Так же поступаем и с обработкой событий на сокетах, когда сам наш прокси является
клиентом другого прокси, на который мы делаем перенаправление. Проще говоря, у нас
в программе будут две группы сокетов. case WM_ASYNC_PROXYEVENT: Найдем соответствующий дескриптор в массиве. for (i=0;i<MAXCONN;i++) if (sockets[i] == wParam) { currentsock=i; break; } Теперь в currentsock - наше соединение с клиентом, а в sockets[currentsock] - соответствующее ему соединение с удаленным прокси. WSAEvent = WSAGETSELECTEVENT (lParam); switch (WSAEvent) { Произошло подключение к удаленному хосту. case FD_CONNECT : i=WSAGETSELECTERROR(lParam); if (i!=0) Если соединение не удалось, закроем уже установленное соединение с клиентом и сокет, который мы создали, пытаясь установить соединение с удаленным прокси. { shutdown(currentsock,1); closesocket(sockets[currentsock]); sockets[currentsock]=INVALID_SOCKET; } return 0; Сервер нас отрубает... case FD_CLOSE : shutdown(currentsock,1); closesocket(sockets[currentsock]); sockets[currentsock]=INVALID_SOCKET; return 0; Перенаправление данных клиенту. case FD_READ: i=recv(sockets[currentsock], buf, MAX_DATA, 0); send(currentsock,buf, i, 0); return 0; } break; А теперь рассмотрим функцию соединения с прокси. void ConnectToProxy(SOCKET nofsock) { Заполняем структуру - IP, с которым мы будем связываться, порт, тип протокола. SOCKADDR_IN rmaddr; rmaddr.sin_family = AF_INET; rmaddr.sin_addr.s_addr = inet_addr(OUT_IP); rmaddr.sin_port = htons (OUT_PORT); Создание сокета TCP. sockets[nofsock] = socket (AF_INET,SOCK_STREAM,0); Привязываем события FD_READ и FD_CLOSE с этим сокетом к главному окну приложения сообщением WM_ASYNC_PROXYEVENT. Тем самым мы переводим сокет в не блокирующий режим. WSAAsyncSelect (sockets[numofsock],hwndMain,WM_ASYNC_PROXYEVENT, FD_CONNECT|FD_READ|FD_CLOSE); Пытаемся соединиться. connect (sockets[nofsock], (struct sockaddr *)&rmaddr,sizeof(rmaddr)); Результат функции connect() мы не проверяем, так как она завершится до того, как соединение будет установлено. return; } Warning! hsocket=accept(hListenSockTCP,NULL,NULL); if(hsocket>MAXCONN) { shutdown(hsocket); close(hsocket); }4. Еще раз внимательно посмотрите на код, и, возможно, вы найдете еще что-то, что я упустил. Код программы. Скачать можно здесь. (Скачано 11919 раз)
Ну что, вы разобрались и готовы к реализации полноценной программы? Тогда заходите сервер UInC в раздел проектов (или на kmint21.com). Там вы найдете пример полнофункциональной реализации такого port-mapper-а. В нем вы увидите обработку всех ошибок, ведение лога, обработку командной строки и многое другое.Попутно замечу, что его размер 5 Kb! А о том, как писать такие маленькие программы, вы можете прочитать в статье "Написание экстра-маленьких Win32 приложений на С++" (c) Copyright 2001. Украина, Запорожье. KMiNT21 (mailto:kmint21@mail.ru).uinC Member [c]uinC
Статья написана специально для UInC (www.uinc.ru).
Все документы и программы на этом сайте собраны ТОЛЬКО для образовательных целей, мы
не отвечаем ни за какие последствия, которые имели место как следствие использования
этих материалов\программ. Вы используете все вышеперечисленное на свой страх и риск. |
[network & security news] [RSS & Twitter] [articles, programing info] [books] [links, soft & more...] [soft archive] | [home] |
Underground InformatioN Center [&articles] |
2000-2015 © uinC Team |