diff --git modules/bindbackend/bindbackend2.cc modules/bindbackend/bindbackend2.cc index 8e3482f60..dc5afae9c 100644 --- modules/bindbackend/bindbackend2.cc +++ modules/bindbackend/bindbackend2.cc @@ -273,27 +273,56 @@ bool Bind2Backend::abortTransaction() } return true; } +// Perform adequate escaping of characters which have special meaning in +// Bind zone files. +// Note that the input is supposed to be a DNSName::toString() - or any of +// its variants - so we assume \ and . have been correctly escaped by +// DNSName::appendEscapedLabel already. +static const std::string bindEscape(const std::string& name) +{ + std::string ret; + std::array ebuf{}; + + for (char letter : name) { + switch (letter) { + case '$': + case '@': + case '"': + case ';': + case '(': + case ')': + snprintf(ebuf.data(), ebuf.size(), "\\%03u", static_cast(letter)); + ret += ebuf.data(); + break; + default: + ret += letter; + break; + } + } + return ret; +} + bool Bind2Backend::feedRecord(const DNSResourceRecord& rr, const DNSName& /* ordername */, bool /* ordernameIsNSEC3 */) { if (d_transaction_id < 1) { throw DBException("Bind2Backend::feedRecord() called outside of transaction"); } string qname; if (d_transaction_qname.empty()) { - qname = rr.qname.toString(); + qname = bindEscape(rr.qname.toString()); } else if (rr.qname.isPartOf(d_transaction_qname)) { if (rr.qname == d_transaction_qname) { qname = "@"; } else { DNSName relName = rr.qname.makeRelative(d_transaction_qname); - qname = relName.toStringNoDot(); + qname = bindEscape(relName.toStringNoDot()); } } else { throw DBException("out-of-zone data '" + rr.qname.toLogString() + "' during AXFR of zone '" + d_transaction_qname.toLogString() + "'"); }