Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
# The email address is not required for organizations.

Google Inc.
Donuts Inc.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Don't forget about trailing newline here. (or else our presubmits fail)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

1 change: 1 addition & 0 deletions CONTRIBUTORS
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ Jared Brothers <brothers@google.com>
Pablo Mayrgundter <pmy@google.com>
Daisuke Yabuki <dxy@google.com>
Tim Boring <tjb@google.com>
Hans Ridder <hans.ridder@gmail.com>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to say, I'm really really happy with this contribution. I think you've done some excellent work in this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! Well done on the reviewing too. I think it's far better than when we started.

11 changes: 9 additions & 2 deletions java/com/google/domain/registry/dns/writer/api/DnsWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,22 @@ 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
* @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
* @param hostName the fully qualified host name, with no trailing dot
*/
void publishHost(String hostName);

Expand Down
26 changes: 26 additions & 0 deletions java/com/google/domain/registry/dns/writer/dnsupdate/BUILD
Original file line number Diff line number Diff line change
@@ -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",
],
)
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading