* Try to read a specified number of bytes from a socket. Please read the "see * also" section for more detail. * * @param data The buffer where the read bytes should be stored. * @param len The number of bytes to read into the specified buffer. * @param timeout The total timeout for this read. * @param sock The socket (has to be in non-blocking mode) from which to read bytes. * * @returns
| 316 | * Sockets can be made non-blocking with Sock::SetNonBlocking(). |
| 317 | */ |
| 318 | static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, std::chrono::milliseconds timeout, const Sock& sock) |
| 319 | { |
| 320 | auto curTime{Now<SteadyMilliseconds>()}; |
| 321 | const auto endTime{curTime + timeout}; |
| 322 | while (len > 0 && curTime < endTime) { |
| 323 | ssize_t ret = sock.Recv(data, len, 0); // Optimistically try the recv first |
| 324 | if (ret > 0) { |
| 325 | len -= ret; |
| 326 | data += ret; |
| 327 | } else if (ret == 0) { // Unexpected disconnection |
| 328 | return IntrRecvError::Disconnected; |
| 329 | } else { // Other error or blocking |
| 330 | int nErr = WSAGetLastError(); |
| 331 | if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL) { |
| 332 | // Only wait at most MAX_WAIT_FOR_IO at a time, unless |
| 333 | // we're approaching the end of the specified total timeout |
| 334 | const auto remaining = std::chrono::milliseconds{endTime - curTime}; |
| 335 | const auto timeout = std::min(remaining, std::chrono::milliseconds{MAX_WAIT_FOR_IO}); |
| 336 | if (!sock.Wait(timeout, Sock::RecvEvent)) { |
| 337 | return IntrRecvError::NetworkError; |
| 338 | } |
| 339 | } else { |
| 340 | return IntrRecvError::NetworkError; |
| 341 | } |
| 342 | } |
| 343 | if (g_socks5_interrupt) { |
| 344 | return IntrRecvError::Interrupted; |
| 345 | } |
| 346 | curTime = Now<SteadyMilliseconds>(); |
| 347 | } |
| 348 | return len == 0 ? IntrRecvError::OK : IntrRecvError::Timeout; |
| 349 | } |
| 350 | |
| 351 | /** Convert SOCKS5 reply to an error message */ |
| 352 | static std::string Socks5ErrorString(uint8_t err) |