IXWebSocket/ixwebsocket/IXDNSLookup.cpp

172 lines
5.2 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>
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-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 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 ?
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::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 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);
_thread = std::thread(&DNSLookup::run, this, self, 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 ?
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
_condition.notify_one();
_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
}