|
@@ -278,6 +278,14 @@ using socket_t = int;
|
|
|
#include <unordered_set>
|
|
#include <unordered_set>
|
|
|
#include <utility>
|
|
#include <utility>
|
|
|
|
|
|
|
|
|
|
+#if defined(__APPLE__)
|
|
|
|
|
+#include <TargetConditionals.h>
|
|
|
|
|
+#if TARGET_OS_OSX || TARGET_OS_IPHONE
|
|
|
|
|
+#include <CFNetwork/CFHost.h>
|
|
|
|
|
+#include <CoreFoundation/CoreFoundation.h>
|
|
|
|
|
+#endif
|
|
|
|
|
+#endif
|
|
|
|
|
+
|
|
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
|
|
#ifdef _WIN32
|
|
#ifdef _WIN32
|
|
|
#include <wincrypt.h>
|
|
#include <wincrypt.h>
|
|
@@ -292,13 +300,16 @@ using socket_t = int;
|
|
|
#ifdef _MSC_VER
|
|
#ifdef _MSC_VER
|
|
|
#pragma comment(lib, "crypt32.lib")
|
|
#pragma comment(lib, "crypt32.lib")
|
|
|
#endif
|
|
#endif
|
|
|
-#elif defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN) && defined(__APPLE__)
|
|
|
|
|
|
|
+#endif // _WIN32
|
|
|
|
|
+
|
|
|
|
|
+#if defined(__APPLE__)
|
|
|
|
|
+#if defined(CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN)
|
|
|
#include <TargetConditionals.h>
|
|
#include <TargetConditionals.h>
|
|
|
#if TARGET_OS_OSX
|
|
#if TARGET_OS_OSX
|
|
|
-#include <CoreFoundation/CoreFoundation.h>
|
|
|
|
|
#include <Security/Security.h>
|
|
#include <Security/Security.h>
|
|
|
#endif // TARGET_OS_OSX
|
|
#endif // TARGET_OS_OSX
|
|
|
-#endif // _WIN32
|
|
|
|
|
|
|
+#endif // CPPHTTPLIB_USE_CERTS_FROM_MACOSX_KEYCHAIN
|
|
|
|
|
+#endif // ___APPLE__
|
|
|
|
|
|
|
|
#include <openssl/err.h>
|
|
#include <openssl/err.h>
|
|
|
#include <openssl/evp.h>
|
|
#include <openssl/evp.h>
|
|
@@ -321,7 +332,7 @@ using socket_t = int;
|
|
|
#error Sorry, OpenSSL versions prior to 3.0.0 are not supported
|
|
#error Sorry, OpenSSL versions prior to 3.0.0 are not supported
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
-#endif
|
|
|
|
|
|
|
+#endif // CPPHTTPLIB_OPENSSL_SUPPORT
|
|
|
|
|
|
|
|
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
|
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
|
|
|
#include <zlib.h>
|
|
#include <zlib.h>
|
|
@@ -3369,11 +3380,323 @@ unescape_abstract_namespace_unix_domain(const std::string &s) {
|
|
|
return s;
|
|
return s;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+inline int getaddrinfo_with_timeout(const char *node, const char *service,
|
|
|
|
|
+ const struct addrinfo *hints,
|
|
|
|
|
+ struct addrinfo **res, time_t timeout_sec) {
|
|
|
|
|
+ if (timeout_sec <= 0) {
|
|
|
|
|
+ // No timeout specified, use standard getaddrinfo
|
|
|
|
|
+ return getaddrinfo(node, service, hints, res);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+#ifdef _WIN32
|
|
|
|
|
+ // Windows-specific implementation using GetAddrInfoEx with overlapped I/O
|
|
|
|
|
+ OVERLAPPED overlapped = {0};
|
|
|
|
|
+ HANDLE event = CreateEventW(nullptr, TRUE, FALSE, nullptr);
|
|
|
|
|
+ if (!event) { return EAI_FAIL; }
|
|
|
|
|
+
|
|
|
|
|
+ overlapped.hEvent = event;
|
|
|
|
|
+
|
|
|
|
|
+ PADDRINFOEXW result_addrinfo = nullptr;
|
|
|
|
|
+ HANDLE cancel_handle = nullptr;
|
|
|
|
|
+
|
|
|
|
|
+ ADDRINFOEXW hints_ex = {0};
|
|
|
|
|
+ if (hints) {
|
|
|
|
|
+ hints_ex.ai_flags = hints->ai_flags;
|
|
|
|
|
+ hints_ex.ai_family = hints->ai_family;
|
|
|
|
|
+ hints_ex.ai_socktype = hints->ai_socktype;
|
|
|
|
|
+ hints_ex.ai_protocol = hints->ai_protocol;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ auto wnode = u8string_to_wstring(node);
|
|
|
|
|
+ auto wservice = u8string_to_wstring(service);
|
|
|
|
|
+
|
|
|
|
|
+ auto ret = ::GetAddrInfoExW(wnode.data(), wservice.data(), NS_DNS, nullptr,
|
|
|
|
|
+ hints ? &hints_ex : nullptr, &result_addrinfo,
|
|
|
|
|
+ nullptr, &overlapped, nullptr, &cancel_handle);
|
|
|
|
|
+
|
|
|
|
|
+ if (ret == WSA_IO_PENDING) {
|
|
|
|
|
+ auto wait_result =
|
|
|
|
|
+ ::WaitForSingleObject(event, static_cast<DWORD>(timeout_sec * 1000));
|
|
|
|
|
+ if (wait_result == WAIT_TIMEOUT) {
|
|
|
|
|
+ if (cancel_handle) { ::GetAddrInfoExCancel(&cancel_handle); }
|
|
|
|
|
+ ::CloseHandle(event);
|
|
|
|
|
+ return EAI_AGAIN;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ DWORD bytes_returned;
|
|
|
|
|
+ if (!::GetOverlappedResult((HANDLE)INVALID_SOCKET, &overlapped,
|
|
|
|
|
+ &bytes_returned, FALSE)) {
|
|
|
|
|
+ ::CloseHandle(event);
|
|
|
|
|
+ return ::WSAGetLastError();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ::CloseHandle(event);
|
|
|
|
|
+
|
|
|
|
|
+ if (ret == NO_ERROR || ret == WSA_IO_PENDING) {
|
|
|
|
|
+ *res = reinterpret_cast<struct addrinfo *>(result_addrinfo);
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return ret;
|
|
|
|
|
+#elif defined(__APPLE__)
|
|
|
|
|
+ // macOS implementation using CFHost API for asynchronous DNS resolution
|
|
|
|
|
+ CFStringRef hostname_ref = CFStringCreateWithCString(
|
|
|
|
|
+ kCFAllocatorDefault, node, kCFStringEncodingUTF8);
|
|
|
|
|
+ if (!hostname_ref) { return EAI_MEMORY; }
|
|
|
|
|
+
|
|
|
|
|
+ CFHostRef host_ref = CFHostCreateWithName(kCFAllocatorDefault, hostname_ref);
|
|
|
|
|
+ CFRelease(hostname_ref);
|
|
|
|
|
+ if (!host_ref) { return EAI_MEMORY; }
|
|
|
|
|
+
|
|
|
|
|
+ // Set up context for callback
|
|
|
|
|
+ struct CFHostContext {
|
|
|
|
|
+ bool completed = false;
|
|
|
|
|
+ bool success = false;
|
|
|
|
|
+ CFArrayRef addresses = nullptr;
|
|
|
|
|
+ std::mutex mutex;
|
|
|
|
|
+ std::condition_variable cv;
|
|
|
|
|
+ } context;
|
|
|
|
|
+
|
|
|
|
|
+ CFHostClientContext client_context;
|
|
|
|
|
+ memset(&client_context, 0, sizeof(client_context));
|
|
|
|
|
+ client_context.info = &context;
|
|
|
|
|
+
|
|
|
|
|
+ // Set callback
|
|
|
|
|
+ auto callback = [](CFHostRef theHost, CFHostInfoType /*typeInfo*/,
|
|
|
|
|
+ const CFStreamError *error, void *info) {
|
|
|
|
|
+ auto ctx = static_cast<CFHostContext *>(info);
|
|
|
|
|
+ std::lock_guard<std::mutex> lock(ctx->mutex);
|
|
|
|
|
+
|
|
|
|
|
+ if (error && error->error != 0) {
|
|
|
|
|
+ ctx->success = false;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Boolean hasBeenResolved;
|
|
|
|
|
+ ctx->addresses = CFHostGetAddressing(theHost, &hasBeenResolved);
|
|
|
|
|
+ if (ctx->addresses && hasBeenResolved) {
|
|
|
|
|
+ CFRetain(ctx->addresses);
|
|
|
|
|
+ ctx->success = true;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ctx->success = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ ctx->completed = true;
|
|
|
|
|
+ ctx->cv.notify_one();
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ if (!CFHostSetClient(host_ref, callback, &client_context)) {
|
|
|
|
|
+ CFRelease(host_ref);
|
|
|
|
|
+ return EAI_SYSTEM;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Schedule on run loop
|
|
|
|
|
+ CFRunLoopRef run_loop = CFRunLoopGetCurrent();
|
|
|
|
|
+ CFHostScheduleWithRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
|
|
|
|
|
+
|
|
|
|
|
+ // Start resolution
|
|
|
|
|
+ CFStreamError stream_error;
|
|
|
|
|
+ if (!CFHostStartInfoResolution(host_ref, kCFHostAddresses, &stream_error)) {
|
|
|
|
|
+ CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
|
|
|
|
|
+ CFRelease(host_ref);
|
|
|
|
|
+ return EAI_FAIL;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Wait for completion with timeout
|
|
|
|
|
+ auto timeout_time =
|
|
|
|
|
+ std::chrono::steady_clock::now() + std::chrono::seconds(timeout_sec);
|
|
|
|
|
+ bool timed_out = false;
|
|
|
|
|
+
|
|
|
|
|
+ {
|
|
|
|
|
+ std::unique_lock<std::mutex> lock(context.mutex);
|
|
|
|
|
+
|
|
|
|
|
+ while (!context.completed) {
|
|
|
|
|
+ auto now = std::chrono::steady_clock::now();
|
|
|
|
|
+ if (now >= timeout_time) {
|
|
|
|
|
+ timed_out = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Run the runloop for a short time
|
|
|
|
|
+ lock.unlock();
|
|
|
|
|
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, true);
|
|
|
|
|
+ lock.lock();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Clean up
|
|
|
|
|
+ CFHostUnscheduleFromRunLoop(host_ref, run_loop, kCFRunLoopDefaultMode);
|
|
|
|
|
+ CFHostSetClient(host_ref, nullptr, nullptr);
|
|
|
|
|
+
|
|
|
|
|
+ if (timed_out || !context.completed) {
|
|
|
|
|
+ CFHostCancelInfoResolution(host_ref, kCFHostAddresses);
|
|
|
|
|
+ CFRelease(host_ref);
|
|
|
|
|
+ return EAI_AGAIN;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!context.success || !context.addresses) {
|
|
|
|
|
+ CFRelease(host_ref);
|
|
|
|
|
+ return EAI_NODATA;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Convert CFArray to addrinfo
|
|
|
|
|
+ CFIndex count = CFArrayGetCount(context.addresses);
|
|
|
|
|
+ if (count == 0) {
|
|
|
|
|
+ CFRelease(context.addresses);
|
|
|
|
|
+ CFRelease(host_ref);
|
|
|
|
|
+ return EAI_NODATA;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ struct addrinfo *result_addrinfo = nullptr;
|
|
|
|
|
+ struct addrinfo **current = &result_addrinfo;
|
|
|
|
|
+
|
|
|
|
|
+ for (CFIndex i = 0; i < count; i++) {
|
|
|
|
|
+ CFDataRef addr_data =
|
|
|
|
|
+ static_cast<CFDataRef>(CFArrayGetValueAtIndex(context.addresses, i));
|
|
|
|
|
+ if (!addr_data) continue;
|
|
|
|
|
+
|
|
|
|
|
+ const struct sockaddr *sockaddr_ptr =
|
|
|
|
|
+ reinterpret_cast<const struct sockaddr *>(CFDataGetBytePtr(addr_data));
|
|
|
|
|
+ socklen_t sockaddr_len = static_cast<socklen_t>(CFDataGetLength(addr_data));
|
|
|
|
|
+
|
|
|
|
|
+ // Allocate addrinfo structure
|
|
|
|
|
+ *current = static_cast<struct addrinfo *>(malloc(sizeof(struct addrinfo)));
|
|
|
|
|
+ if (!*current) {
|
|
|
|
|
+ freeaddrinfo(result_addrinfo);
|
|
|
|
|
+ CFRelease(context.addresses);
|
|
|
|
|
+ CFRelease(host_ref);
|
|
|
|
|
+ return EAI_MEMORY;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ memset(*current, 0, sizeof(struct addrinfo));
|
|
|
|
|
+
|
|
|
|
|
+ // Set up addrinfo fields
|
|
|
|
|
+ (*current)->ai_family = sockaddr_ptr->sa_family;
|
|
|
|
|
+ (*current)->ai_socktype = hints ? hints->ai_socktype : SOCK_STREAM;
|
|
|
|
|
+ (*current)->ai_protocol = hints ? hints->ai_protocol : IPPROTO_TCP;
|
|
|
|
|
+ (*current)->ai_addrlen = sockaddr_len;
|
|
|
|
|
+
|
|
|
|
|
+ // Copy sockaddr
|
|
|
|
|
+ (*current)->ai_addr = static_cast<struct sockaddr *>(malloc(sockaddr_len));
|
|
|
|
|
+ if (!(*current)->ai_addr) {
|
|
|
|
|
+ freeaddrinfo(result_addrinfo);
|
|
|
|
|
+ CFRelease(context.addresses);
|
|
|
|
|
+ CFRelease(host_ref);
|
|
|
|
|
+ return EAI_MEMORY;
|
|
|
|
|
+ }
|
|
|
|
|
+ memcpy((*current)->ai_addr, sockaddr_ptr, sockaddr_len);
|
|
|
|
|
+
|
|
|
|
|
+ // Set port if service is specified
|
|
|
|
|
+ if (service && strlen(service) > 0) {
|
|
|
|
|
+ int port = atoi(service);
|
|
|
|
|
+ if (port > 0) {
|
|
|
|
|
+ if (sockaddr_ptr->sa_family == AF_INET) {
|
|
|
|
|
+ reinterpret_cast<struct sockaddr_in *>((*current)->ai_addr)
|
|
|
|
|
+ ->sin_port = htons(static_cast<uint16_t>(port));
|
|
|
|
|
+ } else if (sockaddr_ptr->sa_family == AF_INET6) {
|
|
|
|
|
+ reinterpret_cast<struct sockaddr_in6 *>((*current)->ai_addr)
|
|
|
|
|
+ ->sin6_port = htons(static_cast<uint16_t>(port));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ current = &((*current)->ai_next);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ CFRelease(context.addresses);
|
|
|
|
|
+ CFRelease(host_ref);
|
|
|
|
|
+
|
|
|
|
|
+ *res = result_addrinfo;
|
|
|
|
|
+ return 0;
|
|
|
|
|
+#elif defined(_GNU_SOURCE) && defined(__GLIBC__) && \
|
|
|
|
|
+ (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 2))
|
|
|
|
|
+ // Linux implementation using getaddrinfo_a for asynchronous DNS resolution
|
|
|
|
|
+ struct gaicb request;
|
|
|
|
|
+ struct gaicb *requests[1] = {&request};
|
|
|
|
|
+ struct sigevent sevp;
|
|
|
|
|
+ struct timespec timeout;
|
|
|
|
|
+
|
|
|
|
|
+ // Initialize the request structure
|
|
|
|
|
+ memset(&request, 0, sizeof(request));
|
|
|
|
|
+ request.ar_name = node;
|
|
|
|
|
+ request.ar_service = service;
|
|
|
|
|
+ request.ar_request = hints;
|
|
|
|
|
+
|
|
|
|
|
+ // Set up timeout
|
|
|
|
|
+ timeout.tv_sec = timeout_sec;
|
|
|
|
|
+ timeout.tv_nsec = 0;
|
|
|
|
|
+
|
|
|
|
|
+ // Initialize sigevent structure (not used, but required)
|
|
|
|
|
+ memset(&sevp, 0, sizeof(sevp));
|
|
|
|
|
+ sevp.sigev_notify = SIGEV_NONE;
|
|
|
|
|
+
|
|
|
|
|
+ // Start asynchronous resolution
|
|
|
|
|
+ int start_result = getaddrinfo_a(GAI_NOWAIT, requests, 1, &sevp);
|
|
|
|
|
+ if (start_result != 0) { return start_result; }
|
|
|
|
|
+
|
|
|
|
|
+ // Wait for completion with timeout
|
|
|
|
|
+ int wait_result =
|
|
|
|
|
+ gai_suspend((const struct gaicb *const *)requests, 1, &timeout);
|
|
|
|
|
+
|
|
|
|
|
+ if (wait_result == 0) {
|
|
|
|
|
+ // Completed successfully, get the result
|
|
|
|
|
+ int gai_result = gai_error(&request);
|
|
|
|
|
+ if (gai_result == 0) {
|
|
|
|
|
+ *res = request.ar_result;
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Clean up on error
|
|
|
|
|
+ if (request.ar_result) { freeaddrinfo(request.ar_result); }
|
|
|
|
|
+ return gai_result;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (wait_result == EAI_AGAIN) {
|
|
|
|
|
+ // Timeout occurred, cancel the request
|
|
|
|
|
+ gai_cancel(&request);
|
|
|
|
|
+ return EAI_AGAIN;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Other error occurred
|
|
|
|
|
+ gai_cancel(&request);
|
|
|
|
|
+ return wait_result;
|
|
|
|
|
+ }
|
|
|
|
|
+#else
|
|
|
|
|
+ // Fallback implementation using thread-based timeout for other Unix systems
|
|
|
|
|
+ std::mutex result_mutex;
|
|
|
|
|
+ std::condition_variable result_cv;
|
|
|
|
|
+ auto completed = false;
|
|
|
|
|
+ auto result = EAI_SYSTEM;
|
|
|
|
|
+ struct addrinfo *result_addrinfo = nullptr;
|
|
|
|
|
+
|
|
|
|
|
+ std::thread resolve_thread([&]() {
|
|
|
|
|
+ auto thread_result = getaddrinfo(node, service, hints, &result_addrinfo);
|
|
|
|
|
+
|
|
|
|
|
+ std::lock_guard<std::mutex> lock(result_mutex);
|
|
|
|
|
+ result = thread_result;
|
|
|
|
|
+ completed = true;
|
|
|
|
|
+ result_cv.notify_one();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // Wait for completion or timeout
|
|
|
|
|
+ std::unique_lock<std::mutex> lock(result_mutex);
|
|
|
|
|
+ auto finished = result_cv.wait_for(lock, std::chrono::seconds(timeout_sec),
|
|
|
|
|
+ [&] { return completed; });
|
|
|
|
|
+
|
|
|
|
|
+ if (finished) {
|
|
|
|
|
+ // Operation completed within timeout
|
|
|
|
|
+ resolve_thread.join();
|
|
|
|
|
+ *res = result_addrinfo;
|
|
|
|
|
+ return result;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Timeout occurred
|
|
|
|
|
+ resolve_thread.detach(); // Let the thread finish in background
|
|
|
|
|
+ return EAI_AGAIN; // Return timeout error
|
|
|
|
|
+ }
|
|
|
|
|
+#endif
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
template <typename BindOrConnect>
|
|
template <typename BindOrConnect>
|
|
|
socket_t create_socket(const std::string &host, const std::string &ip, int port,
|
|
socket_t create_socket(const std::string &host, const std::string &ip, int port,
|
|
|
int address_family, int socket_flags, bool tcp_nodelay,
|
|
int address_family, int socket_flags, bool tcp_nodelay,
|
|
|
bool ipv6_v6only, SocketOptions socket_options,
|
|
bool ipv6_v6only, SocketOptions socket_options,
|
|
|
- BindOrConnect bind_or_connect) {
|
|
|
|
|
|
|
+ BindOrConnect bind_or_connect, time_t timeout_sec = 0) {
|
|
|
// Get address info
|
|
// Get address info
|
|
|
const char *node = nullptr;
|
|
const char *node = nullptr;
|
|
|
struct addrinfo hints;
|
|
struct addrinfo hints;
|
|
@@ -3443,7 +3766,8 @@ socket_t create_socket(const std::string &host, const std::string &ip, int port,
|
|
|
|
|
|
|
|
auto service = std::to_string(port);
|
|
auto service = std::to_string(port);
|
|
|
|
|
|
|
|
- if (getaddrinfo(node, service.c_str(), &hints, &result)) {
|
|
|
|
|
|
|
+ if (getaddrinfo_with_timeout(node, service.c_str(), &hints, &result,
|
|
|
|
|
+ timeout_sec)) {
|
|
|
#if defined __linux__ && !defined __ANDROID__
|
|
#if defined __linux__ && !defined __ANDROID__
|
|
|
res_init();
|
|
res_init();
|
|
|
#endif
|
|
#endif
|
|
@@ -3541,7 +3865,9 @@ inline bool bind_ip_address(socket_t sock, const std::string &host) {
|
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
hints.ai_protocol = 0;
|
|
hints.ai_protocol = 0;
|
|
|
|
|
|
|
|
- if (getaddrinfo(host.c_str(), "0", &hints, &result)) { return false; }
|
|
|
|
|
|
|
+ if (getaddrinfo_with_timeout(host.c_str(), "0", &hints, &result, 0)) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
auto se = detail::scope_exit([&] { freeaddrinfo(result); });
|
|
auto se = detail::scope_exit([&] { freeaddrinfo(result); });
|
|
|
|
|
|
|
|
auto ret = false;
|
|
auto ret = false;
|
|
@@ -3646,7 +3972,8 @@ inline socket_t create_client_socket(
|
|
|
|
|
|
|
|
error = Error::Success;
|
|
error = Error::Success;
|
|
|
return true;
|
|
return true;
|
|
|
- });
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ connection_timeout_sec); // Pass DNS timeout
|
|
|
|
|
|
|
|
if (sock != INVALID_SOCKET) {
|
|
if (sock != INVALID_SOCKET) {
|
|
|
error = Error::Success;
|
|
error = Error::Success;
|
|
@@ -5867,7 +6194,8 @@ inline void hosted_at(const std::string &hostname,
|
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
hints.ai_protocol = 0;
|
|
hints.ai_protocol = 0;
|
|
|
|
|
|
|
|
- if (getaddrinfo(hostname.c_str(), nullptr, &hints, &result)) {
|
|
|
|
|
|
|
+ if (detail::getaddrinfo_with_timeout(hostname.c_str(), nullptr, &hints,
|
|
|
|
|
+ &result, 0)) {
|
|
|
#if defined __linux__ && !defined __ANDROID__
|
|
#if defined __linux__ && !defined __ANDROID__
|
|
|
res_init();
|
|
res_init();
|
|
|
#endif
|
|
#endif
|