2018-12-15 01:28:11 +01:00
|
|
|
/*
|
|
|
|
* IXDNSLookup.cpp
|
|
|
|
* Author: Benjamin Sergeant
|
|
|
|
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "IXDNSLookup.h"
|
2019-01-06 05:38:43 +01:00
|
|
|
#include "IXNetSystem.h"
|
2019-01-05 02:28:13 +01:00
|
|
|
|
2018-12-15 01:28:11 +01:00
|
|
|
#include <string.h>
|
|
|
|
#include <chrono>
|
|
|
|
|
2019-02-21 03:59:07 +01:00
|
|
|
namespace ix
|
2018-12-15 01:28:11 +01:00
|
|
|
{
|
2019-01-03 21:53:44 +01:00
|
|
|
const int64_t DNSLookup::kDefaultWait = 10; // ms
|
2018-12-15 01:28:11 +01:00
|
|
|
|
2018-12-15 02:49:42 +01:00
|
|
|
std::atomic<uint64_t> DNSLookup::_nextId(0);
|
|
|
|
std::set<uint64_t> DNSLookup::_activeJobs;
|
|
|
|
std::mutex DNSLookup::_activeJobsMutex;
|
|
|
|
|
2018-12-23 23:14:38 +01:00
|
|
|
DNSLookup::DNSLookup(const std::string& hostname, int port, int64_t wait) :
|
2018-12-15 01:28:11 +01:00
|
|
|
_hostname(hostname),
|
|
|
|
_port(port),
|
2018-12-23 23:17:30 +01:00
|
|
|
_wait(wait),
|
2018-12-15 01:28:11 +01:00
|
|
|
_res(nullptr),
|
|
|
|
_done(false),
|
2018-12-15 02:49:42 +01:00
|
|
|
_id(_nextId++)
|
2018-12-15 01:28:11 +01:00
|
|
|
{
|
2019-04-26 01:38:15 +02:00
|
|
|
;
|
2018-12-15 01:28:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
DNSLookup::~DNSLookup()
|
|
|
|
{
|
2018-12-15 02:49:42 +01:00
|
|
|
// Remove this job from the active jobs list
|
|
|
|
std::unique_lock<std::mutex> lock(_activeJobsMutex);
|
|
|
|
_activeJobs.erase(_id);
|
2018-12-15 01:28:11 +01:00
|
|
|
}
|
|
|
|
|
2019-04-25 05:50:10 +02:00
|
|
|
// we want hostname to be copied, not passed as a const reference
|
|
|
|
struct addrinfo* DNSLookup::getAddrInfo(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-02-21 03:59:07 +01:00
|
|
|
int getaddrinfo_result = getaddrinfo(hostname.c_str(), sport.c_str(),
|
2018-12-15 01:28:11 +01:00
|
|
|
&hints, &res);
|
|
|
|
if (getaddrinfo_result)
|
|
|
|
{
|
|
|
|
errMsg = gai_strerror(getaddrinfo_result);
|
|
|
|
res = nullptr;
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct addrinfo* DNSLookup::resolve(std::string& errMsg,
|
|
|
|
const CancellationRequest& isCancellationRequested,
|
|
|
|
bool blocking)
|
|
|
|
{
|
|
|
|
return blocking ? resolveBlocking(errMsg, isCancellationRequested)
|
|
|
|
: resolveAsync(errMsg, isCancellationRequested);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct addrinfo* DNSLookup::resolveBlocking(std::string& errMsg,
|
|
|
|
const CancellationRequest& isCancellationRequested)
|
|
|
|
{
|
|
|
|
errMsg = "no error";
|
|
|
|
|
|
|
|
// Maybe a cancellation request got in before the background thread terminated ?
|
2019-03-20 22:29:02 +01:00
|
|
|
if (isCancellationRequested && isCancellationRequested())
|
2018-12-15 01:28:11 +01:00
|
|
|
{
|
|
|
|
errMsg = "cancellation requested";
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return getAddrInfo(_hostname, _port, errMsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct addrinfo* DNSLookup::resolveAsync(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.
|
|
|
|
}
|
|
|
|
|
2018-12-15 02:49:42 +01:00
|
|
|
// Record job in the active Job set
|
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lock(_activeJobsMutex);
|
|
|
|
_activeJobs.insert(_id);
|
|
|
|
}
|
|
|
|
|
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-01-07 03:27:26 +01:00
|
|
|
_thread = std::thread(&DNSLookup::run, this, _id, _hostname, _port);
|
2018-12-15 01:28:11 +01:00
|
|
|
_thread.detach();
|
|
|
|
|
2018-12-15 02:49:42 +01:00
|
|
|
std::unique_lock<std::mutex> lock(_conditionVariableMutex);
|
2018-12-15 01:28:11 +01:00
|
|
|
|
|
|
|
while (!_done)
|
|
|
|
{
|
|
|
|
// Wait for 10 milliseconds on the condition variable, to see
|
|
|
|
// if the bg thread has terminated.
|
|
|
|
if (_condition.wait_for(lock, std::chrono::milliseconds(_wait)) == std::cv_status::no_timeout)
|
|
|
|
{
|
|
|
|
// Background thread has terminated, so we can break of this loop
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Were we cancelled ?
|
2019-03-20 22:29:02 +01:00
|
|
|
if (isCancellationRequested && 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-03-20 22:29:02 +01:00
|
|
|
if (isCancellationRequested && 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-04-30 02:27:53 +02:00
|
|
|
void DNSLookup::run(uint64_t id, 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
|
|
|
|
|
|
|
// if this isn't an active job, and the control thread is gone
|
2019-04-30 04:29:27 +02:00
|
|
|
// there is nothing to do, and we don't want to touch the defunct
|
2018-12-15 02:49:42 +01:00
|
|
|
// object data structure such as _errMsg or _condition
|
|
|
|
std::unique_lock<std::mutex> lock(_activeJobsMutex);
|
|
|
|
if (_activeJobs.count(id) == 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-01-07 03:27:26 +01:00
|
|
|
// Copy result into the member variables
|
2019-04-30 04:29:27 +02:00
|
|
|
setRes(res);
|
|
|
|
setErrMsg(errMsg);
|
|
|
|
|
2018-12-15 01:28:11 +01:00
|
|
|
_condition.notify_one();
|
|
|
|
_done = true;
|
|
|
|
}
|
2019-04-30 04:29:27 +02:00
|
|
|
|
|
|
|
void DNSLookup::setErrMsg(const std::string& errMsg)
|
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lock(_errMsgMutex);
|
|
|
|
_errMsg = errMsg;
|
|
|
|
}
|
|
|
|
|
|
|
|
const std::string& DNSLookup::getErrMsg()
|
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lock(_errMsgMutex);
|
|
|
|
return _errMsg;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DNSLookup::setRes(struct addrinfo* addr)
|
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lock(_resMutex);
|
|
|
|
_res = addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct addrinfo* DNSLookup::getRes()
|
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lock(_resMutex);
|
|
|
|
return _res;
|
|
|
|
}
|
2018-12-15 01:28:11 +01:00
|
|
|
}
|