mirror of
https://github.com/google/nomulus.git
synced 2025-06-29 07:43:37 +02:00
mv com/google/domain/registry google/registry
This change renames directories in preparation for the great package rename. The repository is now in a broken state because the code itself hasn't been updated. However this should ensure that git correctly preserves history for each file.
This commit is contained in:
parent
a41677aea1
commit
5012893c1d
2396 changed files with 0 additions and 0 deletions
41
java/google/registry/dns/BUILD
Normal file
41
java/google/registry/dns/BUILD
Normal file
|
@ -0,0 +1,41 @@
|
|||
# Description:
|
||||
# Routines to publish authoritative DNS.
|
||||
|
||||
package(
|
||||
default_visibility = ["//java/com/google/domain/registry:registry_project"],
|
||||
)
|
||||
|
||||
|
||||
java_library(
|
||||
name = "constants",
|
||||
srcs = ["DnsConstants.java"],
|
||||
)
|
||||
|
||||
java_library(
|
||||
name = "dns",
|
||||
srcs = glob(
|
||||
["*.java"],
|
||||
exclude = ["DnsConstants.java"],
|
||||
),
|
||||
deps = [
|
||||
":constants",
|
||||
"//java/com/google/common/annotations",
|
||||
"//java/com/google/common/base",
|
||||
"//java/com/google/common/collect",
|
||||
"//java/com/google/common/html",
|
||||
"//java/com/google/common/io",
|
||||
"//java/com/google/common/net",
|
||||
"//java/com/google/domain/registry/config",
|
||||
"//java/com/google/domain/registry/dns/writer/api",
|
||||
"//java/com/google/domain/registry/model",
|
||||
"//java/com/google/domain/registry/request",
|
||||
"//java/com/google/domain/registry/util",
|
||||
"//third_party/java/appengine:appengine-api",
|
||||
"//third_party/java/dagger",
|
||||
"//third_party/java/joda_time",
|
||||
"//third_party/java/jsr305_annotations",
|
||||
"//third_party/java/jsr330_inject",
|
||||
"//third_party/java/objectify:objectify-v4_1",
|
||||
"//third_party/java/servlet/servlet_api",
|
||||
],
|
||||
)
|
35
java/google/registry/dns/DnsConstants.java
Normal file
35
java/google/registry/dns/DnsConstants.java
Normal file
|
@ -0,0 +1,35 @@
|
|||
// 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 com.google.domain.registry.dns;
|
||||
|
||||
/** Static class for DNS-related constants. */
|
||||
public class DnsConstants {
|
||||
private DnsConstants() {}
|
||||
|
||||
/** The name of the DNS pull queue. */
|
||||
public static final String DNS_PULL_QUEUE_NAME = "dns-pull"; // See queue.xml.
|
||||
|
||||
/** The name of the DNS publish push queue. */
|
||||
public static final String DNS_PUBLISH_PUSH_QUEUE_NAME = "dns-publish"; // See queue.xml.
|
||||
|
||||
/** The parameter to use for storing the target type ("domain" or "host" or "zone"). */
|
||||
public static final String DNS_TARGET_TYPE_PARAM = "Target-Type";
|
||||
|
||||
/** The parameter to use for storing the target name (domain or host name) with the task. */
|
||||
public static final String DNS_TARGET_NAME_PARAM = "Target-Name";
|
||||
|
||||
/** The possible values of the {@code DNS_TARGET_NAME_PARAM} parameter. */
|
||||
public enum TargetType { DOMAIN, HOST, ZONE }
|
||||
}
|
93
java/google/registry/dns/DnsModule.java
Normal file
93
java/google/registry/dns/DnsModule.java
Normal file
|
@ -0,0 +1,93 @@
|
|||
// 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 com.google.domain.registry.dns;
|
||||
|
||||
import static com.google.domain.registry.dns.DnsConstants.DNS_PUBLISH_PUSH_QUEUE_NAME;
|
||||
import static com.google.domain.registry.dns.DnsConstants.DNS_PULL_QUEUE_NAME;
|
||||
import static com.google.domain.registry.dns.PublishDnsUpdatesAction.DOMAINS_PARAM;
|
||||
import static com.google.domain.registry.dns.PublishDnsUpdatesAction.HOSTS_PARAM;
|
||||
import static com.google.domain.registry.dns.ReadDnsQueueAction.KEEP_TASKS_PARAM;
|
||||
import static com.google.domain.registry.request.RequestParameters.extractBooleanParameter;
|
||||
import static com.google.domain.registry.request.RequestParameters.extractEnumParameter;
|
||||
import static com.google.domain.registry.request.RequestParameters.extractRequiredParameter;
|
||||
import static com.google.domain.registry.request.RequestParameters.extractSetOfParameters;
|
||||
|
||||
import com.google.appengine.api.taskqueue.Queue;
|
||||
import com.google.appengine.api.taskqueue.QueueFactory;
|
||||
import com.google.domain.registry.dns.DnsConstants.TargetType;
|
||||
import com.google.domain.registry.dns.writer.api.DnsWriterZone;
|
||||
import com.google.domain.registry.request.Parameter;
|
||||
import com.google.domain.registry.request.RequestParameters;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/** Dagger module for the dns package. */
|
||||
@Module
|
||||
public final class DnsModule {
|
||||
|
||||
@Provides
|
||||
@DnsWriterZone
|
||||
static String provideZoneName(@Parameter(RequestParameters.PARAM_TLD) String tld) {
|
||||
return tld;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named(DNS_PULL_QUEUE_NAME)
|
||||
static Queue provideDnsPullQueue() {
|
||||
return QueueFactory.getQueue(DNS_PULL_QUEUE_NAME);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named(DNS_PUBLISH_PUSH_QUEUE_NAME)
|
||||
static Queue provideDnsUpdatePushQueue() {
|
||||
return QueueFactory.getQueue(DNS_PUBLISH_PUSH_QUEUE_NAME);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter(DOMAINS_PARAM)
|
||||
static Set<String> provideDomains(HttpServletRequest req) {
|
||||
return extractSetOfParameters(req, DOMAINS_PARAM);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter(HOSTS_PARAM)
|
||||
static Set<String> provideHosts(HttpServletRequest req) {
|
||||
return extractSetOfParameters(req, HOSTS_PARAM);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter(KEEP_TASKS_PARAM)
|
||||
static boolean provideKeepTasks(HttpServletRequest req) {
|
||||
return extractBooleanParameter(req, KEEP_TASKS_PARAM);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("name")
|
||||
static String provideName(HttpServletRequest req) {
|
||||
return extractRequiredParameter(req, "name");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("type")
|
||||
static TargetType provideType(HttpServletRequest req) {
|
||||
return extractEnumParameter(req, TargetType.class, "type");
|
||||
}
|
||||
}
|
161
java/google/registry/dns/DnsQueue.java
Normal file
161
java/google/registry/dns/DnsQueue.java
Normal file
|
@ -0,0 +1,161 @@
|
|||
// 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 com.google.domain.registry.dns;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static com.google.domain.registry.dns.DnsConstants.DNS_PULL_QUEUE_NAME;
|
||||
import static com.google.domain.registry.dns.DnsConstants.DNS_TARGET_NAME_PARAM;
|
||||
import static com.google.domain.registry.dns.DnsConstants.DNS_TARGET_TYPE_PARAM;
|
||||
import static com.google.domain.registry.model.registry.Registries.assertTldExists;
|
||||
import static com.google.domain.registry.request.RequestParameters.PARAM_TLD;
|
||||
import static com.google.domain.registry.util.DomainNameUtils.getTldFromDomainName;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import com.google.appengine.api.taskqueue.Queue;
|
||||
import com.google.appengine.api.taskqueue.QueueConstants;
|
||||
import com.google.appengine.api.taskqueue.QueueFactory;
|
||||
import com.google.appengine.api.taskqueue.TaskHandle;
|
||||
import com.google.appengine.api.taskqueue.TaskOptions;
|
||||
import com.google.appengine.api.taskqueue.TaskOptions.Method;
|
||||
import com.google.appengine.api.taskqueue.TransientFailureException;
|
||||
import com.google.apphosting.api.DeadlineExceededException;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.google.domain.registry.config.ConfigModule.Config;
|
||||
import com.google.domain.registry.dns.DnsConstants.TargetType;
|
||||
import com.google.domain.registry.model.registry.Registries;
|
||||
import com.google.domain.registry.util.FormattingLogger;
|
||||
|
||||
import org.joda.time.Duration;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
/** Methods for manipulating the queue used for DNS write tasks. */
|
||||
public class DnsQueue {
|
||||
|
||||
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
|
||||
|
||||
@Inject @Config("dnsWriteLockTimeout") Duration writeLockTimeout;
|
||||
@Inject @Named(DNS_PULL_QUEUE_NAME) Queue queue;
|
||||
@Inject DnsQueue() {}
|
||||
|
||||
long writeBatchSize = QueueConstants.maxLeaseCount();
|
||||
|
||||
/**
|
||||
* Enqueues the given task type with the given target name to the DNS queue, tagged with the
|
||||
* specified TLD.
|
||||
*/
|
||||
private TaskHandle addToQueue(TargetType targetType, String targetName, String tld) {
|
||||
return queue.add(TaskOptions.Builder
|
||||
// TODO(b/24564175): don't set the tag
|
||||
.withTag(tld)
|
||||
.method(Method.PULL)
|
||||
.param(DNS_TARGET_TYPE_PARAM, targetType.toString())
|
||||
.param(DNS_TARGET_NAME_PARAM, targetName)
|
||||
.param(PARAM_TLD, tld));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a task to the queue to refresh the DNS information for the specified subordinate host.
|
||||
*/
|
||||
public TaskHandle addHostRefreshTask(String fullyQualifiedHostName) {
|
||||
Optional<InternetDomainName> tld =
|
||||
Registries.findTldForName(InternetDomainName.from(fullyQualifiedHostName));
|
||||
checkArgument(tld.isPresent(),
|
||||
String.format("%s is not a subordinate host to a known tld", fullyQualifiedHostName));
|
||||
return addToQueue(TargetType.HOST, fullyQualifiedHostName, tld.get().toString());
|
||||
}
|
||||
|
||||
/** Adds a task to the queue to refresh the DNS information for the specified domain. */
|
||||
public TaskHandle addDomainRefreshTask(String fullyQualifiedDomainName) {
|
||||
return addToQueue(
|
||||
TargetType.DOMAIN,
|
||||
fullyQualifiedDomainName,
|
||||
assertTldExists(getTldFromDomainName(fullyQualifiedDomainName)));
|
||||
}
|
||||
|
||||
/** Adds a task to the queue to refresh the DNS information for the specified zone. */
|
||||
public TaskHandle addZoneRefreshTask(String fullyQualifiedZoneName) {
|
||||
return addToQueue(TargetType.ZONE, fullyQualifiedZoneName, fullyQualifiedZoneName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a batch of pending tasks.
|
||||
*/
|
||||
public List<TaskHandle> leaseTasks() {
|
||||
return leaseTasks(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a batch of pending tasks.
|
||||
*
|
||||
* @param tag the filter used to lease only those tasks that match
|
||||
*/
|
||||
public List<TaskHandle> leaseTasks(@Nullable String tag) {
|
||||
try {
|
||||
return isNullOrEmpty(tag)
|
||||
? queue.leaseTasks(writeLockTimeout.getMillis(), MILLISECONDS, writeBatchSize)
|
||||
: queue.leaseTasksByTag(writeLockTimeout.getMillis(), MILLISECONDS, writeBatchSize, tag);
|
||||
} catch (TransientFailureException | DeadlineExceededException e) {
|
||||
logger.severe(e, "Failed leasing tasks too fast");
|
||||
return ImmutableList.of();
|
||||
}
|
||||
}
|
||||
|
||||
/** Reduce the task lease time to zero, making it immediately available to be leased again. */
|
||||
public void dropTaskLease(TaskHandle task) {
|
||||
try {
|
||||
queue.modifyTaskLease(task, 0, TimeUnit.SECONDS);
|
||||
} catch (IllegalStateException e) {
|
||||
logger.warningfmt(e, "Failed dropping expired lease: %s", task.getName());
|
||||
} catch (TransientFailureException | DeadlineExceededException e) {
|
||||
logger.severe(e, "Failed dropping task leases too fast");
|
||||
}
|
||||
}
|
||||
|
||||
/** Delete the task, removing it from the queue permanently. */
|
||||
public void deleteTask(TaskHandle task) {
|
||||
try {
|
||||
queue.deleteTask(task);
|
||||
} catch (TransientFailureException | DeadlineExceededException e) {
|
||||
logger.severe(e, "Failed deleting tasks too fast");
|
||||
}
|
||||
}
|
||||
|
||||
/** Delete a list of tasks, removing them from the queue permanently. */
|
||||
public void deleteTasks(List<TaskHandle> tasks) {
|
||||
try {
|
||||
queue.deleteTask(tasks);
|
||||
} catch (TransientFailureException | DeadlineExceededException e) {
|
||||
logger.severe(e, "Failed deleting tasks too fast");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(b/19483428): Remove me when flows package is ported to Dagger.
|
||||
/** Creates a new instance. */
|
||||
public static DnsQueue create() {
|
||||
DnsQueue result = new DnsQueue();
|
||||
result.writeLockTimeout = Duration.standardSeconds(120);
|
||||
result.queue = QueueFactory.getQueue(DNS_PULL_QUEUE_NAME);
|
||||
return result;
|
||||
}
|
||||
}
|
98
java/google/registry/dns/PublishDnsUpdatesAction.java
Normal file
98
java/google/registry/dns/PublishDnsUpdatesAction.java
Normal file
|
@ -0,0 +1,98 @@
|
|||
// 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 com.google.domain.registry.dns;
|
||||
|
||||
import static com.google.domain.registry.model.server.Lock.executeWithLocks;
|
||||
import static com.google.domain.registry.request.Action.Method.POST;
|
||||
import static com.google.domain.registry.util.CollectionUtils.nullToEmpty;
|
||||
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.google.domain.registry.config.ConfigModule.Config;
|
||||
import com.google.domain.registry.dns.writer.api.DnsWriter;
|
||||
import com.google.domain.registry.request.Action;
|
||||
import com.google.domain.registry.request.HttpException.ServiceUnavailableException;
|
||||
import com.google.domain.registry.request.Parameter;
|
||||
import com.google.domain.registry.request.RequestParameters;
|
||||
import com.google.domain.registry.util.DomainNameUtils;
|
||||
import com.google.domain.registry.util.FormattingLogger;
|
||||
|
||||
import org.joda.time.Duration;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
|
||||
/** Task that sends domain and host updates to the DNS server. */
|
||||
@Action(path = PublishDnsUpdatesAction.PATH, method = POST, automaticallyPrintOk = true)
|
||||
public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
|
||||
|
||||
public static final String PATH = "/_dr/task/publishDnsUpdates";
|
||||
public static final String DOMAINS_PARAM = "domains";
|
||||
public static final String HOSTS_PARAM = "hosts";
|
||||
|
||||
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
|
||||
|
||||
@Inject DnsQueue dnsQueue;
|
||||
@Inject Provider<DnsWriter> writerProvider;
|
||||
@Inject @Config("dnsWriteLockTimeout") Duration timeout;
|
||||
@Inject @Parameter(RequestParameters.PARAM_TLD) String tld;
|
||||
@Inject @Parameter(DOMAINS_PARAM) Set<String> domains;
|
||||
@Inject @Parameter(HOSTS_PARAM) Set<String> hosts;
|
||||
@Inject PublishDnsUpdatesAction() {}
|
||||
|
||||
/** Runs the task. */
|
||||
@Override
|
||||
public void run() {
|
||||
String lockName = String.format("DNS zone %s", tld);
|
||||
// If executeWithLocks fails to get the lock, it does not throw an exception, simply returns
|
||||
// false. We need to make sure to take note of this error; otherwise, a failed lock might result
|
||||
// in the update task being dequeued and dropped. A message will already have been logged
|
||||
// to indicate the problem.
|
||||
if (!executeWithLocks(this, getClass(), tld, timeout, lockName)) {
|
||||
throw new ServiceUnavailableException("Lock failure");
|
||||
}
|
||||
}
|
||||
|
||||
/** Runs the task, with the lock. */
|
||||
@Override
|
||||
public Void call() {
|
||||
processBatch();
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Steps through the domain and host refreshes contained in the parameters and processes them. */
|
||||
private void processBatch() {
|
||||
try (DnsWriter writer = writerProvider.get()) {
|
||||
for (String domain : nullToEmpty(domains)) {
|
||||
if (!DomainNameUtils.isUnder(
|
||||
InternetDomainName.from(domain), InternetDomainName.from(tld))) {
|
||||
logger.severefmt("%s: skipping domain %s not under tld", tld, domain);
|
||||
} else {
|
||||
writer.publishDomain(domain);
|
||||
}
|
||||
}
|
||||
for (String host : nullToEmpty(hosts)) {
|
||||
if (!DomainNameUtils.isUnder(
|
||||
InternetDomainName.from(host), InternetDomainName.from(tld))) {
|
||||
logger.severefmt("%s: skipping host %s not under tld", tld, host);
|
||||
} else {
|
||||
writer.publishHost(host);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
204
java/google/registry/dns/ReadDnsQueueAction.java
Normal file
204
java/google/registry/dns/ReadDnsQueueAction.java
Normal file
|
@ -0,0 +1,204 @@
|
|||
// 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 com.google.domain.registry.dns;
|
||||
|
||||
import static com.google.appengine.api.taskqueue.TaskOptions.Builder.withUrl;
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static com.google.domain.registry.dns.DnsConstants.DNS_PUBLISH_PUSH_QUEUE_NAME;
|
||||
import static com.google.domain.registry.dns.DnsConstants.DNS_TARGET_NAME_PARAM;
|
||||
import static com.google.domain.registry.dns.DnsConstants.DNS_TARGET_TYPE_PARAM;
|
||||
import static com.google.domain.registry.model.registry.Registries.getTlds;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import com.google.appengine.api.taskqueue.Queue;
|
||||
import com.google.appengine.api.taskqueue.TaskHandle;
|
||||
import com.google.appengine.api.taskqueue.TaskOptions;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.ComparisonChain;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.TreeMultimap;
|
||||
import com.google.domain.registry.config.ConfigModule.Config;
|
||||
import com.google.domain.registry.dns.DnsConstants.TargetType;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.request.Action;
|
||||
import com.google.domain.registry.request.Parameter;
|
||||
import com.google.domain.registry.request.RequestParameters;
|
||||
import com.google.domain.registry.util.FormattingLogger;
|
||||
import com.google.domain.registry.util.TaskEnqueuer;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
/**
|
||||
* Action for fanning out DNS refresh tasks by TLD, using data taken from the DNS pull queue.
|
||||
*
|
||||
* <h3>Parameters Reference</h3>
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code jitterSeconds} Randomly delay each task by up to this many seconds.
|
||||
* <li>{@code keepTasks} Do not delete any tasks from the pull queue, whether they are processed or
|
||||
* not.
|
||||
* </ul>
|
||||
*/
|
||||
@Action(path = "/_dr/cron/readDnsQueue", automaticallyPrintOk = true)
|
||||
public final class ReadDnsQueueAction implements Runnable {
|
||||
|
||||
public static final String KEEP_TASKS_PARAM = "keepTasks";
|
||||
|
||||
private static final String JITTER_SECONDS_PARAM = "jitterSeconds";
|
||||
private static final Random random = new Random();
|
||||
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
|
||||
|
||||
@Inject @Config("dnsTldUpdateBatchSize") int tldUpdateBatchSize;
|
||||
@Inject @Named(DNS_PUBLISH_PUSH_QUEUE_NAME) Queue dnsPublishPushQueue;
|
||||
@Inject @Parameter(JITTER_SECONDS_PARAM) Optional<Integer> jitterSeconds;
|
||||
@Inject @Parameter(KEEP_TASKS_PARAM) boolean keepTasks;
|
||||
@Inject DnsQueue dnsQueue;
|
||||
@Inject TaskEnqueuer taskEnqueuer;
|
||||
@Inject ReadDnsQueueAction() {}
|
||||
|
||||
/** Container for items we pull out of the DNS pull queue and process for fanout. */
|
||||
private class RefreshItem implements Comparable<RefreshItem> {
|
||||
final TargetType type;
|
||||
final String name;
|
||||
|
||||
public RefreshItem(final TargetType type, final String name) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(RefreshItem other) {
|
||||
return ComparisonChain.start()
|
||||
.compare(this.type, other.type)
|
||||
.compare(this.name, other.name)
|
||||
.result();
|
||||
}
|
||||
}
|
||||
|
||||
/** Leases all tasks from the pull queue and creates per-tld update actions for them. */
|
||||
@Override
|
||||
public void run() {
|
||||
Set<String> tldsOfInterest = getTlds();
|
||||
|
||||
List<TaskHandle> tasks = dnsQueue.leaseTasks();
|
||||
if (tasks.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
logger.infofmt("leased %d tasks", tasks.size());
|
||||
// Normally, all tasks will be deleted from the pull queue. But some might have to remain if
|
||||
// we are not interested in the associated TLD, or if the TLD is paused. Remember which these
|
||||
// are.
|
||||
Set<TaskHandle> tasksToKeep = new HashSet<>();
|
||||
// The paused TLDs for which we found at least one refresh request.
|
||||
Set<String> pausedTlds = new HashSet<>();
|
||||
// Create a sorted multimap into which we will insert the refresh items, so that the items for
|
||||
// each TLD will be grouped together, and domains and hosts will be grouped within a TLD. The
|
||||
// grouping and ordering of domains and hosts is not technically necessary, but a predictable
|
||||
// ordering makes it possible to write detailed tests.
|
||||
TreeMultimap<String, RefreshItem> refreshItemMultimap = TreeMultimap.create();
|
||||
// Read all tasks on the DNS pull queue and load them into the refresh item multimap.
|
||||
for (TaskHandle task : tasks) {
|
||||
try {
|
||||
Map<String, String> params = ImmutableMap.copyOf(task.extractParams());
|
||||
// Dual-read the TLD from either the parameter (new methodology) or the tag (old way).
|
||||
// TODO(b/24564175): get the TLD from the regular parameter only.
|
||||
String tld = task.getTag();
|
||||
if (tld == null) {
|
||||
tld = params.get(RequestParameters.PARAM_TLD);
|
||||
}
|
||||
if (tld == null) {
|
||||
logger.severe("discarding invalid DNS refresh request; no TLD specified");
|
||||
} else if (!tldsOfInterest.contains(tld)) {
|
||||
tasksToKeep.add(task);
|
||||
} else if (Registry.get(tld).getDnsPaused()) {
|
||||
tasksToKeep.add(task);
|
||||
pausedTlds.add(tld);
|
||||
} else {
|
||||
String typeString = params.get(DNS_TARGET_TYPE_PARAM);
|
||||
String name = params.get(DNS_TARGET_NAME_PARAM);
|
||||
if (typeString == null) {
|
||||
logger.severe("discarding invalid DNS refresh request; no type specified");
|
||||
} else if (name == null) {
|
||||
logger.severe("discarding invalid DNS refresh request; no name specified");
|
||||
} else {
|
||||
TargetType type = TargetType.valueOf(typeString);
|
||||
switch (type) {
|
||||
case DOMAIN:
|
||||
case HOST:
|
||||
refreshItemMultimap.put(tld, new RefreshItem(type, name));
|
||||
break;
|
||||
default:
|
||||
logger.severefmt("discarding DNS refresh request of type %s", typeString);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
logger.severefmt(e, "discarding invalid DNS refresh request (task %s)", task);
|
||||
}
|
||||
}
|
||||
if (!pausedTlds.isEmpty()) {
|
||||
logger.infofmt("the dns-pull queue is paused for tlds: %s", pausedTlds);
|
||||
}
|
||||
// Loop through the multimap by TLD and generate refresh tasks for the hosts and domains.
|
||||
for (Map.Entry<String, Collection<RefreshItem>> tldRefreshItemsEntry
|
||||
: refreshItemMultimap.asMap().entrySet()) {
|
||||
for (List<RefreshItem> chunk : Iterables.partition(
|
||||
tldRefreshItemsEntry.getValue(), tldUpdateBatchSize)) {
|
||||
TaskOptions options = withUrl(PublishDnsUpdatesAction.PATH)
|
||||
.countdownMillis(jitterSeconds.isPresent()
|
||||
? random.nextInt((int) SECONDS.toMillis(jitterSeconds.get()))
|
||||
: 0)
|
||||
.param(RequestParameters.PARAM_TLD, tldRefreshItemsEntry.getKey());
|
||||
for (RefreshItem refreshItem : chunk) {
|
||||
options.param(
|
||||
(refreshItem.type == TargetType.HOST)
|
||||
? PublishDnsUpdatesAction.HOSTS_PARAM : PublishDnsUpdatesAction.DOMAINS_PARAM,
|
||||
refreshItem.name);
|
||||
}
|
||||
taskEnqueuer.enqueue(dnsPublishPushQueue, options);
|
||||
}
|
||||
}
|
||||
Set<TaskHandle> tasksToDelete = difference(ImmutableSet.copyOf(tasks), tasksToKeep);
|
||||
// In keepTasks mode, never delete any tasks.
|
||||
if (keepTasks) {
|
||||
logger.infofmt("would have deleted %d tasks", tasksToDelete.size());
|
||||
for (TaskHandle task : tasks) {
|
||||
dnsQueue.dropTaskLease(task);
|
||||
}
|
||||
// Otherwise, either delete or drop the lease of each task.
|
||||
} else {
|
||||
logger.infofmt("deleting %d tasks", tasksToDelete.size());
|
||||
dnsQueue.deleteTasks(ImmutableList.copyOf(tasksToDelete));
|
||||
logger.infofmt("dropping %d tasks", tasksToKeep.size());
|
||||
for (TaskHandle task : tasksToKeep) {
|
||||
dnsQueue.dropTaskLease(task);
|
||||
}
|
||||
logger.infofmt("done");
|
||||
}
|
||||
}
|
||||
}
|
80
java/google/registry/dns/RefreshDnsAction.java
Normal file
80
java/google/registry/dns/RefreshDnsAction.java
Normal file
|
@ -0,0 +1,80 @@
|
|||
// 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 com.google.domain.registry.dns;
|
||||
|
||||
import static com.google.domain.registry.model.EppResourceUtils.loadByUniqueId;
|
||||
|
||||
import com.google.domain.registry.dns.DnsConstants.TargetType;
|
||||
import com.google.domain.registry.model.EppResource;
|
||||
import com.google.domain.registry.model.domain.DomainResource;
|
||||
import com.google.domain.registry.model.host.HostResource;
|
||||
import com.google.domain.registry.request.Action;
|
||||
import com.google.domain.registry.request.HttpException.BadRequestException;
|
||||
import com.google.domain.registry.request.HttpException.NotFoundException;
|
||||
import com.google.domain.registry.request.Parameter;
|
||||
import com.google.domain.registry.util.Clock;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Action that manually triggers refresh of DNS information. */
|
||||
@Action(path = "/_dr/dnsRefresh", automaticallyPrintOk = true)
|
||||
public final class RefreshDnsAction implements Runnable {
|
||||
|
||||
@Inject Clock clock;
|
||||
@Inject DnsQueue dnsQueue;
|
||||
@Inject @Parameter("name") String domainOrHostName;
|
||||
@Inject @Parameter("type") TargetType type;
|
||||
@Inject RefreshDnsAction() {}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!domainOrHostName.contains(".")) {
|
||||
throw new BadRequestException("URL parameter 'name' must be fully qualified");
|
||||
}
|
||||
|
||||
boolean domainLookup;
|
||||
Class<? extends EppResource> clazz;
|
||||
switch (type) {
|
||||
case DOMAIN:
|
||||
domainLookup = true;
|
||||
clazz = DomainResource.class;
|
||||
break;
|
||||
case HOST:
|
||||
domainLookup = false;
|
||||
clazz = HostResource.class;
|
||||
break;
|
||||
default:
|
||||
throw new BadRequestException("Unsupported type: " + type);
|
||||
}
|
||||
|
||||
EppResource eppResource = loadByUniqueId(clazz, domainOrHostName, clock.nowUtc());
|
||||
if (eppResource == null) {
|
||||
throw new NotFoundException(
|
||||
String.format("%s %s not found", type, domainOrHostName));
|
||||
}
|
||||
|
||||
if (domainLookup) {
|
||||
dnsQueue.addDomainRefreshTask(domainOrHostName);
|
||||
} else {
|
||||
if (((HostResource) eppResource).getSuperordinateDomain() == null) {
|
||||
throw new BadRequestException(
|
||||
String.format("%s isn't a subordinate hostname", domainOrHostName));
|
||||
} else {
|
||||
// Don't enqueue host refresh tasks for external hosts.
|
||||
dnsQueue.addHostRefreshTask(domainOrHostName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
158
java/google/registry/dns/WriteDnsAction.java
Normal file
158
java/google/registry/dns/WriteDnsAction.java
Normal file
|
@ -0,0 +1,158 @@
|
|||
// 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 com.google.domain.registry.dns;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.domain.registry.dns.DnsConstants.DNS_TARGET_NAME_PARAM;
|
||||
import static com.google.domain.registry.dns.DnsConstants.DNS_TARGET_TYPE_PARAM;
|
||||
import static com.google.domain.registry.model.server.Lock.executeWithLocks;
|
||||
import static com.google.domain.registry.request.Action.Method.POST;
|
||||
|
||||
import com.google.appengine.api.LifecycleManager;
|
||||
import com.google.appengine.api.taskqueue.TaskHandle;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.google.domain.registry.config.ConfigModule.Config;
|
||||
import com.google.domain.registry.dns.DnsConstants.TargetType;
|
||||
import com.google.domain.registry.dns.writer.api.DnsWriter;
|
||||
import com.google.domain.registry.model.registry.Registry;
|
||||
import com.google.domain.registry.request.Action;
|
||||
import com.google.domain.registry.request.HttpException;
|
||||
import com.google.domain.registry.request.HttpException.BadRequestException;
|
||||
import com.google.domain.registry.request.Parameter;
|
||||
import com.google.domain.registry.request.RequestParameters;
|
||||
import com.google.domain.registry.util.DomainNameUtils;
|
||||
import com.google.domain.registry.util.FormattingLogger;
|
||||
|
||||
import org.joda.time.Duration;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
|
||||
/** Action that consumes pull-queue for zone updates to write to the DNS server. */
|
||||
@Action(path = "/_dr/task/writeDns", method = POST, automaticallyPrintOk = true)
|
||||
public final class WriteDnsAction implements Runnable, Callable<Void> {
|
||||
|
||||
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
|
||||
|
||||
@Inject DnsQueue dnsQueue;
|
||||
@Inject Provider<DnsWriter> writerProvider;
|
||||
@Inject @Config("dnsWriteLockTimeout") Duration timeout;
|
||||
@Inject @Parameter(RequestParameters.PARAM_TLD) String tld;
|
||||
@Inject WriteDnsAction() {}
|
||||
|
||||
/** Runs the task. */
|
||||
@Override
|
||||
public void run() {
|
||||
String lockName = String.format("DNS zone %s", tld);
|
||||
executeWithLocks(this, getClass(), tld, timeout, lockName);
|
||||
}
|
||||
|
||||
/** Runs the task, with the lock. */
|
||||
@Override
|
||||
public Void call() {
|
||||
processBatch();
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Leases a batch of tasks tagged with the zone name from the pull queue and processes them. */
|
||||
private void processBatch() {
|
||||
if (LifecycleManager.getInstance().isShuttingDown()) {
|
||||
logger.infofmt("%s: lifecycle manager is shutting down", tld);
|
||||
return;
|
||||
}
|
||||
if (Registry.get(tld).getDnsPaused()) {
|
||||
logger.infofmt("%s: the dns-pull queue is paused", tld);
|
||||
return;
|
||||
}
|
||||
// Make a defensive copy to allow mutations.
|
||||
List<TaskHandle> tasks = new ArrayList<>(dnsQueue.leaseTasks(tld));
|
||||
if (tasks.isEmpty()) {
|
||||
logger.infofmt("%s: no tasks in the dns-pull queue", tld);
|
||||
return;
|
||||
}
|
||||
try (DnsWriter writer = writerProvider.get()) {
|
||||
Iterator<TaskHandle> it = tasks.iterator();
|
||||
while (it.hasNext()) {
|
||||
TaskHandle task = it.next();
|
||||
try {
|
||||
processTask(writer, task, tld);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// Handle fatal errors by deleting the task.
|
||||
logger.severefmt(e, "%s: deleting unsupported task %s", tld, task.toString());
|
||||
dnsQueue.deleteTask(task);
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
Throwables.propagateIfInstanceOf(e, HttpException.class);
|
||||
// Handle transient errors by dropping the task leases.
|
||||
logger.severefmt(e, "%s: dropping leases of failed tasks", tld);
|
||||
for (TaskHandle task : tasks) {
|
||||
dnsQueue.dropTaskLease(task);
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (TaskHandle task : tasks) {
|
||||
dnsQueue.deleteTask(task);
|
||||
}
|
||||
logger.infofmt("%s: batch of %s tasks processed", tld, tasks.size());
|
||||
}
|
||||
|
||||
/** Stages a write to authoritative DNS for this task. */
|
||||
private static void processTask(DnsWriter writer, TaskHandle task, String tld) {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
try {
|
||||
for (Map.Entry<String, String> entry : task.extractParams()) {
|
||||
params.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
TargetType type = TargetType.valueOf(params.get(DNS_TARGET_TYPE_PARAM));
|
||||
String name = checkNotNull(params.get(DNS_TARGET_NAME_PARAM));
|
||||
switch (type) {
|
||||
case DOMAIN:
|
||||
checkRequestArgument(
|
||||
DomainNameUtils.isUnder(InternetDomainName.from(name), InternetDomainName.from(tld)),
|
||||
"domain name %s is not under tld %s", name, tld);
|
||||
writer.publishDomain(name);
|
||||
break;
|
||||
case HOST:
|
||||
checkRequestArgument(
|
||||
DomainNameUtils.isUnder(InternetDomainName.from(name), InternetDomainName.from(tld)),
|
||||
"host name %s is not under tld %s", name, tld);
|
||||
writer.publishHost(name);
|
||||
break;
|
||||
default:
|
||||
// TODO(b/11592394): Write a full zone.
|
||||
throw new UnsupportedOperationException(String.format("unexpected Type: %s", type));
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkRequestArgument(boolean condition, String format, Object... args) {
|
||||
if (!condition) {
|
||||
throw new BadRequestException(String.format(format, args));
|
||||
}
|
||||
}
|
||||
}
|
15
java/google/registry/dns/writer/api/BUILD
Normal file
15
java/google/registry/dns/writer/api/BUILD
Normal file
|
@ -0,0 +1,15 @@
|
|||
package(
|
||||
default_visibility = ["//java/com/google/domain/registry:registry_project"],
|
||||
)
|
||||
|
||||
|
||||
java_library(
|
||||
name = "api",
|
||||
srcs = glob(["*.java"]),
|
||||
deps = [
|
||||
"//java/com/google/common/base",
|
||||
"//third_party/java/dagger",
|
||||
"//third_party/java/jsr305_annotations",
|
||||
"//third_party/java/jsr330_inject",
|
||||
],
|
||||
)
|
55
java/google/registry/dns/writer/api/DnsWriter.java
Normal file
55
java/google/registry/dns/writer/api/DnsWriter.java
Normal file
|
@ -0,0 +1,55 @@
|
|||
// 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 com.google.domain.registry.dns.writer.api;
|
||||
|
||||
/**
|
||||
* Transaction object for sending an atomic batch of updates for a single zone to the DNS server.
|
||||
*
|
||||
* <p>Here's an example of how you would publish updates for a domain and host:
|
||||
* <pre>
|
||||
* @Inject Provider<DnsWriter> dnsWriter;
|
||||
* try (DnsWriter writer = dnsWriter.get()) {
|
||||
* writer.publishDomain(domainName);
|
||||
* writer.publishHost(hostName);
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public interface DnsWriter extends AutoCloseable {
|
||||
|
||||
/**
|
||||
* Loads {@code domainName} from datastore and publishes its NS/DS records to the DNS server.
|
||||
* Replaces existing records for the exact name supplied with an NS record for each name server
|
||||
* and a DS record for each delegation signer stored in the registry for the supplied domain name.
|
||||
* If the domain is deleted or is in a "non-publish" state then any existing records are deleted.
|
||||
*
|
||||
* @param domainName the fully qualified domain name, with no trailing dot
|
||||
*/
|
||||
void publishDomain(String domainName);
|
||||
|
||||
/**
|
||||
* Loads {@code hostName} from datastore and publishes its A/AAAA glue records to the DNS server.
|
||||
* Replaces existing records for the exact name supplied, with an A or AAAA record (as
|
||||
* appropriate) for each address stored in the registry, for the supplied host name. If the host is
|
||||
* deleted then the existing records are deleted. Assumes that this method will only be called for
|
||||
* in-bailiwick hosts. The registry does not have addresses for other hosts.
|
||||
*
|
||||
* @param hostName the fully qualified host name, with no trailing dot
|
||||
*/
|
||||
void publishHost(String hostName);
|
||||
|
||||
/** Commits the updates to the DNS server atomically. */
|
||||
@Override
|
||||
void close();
|
||||
}
|
24
java/google/registry/dns/writer/api/DnsWriterZone.java
Normal file
24
java/google/registry/dns/writer/api/DnsWriterZone.java
Normal file
|
@ -0,0 +1,24 @@
|
|||
// 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 com.google.domain.registry.dns.writer.api;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
|
||||
/** Dagger qualifier for the fully-qualified zone name that's being updated. */
|
||||
@Qualifier
|
||||
@Documented
|
||||
public @interface DnsWriterZone {}
|
49
java/google/registry/dns/writer/api/VoidDnsWriter.java
Normal file
49
java/google/registry/dns/writer/api/VoidDnsWriter.java
Normal file
|
@ -0,0 +1,49 @@
|
|||
// 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 com.google.domain.registry.dns.writer.api;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* {@link DnsWriter} that doesn't actually update records in a DNS server.
|
||||
*
|
||||
* <p>All this class does is write its displeasure to the logs.
|
||||
*/
|
||||
public final class VoidDnsWriter implements DnsWriter {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(VoidDnsWriter.class.getName());
|
||||
|
||||
private final Set<String> names = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public void publishDomain(String domainName) {
|
||||
names.add(domainName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishHost(String hostName) {
|
||||
names.add(hostName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
logger.warning("Ignoring DNS zone updates! No DnsWriterFactory implementation specified!\n"
|
||||
+ Joiner.on('\n').join(names));
|
||||
}
|
||||
}
|
28
java/google/registry/dns/writer/api/VoidDnsWriterModule.java
Normal file
28
java/google/registry/dns/writer/api/VoidDnsWriterModule.java
Normal file
|
@ -0,0 +1,28 @@
|
|||
// 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 com.google.domain.registry.dns.writer.api;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
/** Dagger module that disables DNS updates. */
|
||||
@Module
|
||||
public final class VoidDnsWriterModule {
|
||||
|
||||
@Provides
|
||||
static DnsWriter provideDnsWriter() {
|
||||
return new VoidDnsWriter();
|
||||
}
|
||||
}
|
26
java/google/registry/dns/writer/dnsupdate/BUILD
Normal file
26
java/google/registry/dns/writer/dnsupdate/BUILD
Normal file
|
@ -0,0 +1,26 @@
|
|||
package(
|
||||
default_visibility = ["//java/com/google/domain/registry:registry_project"],
|
||||
)
|
||||
|
||||
|
||||
java_library(
|
||||
name = "dnsupdate",
|
||||
srcs = glob(["*.java"]),
|
||||
deps = [
|
||||
"//java/com/google/common/annotations",
|
||||
"//java/com/google/common/base",
|
||||
"//java/com/google/common/collect",
|
||||
"//java/com/google/common/io",
|
||||
"//java/com/google/common/net",
|
||||
"//java/com/google/common/primitives",
|
||||
"//java/com/google/domain/registry/config",
|
||||
"//java/com/google/domain/registry/dns/writer/api",
|
||||
"//java/com/google/domain/registry/model",
|
||||
"//java/com/google/domain/registry/util",
|
||||
"//third_party/java/joda_time",
|
||||
"//third_party/java/dagger",
|
||||
"//third_party/java/dnsjava",
|
||||
"//third_party/java/jsr305_annotations",
|
||||
"//third_party/java/jsr330_inject",
|
||||
],
|
||||
)
|
|
@ -0,0 +1,135 @@
|
|||
// 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 com.google.domain.registry.dns.writer.dnsupdate;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Verify.verify;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.domain.registry.config.ConfigModule.Config;
|
||||
|
||||
import org.joda.time.Duration;
|
||||
import org.xbill.DNS.Message;
|
||||
import org.xbill.DNS.Opcode;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
/**
|
||||
* A transport for DNS messages. Sends/receives DNS messages over TCP using old-style {@link Socket}
|
||||
* s and the message framing defined in <a href="https://tools.ietf.org/html/rfc1035">RFC 1035</a>.
|
||||
* We would like use the dnsjava library's {@link SimpleResolver} class for this, but it requires
|
||||
* {@link SocketChannel} which is not supported on AppEngine.
|
||||
*/
|
||||
public class DnsMessageTransport {
|
||||
|
||||
/**
|
||||
* Size of message length field for DNS TCP transport.
|
||||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc1035">RFC 1035</a>
|
||||
*/
|
||||
static final int MESSAGE_LENGTH_FIELD_BYTES = 2;
|
||||
private static final int MESSAGE_MAXIMUM_LENGTH = (1 << (MESSAGE_LENGTH_FIELD_BYTES * 8)) - 1;
|
||||
|
||||
/**
|
||||
* The standard DNS port number.
|
||||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc1035">RFC 1035</a>
|
||||
*/
|
||||
@VisibleForTesting static final int DNS_PORT = 53;
|
||||
|
||||
private final SocketFactory factory;
|
||||
private final String updateHost;
|
||||
private final int updateTimeout;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param factory a factory for TCP sockets
|
||||
* @param updateHost host name of the DNS server
|
||||
* @param updateTimeout update I/O timeout
|
||||
*/
|
||||
@Inject
|
||||
public DnsMessageTransport(
|
||||
SocketFactory factory,
|
||||
@Config("dnsUpdateHost") String updateHost,
|
||||
@Config("dnsUpdateTimeout") Duration updateTimeout) {
|
||||
this.factory = factory;
|
||||
this.updateHost = updateHost;
|
||||
this.updateTimeout = Ints.checkedCast(updateTimeout.getMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a DNS "query" message (most likely an UPDATE) and returns the response. The response is
|
||||
* checked for matching ID and opcode.
|
||||
*
|
||||
* @param query a message to send
|
||||
* @return the response received from the server
|
||||
* @throws IOException if the Socket input/output streams throws one
|
||||
* @throws IllegalArgumentException if the query is too large to be sent (> 65535 bytes)
|
||||
*/
|
||||
public Message send(Message query) throws IOException {
|
||||
try (Socket socket = factory.createSocket(InetAddress.getByName(updateHost), DNS_PORT)) {
|
||||
socket.setSoTimeout(updateTimeout);
|
||||
writeMessage(socket.getOutputStream(), query);
|
||||
Message response = readMessage(socket.getInputStream());
|
||||
checkValidResponse(query, response);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkValidResponse(Message query, Message response) {
|
||||
verify(
|
||||
response.getHeader().getID() == query.getHeader().getID(),
|
||||
"response ID %s does not match query ID %s",
|
||||
response.getHeader().getID(),
|
||||
query.getHeader().getID());
|
||||
verify(
|
||||
response.getHeader().getOpcode() == query.getHeader().getOpcode(),
|
||||
"response opcode '%s' does not match query opcode '%s'",
|
||||
Opcode.string(response.getHeader().getOpcode()),
|
||||
Opcode.string(query.getHeader().getOpcode()));
|
||||
}
|
||||
|
||||
private void writeMessage(OutputStream outputStream, Message message) throws IOException {
|
||||
byte[] messageData = message.toWire();
|
||||
checkArgument(
|
||||
messageData.length <= MESSAGE_MAXIMUM_LENGTH,
|
||||
"DNS request message larger than maximum of %s: %s",
|
||||
MESSAGE_MAXIMUM_LENGTH,
|
||||
messageData.length);
|
||||
ByteBuffer buffer = ByteBuffer.allocate(messageData.length + MESSAGE_LENGTH_FIELD_BYTES);
|
||||
buffer.putShort((short) messageData.length);
|
||||
buffer.put(messageData);
|
||||
outputStream.write(buffer.array());
|
||||
}
|
||||
|
||||
private Message readMessage(InputStream inputStream) throws IOException {
|
||||
DataInputStream stream = new DataInputStream(inputStream);
|
||||
int length = stream.readUnsignedShort();
|
||||
byte[] messageData = new byte[length];
|
||||
stream.readFully(messageData);
|
||||
return new Message(messageData);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// 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 com.google.domain.registry.dns.writer.dnsupdate;
|
||||
|
||||
import com.google.domain.registry.config.ConfigModule.Config;
|
||||
|
||||
import org.joda.time.Duration;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class DnsUpdateConfigModule {
|
||||
|
||||
/**
|
||||
* Host that receives DNS updates from the registry.
|
||||
* Usually a "hidden master" for the TLDs.
|
||||
*/
|
||||
@Provides
|
||||
@Config("dnsUpdateHost")
|
||||
public static String provideDnsUpdateHost() {
|
||||
return "localhost";
|
||||
}
|
||||
|
||||
/**
|
||||
* Timeout on the socket for DNS update requests.
|
||||
*/
|
||||
@Provides
|
||||
@Config("dnsUpdateTimeout")
|
||||
public static Duration provideDnsUpdateTimeout() {
|
||||
return Duration.standardSeconds(30);
|
||||
}
|
||||
|
||||
/**
|
||||
* The DNS time-to-live (TTL) for resource records created by the registry.
|
||||
*/
|
||||
@Provides
|
||||
@Config("dnsUpdateTimeToLive")
|
||||
public static Duration provideDnsUpdateTimeToLive() {
|
||||
return Duration.standardHours(2);
|
||||
}
|
||||
}
|
215
java/google/registry/dns/writer/dnsupdate/DnsUpdateWriter.java
Normal file
215
java/google/registry/dns/writer/dnsupdate/DnsUpdateWriter.java
Normal file
|
@ -0,0 +1,215 @@
|
|||
// 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 com.google.domain.registry.dns.writer.dnsupdate;
|
||||
|
||||
import static com.google.common.base.Verify.verify;
|
||||
import static com.google.domain.registry.model.EppResourceUtils.loadByUniqueId;
|
||||
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import com.google.domain.registry.config.ConfigModule.Config;
|
||||
import com.google.domain.registry.dns.writer.api.DnsWriter;
|
||||
import com.google.domain.registry.model.domain.DomainResource;
|
||||
import com.google.domain.registry.model.domain.secdns.DelegationSignerData;
|
||||
import com.google.domain.registry.model.host.HostResource;
|
||||
import com.google.domain.registry.model.registry.Registries;
|
||||
import com.google.domain.registry.util.Clock;
|
||||
|
||||
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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* A DnsWriter that implements the DNS UPDATE protocol as specified in
|
||||
* <a href="https://tools.ietf.org/html/rfc2136">RFC 2136</a>. Publishes changes in the
|
||||
* domain-registry to a (capable) external DNS server, sometimes called a "hidden master". DNS
|
||||
* UPDATE messages are sent via a "resolver" class which implements the network transport. 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.
|
||||
*
|
||||
* <p>The general strategy of the publish methods is to delete <em>all</em> resource records of any
|
||||
* <em>type</em> 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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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 resolver;
|
||||
private final Clock clock;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param dnsTimeToLive TTL used for any created resource records
|
||||
* @param resolver a resolver used to send/receive the UPDATE messages
|
||||
* @param clock a source of time
|
||||
*/
|
||||
@Inject
|
||||
public DnsUpdateWriter(
|
||||
@Config("dnsUpdateTimeToLive") Duration dnsTimeToLive,
|
||||
DnsMessageTransport resolver,
|
||||
Clock clock) {
|
||||
this.dnsTimeToLive = dnsTimeToLive;
|
||||
this.resolver = resolver;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishDomain(String domainName) {
|
||||
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 && domain.shouldPublishToDns()) {
|
||||
update.add(makeNameServerSet(domainName, domain.loadNameservers()));
|
||||
update.add(makeDelegationSignerSet(domainName, domain.getDsData()));
|
||||
}
|
||||
|
||||
Message response = resolver.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 publishHost(String hostName) {
|
||||
HostResource host = loadByUniqueId(HostResource.class, hostName, clock.nowUtc());
|
||||
try {
|
||||
Update update = new Update(toAbsoluteName(findTldFromName(hostName)));
|
||||
update.delete(toAbsoluteName(hostName), Type.ANY);
|
||||
if (host != null) {
|
||||
update.add(makeAddressSet(hostName, host.getInetAddresses()));
|
||||
update.add(makeV6AddressSet(hostName, host.getInetAddresses()));
|
||||
}
|
||||
|
||||
Message response = resolver.send(update);
|
||||
verify(
|
||||
response.getRcode() == Rcode.NOERROR,
|
||||
"DNS server failed host update for '%s' rcode: %s",
|
||||
hostName,
|
||||
Rcode.string(response.getRcode()));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("publishHost failed: " + hostName, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing. Publish calls are synchronous and atomic.
|
||||
*/
|
||||
@Override
|
||||
public void close() {}
|
||||
|
||||
private RRset makeDelegationSignerSet(String domainName, Iterable<DelegationSignerData> dsData)
|
||||
throws TextParseException {
|
||||
RRset signerSet = new RRset();
|
||||
for (DelegationSignerData signerData : dsData) {
|
||||
DSRecord dsRecord =
|
||||
new DSRecord(
|
||||
toAbsoluteName(domainName),
|
||||
DClass.IN,
|
||||
dnsTimeToLive.getStandardSeconds(),
|
||||
signerData.getKeyTag(),
|
||||
signerData.getAlgorithm(),
|
||||
signerData.getDigestType(),
|
||||
signerData.getDigest());
|
||||
signerSet.addRR(dsRecord);
|
||||
}
|
||||
return signerSet;
|
||||
}
|
||||
|
||||
private RRset makeNameServerSet(String domainName, Iterable<HostResource> nameservers)
|
||||
throws TextParseException {
|
||||
RRset nameServerSet = new RRset();
|
||||
for (HostResource host : nameservers) {
|
||||
NSRecord record =
|
||||
new NSRecord(
|
||||
toAbsoluteName(domainName),
|
||||
DClass.IN,
|
||||
dnsTimeToLive.getStandardSeconds(),
|
||||
toAbsoluteName(host.getFullyQualifiedHostName()));
|
||||
nameServerSet.addRR(record);
|
||||
}
|
||||
return nameServerSet;
|
||||
}
|
||||
|
||||
private RRset makeAddressSet(String hostName, Iterable<InetAddress> addresses)
|
||||
throws TextParseException {
|
||||
RRset addressSet = new RRset();
|
||||
for (InetAddress address : addresses) {
|
||||
if (address instanceof Inet4Address) {
|
||||
ARecord record =
|
||||
new ARecord(
|
||||
toAbsoluteName(hostName), DClass.IN, dnsTimeToLive.getStandardSeconds(), address);
|
||||
addressSet.addRR(record);
|
||||
}
|
||||
}
|
||||
return addressSet;
|
||||
}
|
||||
|
||||
private RRset makeV6AddressSet(String hostName, Iterable<InetAddress> addresses)
|
||||
throws TextParseException {
|
||||
RRset addressSet = new RRset();
|
||||
for (InetAddress address : addresses) {
|
||||
if (address instanceof Inet6Address) {
|
||||
AAAARecord record =
|
||||
new AAAARecord(
|
||||
toAbsoluteName(hostName), DClass.IN, dnsTimeToLive.getStandardSeconds(), address);
|
||||
addressSet.addRR(record);
|
||||
}
|
||||
}
|
||||
return addressSet;
|
||||
}
|
||||
|
||||
private String findTldFromName(String name) {
|
||||
return Registries.findTldForNameOrThrow(InternetDomainName.from(name)).toString();
|
||||
}
|
||||
|
||||
private Name toAbsoluteName(String name) throws TextParseException {
|
||||
return Name.fromString(name, Name.root);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
// 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 com.google.domain.registry.dns.writer.dnsupdate;
|
||||
|
||||
import com.google.domain.registry.dns.writer.api.DnsWriter;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
/** Dagger module that provides a DnsUpdateWriter. */
|
||||
@Module
|
||||
public final class DnsUpdateWriterModule {
|
||||
|
||||
@Provides
|
||||
static DnsWriter provideDnsWriter(DnsUpdateWriter dnsWriter) {
|
||||
return dnsWriter;
|
||||
}
|
||||
|
||||
@Provides
|
||||
static SocketFactory provideSocketFactory() {
|
||||
return SocketFactory.getDefault();
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue