IXWebSocket/ixwebsocket/IXDNSLookup.cpp

171 lines
5.3 KiB
C++
Raw Normal View History

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-06-27 04:12:48 +02:00
#include <thread>
2018-12-15 01:28:11 +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
2018-12-23 23:14:38 +01:00
DNSLookup::DNSLookup(const std::string& hostname, int port, int64_t wait) :
2019-06-27 01:25:07 +02:00
_hostname(hostname),
2018-12-15 01:28:11 +01:00
_port(port),
_wait(wait),
2018-12-15 01:28:11 +01:00
_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
}
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;
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 cancellable)
2018-12-15 01:28:11 +01:00
{
return cancellable ? resolveCancellable(errMsg, isCancellationRequested)
: resolveUnCancellable(errMsg, isCancellationRequested);
2018-12-15 01:28:11 +01: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 ?
if (isCancellationRequested && 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
}
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.
}
//
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
//
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
// 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 ?
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 ?
if (isCancellationRequested && isCancellationRequested())
2018-12-15 01:28:11 +01:00
{
errMsg = "cancellation requested";
return nullptr;
}
errMsg = getErrMsg();
return getRes();
2018-12-15 01:28:11 +01:00
}
2019-06-27 01:25:07 +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
// 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 (self.lock())
2018-12-15 02:49:42 +01:00
{
// Copy result into the member variables
setRes(res);
setErrMsg(errMsg);
2018-12-15 02:49:42 +01:00
_done = true;
}
2018-12-15 01:28:11 +01:00
}
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(struct addrinfo* addr)
{
std::lock_guard<std::mutex> lock(_resMutex);
_res = addr;
}
struct addrinfo* DNSLookup::getRes()
{
std::lock_guard<std::mutex> lock(_resMutex);
return _res;
}
2018-12-15 01:28:11 +01:00
}