kopia lustrzana https://github.com/jameshball/osci-render
192 wiersze
5.8 KiB
C++
192 wiersze
5.8 KiB
C++
/*
|
|
* IXDNSLookup.cpp
|
|
* Author: Benjamin Sergeant
|
|
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
|
*/
|
|
|
|
//
|
|
// On Windows Universal Platform (uwp), gai_strerror defaults behavior is to returns wchar_t
|
|
// which is different from all other platforms. We want the non unicode version.
|
|
// See https://github.com/microsoft/vcpkg/pull/11030
|
|
// We could do this in IXNetSystem.cpp but so far we are only using gai_strerror in here.
|
|
//
|
|
#ifdef _UNICODE
|
|
#undef _UNICODE
|
|
#endif
|
|
#ifdef UNICODE
|
|
#undef UNICODE
|
|
#endif
|
|
|
|
#include "IXDNSLookup.h"
|
|
|
|
#include "IXNetSystem.h"
|
|
#include <chrono>
|
|
#include <string.h>
|
|
#include <thread>
|
|
#include <utility>
|
|
|
|
// mingw build quirks
|
|
#if defined(_WIN32) && defined(__GNUC__)
|
|
#define AI_NUMERICSERV NI_NUMERICSERV
|
|
#define AI_ADDRCONFIG LUP_ADDRCONFIG
|
|
#endif
|
|
|
|
namespace ix
|
|
{
|
|
const int64_t DNSLookup::kDefaultWait = 1; // ms
|
|
|
|
DNSLookup::DNSLookup(const std::string& hostname, int port, int64_t wait)
|
|
: _hostname(hostname)
|
|
, _port(port)
|
|
, _wait(wait)
|
|
, _res(nullptr)
|
|
, _done(false)
|
|
{
|
|
;
|
|
}
|
|
|
|
DNSLookup::AddrInfoPtr DNSLookup::getAddrInfo(const std::string& hostname,
|
|
int port,
|
|
std::string& errMsg)
|
|
{
|
|
struct addrinfo hints;
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
|
|
hints.ai_family = AF_UNSPEC;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
std::string sport = std::to_string(port);
|
|
|
|
struct addrinfo* res;
|
|
int getaddrinfo_result = getaddrinfo(hostname.c_str(), sport.c_str(), &hints, &res);
|
|
if (getaddrinfo_result)
|
|
{
|
|
errMsg = gai_strerror(getaddrinfo_result);
|
|
res = nullptr;
|
|
}
|
|
return AddrInfoPtr{ res, freeaddrinfo };
|
|
}
|
|
|
|
DNSLookup::AddrInfoPtr DNSLookup::resolve(std::string& errMsg,
|
|
const CancellationRequest& isCancellationRequested,
|
|
bool cancellable)
|
|
{
|
|
return cancellable ? resolveCancellable(errMsg, isCancellationRequested)
|
|
: resolveUnCancellable(errMsg, isCancellationRequested);
|
|
}
|
|
|
|
DNSLookup::AddrInfoPtr DNSLookup::resolveUnCancellable(
|
|
std::string& errMsg, const CancellationRequest& isCancellationRequested)
|
|
{
|
|
errMsg = "no error";
|
|
|
|
// Maybe a cancellation request got in before the background thread terminated ?
|
|
if (isCancellationRequested())
|
|
{
|
|
errMsg = "cancellation requested";
|
|
return nullptr;
|
|
}
|
|
|
|
return getAddrInfo(_hostname, _port, errMsg);
|
|
}
|
|
|
|
DNSLookup::AddrInfoPtr DNSLookup::resolveCancellable(
|
|
std::string& errMsg, const CancellationRequest& isCancellationRequested)
|
|
{
|
|
errMsg = "no error";
|
|
|
|
// Can only be called once, otherwise we would have to manage a pool
|
|
// of background thread which is overkill for our usage.
|
|
if (_done)
|
|
{
|
|
return nullptr; // programming error, create a second DNSLookup instance
|
|
// if you need a second lookup.
|
|
}
|
|
|
|
//
|
|
// Good resource on thread forced termination
|
|
// https://www.bo-yang.net/2017/11/19/cpp-kill-detached-thread
|
|
//
|
|
auto ptr = shared_from_this();
|
|
std::weak_ptr<DNSLookup> self(ptr);
|
|
|
|
int port = _port;
|
|
std::string hostname(_hostname);
|
|
|
|
// We make the background thread doing the work a shared pointer
|
|
// instead of a member variable, because it can keep running when
|
|
// this object goes out of scope, in case of cancellation
|
|
auto t = std::make_shared<std::thread>(&DNSLookup::run, this, self, hostname, port);
|
|
t->detach();
|
|
|
|
while (!_done)
|
|
{
|
|
// Wait for 1 milliseconds, to see if the bg thread has terminated.
|
|
// We do not use a condition variable to wait, as destroying this one
|
|
// if the bg thread is alive can cause undefined behavior.
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(_wait));
|
|
|
|
// Were we cancelled ?
|
|
if (isCancellationRequested())
|
|
{
|
|
errMsg = "cancellation requested";
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
// Maybe a cancellation request got in before the bg terminated ?
|
|
if (isCancellationRequested())
|
|
{
|
|
errMsg = "cancellation requested";
|
|
return nullptr;
|
|
}
|
|
|
|
errMsg = getErrMsg();
|
|
return getRes();
|
|
}
|
|
|
|
void DNSLookup::run(std::weak_ptr<DNSLookup> self,
|
|
std::string hostname,
|
|
int port) // thread runner
|
|
{
|
|
// We don't want to read or write into members variables of an object that could be
|
|
// gone, so we use temporary variables (res) or we pass in by copy everything that
|
|
// getAddrInfo needs to work.
|
|
std::string errMsg;
|
|
auto res = getAddrInfo(hostname, port, errMsg);
|
|
|
|
if (auto lock = self.lock())
|
|
{
|
|
// Copy result into the member variables
|
|
setRes(res);
|
|
setErrMsg(errMsg);
|
|
|
|
_done = true;
|
|
}
|
|
}
|
|
|
|
void DNSLookup::setErrMsg(const std::string& errMsg)
|
|
{
|
|
std::lock_guard<std::mutex> lock(_errMsgMutex);
|
|
_errMsg = errMsg;
|
|
}
|
|
|
|
const std::string& DNSLookup::getErrMsg()
|
|
{
|
|
std::lock_guard<std::mutex> lock(_errMsgMutex);
|
|
return _errMsg;
|
|
}
|
|
|
|
void DNSLookup::setRes(DNSLookup::AddrInfoPtr addr)
|
|
{
|
|
std::lock_guard<std::mutex> lock(_resMutex);
|
|
_res = std::move(addr);
|
|
}
|
|
|
|
DNSLookup::AddrInfoPtr DNSLookup::getRes()
|
|
{
|
|
std::lock_guard<std::mutex> lock(_resMutex);
|
|
return _res;
|
|
}
|
|
} // namespace ix
|