开发者

Non-Blocking socket and poll() quirks in proxy

Thanks to the help here i got a mostly working socks4 proxy based on poll(). I am using this programm to learn C and socket programming. This programm is still lacking send() checks for partial writes, but i am fairly certain that is not the cause of the current issues with it. It is working OK with some proxied TCP-connections, for instance netcat or even chrome (though that throws excessives recv() errors). It doesn't work with firefox or ssh. I am not sure what difference is there, but i think somehow i must be handling the sockets incorrectly. The error currently presents itself as such with nonworking clients:

  1. client sends socks4 connection request to proxy (OK)
  2. proxy sends socks4 OK to client (OK)
  3. client sends TCP segment(s) with payload data to proxy (OK)
  4. Proxy forwards TCP segment(s) with payload data to server (OK)
  5. Server sends TCP segment(s) with payload data to proxy (OK)
  6. Proxy sends TCP segment(s) with payload to client (OK)
  7. client does nothing (NOT OK)

I double-checked the tcp-segment payload and it is exactly the same for a tcp segment from a proxy and from a connection directly from a server. For instance an ssh client should send a ssh client hello to the server after he received an ssh server hello.

if i connect via netcat (as a client) i can verify that i get the banner correctly:

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

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

I see in wireshark that the ssh client gets this banner as well, but he do开发者_StackOverflow中文版es nothing. The same behavior takes place with SSL connections and normal connections in firefox (firefox tells me whether i want to save "binary data"). There is probably something wrong in my code:

#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);
}

Added info: For instance this is a TCP segment from the proxy vs. the ssh server - the first one gets ignored by the client and the connection times out, the second one gets responsed with a ssh client response. I am totally confused since it is exactly the same payload:

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..


As you said, you are not handling partial writes. Given the extreme size of your buffers, it is not impossible that the writes return prematurely. Just for testing: try lowering the buffer sizes to a sane amount (a few hundred bytes).

Also, to get this thing robust, you really need to add some (cyclic) buffering code.


Just FYI for anyone stumbling on this, the error was in the socks4 protocol implementation (one byte to much in the response).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