2018-10-01 04:01:06 +02:00
|
|
|
// Matrix Construct
|
|
|
|
//
|
|
|
|
// Copyright (C) Matrix Construct Developers, Authors & Contributors
|
|
|
|
// Copyright (C) 2016-2018 Jason Volk <jason@zemos.net>
|
|
|
|
//
|
|
|
|
// Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
// purpose with or without fee is hereby granted, provided that the above
|
|
|
|
// copyright notice and this permission notice is present in all copies. The
|
|
|
|
// full license for this software is available in the LICENSE file.
|
|
|
|
|
2018-10-01 22:27:46 +02:00
|
|
|
#include "s_dns.h"
|
2018-10-01 04:01:06 +02:00
|
|
|
|
2018-10-01 22:27:46 +02:00
|
|
|
ircd::mapi::header
|
2018-10-01 04:01:06 +02:00
|
|
|
IRCD_MODULE
|
|
|
|
{
|
2018-10-01 22:27:46 +02:00
|
|
|
"Domain Name System Client, Cache & Components",
|
|
|
|
[] // init
|
|
|
|
{
|
|
|
|
ircd::net::dns::resolver_init();
|
|
|
|
},
|
|
|
|
[] // fini
|
|
|
|
{
|
|
|
|
ircd::net::dns::resolver_fini();
|
|
|
|
}
|
2018-10-01 04:01:06 +02:00
|
|
|
};
|
|
|
|
|
2018-10-01 21:46:35 +02:00
|
|
|
/// Convenience composition with a single ipport callback. This is the result of
|
|
|
|
/// an automatic chain of queries such as SRV and A/AAAA based on the input and
|
|
|
|
/// intermediate results.
|
|
|
|
void
|
|
|
|
ircd::net::dns::_resolve_ipport(const hostport &hp,
|
2018-10-03 23:18:27 +02:00
|
|
|
opts opts,
|
2018-10-01 21:46:35 +02:00
|
|
|
callback_ipport_one callback)
|
|
|
|
{
|
2018-10-03 23:18:27 +02:00
|
|
|
auto handler
|
2018-10-01 21:46:35 +02:00
|
|
|
{
|
2018-10-03 23:18:27 +02:00
|
|
|
std::bind(&handle_ipport__A, std::move(callback), ph::_1, ph::_2, ph::_3)
|
|
|
|
};
|
2018-10-01 21:46:35 +02:00
|
|
|
|
|
|
|
if(!hp.service)
|
2018-10-03 23:18:27 +02:00
|
|
|
return _resolve__A(hp, opts, std::move(handler));
|
2018-10-01 21:46:35 +02:00
|
|
|
|
2018-10-03 23:18:27 +02:00
|
|
|
opts.nxdomain_exceptions = false;
|
|
|
|
_resolve__SRV(hp, opts, [opts(opts), handler(std::move(handler))]
|
2018-10-01 21:46:35 +02:00
|
|
|
(std::exception_ptr eptr, hostport hp, const rfc1035::record::SRV &record)
|
|
|
|
mutable
|
|
|
|
{
|
|
|
|
if(eptr)
|
2018-10-03 23:18:27 +02:00
|
|
|
{
|
|
|
|
static const rfc1035::record::A empty;
|
|
|
|
return handler(std::move(eptr), hp, empty);
|
|
|
|
}
|
2018-10-01 21:46:35 +02:00
|
|
|
|
2018-10-03 07:38:08 +02:00
|
|
|
opts.qtype = 0;
|
2018-10-03 23:18:27 +02:00
|
|
|
opts.nxdomain_exceptions = true;
|
|
|
|
hp.host = record.tgt?: unmake_SRV_key(hp.host);
|
|
|
|
hp.port = record.port? record.port : hp.port;
|
|
|
|
_resolve__A(hp, opts, std::move(handler));
|
2018-10-01 21:46:35 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-10-03 23:18:27 +02:00
|
|
|
void
|
|
|
|
ircd::net::dns::handle_ipport__A(callback_ipport_one callback,
|
|
|
|
std::exception_ptr eptr,
|
|
|
|
const hostport &hp,
|
|
|
|
const rfc1035::record::A &record)
|
|
|
|
{
|
|
|
|
static const ircd::net::not_found no_record
|
|
|
|
{
|
|
|
|
"Host has no A record"
|
|
|
|
};
|
|
|
|
|
|
|
|
if(!eptr && !record.ip4)
|
|
|
|
eptr = std::make_exception_ptr(no_record);
|
|
|
|
|
|
|
|
const ipport ipport
|
|
|
|
{
|
|
|
|
record.ip4, port(hp)
|
|
|
|
};
|
|
|
|
|
|
|
|
callback(std::move(eptr), hp, ipport);
|
|
|
|
}
|
|
|
|
|
2018-10-01 21:46:35 +02:00
|
|
|
/// Convenience callback with a single SRV record which was selected from
|
|
|
|
/// the vector with stochastic respect for weighting and priority.
|
|
|
|
void
|
|
|
|
ircd::net::dns::_resolve__SRV(const hostport &hp,
|
2018-10-03 07:38:08 +02:00
|
|
|
opts opts,
|
2018-10-01 21:46:35 +02:00
|
|
|
callback_SRV_one callback)
|
|
|
|
{
|
2018-10-03 07:38:08 +02:00
|
|
|
static const auto &qtype
|
|
|
|
{
|
|
|
|
rfc1035::qtype.at("SRV")
|
|
|
|
};
|
|
|
|
|
|
|
|
if(unlikely(opts.qtype && opts.qtype != qtype))
|
|
|
|
throw error
|
|
|
|
{
|
|
|
|
"Specified query type '%s' (%u) but user's callback is for SRV records only.",
|
|
|
|
rfc1035::rqtype.at(opts.qtype),
|
|
|
|
opts.qtype
|
|
|
|
};
|
|
|
|
|
|
|
|
if(!opts.qtype)
|
|
|
|
opts.qtype = qtype;
|
|
|
|
|
2018-10-03 23:18:27 +02:00
|
|
|
auto handler
|
2018-10-01 21:46:35 +02:00
|
|
|
{
|
2018-10-03 23:18:27 +02:00
|
|
|
std::bind(&handle__SRV, std::move(callback), ph::_1, ph::_2, ph::_3)
|
|
|
|
};
|
2018-10-01 21:46:35 +02:00
|
|
|
|
2018-10-03 23:18:27 +02:00
|
|
|
_resolve__(hp, opts, std::move(handler));
|
|
|
|
}
|
2018-10-01 21:46:35 +02:00
|
|
|
|
2018-10-03 23:18:27 +02:00
|
|
|
void
|
|
|
|
ircd::net::dns::handle__SRV(callback_SRV_one callback,
|
|
|
|
std::exception_ptr eptr,
|
|
|
|
const hostport &hp,
|
|
|
|
const records &rrs)
|
|
|
|
{
|
|
|
|
static const rfc1035::record::SRV empty;
|
2018-11-14 08:19:57 +01:00
|
|
|
static const auto &qtype
|
|
|
|
{
|
|
|
|
rfc1035::qtype.at("SRV")
|
|
|
|
};
|
2018-10-01 04:01:06 +02:00
|
|
|
|
2018-10-03 23:18:27 +02:00
|
|
|
if(eptr)
|
2018-10-01 21:46:35 +02:00
|
|
|
return callback(std::move(eptr), hp, empty);
|
2018-10-03 23:18:27 +02:00
|
|
|
|
|
|
|
//TODO: prng on weight / prio plz
|
|
|
|
for(size_t i(0); i < rrs.size(); ++i)
|
|
|
|
{
|
2018-11-14 08:19:57 +01:00
|
|
|
const auto &rr(*rrs.at(i));
|
|
|
|
if(rr.type != qtype)
|
2018-10-03 23:18:27 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
const auto &record(rr.as<const rfc1035::record::SRV>());
|
|
|
|
return callback(std::move(eptr), hp, record);
|
|
|
|
}
|
|
|
|
|
|
|
|
return callback(std::move(eptr), hp, empty);
|
2018-10-01 21:46:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Convenience callback with a single A record which was selected from
|
|
|
|
/// the vector randomly.
|
|
|
|
void
|
|
|
|
ircd::net::dns::_resolve__A(const hostport &hp,
|
2018-10-03 07:38:08 +02:00
|
|
|
opts opts,
|
2018-10-01 21:46:35 +02:00
|
|
|
callback_A_one callback)
|
|
|
|
{
|
2018-10-03 07:38:08 +02:00
|
|
|
static const auto &qtype
|
|
|
|
{
|
|
|
|
rfc1035::qtype.at("A")
|
|
|
|
};
|
|
|
|
|
|
|
|
if(unlikely(opts.qtype && opts.qtype != qtype))
|
|
|
|
throw error
|
|
|
|
{
|
|
|
|
"Specified query type '%s' (%u) but user's callback is for A records only.",
|
|
|
|
rfc1035::rqtype.at(opts.qtype),
|
|
|
|
opts.qtype
|
|
|
|
};
|
|
|
|
|
|
|
|
if(!opts.qtype)
|
|
|
|
opts.qtype = qtype;
|
|
|
|
|
2018-10-03 23:18:27 +02:00
|
|
|
auto handler
|
2018-10-01 21:46:35 +02:00
|
|
|
{
|
2018-10-03 23:18:27 +02:00
|
|
|
std::bind(&handle__A, std::move(callback), ph::_1, ph::_2, ph::_3)
|
|
|
|
};
|
2018-10-01 21:46:35 +02:00
|
|
|
|
2018-10-03 23:18:27 +02:00
|
|
|
_resolve__(hp, opts, std::move(handler));
|
|
|
|
}
|
2018-10-01 21:46:35 +02:00
|
|
|
|
2018-10-03 23:18:27 +02:00
|
|
|
void
|
|
|
|
ircd::net::dns::handle__A(callback_A_one callback,
|
|
|
|
std::exception_ptr eptr,
|
|
|
|
const hostport &hp,
|
|
|
|
const records &rrs)
|
|
|
|
{
|
|
|
|
static const rfc1035::record::A empty;
|
2018-11-14 08:19:57 +01:00
|
|
|
static const auto &qtype
|
|
|
|
{
|
|
|
|
rfc1035::qtype.at("A")
|
|
|
|
};
|
2018-10-01 21:46:35 +02:00
|
|
|
|
2018-10-03 23:18:27 +02:00
|
|
|
if(eptr)
|
2018-10-01 21:46:35 +02:00
|
|
|
return callback(std::move(eptr), hp, empty);
|
2018-10-03 23:18:27 +02:00
|
|
|
|
2019-03-13 00:48:38 +01:00
|
|
|
// Get the actual number of A records in these results
|
|
|
|
size_t rec_count(0);
|
|
|
|
for(size_t i(0); i < rrs.size(); ++i)
|
|
|
|
rec_count += rrs.at(i)->type == qtype;
|
|
|
|
|
|
|
|
// Make a random selection for round-robin; rand::integer's range
|
|
|
|
// is inclusive so it's shifted down by one.
|
|
|
|
uint64_t selection
|
|
|
|
{
|
|
|
|
rec_count > 1?
|
|
|
|
rand::integer(1, rec_count) - 1:
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
assert(!rec_count || selection < rec_count);
|
|
|
|
assert(rec_count || selection == 0);
|
2018-10-03 23:18:27 +02:00
|
|
|
for(size_t i(0); i < rrs.size(); ++i)
|
|
|
|
{
|
2018-11-14 08:19:57 +01:00
|
|
|
const auto &rr(*rrs.at(i));
|
|
|
|
if(rr.type != qtype)
|
2018-10-03 23:18:27 +02:00
|
|
|
continue;
|
|
|
|
|
2019-03-13 00:48:38 +01:00
|
|
|
if(selection-- != 0)
|
|
|
|
continue;
|
|
|
|
|
2018-10-03 23:18:27 +02:00
|
|
|
const auto &record(rr.as<const rfc1035::record::A>());
|
|
|
|
return callback(std::move(eptr), hp, record);
|
|
|
|
}
|
|
|
|
|
|
|
|
return callback(std::move(eptr), hp, empty);
|
2018-10-01 21:46:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Fundamental callback with a vector of abstract resource records.
|
|
|
|
void
|
|
|
|
ircd::net::dns::_resolve__(const hostport &hp,
|
2018-10-03 07:38:08 +02:00
|
|
|
const opts &opts,
|
2018-10-01 21:46:35 +02:00
|
|
|
callback cb)
|
|
|
|
{
|
2018-10-03 07:38:08 +02:00
|
|
|
if(unlikely(!opts.qtype))
|
|
|
|
throw error
|
|
|
|
{
|
|
|
|
"A query type is required; not specified; cannot be deduced here."
|
|
|
|
};
|
|
|
|
|
|
|
|
if(opts.cache_check)
|
|
|
|
if(cache::get(hp, opts, cb))
|
2018-10-01 21:46:35 +02:00
|
|
|
return;
|
|
|
|
|
2018-10-03 07:38:08 +02:00
|
|
|
resolver_call(hp, opts, std::move(cb));
|
2018-10-01 04:01:06 +02:00
|
|
|
}
|