// Copyright 2016 The Domain Registry Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package google.registry.dns.writer.dnsupdate; import static com.google.common.base.Verify.verify; import static com.google.common.collect.Sets.intersection; import static com.google.common.collect.Sets.union; import static google.registry.model.EppResourceUtils.loadByUniqueId; import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.net.InternetDomainName; import google.registry.config.ConfigModule.Config; import google.registry.model.dns.DnsWriter; import google.registry.model.domain.DomainResource; import google.registry.model.domain.secdns.DelegationSignerData; import google.registry.model.host.HostResource; import google.registry.model.registry.Registries; import google.registry.util.Clock; import java.io.IOException; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import javax.inject.Inject; import org.joda.time.Duration; import org.xbill.DNS.AAAARecord; import org.xbill.DNS.ARecord; import org.xbill.DNS.DClass; import org.xbill.DNS.DSRecord; import org.xbill.DNS.Message; import org.xbill.DNS.NSRecord; import org.xbill.DNS.Name; import org.xbill.DNS.RRset; import org.xbill.DNS.Rcode; import org.xbill.DNS.TextParseException; import org.xbill.DNS.Type; import org.xbill.DNS.Update; /** * A DnsWriter that implements the DNS UPDATE protocol as specified in * RFC 2136. Publishes changes in the * domain-registry to a (capable) external DNS server, sometimes called a "hidden master". DNS * UPDATE messages are sent via a supplied "transport" class. For each publish call, a single * UPDATE message is created containing the records required to "synchronize" the DNS with the * current (at the time of processing) state of the registry, for the supplied domain/host. * *
The general strategy of the publish methods is to delete all resource records of any * type that match the exact domain/host name supplied. And then for create/update cases, * add any required records. Deleting all records of any type assumes that the registry is * authoritative for all records for names in the zone. This seems appropriate for a TLD DNS server, * which should only contain records required for proper DNS delegation. * *
Only NS, DS, A, and AAAA records are published, and in particular no DNSSEC signing is done * assuming that this will be done by a third party DNS provider. * *
Each publish call is treated as an atomic update to the DNS. If an update fails an exception
* is thrown, expecting the caller to retry the update later. The SOA record serial number is
* implicitly incremented by the server on each UPDATE message, as required by RFC 2136. Care must
* be taken to make sure the SOA serial number does not go backwards if the entire TLD (zone) is
* "reset" to empty and republished.
*/
public class DnsUpdateWriter implements DnsWriter {
private final Duration dnsTimeToLive;
private final DnsMessageTransport transport;
private final Clock clock;
/**
* Class constructor.
*
* @param dnsTimeToLive TTL used for any created resource records
* @param transport the transport used to send/receive the UPDATE messages
* @param clock a source of time
*/
@Inject
public DnsUpdateWriter(
@Config("dnsUpdateTimeToLive") Duration dnsTimeToLive,
DnsMessageTransport transport,
Clock clock) {
this.dnsTimeToLive = dnsTimeToLive;
this.transport = transport;
this.clock = clock;
}
/**
* Publish the domain, while keeping tracking of which host refresh quest triggered this domain
* refresh. Delete the requesting host in addition to all subordinate hosts.
*
* @param domainName the fully qualified domain name, with no trailing dot
* @param requestingHostName the fully qualified host name, with no trailing dot, that triggers
* this domain refresh request
*/
private void publishDomain(String domainName, String requestingHostName) {
DomainResource domain = loadByUniqueId(DomainResource.class, domainName, clock.nowUtc());
try {
Update update = new Update(toAbsoluteName(findTldFromName(domainName)));
update.delete(toAbsoluteName(domainName), Type.ANY);
if (domain != null) {
// As long as the domain exists, orphan glues should be cleaned.
deleteSubordinateHostAddressSet(domain, requestingHostName, update);
if (domain.shouldPublishToDns()) {
addInBailiwickNameServerSet(domain, update);
update.add(makeNameServerSet(domain));
update.add(makeDelegationSignerSet(domain));
}
}
Message response = transport.send(update);
verify(
response.getRcode() == Rcode.NOERROR,
"DNS server failed domain update for '%s' rcode: %s",
domainName,
Rcode.string(response.getRcode()));
} catch (IOException e) {
throw new RuntimeException("publishDomain failed: " + domainName, e);
}
}
@Override
public void publishDomain(String domainName) {
publishDomain(domainName, null);
}
@Override
public void publishHost(String hostName) {
// Get the superordinate domain name of the host.
InternetDomainName host = InternetDomainName.from(hostName);
ImmutableList