Неблокирующие сокеты и опросы poll () в прокси - PullRequest
1 голос
/ 02 сентября 2011

Благодаря помощи здесь я получил в основном работающий прокси socks4, основанный на опросе ().Я использую эту программу для изучения программирования C и сокетов.В этой программе по-прежнему отсутствуют проверки send () для частичной записи, но я вполне уверен, что это не является причиной текущих проблем с ней.Он работает нормально с некоторыми прокси-TCP-соединениями, например, netcat или даже chrome (хотя это приводит к избыточным ошибкам recv ()).Это не работает с Firefox или SSH.Я не уверен, какая разница, но я думаю, что почему-то я должен обращаться с сокетами неправильно.В настоящее время ошибка проявляется как таковая для нерабочих клиентов:

  1. клиент отправляет запрос соединения socks4 на прокси (OK)
  2. прокси отправляет socks4 OK клиенту (OK)
  3. клиент отправляет сегмент (ы) TCP с данными полезной нагрузки на прокси (OK)
  4. Прокси-сервер пересылает сегмент (ы) TCP с данными полезной нагрузки на сервер (OK)
  5. Сервер отправляет сегмент (ы) TCPс данными полезной нагрузки на прокси (OK)
  6. Прокси-сервер отправляет сегменты TCP с полезной нагрузкой клиенту (OK)
  7. клиент ничего не делает (НЕ ОК)

Я дважды проверил полезную нагрузку tcp-сегмента, и она точно такая же для сегмента tcp от прокси и от соединения напрямую с сервера.Например, ssh-клиент должен отправить ssh-клиент привет на сервер после того, как он получил ssh-сервер hello.

Если я подключаюсь через Netcat (как клиент), я могу проверить, правильно ли я получаю баннер:

root @ ubuntu # nc -X 4 -x 127.0.0.1:8000 127.0.0.1 22 * ​​1023 *

SSH-2.0-OpenSSH_5.3p1 Debian-3ubuntu7

В Wireshark я вижу, чтоssh-клиент тоже получает этот баннер, но ничего не делает.Такое же поведение имеет место с SSL-соединениями и обычными соединениями в Firefox (Firefox говорит мне, хочу ли я сохранить «двоичные данные»).Возможно, что-то не так в моем коде:

#include <stdio.h>      
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>    
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <errno.h>

#define RCVBUFSIZE 1000000


