开发者

Non-Blocking socket and poll() quirks in proxy - newbie

开发者 https://www.devze.com 2023-04-01 01:46 出处:网络
I am a newbie dabbling in C and my little project is to write a simple SOCKS4 proxy. Thanks to the help here i got so far to use non-blocking sockets and poll() for my routine. However at this point i

I am a newbie dabbling in C and my little project is to write a simple SOCKS4 proxy. Thanks to the help here i got so far to use non-blocking sockets and poll() for my routine. However at this point i seem to have 2 problems:

  1. The outgoing Socket dstSocket doesn't get closed if the incoming Socket rcvSocket gets closed and vice versa. I don't check for this in the loop, but i don't know how. I tried POLLHUP as revents, but that doesn't seem to do the trick. A normal check seems to be whether recv() returns 0, but is that also valid for non-blocking sockets? And if so, how does that work with revents, i can't seem to figure out where i would put this, since if POLLIN | POLLPRI are set it seems to me recv() never should return 0? Also i don't understand what the exact difference is between POLLIN and POLLPRI, seems to me just a check "data is available for reading" in either case?

  2. The proxy seems to work for connections i tested with netcat. However if i use a browser, it says (when i target a website) whether i want to save "binary data". I checked the data in wireshark and what is received from the server is correctly forwarded to the client byte by byte it seems. If anyone maybe has an idea why that could happen with this program it would be nice :)

Attached the relevant code (beware programming newbie):

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

 timer = poll(fds, 2, timeout_msecs); /* i dont use this yet */

 fcntl(rcvSocket, F_SETFL, O_NONBLOCK);
 fcntl(dstSocket, F_SETFL, O_NONBLOCK);

 while (1 == 1)
 {
     if (fds[0].revents &开发者_开发技巧 POLLIN | POLLPRI)
     {
        recvMsgSize = recv(rcvSocket, rcvBuffer, RCVBUFSIZE, 0);
        if (recvMsgSize > 0) {send(dstSocket, rcvBuffer, recvMsgSize, 0);}
     }
     if (fds[1].revents & POLLIN | POLLPRI)
     { 
        sndMsgSize = recv(dstSocket, sndBuffer, RCVBUFSIZE, 0);
        if (sndMsgSize > 0) { send(rcvSocket, sndBuffer, sndMsgSize, 0);}
     }         

     if ((fds[0].revents & POLLHUP) || (fds[1].revents & POLLHUP))
     {
        close(rcvSocket);
        close(dstSocket);
     }
} 


recv() returns 0 on a clean remote shutdown - this is true even for nonblocking sockets. In this case, POLLIN will be returned - the notification that the remote side has closed the socket is considered a "readable" event.

You shouldn't need to use POLLPRI for SOCKS / HTTP connections - it indicates TCP "urgent data", which isn't used by those protocols (or indeed, much used at all).


Apart from your direct questions, you need to do more to implement a reliable proxy:

  1. You need to be calling poll() on every loop, not just once. The way you have it written it is busy-looping, which is generally not considered acceptable practise.
  2. You should be setting the disposition of SIGPIPE to ignored with signal(SIGPIPE, SIG_IGN);. This allows you to gracefully handle write failures.
  3. You should be checking the result of send(). Note that it can write less than the amount you requested - in this case, you will have to keep the unsent data buffered, return to the poll() and try sending the remaining data again if POLLOUT is raised on the socket. You only want to request POLLOUT if there is unsent data remaining, so you need to make sure .events is set correctly before every poll() call.
  4. You should be checking errno if recv() or send() returns a value less than 0. EINTR and EWOULDBLOCK should be ignored; any other error should be treated as a connection failure.
  5. You should not be closing both directions immediately when one socket closes - you must support asymmetric shutdowns. This means that when you see that fds[0] has been closed by the remote end, you should call shutdown(fds[1], SHUT_WR);, and vice-versa; only when both have been shutdown (or a connection failure has occured) should you call close() on both file descriptors and finish up.
0

精彩评论

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