diff --git a/makespec/BUILDVERSION b/makespec/BUILDVERSION index 2f114b59..80969167 100644 --- a/makespec/BUILDVERSION +++ b/makespec/BUILDVERSION @@ -1 +1 @@ -5366 \ No newline at end of file +5367 \ No newline at end of file diff --git a/src/components/latency/QvTCPing.cpp b/src/components/latency/QvTCPing.cpp index 77046fec..69c732f0 100644 --- a/src/components/latency/QvTCPing.cpp +++ b/src/components/latency/QvTCPing.cpp @@ -2,7 +2,9 @@ #include #include #else + #include #include + #include #include #include #include @@ -11,6 +13,110 @@ #include "QvTCPing.hpp" #include "core/handler/ConfigHandler.hpp" +#ifdef _WIN32 +using qv_socket_t = SOCKET; +#else +using qv_socket_t = int; +#endif +namespace +{ + inline int setnonblocking(qv_socket_t sockno, int &opt) + { +#ifdef _WIN32 + ULONG block = 1; + if (ioctlsocket(sockno, FIONBIO, &block) == SOCKET_ERROR) + { + return -1; + } +#else + if ((opt = fcntl(sockno, F_GETFL, NULL)) < 0) + { + // get socket flags + return -1; + } + if (fcntl(sockno, F_SETFL, opt | O_NONBLOCK) < 0) + { + // set socket non-blocking + return -1; + } +#endif + return 0; + } + + inline int setblocking(qv_socket_t sockno, int &opt) + { +#ifdef _WIN32 + ULONG block = 0; + if (ioctlsocket(sockno, FIONBIO, &block) == SOCKET_ERROR) + { + return -1; + } +#else + if (fcntl(sockno, F_SETFL, opt) < 0) + { + // reset socket flags + return -1; + } +#endif + return 0; + } + + int connect_wait(qv_socket_t sockno, struct sockaddr *addr, size_t addrlen, int timeout_sec = 5) + { + int res; + int opt; + timeval tv = { 0 }; + tv.tv_sec = timeout_sec; + tv.tv_usec = 0; + if ((res = setnonblocking(sockno, opt)) != 0) + return -1; + if ((res = ::connect(sockno, addr, addrlen)) < 0) + { +#ifdef _WIN32 + if (WSAGetLastError() != WSAEWOULDBLOCK) + { +#else + if (errno == EINPROGRESS) + { +#endif + // connecting + fd_set wait_set; + FD_ZERO(&wait_set); + FD_SET(sockno, &wait_set); + res = select(sockno + 1, NULL, &wait_set, NULL, &tv); + } + } + else + { + // connect immediately + res = 1; + } + if (setblocking(sockno, opt) != 0) + { + return -1; + } + if (res < 0) + { + // an error occured + return -1; + } + else if (res == 0) + { + // timeout + return 1; + } + else + { + socklen_t len = sizeof(opt); + if (getsockopt(sockno, SOL_SOCKET, SO_ERROR, reinterpret_cast(&opt), &len) < 0) + { + return -1; + } + } + return 0; + } +} // namespace + namespace Qv2ray::components::tcping { static int resolveHost(const std::string &host, int portnr, struct addrinfo **res); @@ -159,11 +265,8 @@ namespace Qv2ray::components::tcping int testLatency(struct addrinfo *addr, std::chrono::system_clock::time_point *start, std::chrono::system_clock::time_point *end) { -#ifdef _WIN32 - SOCKET fd; -#else - int fd; -#endif + qv_socket_t fd; + const int on = 1; /* int flags; */ int rv = 0; @@ -198,7 +301,7 @@ namespace Qv2ray::components::tcping /* connect to peer */ // Qt has its own connect() function in QObject.... // So we add "::" here - if (::connect(fd, addr->ai_addr, addr->ai_addrlen) == 0) + if (connect_wait(fd, addr->ai_addr, addr->ai_addrlen) == 0) { *end = system_clock::now(); #ifdef Q_OS_WIN