void ProxyData(int rcvSocket)
{
   char rcvBuffer[RCVBUFSIZE];
   char sndBuffer[RCVBUFSIZE];
   int recvMsgSize;
   int sndMsgSize;
   char Socks4Response[] = "\x00\x5a\x00\x00\x00\x00\x00\x00";

   int errno;

   int dstSocket;
   struct sockaddr_in dstAddr;

   struct pollfd fds[2];
   int timeout_msecs = 67000; /* dont use this yet */ 

   /* Receive message from client */
   if ((recvMsgSize = recv(rcvSocket, rcvBuffer, RCVBUFSIZE, 0)) < 0)
   {
      perror("recv() failed");
      close(rcvSocket);
      exit;
   }

   /* Send Sock 4 Response... this is not robust ofc... */
   if ((rcvBuffer[0] == 0x04))
      if (send(rcvSocket, Socks4Response, 9, 0) < 0)
      {
         perror("send() failed");
         close (rcvSocket);
         exit;
      }

   /* todo implement socks error responsees*/


   /* setting up the destination socket for the socks request */
   if((dstSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
   {        
      perror("socket() failed");
      close(rcvSocket);
      exit;
   } 

   memset(&dstAddr, 0, sizeof(dstAddr));
   dstAddr.sin_family = AF_INET;
   memcpy(&dstAddr.sin_addr.s_addr, &rcvBuffer[4], 4);
   memcpy(&dstAddr.sin_port, &rcvBuffer[2], 2);


   if (connect(dstSocket, (struct sockaddr *) &dstAddr, sizeof(dstAddr)) < 0 )
   {
      perror("connect() failed");
      close(rcvSocket);
      close(dstSocket);
      exit;  
   }

   fds[0].fd = rcvSocket;
   fds[1].fd = dstSocket;
   fds[0].events = POLLIN;
   fds[1].events = POLLIN;

   signal(SIGPIPE, SIG_IGN);
   fcntl(rcvSocket, F_SETFL, O_NONBLOCK);
   fcntl(dstSocket, F_SETFL, O_NONBLOCK);

   recvMsgSize = 1; /* set this so condition doesnt fail on first while loop */
   sndMsgSize = 1; /* see above */

   while (1)
   {
         poll(fds, 2, -1); /* polling indefinately */

         /* client to server block */

         if ((fds[0].revents & POLLIN) && (recvMsgSize > 0)) /* if data is available and the socket wasnt closed before we read on it */
         {
            recvMsgSize = recv(rcvSocket, rcvBuffer, RCVBUFSIZE, 0);
            if (recvMsgSize > 0)
            {
               sndcheck = send(dstSocket, rcvBuffer, recvMsgSize, 0);
               if (sndcheck < 0)
               {
                  perror("send() dstSocket failed");
                  if ((errno != EINTR) && (errno != EWOULDBLOCK)) break; /* connection failure -> going to close() outside loop*/
               }
            }
            if (recvMsgSize == 0) shutdown(dstSocket, SHUT_WR);
            if (recvMsgSize < 0) 
            {
               perror("recv() rcvSocket failed");
               if ((errno != EINTR) && (errno != EWOULDBLOCK))  break; /*connection failure -> going to close() outside loop*/
            }
         }


         /* server to client block */

         if ((fds[1].revents & POLLIN) && (sndMsgSize > 0)) /* if data is available and the socket wasnt closed before we read on it */
         {   
            sndMsgSize = recv(dstSocket, sndBuffer, RCVBUFSIZE, 0);
            if (sndMsgSize > 0) 
            {
               if (send(rcvSocket, sndBuffer, sndMsgSize, 0) < 0)
               {
                  perror("send() rcvSocket failed");
                  if ((errno != EINTR) && (errno != EWOULDBLOCK)) break; /* connection failure -> going to close() outside loop*/
               }
            }
            if (sndMsgSize == 0) shutdown(rcvSocket, SHUT_WR);
            if (sndMsgSize < 0)
            {
               perror("recv() dstSocket failed");
               if ((errno != EINTR) && (errno != EWOULDBLOCK)) break; /* connection failure -> going to close() outside loop*/
            }
         }   


         if ((sndMsgSize == 0) && (recvMsgSize == 0)) break; /* both sockets shutdowned() cleanly, close() outside loop*/
   }                
   close(rcvSocket);
   close(dstSocket);
}

Добавлена ​​информация: Например, это сегмент TCP от прокси против сервера ssh - первый игнорируется клиентом, и время ожидания соединения истекаетвторой получает ответ от клиента ssh.Я полностью сбит с толку, так как это точно такая же полезная нагрузка:

0000   00 50 56 c0 00 08 00 0c 29 38 32 d4 08 00 45 00  .PV.....)82...E.
0010   00 4f 4a 21 40 00 40 06 be b4 c0 a8 58 81 c0 a8  .OJ!@.@.....X...
0020   58 01 1f 40 c1 08 a1 29 ff 93 72 1b 9c a6 50 18  X..@...)..r...P.
0030   00 b7 2f 63 00 00 53 53 48 2d 32 2e 30 2d 4f 70  ../c..SSH-2.0-Op
0040   65 6e 53 53 48 5f 35 2e 33 70 31 20 44 65 62 69  enSSH_5.3p1 Debi
0050   61 6e 2d 33 75 62 75 6e 74 75 37 0d 0a           an-3ubuntu7..


0000   00 50 56 c0 00 08 00 0c 29 38 32 d4 08 00 45 00  .PV.....)82...E.
0010   00 4f 2d c9 40 00 40 06 db 0c c0 a8 58 81 c0 a8  .O-.@.@.....X...
0020   58 01 00 16 c1 0a 74 9e bf a4 9b 43 5e c4 50 18  X.....t....C^.P.
0030   00 b7 cf bf 00 00 53 53 48 2d 32 2e 30 2d 4f 70  ......SSH-2.0-Op
0040   65 6e 53 53 48 5f 35 2e 33 70 31 20 44 65 62 69  enSSH_5.3p1 Debi
0050   61 6e 2d 33 75 62 75 6e 74 75 37 0d 0a           an-3ubuntu7..

Ответы [ 2 ]

0 голосов
/ 03 сентября 2011

Просто к сведению всех, кто сталкивался с этим, ошибка была в реализации протокола socks4 (один байт в ответ).

0 голосов
/ 03 сентября 2011

Как вы сказали, вы не обрабатываете частичные записи. Учитывая экстремальный размер ваших буферов, не исключено, что записи возвращаются преждевременно. Только для тестирования: попробуйте уменьшить размеры буфера до нормального значения (несколько сотен байт).

Кроме того, чтобы сделать эту штуку надежной, вам действительно нужно добавить некоторый (циклический) буферный код.

...