2leonid,
Интересно, какое Вы приняли решение?
Может Вы неправильно меня поняли? Я постил, что действительно нужно в общем случае знать особенности межсетевого экрана (в Канаде говорят firewall), который отделяет локальную сеть от инет. Там я постил про SOCKS 4, SOCKS 5. Но может у Вас сложилось неверное впечатление, что раз firewall, то значит что-то жутко сложное?
Протоколы SOCKS - не то, что просто, а очень просто. Просто некоторые недопрограммеры сознательно наводят здесь туман, дескать - как всё сложно! Наверное, чтоб подчеркнуть собственную значимость, или урвать больше денег. На самом деле работа с этими протоколами ничуть не сложнее, чем всё делается в LAN. Тем, более, что практически все firewall (как промышленные, так и самоделки) поддерживают не только SOCKS 5, но и старый SOCKS 4. А там не то, чтоб очень просто - там проще пареной репы. В двух словах:
1) программер создаёт на клиенте сокет, положим socket, и коннектится не к инет-хосту, а к SOCKS-серверу. Этот SOCKS-сервер расположен "на firewall", и доступен из локальной сети. Обычно он слушает порт 1080.
2) программер посылает SOCKS-серверу запрос (а лучше здесь сказать сообщение) очень простого формата, в котором "говорит", что он хочет в дальнейшем посылать пакеты на такой-то хост в инет, и на такой-то порт. Этот запрос посылают обычным send API на сокет socket, который бы создан на шаге 1.
3) Всё. Если SOCKS-сервер ответит OK!, (это вычитывается из сокета socket обычной функцией API recv), то дальше пишут и читают из сокета socket обычным образом, как для LAN.
Ниже - работающий пример, как послать почту по smtp с компа, который расположен в LAN и прикрыт firewall, понимающим SOCKS 4. Таких примеров в инете море. И это уровень джуниор С-программера, по крайней мере, в совке. Пример, намеренно написан "размашесто" в учебных целях для ясности того, что делается:
Код: Выделить всё
int _tmain(int argc, _TCHAR* argv[])
{
WSAData wsaData;
if (WSAStartup(0x202,(WSADATA *)&wsaData ))
{
printf("WSAStart error %d\n",WSAGetLastError());
return -1;
}
// Cоздаём обычный сокет, как для LAN.
SOCKET sock;
sock=socket(AF_INET,SOCK_STREAM,0);
if ( sock < 0 )
{
printf("Socket() error %d\n",WSAGetLastError());
closesocket(sock);
WSACleanup();
return -1;
}
// socks-сервер у нас в локальной сети здесь
#define SOCKSSERVER "192.168.x.y"
#define SOCKSPORT 1080
sockaddr_in socks_addr;
socks_addr.sin_family = AF_INET;
socks_addr.sin_port = htons(SOCKSPORT);
socks_addr.sin_addr.s_addr= inet_addr(SOCKSSERVER);
// коннектимся не к майл-серверу, а к сокс-серверу (посреднику)
if ( connect( sock,(sockaddr *)&socks_addr, sizeof(socks_addr)) )
{
printf("Connect error %d\n",WSAGetLastError());
closesocket( sock );
WSACleanup();
return -1;
}
printf("Connection with SOCKSSEREVER %s Ok\n",SOCKSSERVER);
// подготовим запрос к сокс-серверу, в котором только расскажем, чтобы
// нам необходим туннель к хосту 194.67.23.10 и порту 25
// весь сокс-запрос поместится в char sockscmd[9]
// (часть полей запроса не будем заполнять)
_DWORD sin_addr = inet_addr( "194.67.23.10" ); // это инет-адрес майлера
_WORD sin_port = htons( 25 );
char sockscmd[9];
memset( sockscmd, 0, 9 );
sockscmd[0] = 4; // версия. socks 4
sockscmd[1] = 1; // команда
memmove( &sockscmd[2], &sin_port, 2 ); // порт инет-хоста
memmove( &sockscmd[4], &sin_addr, 4 ); // его инет-адрес
_send( sock, (_BYTE *) sockscmd, 9 , 0, 1 );
int num = _recv( sock, (_BYTE *) sockscmd, 8, 0 , 1);
if ( num != 8 || sockscmd[1]!=90 )
{
printf("Socks request failed. Press any key ...\n");
closesocket(sock);
WSACleanup();
getch();
return -1;
}
printf("Socks request success.\n");
// Эти строчки и далее уже не имеют отношения к SOCKS.
// Это обычная запись и чтение из сокета, как если б он был в LAN
// или наш хост смотрел бы непосредственно в инет
// В данном случае мы посылаем письмо на mail.ru
char *shelo = "HELO mail.ru\r\n";
char *sfrom = "MAIL FROM:<vg_123@mail.ru>\r\n";
char *srcpt = "RCPT TO:<vg_123@mail.ru>\r\n";
char *sdata = "DATA\r\n";
char *smsg = "SUBJECT: SOCKS 4\r\nHello!\r\n\r\n.\r\n";
char *squit = "QUIT\r\n";
// строки откликов smtp невелики. хватит с избытком.
char rcvbuf[256];
// здесь прочитаем приветствие майлера.
// читаем из сокета сокс-сервера обычным образом,
// как если б это был сокет майлера
// сокс-сервер прозрачно для нас мапит все запросы на удалённый хост
memset( rcvbuf, 0, 256 );
int rcvbytes = _recv( sock, (_BYTE *) rcvbuf, 256, 0, 1);
printf("%s\n",rcvbuf);
// HELO
printf("%s\n",shelo);
_send( sock, (_BYTE *) shelo, (_DWORD) strlen(shelo), 0, 1);
memset( rcvbuf, 0, 256);
rcvbytes = _recv( sock, (_BYTE *) rcvbuf, 256, 0, 1);
printf("%s\n",rcvbuf);
// MAIL FROM
printf("%s\n",sfrom);
_send( sock, (_BYTE *) sfrom, (_DWORD) strlen(sfrom), 0, 1);
memset( rcvbuf, 0, 256);
rcvbytes = _recv( sock, (_BYTE *) rcvbuf, 256, 0, 1);
printf("%s\n",rcvbuf);
// RCPT TO
printf("%s\n",srcpt);
_send( sock, (_BYTE *) srcpt, (_DWORD) strlen(srcpt), 0, 1);
memset( rcvbuf, 0, 256 );
rcvbytes = _recv( sock, (_BYTE *) rcvbuf, 256, 0 , 1);
printf("%s\n",rcvbuf);
// DATA
printf("%s\n",sdata);
_send( sock, (_BYTE *) sdata, (_DWORD) strlen(sdata), 0, 1);
memset( rcvbuf, 0, 256 );
rcvbytes = _recv( sock, (_BYTE *) rcvbuf, 256, 0 , 1);
printf("%s\n",rcvbuf);
// MSG
printf("%s\n",smsg);
_send( sock, (_BYTE *) smsg, (_DWORD) strlen(smsg), 0, 1);
memset( rcvbuf, 0, 256 );
rcvbytes = _recv( sock, (_BYTE *) rcvbuf, 256, 0 , 1);
printf("%s\n",rcvbuf);
// QUIT
printf("%s\n",squit);
_send( sock, (_BYTE *) squit, (_DWORD) strlen(squit), 0, 1);
memset( rcvbuf, 0, 256 );
rcvbytes = _recv( sock, (_BYTE *) rcvbuf, 256, 0 , 1);
printf("%s\n",rcvbuf);
shutdown( sock, SD_SEND );
closesocket( sock );
WSACleanup();
printf("\nAny key for exit...");
getch();
return 0;
}
В этом коде видно - то, что связано с SOCKS, и что обеспечивает "работу сквозь прокси" - это копейки. Здесь мы подконнектились к сокс-серверу в локальной сети 192.168.x.y:1080. Сказали, что дальше в инете будем "говорить" с майлером 194.67.23.10:25. А дальше всё делаем, как для локальной сети. Пишем и читаем в
тот же сокет SOCKS-сервера:
Код: Выделить всё
#define SOCKSSERVER "192.168.x.y"
#define SOCKSPORT 1080
sockaddr_in socks_addr;
socks_addr.sin_family = AF_INET;
socks_addr.sin_port = htons(SOCKSPORT);
socks_addr.sin_addr.s_addr= inet_addr(SOCKSSERVER);
// коннектимся не к майл-серверу, а к сокс-серверу (посреднику)
if ( connect( sock,(sockaddr *)&socks_addr, sizeof(socks_addr)) )
{
printf("Connect error %d\n",WSAGetLastError());
closesocket( sock );
WSACleanup();
return -1;
}
printf("Connection with SOCKSSEREVER %s Ok\n",SOCKSSERVER);
// подготовим запрос к сокс-серверу, в котором только расскажем, чтобы
// нам необходим туннель к хосту 194.67.23.10 и порту 25
// весь сокс-запрос поместится в char sockscmd[9]
// (часть полей запроса не будем заполнять)
_DWORD sin_addr = inet_addr( "194.67.23.10" ); // это инет-адрес майлера
_WORD sin_port = htons( 25 );
char sockscmd[9];
memset( sockscmd, 0, 9 );
sockscmd[0] = 4; // версия. socks 4
sockscmd[1] = 1; // команда
memmove( &sockscmd[2], &sin_port, 2 ); // порт инет-хоста
memmove( &sockscmd[4], &sin_addr, 4 ); // его инет-адрес
_send( sock, (_BYTE *) sockscmd, 9 , 0, 1 );
int num = _recv( sock, (_BYTE *) sockscmd, 8, 0 , 1);
if ( num != 8 || sockscmd[1]!=90 )
{
printf("Socks request failed. Press any key ...\n");
closesocket(sock);
WSACleanup();
getch();
return -1;
}
printf("Socks request success.\n");
И последнее, я выше постил про то, что таким образом можно на любой из портов. Не нравится "нестандарный" сервис - так у вас теперь практически действующий пример использования стандартного smtp. Пусть аналогичная прога у Ваших кастомеров
автоматически шлёт статистику
по smtp на Ваш сервер. Канва есть - дальше можно наворачивать.
ПС. При тестовой компилляции и проверке примера выше замените "192.168.x.y" на адрес своего firewall, а _send/_recv на стандартные send / recv. У меня эти функции написаны давно на select - aх. И вычитывается ровно столько, сколько необходимо. В примере, что выше не стал править. Там можно задавать тайм-ауты. В принципе, при программировании для интернет, думаю, всем необходимо задавать тайм-ауты. Конечно, в реальной задаче надо будет обрабатывать ошибки чтения/записи, уметь читать некую конфигурационную инфу на клиентах. Но это мелочи.