* 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 in milliseconds for this read. * @param sock The socket (has to be in non-blocking mode) from which to read bytes.
| 308 | * SOCKET&, bool). |
| 309 | */ |
| 310 | static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, const Sock& sock) |
| 311 | { |
| 312 | int64_t curTime = GetTimeMillis(); |
| 313 | int64_t endTime = curTime + timeout; |
| 314 | while (len > 0 && curTime < endTime) { |
| 315 | ssize_t ret = sock.Recv(data, len, 0); // Optimistically try the recv first |
| 316 | if (ret > 0) { |
| 317 | len -= ret; |
| 318 | data += ret; |
| 319 | } else if (ret == 0) { // Unexpected disconnection |
| 320 | return IntrRecvError::Disconnected; |
| 321 | } else { // Other error or blocking |
| 322 | int nErr = WSAGetLastError(); |
| 323 | if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL) { |
| 324 | // Only wait at most MAX_WAIT_FOR_IO at a time, unless |
| 325 | // we're approaching the end of the specified total timeout |
| 326 | const auto remaining = std::chrono::milliseconds{endTime - curTime}; |
| 327 | const auto timeout = std::min(remaining, std::chrono::milliseconds{MAX_WAIT_FOR_IO}); |
| 328 | if (!sock.Wait(timeout, Sock::RECV)) { |
| 329 | return IntrRecvError::NetworkError; |
| 330 | } |
| 331 | } else { |
| 332 | return IntrRecvError::NetworkError; |
| 333 | } |
| 334 | } |
| 335 | if (interruptSocks5Recv) |
| 336 | return IntrRecvError::Interrupted; |
| 337 | curTime = GetTimeMillis(); |
| 338 | } |
| 339 | return len == 0 ? IntrRecvError::OK : IntrRecvError::Timeout; |
| 340 | } |
| 341 | |
| 342 | /** Convert SOCKS5 reply to an error message */ |
| 343 | static std::string Socks5ErrorString(uint8_t err) |
no test coverage detected