Commit f06ec6e6 authored by Ivan Vilata-i-Balaguer's avatar Ivan Vilata-i-Balaguer
Browse files

Avoid ports in DoH response parsing.

It now returns a vector of addresses.  Client code uses an iterator
transforming addresses into TCP endpoints to create the TCP lookup results
object.
parent ae312c98
......@@ -15,6 +15,7 @@
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/adaptor/indexed.hpp>
#include <boost/regex.hpp>
#include <iterator>
#include <iostream>
#include <cstdlib> // for atexit()
......@@ -632,6 +633,40 @@ Client::State::fetch_via_self( Rq request, const UserAgentMetaData& meta
return Session::create(move(con), cancel, yield);
}
// Transforms addresses to TCP endpoints with the given port.
template<class Addrs>
class AddrsAsEndpoints {
public:
using value_type = TcpLookup::endpoint_type;
using addrs_iterator = typename Addrs::const_iterator;
AddrsAsEndpoints(const Addrs& addrs, unsigned short port)
: _addrs(addrs), _port(port)
{}
class const_iterator : public std::iterator<std::input_iterator_tag, value_type> {
public:
const_iterator(const addrs_iterator& it, unsigned short port)
: _it(it), _port(port)
{}
value_type operator*() const { return {*_it, _port}; }
const_iterator& operator++() { ++_it; return *this; }
bool operator==(const_iterator other) const { return _it == other._it; }
bool operator!=(const_iterator other) const { return _it != other._it; }
private:
addrs_iterator _it;
unsigned short _port;
};
const_iterator begin() const { return {_addrs.begin(), _port}; };
const_iterator end() const { return {_addrs.end(), _port}; };
private:
const Addrs& _addrs;
unsigned short _port;
};
TcpLookup
Client::State::resolve_tcp_doh( const std::string& host
, const std::string& port
......@@ -667,8 +702,11 @@ Client::State::resolve_tcp_doh( const std::string& host
(s, doh::payload_size, cancel, yield[ec].tag("slurp"));
return_or_throw_on_error(yield, cancel, ec, TcpLookup());
auto lookup = doh::parse_response(rs, host, *portn_o, ec);
return or_throw(yield, ec, move(lookup));
auto answers = doh::parse_response(rs, host, ec);
if (ec) return or_throw<TcpLookup>(yield, ec);
AddrsAsEndpoints eps{answers, *portn_o};
return TcpLookup::create(eps.begin(), eps.end(), host, port);
}
TcpLookup
......
......@@ -139,14 +139,12 @@ build_request( const std::string& name
return rq;
}
using EndpointVector = std::vector<TcpLookup::endpoint_type>;
// Appends endpoints to the given vector on answers for the given host.
// Appends addresses to the given vector on answers for the given host.
class Listener : public DnsParserListener {
public:
Listener( const std::string& host, unsigned short port
, EndpointVector& epv)
: _host(host), _port(port), _epv(epv)
Listener( const std::string& host
, Answers& answers)
: _host(host), _answers(answers)
{}
void onDnsRec(in_addr addr, std::string name, std::string) override
......@@ -154,7 +152,7 @@ public:
if (name != _host) return; // unrelated answer, ignore
auto ip4addr = asio::ip::make_address_v4(::ntohl(addr.s_addr));
LOG_DEBUG("DoH: ", name, " -> ", ip4addr);
_epv.push_back({std::move(ip4addr), _port});
_answers.push_back(std::move(ip4addr));
}
void onDnsRec(in6_addr addr, std::string name, std::string) override
......@@ -165,19 +163,17 @@ public:
std::memcpy(addrb.data(), addr.s6_addr, addrb.size());
auto ip6addr = asio::ip::make_address_v6(addrb);
LOG_DEBUG("DoH: ", name, " -> ", ip6addr);
_epv.push_back({std::move(ip6addr), _port});
_answers.push_back(std::move(ip6addr));
}
private:
const std::string& _host;
unsigned short _port;
EndpointVector& _epv;
Answers& _answers;
};
TcpLookup
Answers
parse_response( const Response& rs
, const std::string& host
, unsigned short port
, sys::error_code& ec)
{
if ( rs.result() != http::status::ok
......@@ -186,9 +182,9 @@ parse_response( const Response& rs
return {};
}
EndpointVector epv;
Answers answers;
try {
Listener dnsl(host, port, epv);
Listener dnsl(host, answers);
std::unique_ptr<DnsParser> dnsp(DnsParserNew(&dnsl, false, true)); // no paths, no CNAMEs
assert(dnsp);
// The DNS parser not specifying pointer-to-const arguments
......@@ -203,12 +199,10 @@ parse_response( const Response& rs
}
// Assume that the DoH server is not authoritative.
if (!ec && epv.empty()) ec = asio::error::host_not_found_try_again;
if (!ec && answers.empty()) ec = asio::error::host_not_found_try_again;
if (ec) return {};
auto port_s = std::to_string(port);
return TcpLookup::create(epv.begin(), epv.end(), host, port_s);
return answers;
}
}} // ouinet::doh namespace
......@@ -5,8 +5,9 @@
#pragma once
#include <string>
#include <vector>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ip/address.hpp>
#include <boost/beast/http/message.hpp>
#include <boost/beast/http/empty_body.hpp>
#include <boost/beast/http/string_body.hpp>
......@@ -26,7 +27,7 @@ using Request = http::request<http::empty_body>;
using Response = http::response<http::string_body>;
using TcpLookup = asio::ip::tcp::resolver::results_type;
using Answers = std::vector<asio::ip::address>;
using Endpoint = std::string;
......@@ -43,17 +44,12 @@ boost::optional<Endpoint> endpoint_from_base(const std::string&);
boost::optional<Request> build_request( const std::string& name
, const Endpoint&);
// Return a lookup result with the answers for the given host
// Return the addresses in the answers for the given host
// in the given response.
//
// Irrelevant answers in the response are discarded.
//
// TODO: Passing the port in and getting a lookup
// makes for a weird interface,
// we may drop the port and just return a list of IP addresses.
TcpLookup parse_response( const Response&
, const std::string& host
, unsigned short port
, sys::error_code&);
Answers parse_response( const Response&
, const std::string& host
, sys::error_code&);
}} // ouinet::doh namespace
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment