Fix DNSLookup memory leak (#422)
* Fix memory leak with shared_ptr and -fsanitize=address * Replace addrinfo* by shared_ptr * fsanitize=address only on Linux * Add USE_WS Linux CI test * Remove fsanitize from the cmake files * Remove USE_WS in linux test suite
This commit is contained in:
parent
a5d4911a16
commit
679ce519dd
@ -23,6 +23,7 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
// mingw build quirks
|
// mingw build quirks
|
||||||
#if defined(_WIN32) && defined(__GNUC__)
|
#if defined(_WIN32) && defined(__GNUC__)
|
||||||
@ -44,7 +45,7 @@ namespace ix
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct addrinfo* DNSLookup::getAddrInfo(const std::string& hostname,
|
DNSLookup::AddrInfoPtr DNSLookup::getAddrInfo(const std::string& hostname,
|
||||||
int port,
|
int port,
|
||||||
std::string& errMsg)
|
std::string& errMsg)
|
||||||
{
|
{
|
||||||
@ -63,10 +64,10 @@ namespace ix
|
|||||||
errMsg = gai_strerror(getaddrinfo_result);
|
errMsg = gai_strerror(getaddrinfo_result);
|
||||||
res = nullptr;
|
res = nullptr;
|
||||||
}
|
}
|
||||||
return res;
|
return AddrInfoPtr{ res, freeaddrinfo };
|
||||||
}
|
}
|
||||||
|
|
||||||
struct addrinfo* DNSLookup::resolve(std::string& errMsg,
|
DNSLookup::AddrInfoPtr DNSLookup::resolve(std::string& errMsg,
|
||||||
const CancellationRequest& isCancellationRequested,
|
const CancellationRequest& isCancellationRequested,
|
||||||
bool cancellable)
|
bool cancellable)
|
||||||
{
|
{
|
||||||
@ -74,12 +75,7 @@ namespace ix
|
|||||||
: resolveUnCancellable(errMsg, isCancellationRequested);
|
: resolveUnCancellable(errMsg, isCancellationRequested);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DNSLookup::release(struct addrinfo* addr)
|
DNSLookup::AddrInfoPtr DNSLookup::resolveUnCancellable(
|
||||||
{
|
|
||||||
freeaddrinfo(addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct addrinfo* DNSLookup::resolveUnCancellable(
|
|
||||||
std::string& errMsg, const CancellationRequest& isCancellationRequested)
|
std::string& errMsg, const CancellationRequest& isCancellationRequested)
|
||||||
{
|
{
|
||||||
errMsg = "no error";
|
errMsg = "no error";
|
||||||
@ -94,7 +90,7 @@ namespace ix
|
|||||||
return getAddrInfo(_hostname, _port, errMsg);
|
return getAddrInfo(_hostname, _port, errMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct addrinfo* DNSLookup::resolveCancellable(
|
DNSLookup::AddrInfoPtr DNSLookup::resolveCancellable(
|
||||||
std::string& errMsg, const CancellationRequest& isCancellationRequested)
|
std::string& errMsg, const CancellationRequest& isCancellationRequested)
|
||||||
{
|
{
|
||||||
errMsg = "no error";
|
errMsg = "no error";
|
||||||
@ -157,7 +153,7 @@ namespace ix
|
|||||||
// gone, so we use temporary variables (res) or we pass in by copy everything that
|
// gone, so we use temporary variables (res) or we pass in by copy everything that
|
||||||
// getAddrInfo needs to work.
|
// getAddrInfo needs to work.
|
||||||
std::string errMsg;
|
std::string errMsg;
|
||||||
struct addrinfo* res = getAddrInfo(hostname, port, errMsg);
|
auto res = getAddrInfo(hostname, port, errMsg);
|
||||||
|
|
||||||
if (auto lock = self.lock())
|
if (auto lock = self.lock())
|
||||||
{
|
{
|
||||||
@ -181,13 +177,13 @@ namespace ix
|
|||||||
return _errMsg;
|
return _errMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DNSLookup::setRes(struct addrinfo* addr)
|
void DNSLookup::setRes(DNSLookup::AddrInfoPtr addr)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(_resMutex);
|
std::lock_guard<std::mutex> lock(_resMutex);
|
||||||
_res = addr;
|
_res = std::move(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct addrinfo* DNSLookup::getRes()
|
DNSLookup::AddrInfoPtr DNSLookup::getRes()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(_resMutex);
|
std::lock_guard<std::mutex> lock(_resMutex);
|
||||||
return _res;
|
return _res;
|
||||||
|
@ -24,22 +24,21 @@ namespace ix
|
|||||||
class DNSLookup : public std::enable_shared_from_this<DNSLookup>
|
class DNSLookup : public std::enable_shared_from_this<DNSLookup>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
using AddrInfoPtr = std::shared_ptr<addrinfo>;
|
||||||
DNSLookup(const std::string& hostname, int port, int64_t wait = DNSLookup::kDefaultWait);
|
DNSLookup(const std::string& hostname, int port, int64_t wait = DNSLookup::kDefaultWait);
|
||||||
~DNSLookup() = default;
|
~DNSLookup() = default;
|
||||||
|
|
||||||
struct addrinfo* resolve(std::string& errMsg,
|
AddrInfoPtr resolve(std::string& errMsg,
|
||||||
const CancellationRequest& isCancellationRequested,
|
const CancellationRequest& isCancellationRequested,
|
||||||
bool cancellable = true);
|
bool cancellable = true);
|
||||||
|
|
||||||
void release(struct addrinfo* addr);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct addrinfo* resolveCancellable(std::string& errMsg,
|
AddrInfoPtr resolveCancellable(std::string& errMsg,
|
||||||
const CancellationRequest& isCancellationRequested);
|
const CancellationRequest& isCancellationRequested);
|
||||||
struct addrinfo* resolveUnCancellable(std::string& errMsg,
|
AddrInfoPtr resolveUnCancellable(std::string& errMsg,
|
||||||
const CancellationRequest& isCancellationRequested);
|
const CancellationRequest& isCancellationRequested);
|
||||||
|
|
||||||
static struct addrinfo* getAddrInfo(const std::string& hostname,
|
AddrInfoPtr getAddrInfo(const std::string& hostname,
|
||||||
int port,
|
int port,
|
||||||
std::string& errMsg);
|
std::string& errMsg);
|
||||||
|
|
||||||
@ -48,15 +47,15 @@ namespace ix
|
|||||||
void setErrMsg(const std::string& errMsg);
|
void setErrMsg(const std::string& errMsg);
|
||||||
const std::string& getErrMsg();
|
const std::string& getErrMsg();
|
||||||
|
|
||||||
void setRes(struct addrinfo* addr);
|
void setRes(AddrInfoPtr addr);
|
||||||
struct addrinfo* getRes();
|
AddrInfoPtr getRes();
|
||||||
|
|
||||||
std::string _hostname;
|
std::string _hostname;
|
||||||
int _port;
|
int _port;
|
||||||
int64_t _wait;
|
int64_t _wait;
|
||||||
const static int64_t kDefaultWait;
|
const static int64_t kDefaultWait;
|
||||||
|
|
||||||
struct addrinfo* _res;
|
AddrInfoPtr _res;
|
||||||
std::mutex _resMutex;
|
std::mutex _resMutex;
|
||||||
|
|
||||||
std::string _errMsg;
|
std::string _errMsg;
|
||||||
|
@ -102,7 +102,7 @@ namespace ix
|
|||||||
// First do DNS resolution
|
// First do DNS resolution
|
||||||
//
|
//
|
||||||
auto dnsLookup = std::make_shared<DNSLookup>(hostname, port);
|
auto dnsLookup = std::make_shared<DNSLookup>(hostname, port);
|
||||||
struct addrinfo* res = dnsLookup->resolve(errMsg, isCancellationRequested);
|
auto res = dnsLookup->resolve(errMsg, isCancellationRequested);
|
||||||
if (res == nullptr)
|
if (res == nullptr)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
@ -112,7 +112,7 @@ namespace ix
|
|||||||
|
|
||||||
// iterate through the records to find a working peer
|
// iterate through the records to find a working peer
|
||||||
struct addrinfo* address;
|
struct addrinfo* address;
|
||||||
for (address = res; address != nullptr; address = address->ai_next)
|
for (address = res.get(); address != nullptr; address = address->ai_next)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// Second try to connect to the remote host
|
// Second try to connect to the remote host
|
||||||
@ -124,7 +124,6 @@ namespace ix
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
freeaddrinfo(res);
|
|
||||||
return sockfd;
|
return sockfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,13 +19,9 @@ TEST_CASE("dns", "[net]")
|
|||||||
auto dnsLookup = std::make_shared<DNSLookup>("www.google.com", 80);
|
auto dnsLookup = std::make_shared<DNSLookup>("www.google.com", 80);
|
||||||
|
|
||||||
std::string errMsg;
|
std::string errMsg;
|
||||||
struct addrinfo* res;
|
auto res = dnsLookup->resolve(errMsg, [] { return false; });
|
||||||
|
|
||||||
res = dnsLookup->resolve(errMsg, [] { return false; });
|
|
||||||
std::cerr << "Error message: " << errMsg << std::endl;
|
std::cerr << "Error message: " << errMsg << std::endl;
|
||||||
REQUIRE(res != nullptr);
|
REQUIRE(res != nullptr);
|
||||||
|
|
||||||
dnsLookup->release(res);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Test resolving a non-existing hostname")
|
SECTION("Test resolving a non-existing hostname")
|
||||||
@ -33,7 +29,7 @@ TEST_CASE("dns", "[net]")
|
|||||||
auto dnsLookup = std::make_shared<DNSLookup>("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 80);
|
auto dnsLookup = std::make_shared<DNSLookup>("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww", 80);
|
||||||
|
|
||||||
std::string errMsg;
|
std::string errMsg;
|
||||||
struct addrinfo* res = dnsLookup->resolve(errMsg, [] { return false; });
|
auto res = dnsLookup->resolve(errMsg, [] { return false; });
|
||||||
std::cerr << "Error message: " << errMsg << std::endl;
|
std::cerr << "Error message: " << errMsg << std::endl;
|
||||||
REQUIRE(res == nullptr);
|
REQUIRE(res == nullptr);
|
||||||
}
|
}
|
||||||
@ -44,7 +40,7 @@ TEST_CASE("dns", "[net]")
|
|||||||
|
|
||||||
std::string errMsg;
|
std::string errMsg;
|
||||||
// The callback returning true means we are requesting cancellation
|
// The callback returning true means we are requesting cancellation
|
||||||
struct addrinfo* res = dnsLookup->resolve(errMsg, [] { return true; });
|
auto res = dnsLookup->resolve(errMsg, [] { return true; });
|
||||||
std::cerr << "Error message: " << errMsg << std::endl;
|
std::cerr << "Error message: " << errMsg << std::endl;
|
||||||
REQUIRE(res == nullptr);
|
REQUIRE(res == nullptr);
|
||||||
}
|
}
|
||||||
|
@ -892,9 +892,8 @@ namespace ix
|
|||||||
auto dnsLookup = std::make_shared<DNSLookup>(hostname, 80);
|
auto dnsLookup = std::make_shared<DNSLookup>(hostname, 80);
|
||||||
|
|
||||||
std::string errMsg;
|
std::string errMsg;
|
||||||
struct addrinfo* res;
|
|
||||||
|
|
||||||
res = dnsLookup->resolve(errMsg, [] { return false; });
|
auto res = dnsLookup->resolve(errMsg, [] { return false; });
|
||||||
|
|
||||||
auto addr = res->ai_addr;
|
auto addr = res->ai_addr;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user