Skip to content

Commit

Permalink
chore: Create a new DnsResolver class that wraps the clumsy Java DNS …
Browse files Browse the repository at this point in the history
…API. Part of #2043.
  • Loading branch information
hessjcg committed Jul 18, 2024
1 parent d76e892 commit 300da39
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 0 deletions.
24 changes: 24 additions & 0 deletions core/src/main/java/com/google/cloud/sql/core/DnsResolver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2024 Google LLC
*
* 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.cloud.sql.core;

import java.util.Collection;
import javax.naming.NameNotFoundException;

interface DnsResolver {
Collection<DnsSrvRecord> resolveSrv(String domainName) throws NameNotFoundException;
}
78 changes: 78 additions & 0 deletions core/src/main/java/com/google/cloud/sql/core/DnsSrvRecord.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2024 Google LLC
*
* 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.cloud.sql.core;

import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/** This represents the value of an SRV DNS Record. */
public class DnsSrvRecord {
private static final Pattern RECORD_FORMAT = Pattern.compile("(\\d+) +(\\d+) +(\\d+) +(.*)");
private final int priority;
private final int weight;
private final int port;
private final String target;

DnsSrvRecord(String record) {
Matcher m = RECORD_FORMAT.matcher(record);
if (!m.find()) {
throw new IllegalArgumentException("Malformed SRV record: " + record);
}

this.priority = Integer.parseInt(m.group(1));
this.weight = Integer.parseInt(m.group(2));
this.port = Integer.parseInt(m.group(3));
this.target = m.group(4);
}

public int getPriority() {
return priority;
}

public int getWeight() {
return weight;
}

public int getPort() {
return port;
}

public String getTarget() {
return target;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof DnsSrvRecord)) {
return false;
}
DnsSrvRecord that = (DnsSrvRecord) o;
return priority == that.priority
&& weight == that.weight
&& port == that.port
&& target.equals(that.target);
}

@Override
public int hashCode() {
return Objects.hash(priority, weight, port, target);
}
}
75 changes: 75 additions & 0 deletions core/src/main/java/com/google/cloud/sql/core/JndiDnsResolver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright 2024 Google LLC
*
* 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.cloud.sql.core;

import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.stream.Collectors;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.InitialDirContext;

/** Implements DnsResolver using the Java JNDI built-in DNS directory. */
class JndiDnsResolver implements DnsResolver {
private final String jndiPrefix;

/** Creates a resolver using the system DNS settings. */
JndiDnsResolver() {
this.jndiPrefix = "dns:";
}

/**
* Creates a DNS resolver that uses a specific DNS server.
*
* @param dnsServer the DNS server hostname
* @param port the DNS server port (DNS servers usually use port 53)
*/
JndiDnsResolver(String dnsServer, int port) {
this.jndiPrefix = "dns://" + dnsServer + ":" + port + "/";
}

/**
* Returns DNS records for a domain name, sorted by priority, then target alphabetically.
*
* @param domainName the domain name to lookup
* @return the list of record
* @throws javax.naming.NameNotFoundException when the domain name did not resolve.
*/
@Override
public Collection<DnsSrvRecord> resolveSrv(String domainName)
throws javax.naming.NameNotFoundException {
try {
// Notice: This is old Java 1.2 style code. It uses the ancient JNDI DNS Provider api.
// See https://docs.oracle.com/javase/7/docs/technotes/guides/jndi/jndi-dns.html
Attribute attr =
new InitialDirContext()
.getAttributes(jndiPrefix + domainName, new String[] {"SRV"})
.get("SRV");
// attr.getAll() returns a Vector containing strings, one for each record returned by dns.
return Collections.list(attr.getAll()).stream()
.map((Object v) -> new DnsSrvRecord((String) v))
.sorted(Comparator.comparing(DnsSrvRecord::getPriority))
.collect(Collectors.toList());
} catch (NameNotFoundException e) {
throw e;
} catch (NamingException e) {
throw new RuntimeException("Unable to look up domain name " + domainName, e);
}
}
}
39 changes: 39 additions & 0 deletions core/src/test/java/com/google/cloud/sql/core/DnsSrvRecordTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2024 Google LLC
*
* 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.cloud.sql.core;

import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;

import org.junit.Test;

public class DnsSrvRecordTest {

@Test
public void testValidSrvRecord() {
DnsSrvRecord r = new DnsSrvRecord("0 10 3307 sample-project:us-central1:my-database.");
assertThat(r.getTarget()).isEqualTo("sample-project:us-central1:my-database.");
assertThat(r.getPort()).isEqualTo(3307);
assertThat(r.getWeight()).isEqualTo(10);
assertThat(r.getPriority()).isEqualTo(0);
}

@Test
public void testInvalidSrvRecordThrows() {
assertThrows(IllegalArgumentException.class, () -> new DnsSrvRecord("bad record format"));
}
}

0 comments on commit 300da39

Please sign in to comment.