2018-12-15 01:28:11 +01:00
|
|
|
/*
|
|
|
|
* IXDNSLookup.cpp
|
|
|
|
* Author: Benjamin Sergeant
|
|
|
|
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
|
|
|
*/
|
|
|
|
|
2020-05-01 18:22:03 +02:00
|
|
|
// Try to disable unicode on Windows uwp
|
|
|
|
#ifdef _UNICODE
|
|
|
|
#undef _UNICODE
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef UNICODE
|
|
|
|
#undef UNICODE
|
|
|
|
#endif
|
|
|
|
|
2018-12-15 01:28:11 +01:00
|
|
|
#include "IXDNSLookup.h"
|
2019-01-05 02:28:13 +01:00
|
|
|
|
2019-09-23 19:25:23 +02:00
|
|
|
#include "IXNetSystem.h"
|
2018-12-15 01:28:11 +01:00
|
|
|
#include <chrono>
|
2019-09-23 19:25:23 +02:00
|
|
|
#include <string.h>
|
2019-06-27 04:12:48 +02:00
|
|
|
#include <thread>
|
2018-12-15 01:28:11 +01:00
|
|
|
|
2019-02-21 03:59:07 +01:00
|
|
|
namespace ix
|
2018-12-15 01:28:11 +01:00
|
|
|
{
|
2019-06-27 04:12:48 +02:00
|
|
|
const int64_t DNSLookup::kDefaultWait = 1; // ms
|
2018-12-15 01:28:11 +01:00
|
|
|
|
2019-09-23 19:25:23 +02:00
|
|
|
DNSLookup::DNSLookup(const std::string& hostname, int port, int64_t wait)
|
|
|
|
: _hostname(hostname)
|
|
|
|
, _port(port)
|
|
|
|
, _wait(wait)
|
|
|
|
, _res(nullptr)
|
|
|
|
, _done(false)
|
2018-12-15 01:28:11 +01:00
|
|
|
{
|
2019-06-27 01:25:07 +02:00
|
|
|
;
|
2018-12-15 01:28:11 +01:00
|
|
|
}
|
|
|
|
|
2019-04-30 04:29:27 +02:00
|
|
|
struct addrinfo* DNSLookup::getAddrInfo(const std::string& hostname,
|
2018-12-15 01:28:11 +01:00
|
|
|
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;
|
2019-09-23 19:25:23 +02:00
|
|
|
int getaddrinfo_result = getaddrinfo(hostname.c_str(), sport.c_str(), &hints, &res);
|
2018-12-15 01:28:11 +01:00
|
|
|
if (getaddrinfo_result)
|
|
|
|
{
|
|
|
|
errMsg = gai_strerror(getaddrinfo_result);
|
|
|
|
res = nullptr;
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct addrinfo* DNSLookup::resolve(std::string& errMsg,
|
|
|
|
const CancellationRequest& isCancellationRequested,
|
2019-07-01 08:26:14 +02:00
|
|
|
bool cancellable)
|
2018-12-15 01:28:11 +01:00
|
|
|
{
|
2019-07-01 08:26:14 +02:00
|
|
|
return cancellable ? resolveCancellable(errMsg, isCancellationRequested)
|
|
|
|
: resolveUnCancellable(errMsg, isCancellationRequested);
|
2018-12-15 01:28:11 +01:00
|
|
|
}
|
|
|
|
|
2019-09-23 19:25:23 +02:00
|
|
|
struct addrinfo* DNSLookup::resolveUnCancellable(
|
|
|
|
std::string& errMsg, const CancellationRequest& isCancellationRequested)
|
2018-12-15 01:28:11 +01:00
|
|
|
{
|
|
|
|
errMsg = "no error";
|
|
|
|
|
|
|
|
// Maybe a cancellation request got in before the background thread terminated ?
|
2019-12-02 23:52:19 +01:00
|
|
|
if (isCancellationRequested())
|
2018-12-15 01:28:11 +01:00
|
|
|
{
|
|
|
|
errMsg = "cancellation requested";
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2019-06-27 01:25:07 +02:00
|
|
|
return getAddrInfo(_hostname, _port, errMsg);
|
2018-12-15 01:28:11 +01:00
|
|
|
}
|
|
|
|
|
2019-09-23 19:25:23 +02:00
|
|
|
struct addrinfo* DNSLookup::resolveCancellable(
|
|
|
|
std::string& errMsg, const CancellationRequest& isCancellationRequested)
|
2018-12-15 01:28:11 +01:00
|
|
|
{
|
|
|
|
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.
|
|
|
|
}
|
|
|
|
|
2019-02-21 03:59:07 +01:00
|
|
|
//
|
2018-12-15 01:28:11 +01:00
|
|
|
// Good resource on thread forced termination
|
|
|
|
// https://www.bo-yang.net/2017/11/19/cpp-kill-detached-thread
|
|
|
|
//
|
2019-06-19 09:43:59 +02:00
|
|
|
auto ptr = shared_from_this();
|
|
|
|
std::weak_ptr<DNSLookup> self(ptr);
|
|
|
|
|
2019-06-27 01:25:07 +02:00
|
|
|
int port = _port;
|
|
|
|
std::string hostname(_hostname);
|
2018-12-15 01:28:11 +01:00
|
|
|
|
2019-09-12 20:43:52 +02:00
|
|
|
// We make the background thread doing the work a shared pointer
|
2019-06-27 04:12:48 +02:00
|
|
|
// 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();
|
2018-12-15 01:28:11 +01:00
|
|
|
|
|
|
|
while (!_done)
|
|
|
|
{
|
2019-06-27 04:12:48 +02:00
|
|
|
// 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));
|
2018-12-15 01:28:11 +01:00
|
|
|
|
|
|
|
// Were we cancelled ?
|
2019-12-02 23:52:19 +01:00
|
|
|
if (isCancellationRequested())
|
2018-12-15 01:28:11 +01:00
|
|
|
{
|
|
|
|
errMsg = "cancellation requested";
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Maybe a cancellation request got in before the bg terminated ?
|
2019-12-02 23:52:19 +01:00
|
|
|
if (isCancellationRequested())
|
2018-12-15 01:28:11 +01:00
|
|
|
{
|
|
|
|
errMsg = "cancellation requested";
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2019-04-30 04:29:27 +02:00
|
|
|
errMsg = getErrMsg();
|
|
|
|
return getRes();
|
2018-12-15 01:28:11 +01:00
|
|
|
}
|
|
|
|
|
2019-09-23 19:25:23 +02:00
|
|
|
void DNSLookup::run(std::weak_ptr<DNSLookup> self,
|
|
|
|
std::string hostname,
|
|
|
|
int port) // thread runner
|
2018-12-15 01:28:11 +01:00
|
|
|
{
|
2019-01-07 03:27:26 +01:00
|
|
|
// We don't want to read or write into members variables of an object that could be
|
2019-02-21 03:59:07 +01:00
|
|
|
// gone, so we use temporary variables (res) or we pass in by copy everything that
|
2019-01-07 03:27:26 +01:00
|
|
|
// getAddrInfo needs to work.
|
2018-12-15 02:49:42 +01:00
|
|
|
std::string errMsg;
|
2019-01-07 03:27:26 +01:00
|
|
|
struct addrinfo* res = getAddrInfo(hostname, port, errMsg);
|
2018-12-15 02:49:42 +01:00
|
|
|
|
2019-09-19 21:51:11 +02:00
|
|
|
if (auto lock = self.lock())
|
2018-12-15 02:49:42 +01:00
|
|
|
{
|
2019-06-19 09:43:59 +02:00
|
|
|
// Copy result into the member variables
|
|
|
|
setRes(res);
|
|
|
|
setErrMsg(errMsg);
|
2018-12-15 02:49:42 +01:00
|
|
|
|
2019-06-19 09:43:59 +02:00
|
|
|
_done = true;
|
|
|
|
}
|
2018-12-15 01:28:11 +01:00
|
|
|
}
|
2019-04-30 04:29:27 +02:00
|
|
|
|
|
|
|
void DNSLookup::setErrMsg(const std::string& errMsg)
|
|
|
|
{
|
2019-04-30 04:29:27 +02:00
|
|
|
std::lock_guard<std::mutex> lock(_errMsgMutex);
|
2019-04-30 04:29:27 +02:00
|
|
|
_errMsg = errMsg;
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::string& DNSLookup::getErrMsg()
|
|
|
|
{
|
2019-04-30 04:29:27 +02:00
|
|
|
std::lock_guard<std::mutex> lock(_errMsgMutex);
|
2019-04-30 04:29:27 +02:00
|
|
|
return _errMsg;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DNSLookup::setRes(struct addrinfo* addr)
|
|
|
|
{
|
2019-04-30 04:29:27 +02:00
|
|
|
std::lock_guard<std::mutex> lock(_resMutex);
|
2019-04-30 04:29:27 +02:00
|
|
|
_res = addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct addrinfo* DNSLookup::getRes()
|
|
|
|
{
|
2019-04-30 04:29:27 +02:00
|
|
|
std::lock_guard<std::mutex> lock(_resMutex);
|
2019-04-30 04:29:27 +02:00
|
|
|
return _res;
|
|
|
|
}
|
2019-09-23 19:25:23 +02:00
|
|
|
} // namespace ix
|