.. note::
Please do read the :doc:`Backend Writer's guide <../appendices/backend-writers-guide>` carefully. The
PipeBackend, like all other backends, must not do any DNS thinking, but
- answer all questions (INCLUDING THE ANY QUESTION) faithfully.
+ answer all questions (**including the ANY question**) faithfully.
Specifically, the queries that the PipeBackend receives will not
correspond to the queries that arrived over DNS. So, a query for an AAAA
record may turn into a backend query for an ANY record. There is nothing
local-ip-address refers to the IP address the question was received on.
When set to 3, the real remote IP/subnet is added based on edns-subnet
support (this also requires enabling :ref:`setting-edns-subnet-processing`).
-When set to 4 it sends zone name in AXFR request. See also :ref:`PipeBackend Protocol <pipebackend-protocol>` below.
+When set to 4, it will also send the zone name in AXFR requests.
+See also :ref:`PipeBackend Protocol <pipebackend-protocol>` below.
.. _setting-pipe-command:
Handshake
^^^^^^^^^
-PowerDNS sends out ``HELO\t1``, indicating that it wants to speak the
-protocol as defined in this document, version 1. For abi-version 2 or 3,
-PowerDNS sends ``HELO\t2`` or ``HELO\t3``. A PowerDNS Coprocess must
-then send out a banner, prefixed by ``OK\t``, indicating it launched
-successfully. If it does not support the indicated version, it should
-respond with ``FAIL``, but not exit. Suggested behaviour is to try and
-read a further line, and wait to be terminated.
+PowerDNS sends out ``HELO\tN``, where N is the version of the protocol it
+wants to speak (e.g. ``HELO\t1`` for protocol version 1, ``HELO\t2`` for
+protocol version 2, etc).
+A PowerDNS Coprocess must then send out a banner, prefixed by ``OK\t``,
+indicating it launched successfully.
+If it does not support the indicated version, it should respond with ``FAIL``,
+but not exit.
+Suggested behaviour is to try and read a further line, and wait to be
+terminated.
.. note::
Fields are separated by a tab (``\t``) character,
Q qname qclass qtype id remote-ip-address local-ip-address
-pipe-abi-version = 3
-~~~~~~~~~~~~~~~~~~~~
+pipe-abi-version = 3 and higher
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
::
AXFR id zone-name
The ``id`` is gathered from the answer to a SOA query. ``zone-name`` is
-given in ABI version 4.
+given in ABI version 4 and higher.
Answers
^^^^^^^
``content`` is as specified in :doc:`../appendices/types`. For MX and SRV,
content consists of the priority, followed by a tab, followed by the
actual content.
+For ENT (Empty Non-Terminal), content will be ignored and can be omitted.
A sample dialogue may look like this (note that in reality, almost all
queries will actually be for the ANY qtype):
ABI version 3 and higher
~~~~~~~~~~~~~~~~~~~~~~~~
-For abi-version 3, DATA-responses get two extra fields:
+From abi-version 3 onwards, DATA-responses get two extra fields:
::
present for this purpose. Do note that the DS record for a secure
delegation should be authoritative!
-For abi-versions 1 and 2, the two new fields fall back to default
+For abi-version 1 and 2, the two new fields fall back to default
values. The default value for scopebits is 0. The default for auth is 1
(meaning authoritative).
cleanup();
}
+void PipeBackend::throwTooShortDataError(const std::string& what)
+{
+ g_log << Logger::Error << kBackendId << " Coprocess returned incomplete or empty line in data section for query for " << d_qname << endl;
+ throw PDNSException("Format error communicating with coprocess in data section" + what);
+}
+
bool PipeBackend::get(DNSResourceRecord& r)
{
if (d_disavow) // this query has been blocked
// The answer format:
// DATA qname qclass qtype ttl id content
- unsigned int extraFields = 0;
- if (d_abiVersion >= 3)
- extraFields = 2;
try {
launch();
continue;
}
else if (parts[0] == "DATA") { // yay
- if (parts.size() < 7 + extraFields) {
- g_log << Logger::Error << kBackendId << " Coprocess returned incomplete or empty line in data section for query for " << d_qname << endl;
- throw PDNSException("Format error communicating with coprocess in data section");
- // now what?
+ // The shortest records (ENT) require 6 fields. Other may require more
+ // and will have a stricter check once the record type has been
+ // computed.
+ if (parts.size() < 6 + (d_abiVersion >= 3 ? 2 : 0)) {
+ throwTooShortDataError("");
}
if (d_abiVersion >= 3) {
r.scopeMask = std::stoi(parts[1]);
r.auth = (parts[2] == "1");
+ parts.erase(parts.begin() + 1, parts.begin() + 3);
}
else {
r.scopeMask = 0;
r.auth = true;
}
- r.qname = DNSName(parts[1 + extraFields]);
- r.qtype = parts[3 + extraFields];
- pdns::checked_stoi_into(r.ttl, parts[4 + extraFields]);
- pdns::checked_stoi_into(r.domain_id, parts[5 + extraFields]);
-
- if (r.qtype.getCode() != QType::MX && r.qtype.getCode() != QType::SRV) {
+ r.qname = DNSName(parts[1]);
+ r.qtype = parts[3];
+ pdns::checked_stoi_into(r.ttl, parts[4]);
+ pdns::checked_stoi_into(r.domain_id, parts[5]);
+
+ switch (r.qtype.getCode()) {
+ case QType::ENT:
+ // No other data to process
r.content.clear();
- for (unsigned int n = 6 + extraFields; n < parts.size(); ++n) {
- if (n != 6 + extraFields)
- r.content.append(1, ' ');
- r.content.append(parts[n]);
+ break;
+ case QType::MX:
+ case QType::SRV:
+ if (parts.size() < 8) {
+ throwTooShortDataError("of MX/SRV record");
}
- }
- else {
- if (parts.size() < 8 + extraFields) {
- g_log << Logger::Error << kBackendId << " Coprocess returned incomplete MX/SRV line in data section for query for " << d_qname << endl;
- throw PDNSException("Format error communicating with coprocess in data section of MX/SRV record");
+ r.content = parts[6] + " " + parts[7];
+ break;
+ default:
+ if (parts.size() < 7) {
+ throwTooShortDataError("");
}
-
- r.content = parts[6 + extraFields] + " " + parts[7 + extraFields];
+ r.content = parts[6];
+ for (std::vector<std::string>::size_type pos = 7; pos < parts.size(); ++pos) {
+ r.content.append(1, ' ');
+ r.content.append(parts[pos]);
+ }
+ break;
}
break;
}