Repository: mageddo/dns-proxy-server
Pull request #405 (2023-04-02 02:25:38)

Ajusting GUI for AAAA Support
Relates to #389


Patch:
diff --git a/README.md b/README.md
index d363e852..b199e6d5 100644
--- a/README.md
+++ b/README.md
@@ -80,3 +80,4 @@ Or via QR code
 [7]: https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif
 [8]: http://mageddo.github.io/dns-proxy-server/latest/en/1-getting-started/versioning
 [9]: http://mageddo.github.io/dns-proxy-server/latest/en/5-tutorials/
+
diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index cee226a4..5f6aa0fc 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -1,5 +1,6 @@
 ## 3.15
 * Support for AAAA records on Docker container, LocalDB and System solving.
+* AAAA GUI records support 
 * DPS Network IPV6 Support
 * Formalize Recursion Available on query responses as DPS supports this feature already, see #392
 * Defined a new process of releasing the stable versions, see "Getting Started -> Release Version Control" 
diff --git a/app/src/components/RecordForm.js b/app/src/components/RecordForm.js
index 6194be83..08c23651 100644
--- a/app/src/components/RecordForm.js
+++ b/app/src/components/RecordForm.js
@@ -30,7 +30,7 @@ export class RecordForm extends React.Component {
 
 	handleIp(e){
 		let form = this.state.form;
-		form[e.target.name] = e.target.value.split('.').map(it => parseInt(it));
+    form[e.target.name] = e.target.value;
 		this.setState({ form });
 	}
 
@@ -57,7 +57,7 @@ export class RecordForm extends React.Component {
 		let showIp = false;
 		let showTarget = true;
 
-		if(targetValue === 'A'){
+		if(targetValue === 'A' || targetValue === 'AAAA'){
 			showIp = true;
 			showTarget = false;
 		}
@@ -71,6 +71,10 @@ export class RecordForm extends React.Component {
 				label: 'IP *',
 				field: 'ip'
 			},
+      'AAAA': {
+        label: 'IP *',
+        field: 'ip'
+      },
 			'CNAME': {
 				label: 'CNAME *',
 				field: 'target'
@@ -91,7 +95,7 @@ export class RecordForm extends React.Component {
 			data: JSON.stringify({
 				...this.state.form,
 				env: this.props.env,
-        ip: this.state.form.type === 'A' ? this.state.form.ip : null
+        ip: (this.state.form.type === 'A' || this.state.form.type === 'AAAA') ? this.state.form.ip : null
       }),
 		})
 		.done(function(){
@@ -171,7 +175,7 @@ export class RecordForm extends React.Component {
 								{
 									this.state.showIp &&
 									<input className="form-control"
-												 pattern="[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+"
+												 pattern=".+"
 												 title="Please provide a valid IP" name="ip" id="ip"
 												 onChange={(e) => this.handleIp(e)}
 												 required
@@ -185,6 +189,7 @@ export class RecordForm extends React.Component {
 							<td>
 								<select name="type" onChange={(e) => this.handleType(e)} className="form-control" type="text">
 									<option value="A">A</option>
+									<option value="AAAA">AAAA</option>
 									<option value="CNAME">CNAME</option>
 								</select>
 							</td>
diff --git a/app/src/components/RecordTable.js b/app/src/components/RecordTable.js
index 4f9724b5..fd22a4f8 100644
--- a/app/src/components/RecordTable.js
+++ b/app/src/components/RecordTable.js
@@ -76,10 +76,6 @@ export class RecordTable extends React.Component {
 		});
 	}
 
-	formatIp(ip){
-		return ip.join('.');
-	}
-
 	handleChange(evt, row) {
 		row[evt.target.name] = evt.target.value;
 		console.info('m=handleChange, %s=%s, row=%o', evt.target.name, evt.target.value, row);
@@ -93,7 +89,7 @@ export class RecordTable extends React.Component {
 	}
 
 	handleIpChange(e, row) {
-		row[e.target.name] = e.target.value.split('.').map(it => it ? parseInt(it) : "");
+		row[e.target.name] = e.target.value;
 		this.forceUpdate();
 	}
 
@@ -107,7 +103,7 @@ export class RecordTable extends React.Component {
 		return <tr key={k}>
 			<td>{v.hostname}</td>
 			<td className="text-center">{v.type}</td>
-			{(!v.type || v.type === 'A') && <td>{this.formatIp(v.ip)}</td>}
+			{(!v.type || v.type === 'A' || v.type === 'AAAA') && <td>{v.ip}</td>}
 			{v.type === 'CNAME' && <td>{v.target}</td>}
 			<td className="text-right">{v.ttl}</td>
 			<td className="text-center records-actions">
@@ -130,9 +126,9 @@ export class RecordTable extends React.Component {
 					<option value="CNAME">CNAME</option>
 				</select>
 			</td>
-			{(v.type === 'A' || !v.type) &&
+			{(v.type === 'A' || v.type === 'AAAA' || !v.type) &&
 			<td>
-				<input className="form-control" name="ip" type="text" onChange={(e) => this.handleIpChange(e, v)} value={this.formatIp(v.ip)}/>
+				<input className="form-control" name="ip" type="text" onChange={(e) => this.handleIpChange(e, v)} value={v.ip}/>
 			</td>
 			}
 			{v.type === 'CNAME' &&
diff --git a/gradle.properties b/gradle.properties
index 9e68258e..adde2074 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,1 +1,1 @@
-version=3.15.7-snapshot
+version=3.15.8-snapshot
diff --git a/src/main/java/com/mageddo/dnsproxyserver/config/Config.java b/src/main/java/com/mageddo/dnsproxyserver/config/Config.java
index e8729752..129b4d71 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/config/Config.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/config/Config.java
@@ -8,6 +8,7 @@ import lombok.Builder;
 import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
 import lombok.Value;
+import org.apache.commons.lang3.Validate;
 
 import java.io.IOException;
 import java.io.UncheckedIOException;
@@ -22,6 +23,8 @@ import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import static com.mageddo.commons.lang.Objects.*;
+
 /**
  * Domain object which owns the configs.
  *
@@ -128,7 +131,7 @@ public class Config {
     /**
      * Used when {@link #type} in {@link Type#AAAA} , {@link Type#A}
      */
-    private String ip;
+    private IP ip;
 
     /**
      * Target hostname when {@link #type} = {@link Type#CNAME}
@@ -141,6 +144,15 @@ public class Config {
     @NonNull
     private Config.Entry.Type type;
 
+    public String requireTextIp() {
+      Validate.isTrue(this.type.isAddressSolving() && this.ip != null, "IP is required");
+      return this.ip.toText();
+    }
+
+    public String getIpAsText() {
+      return mapOrNull(this.ip, IP::toText);
+    }
+
     public static class EntryBuilder {
       public Entry build() {
         if (this.id == null) {
diff --git a/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJsonV1EnvsConverter.java b/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJsonV1EnvsConverter.java
index aa191e07..1d96f466 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJsonV1EnvsConverter.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJsonV1EnvsConverter.java
@@ -30,7 +30,7 @@ public class ConfigJsonV1EnvsConverter {
       .hostname(entry.getHostname())
       .id(entry.getId())
       .ttl(entry.getTtl())
-      .ip(IpAddr.of(entry.getIp()).getRawIP())
+      .ip(IpAddr.of(entry.getIp()).getIp())
       .type(Config.Entry.Type.A)
       .build();
   }
diff --git a/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJsonV2.java b/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJsonV2.java
index d83e1cc7..4f848eb0 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJsonV2.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJsonV2.java
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.mageddo.dnsproxyserver.config.Config;
 import com.mageddo.dnsproxyserver.config.Config.Entry.Type;
+import com.mageddo.net.IP;
 import com.mageddo.net.IpAddr;
 import com.mageddo.dnsproxyserver.server.dns.SimpleServer;
 import lombok.Data;
@@ -103,7 +104,7 @@ public class ConfigJsonV2 implements ConfigJson {
       return new Entry()
         .setHostname(entry.getHostname())
         .setId(entry.getId())
-        .setIp(entry.getIp())
+        .setIp(entry.getIpAsText())
         .setTtl(entry.getTtl())
         .setTarget(entry.getTarget())
         .setType(entry.getType())
@@ -122,7 +123,7 @@ public class ConfigJsonV2 implements ConfigJson {
         .builder()
         .type(Type.A)
         .hostname("dps-sample.dev")
-        .ip("192.168.0.254")
+        .ip(IP.of("192.168.0.254"))
         .ttl(30)
         .id(1L)
         .build()
diff --git a/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJsonV2EnvsConverter.java b/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJsonV2EnvsConverter.java
index 2c8c28a7..46c71e5e 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJsonV2EnvsConverter.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/config/entrypoint/ConfigJsonV2EnvsConverter.java
@@ -1,6 +1,7 @@
 package com.mageddo.dnsproxyserver.config.entrypoint;
 
 import com.mageddo.dnsproxyserver.config.Config;
+import com.mageddo.net.IP;
 import org.apache.commons.lang3.StringUtils;
 
 import java.util.List;
@@ -30,7 +31,7 @@ public class ConfigJsonV2EnvsConverter {
       .hostname(hostname.getHostname())
       .id(hostname.getId())
       .ttl(hostname.getTtl())
-      .ip(hostname.getIp())
+      .ip(IP.of(hostname.getIp()))
       .target(hostname.getTarget())
       .type(buildType(hostname))
       .build();
diff --git a/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverLocalDB.java b/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverLocalDB.java
index 186c4169..92210a68 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverLocalDB.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/server/dns/solver/SolverLocalDB.java
@@ -45,13 +45,14 @@ public class SolverLocalDB implements Solver {
         );
         return null;
       }
+      final var foundType = found.getType();
       log.trace(
         "status=found, type={}, askedHost={}, time={}, totalTime={}",
-        found.getType(), askedHost, stopWatch.getTime() - stopWatch.getSplitTime(), stopWatch.getTime()
+        foundType, askedHost, stopWatch.getTime() - stopWatch.getSplitTime(), stopWatch.getTime()
       );
 
-      if (found.getType().isAddressSolving()) {
-        final var ip = found.getType().equals(questionType) ? found.getIp() : null;
+      if (foundType.isAddressSolving()) {
+        final var ip = foundType.equals(questionType) ? found.requireTextIp(): null;
         return Response.of(
           Messages.answer(query, ip, questionType.toVersion(), found.getTtl()),
           Duration.ofSeconds(found.getTtl())
diff --git a/src/main/java/com/mageddo/dnsproxyserver/server/rest/reqres/HostnameV1.java b/src/main/java/com/mageddo/dnsproxyserver/server/rest/reqres/HostnameV1.java
index 2b9d77fc..31dbf479 100644
--- a/src/main/java/com/mageddo/dnsproxyserver/server/rest/reqres/HostnameV1.java
+++ b/src/main/java/com/mageddo/dnsproxyserver/server/rest/reqres/HostnameV1.java
@@ -1,7 +1,7 @@
 package com.mageddo.dnsproxyserver.server.rest.reqres;
 
 import com.mageddo.dnsproxyserver.config.Config;
-import com.mageddo.dnsproxyserver.utils.Ips;
+import com.mageddo.net.IP;
 import lombok.Data;
 import lombok.experimental.Accessors;
 import org.apache.commons.lang3.StringUtils;
@@ -12,7 +12,7 @@ public class HostnameV1 {
 
   private String id;
   private String hostname;
-  private Short[] ip;
+  private String ip;
   private String target;
   private int ttl;
   private Config.Entry.Type type;
@@ -22,7 +22,7 @@ public class HostnameV1 {
     return new HostnameV1()
       .setHostname(entry.getHostname())
       .setId(String.valueOf(entry.getId()))
-      .setIp(Ips.toShortArray(entry.getIp()))
+      .setIp(entry.getIpAsText())
       .setTtl(entry.getTtl())
       .setTarget(entry.getTarget())
       .setType(entry.getType())
@@ -33,7 +33,7 @@ public class HostnameV1 {
     return Config.Entry.builder()
       .hostname(this.hostname)
       .ttl(this.ttl)
-      .ip(this.ip != null ? Ips.toIp(this.ip).toText() : null)
+      .ip(IP.of(this.ip))
       .type(this.type)
       .target(this.target)
       .id(StringUtils.isBlank(this.id) ? System.nanoTime() : Long.parseLong(this.id))


Test patch:
diff --git a/src/test/java/com/mageddo/dnsproxyserver/server/rest/HostnameResourceCompTest.java b/src/test/java/com/mageddo/dnsproxyserver/server/rest/HostnameResourceCompTest.java
index 5d5840e7..4f5f9e0b 100644
--- a/src/test/java/com/mageddo/dnsproxyserver/server/rest/HostnameResourceCompTest.java
+++ b/src/test/java/com/mageddo/dnsproxyserver/server/rest/HostnameResourceCompTest.java
@@ -40,7 +40,7 @@ class HostnameResourceCompTest {
     response
       .statusCode(Response.Status.OK.getStatusCode())
       .body(equalTo("""
-        [{"id":"1","hostname":"dps-sample.dev","ip":[192,168,0,254],"target":null,"ttl":30,"type":"A","env":null}]"""))
+        [{"id":"1","hostname":"dps-sample.dev","ip":"192.168.0.254","target":null,"ttl":30,"type":"A","env":null}]"""))
       .log()
     ;
   }
@@ -62,7 +62,7 @@ class HostnameResourceCompTest {
     response
       .statusCode(Response.Status.OK.getStatusCode())
       .body(equalTo("""
-         [{"id":"1231","hostname":"acme.com","ip":[192,168,0,1],"target":null,"ttl":31,"type":"A","env":null}]"""))
+         [{"id":"1231","hostname":"acme.com","ip":"192.168.0.1","target":null,"ttl":31,"type":"A","env":null}]"""))
       .log()
     ;
   }
@@ -75,7 +75,7 @@ class HostnameResourceCompTest {
       {
         "id": 1231,
         "hostname": "acme.com",
-        "ip": [192, 168, 0, 1],
+        "ip": "192.168.0.1",
         "ttl": 31,
         "type": "A",
         "env": "batata-env"
diff --git a/src/test/java/com/mageddo/dnsproxyserver/templates/EntryTemplates.java b/src/test/java/com/mageddo/dnsproxyserver/templates/EntryTemplates.java
index 2c74794b..37eac9c4 100644
--- a/src/test/java/com/mageddo/dnsproxyserver/templates/EntryTemplates.java
+++ b/src/test/java/com/mageddo/dnsproxyserver/templates/EntryTemplates.java
@@ -2,6 +2,7 @@ package com.mageddo.dnsproxyserver.templates;
 
 import com.mageddo.dnsproxyserver.config.Config;
 import com.mageddo.dnsproxyserver.config.Config.Entry.Type;
+import com.mageddo.net.IP;
 
 public class EntryTemplates {
 
@@ -11,7 +12,7 @@ public class EntryTemplates {
     return Config.Entry
       .builder()
       .hostname(host)
-      .ip("10.10.0.1")
+      .ip(IP.of("10.10.0.1"))
       .ttl(45)
       .type(Type.A)
       .build()
@@ -33,7 +34,7 @@ public class EntryTemplates {
     return a(host)
       .toBuilder()
       .type(Type.AAAA)
-      .ip(IpTemplates.LOCAL_IPV6)
+      .ip(IP.of(IpTemplates.LOCAL_IPV6))
       .build();
   }
 
diff --git a/src/test/java/com/mageddo/dnsproxyserver/templates/EnvTemplates.java b/src/test/java/com/mageddo/dnsproxyserver/templates/EnvTemplates.java
index 86005be6..61684436 100644
--- a/src/test/java/com/mageddo/dnsproxyserver/templates/EnvTemplates.java
+++ b/src/test/java/com/mageddo/dnsproxyserver/templates/EnvTemplates.java
@@ -1,6 +1,7 @@
 package com.mageddo.dnsproxyserver.templates;
 
 import com.mageddo.dnsproxyserver.config.Config;
+import com.mageddo.net.IP;
 
 public class EnvTemplates {
 
@@ -11,7 +12,7 @@ public class EnvTemplates {
     return Config.Env.theDefault()
       .add(Config.Entry
         .builder()
-        .ip("192.168.0.1")
+        .ip(IP.of("192.168.0.1"))
         .ttl(30)
         .type(Config.Entry.Type.A)
         .hostname(MAGEDDO_COM)
@@ -23,7 +24,7 @@ public class EnvTemplates {
     return Config.Env.theDefault()
       .add(Config.Entry
         .builder()
-        .ip("192.168.0.1")
+        .ip(IP.of("192.168.0.1"))
         .ttl(30)
         .type(Config.Entry.Type.A)
         .hostname(MAGEDDO_COM_CAMEL_CASE)
diff --git a/src/test/java/com/mageddo/net/IPTest.java b/src/test/java/com/mageddo/net/IPTest.java
new file mode 100644
index 00000000..1f2756e1
--- /dev/null
+++ b/src/test/java/com/mageddo/net/IPTest.java
@@ -0,0 +1,21 @@
+package com.mageddo.net;
+
+import com.mageddo.dnsproxyserver.templates.IpTemplates;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class IPTest {
+
+  @Test
+  void mustBuildIpFromString(){
+    // arrange
+    final var ipStr = IpTemplates.LOCAL;
+
+    // act
+    final var ip = IP.of(ipStr);
+
+    // assert
+    assertEquals(ipStr, ip.toText());
+  }
+}