elsloo closed pull request #1480: Experimental support for Client Subnet in DNS 
Queries RFC7871
URL: https://github.com/apache/incubator-trafficcontrol/pull/1480
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git 
a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/config/ConfigHandler.java
 
b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/config/ConfigHandler.java
index d458651d56..d706e28fb9 100644
--- 
a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/config/ConfigHandler.java
+++ 
b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/config/ConfigHandler.java
@@ -208,6 +208,7 @@ public boolean processConfig(final String jsonStr) throws 
JsonUtilsException, IO
                                federationsWatcher.configure(config);
                                steeringWatcher.configure(config);
                                
trafficRouterManager.setCacheRegister(cacheRegister);
+                               
trafficRouterManager.getNameServer().setEcsEnable(JsonUtils.optBoolean(config, 
"ecsEnable", false));
                                
trafficRouterManager.getTrafficRouter().setRequestHeaders(parseRequestHeaders(config.get("requestHeaders")));
                                
trafficRouterManager.getTrafficRouter().configurationChanged();
 
diff --git 
a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/dns/NameServer.java
 
b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/dns/NameServer.java
index 1bf3c2e003..87102ea331 100644
--- 
a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/dns/NameServer.java
+++ 
b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/dns/NameServer.java
@@ -36,9 +36,12 @@
 import org.xbill.DNS.SetResponse;
 import org.xbill.DNS.Type;
 import org.xbill.DNS.Zone;
+import org.xbill.DNS.EDNSOption;
+import org.xbill.DNS.ClientSubnetOption;
 
 import 
com.comcast.cdn.traffic_control.traffic_router.core.router.TrafficRouterManager;
 
+
 public class NameServer {
        private static final int MAX_SUPPORTED_EDNS_VERS = 0;
        private static final int MAX_ITERATIONS = 6;
@@ -47,6 +50,7 @@
        private static final int FLAG_SIGONLY = 2;
 
        private static final Logger LOGGER = Logger.getLogger(NameServer.class);
+       private boolean ecsEnable = false;
        /**
         * 
         */
@@ -74,7 +78,7 @@ public Message query(final Message request, final InetAddress 
clientAddress, fin
                return response;
        }
 
-       @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"})
+       @SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity", 
"PMD.AvoidDeeplyNestedIfStmts"})
        private void addAnswers(final Message request, final Message response, 
final InetAddress clientAddress, final DNSAccessRecord.Builder builder) {
                final Record question = request.getQuestion();
 
@@ -82,9 +86,9 @@ private void addAnswers(final Message request, final Message 
response, final Ine
                        final int qclass = question.getDClass();
                        final Name qname = question.getName();
                        final OPTRecord qopt = request.getOPT();
+                       List<EDNSOption> list = Collections.EMPTY_LIST;
                        boolean dnssecRequest = false;
                        int qtype = question.getType();
-
                        int flags = 0;
 
                        if ((qopt != null) && (qopt.getVersion() > 
MAX_SUPPORTED_EDNS_VERS)) {
@@ -108,13 +112,46 @@ private void addAnswers(final Message request, final 
Message response, final Ine
                                qtype = Type.ANY;
                                flags |= FLAG_SIGONLY;
                        }
-
-                       lookup(qname, qtype, clientAddress, response, flags, 
dnssecRequest, builder);
-
+                       // Get list of options matching client subnet option 
code (8)
+                       if (qopt != null ){
+                               list = 
qopt.getOptions(EDNSOption.Code.CLIENT_SUBNET);
+                       }
+                       InetAddress ipaddr = null;
+                       int nmask = 0;
+                       if (isEcsEnable()) {
+                               for (final EDNSOption option : list) {
+                                       assert (option instanceof 
ClientSubnetOption);
+                                       // If there are multiple 
ClientSubnetOptions in the Option RR, then
+                                       // choose the one with longest source 
prefix. RFC 7871
+                                       if 
(((ClientSubnetOption)option).getSourceNetmask() > nmask) {
+                                               nmask = 
((ClientSubnetOption)option).getSourceNetmask();
+                                               ipaddr = 
((ClientSubnetOption)option).getAddress();
+                                       }
+                               }
+                       }
+                       if ((ipaddr!= null) && (isEcsEnable())) {
+                               LOGGER.debug("DNS: Using Client IP Address from 
ECS Option" + ipaddr.getHostAddress() + "/" 
+                                               + nmask);
+                               lookup(qname, qtype, ipaddr, response, flags, 
dnssecRequest, builder);
+                       } else {
+                               lookup(qname, qtype, clientAddress, response, 
flags, dnssecRequest, builder);
+                       }
+                       
                        if (response.getHeader().getRcode() == Rcode.REFUSED) {
                                return;
                        }
-
+                       
+                       // Check if we had incoming ClientSubnetOption in 
Option RR, then we need
+                       // to return with the response, setting the scope 
subnet as well
+                       if ((nmask != 0) && (isEcsEnable())) {
+                               final ClientSubnetOption cso = new 
ClientSubnetOption(nmask, nmask, ipaddr);
+                               final List<ClientSubnetOption> csoList = new 
ArrayList<ClientSubnetOption>(1);
+                               csoList.add(cso);       
+                               // OptRecord Arguments: payloadSize = 1280, 
xrcode = 0, version=0, flags=0, option List
+                               final OPTRecord opt = new OPTRecord(1280, 0, 0, 
0, csoList);
+                               response.addRecord(opt, Section.ADDITIONAL);
+                       }
+               
                        if (qopt != null && flags == FLAG_DNSSECOK) {
                                final int optflags = ExtendedFlags.DO;
                                final OPTRecord opt = new OPTRecord(1280, 
(byte) 0, (byte) 0, optflags);
@@ -379,4 +416,13 @@ public void destroy() {
                 */
                ZoneManager.destroy();
        }
+
+       public boolean isEcsEnable() {
+               return ecsEnable;
+       }
+
+       public void setEcsEnable(final boolean ecsEnable) {
+               this.ecsEnable = ecsEnable;
+       }
+
 }
diff --git 
a/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/dns/NameServerTest.java
 
b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/dns/NameServerTest.java
new file mode 100644
index 0000000000..07b85863c6
--- /dev/null
+++ 
b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/dns/NameServerTest.java
@@ -0,0 +1,176 @@
+package com.comcast.cdn.traffic_control.traffic_router.core.dns;
+
+import 
com.comcast.cdn.traffic_control.traffic_router.core.router.TrafficRouter;
+import 
com.comcast.cdn.traffic_control.traffic_router.core.router.TrafficRouterManager;
+import com.comcast.cdn.traffic_control.traffic_router.core.cache.CacheRegister;
+import com.comcast.cdn.traffic_control.traffic_router.core.dns.DNSAccessRecord;
+import com.comcast.cdn.traffic_control.traffic_router.core.dns.NameServer;
+
+import com.comcast.cdn.traffic_control.traffic_router.core.util.JsonUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.xbill.DNS.*;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.not;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.powermock.api.mockito.PowerMockito.*;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import com.fasterxml.jackson.databind.node.JsonNodeFactory;
+import com.fasterxml.jackson.databind.JsonNode;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({Header.class, NameServer.class, TrafficRouterManager.class, 
TrafficRouter.class, CacheRegister.class})
+public class NameServerTest {
+    private NameServer nameServer;
+    private InetAddress client;
+    private TrafficRouterManager trafficRouterManager;
+    private TrafficRouter trafficRouter;
+    private Record ar;
+    private NSRecord ns;
+    
+    @Before
+    public void before() throws Exception {
+
+        client = Inet4Address.getByAddress(new byte[]{(byte) 192, (byte) 168, 
23, 45});
+        nameServer = new NameServer(); 
+        trafficRouterManager = mock(TrafficRouterManager.class);
+        trafficRouter = mock(TrafficRouter.class);
+        CacheRegister cacheRegister = mock(CacheRegister.class);
+        doReturn(cacheRegister).when(trafficRouter).getCacheRegister();
+        JsonNode js = JsonNodeFactory.instance.objectNode().put("ecsEnable", 
true);
+        when(cacheRegister.getConfig()).thenReturn(js);
+        
+        Name m_an, m_host, m_admin;
+           m_an = Name.fromString("dns1.example.com.");
+           m_host = Name.fromString("dns1.example.com.");
+           m_admin = Name.fromString("admin.example.com.");
+           ar = new SOARecord(m_an, DClass.IN, 0x13A8,
+                       m_host, m_admin, 0xABCDEF12L, 0xCDEF1234L,
+                       0xEF123456L, 0x12345678L, 0x3456789AL);
+
+           ns = new NSRecord(m_an, DClass.IN, 12345L, m_an);
+    }
+
+    @Test
+    public void TestARecordQueryWithClientSubnetOption() throws Exception {
+        
+        Name name = Name.fromString("host1.example.com.");
+        Record question = Record.newRecord(name, Type.A, DClass.IN, 12345L);
+        Message query = Message.newQuery(question);
+       
+        //Add opt record, with client subnet option.
+        int nmask = 28;
+        InetAddress ipaddr = Inet4Address.getByName("192.168.33.0");
+        ClientSubnetOption cso = new ClientSubnetOption(nmask, ipaddr);
+        List<ClientSubnetOption> cso_list = new 
ArrayList<ClientSubnetOption>(1);
+        cso_list.add(cso);     
+        OPTRecord opt = new OPTRecord(1280, 0, 0, 0, cso_list);
+        query.addRecord(opt, Section.ADDITIONAL);
+        
+       
+           // Add ARecord Entry in the zone
+        InetAddress resolvedAddress = Inet4Address.getByName("192.168.8.9");
+        Record answer = new ARecord(name, DClass.IN, 12345L, resolvedAddress);
+        Record[] records = new Record[] {ar, ns, answer};
+        
+        Name m_an = Name.fromString("dns1.example.com.");
+        Zone zone = new Zone(m_an, records);
+       
+        DNSAccessRecord.Builder builder = new DNSAccessRecord.Builder(1L, 
client);
+
+        nameServer.setTrafficRouterManager(trafficRouterManager);
+        
nameServer.setEcsEnable(JsonUtils.optBoolean(trafficRouter.getCacheRegister().getConfig(),
 "ecsEnable", false)); // this mimics what happens in ConfigHandler
+
+        // Following is needed to mock this call: zone = 
trafficRouterManager.getTrafficRouter().getZone(qname, qtype, clientAddress, 
dnssecRequest, builder);
+        
when(trafficRouterManager.getTrafficRouter()).thenReturn(trafficRouter);
+        when(trafficRouter.getZone(any(Name.class), any(int.class), 
eq(ipaddr), any(boolean.class), 
any(DNSAccessRecord.Builder.class))).thenReturn(zone);
+
+        // The function call under test:
+        Message res = nameServer.query(query, client, builder);
+
+        
+        //Verification of response
+        OPTRecord qopt = res.getOPT();
+        List<EDNSOption> list = Collections.EMPTY_LIST;
+        list = qopt.getOptions(EDNSOption.Code.CLIENT_SUBNET);
+        assert (list != Collections.EMPTY_LIST);
+        ClientSubnetOption option = (ClientSubnetOption)list.get(0);
+        assertThat(nmask, equalTo(option.getSourceNetmask()));
+        assertThat(nmask, equalTo(option.getScopeNetmask()));
+        assertThat(ipaddr, equalTo(option.getAddress()));
+        nameServer.setEcsEnable(false);
+    }
+    
+    @Test
+    public void TestARecordQueryWithMultipleClientSubnetOption() throws 
Exception {
+        
+        Name name = Name.fromString("host1.example.com.");
+        Record question = Record.newRecord(name, Type.A, DClass.IN, 12345L);
+        Message query = Message.newQuery(question);
+       
+        //Add opt record, with multiple client subnet option.
+        int nmask1 = 16;
+        int nmask2 = 24;
+        InetAddress ipaddr1 = Inet4Address.getByName("192.168.0.0");
+        InetAddress ipaddr2 = Inet4Address.getByName("192.168.33.0");
+        ClientSubnetOption cso1 = new ClientSubnetOption(nmask1, ipaddr1);
+        ClientSubnetOption cso2 = new ClientSubnetOption(nmask2, ipaddr2);
+        List<ClientSubnetOption> cso_list = new 
ArrayList<ClientSubnetOption>(1);
+        cso_list.add(cso1);
+        cso_list.add(cso2);
+        final OPTRecord opt = new OPTRecord(1280, 0, 0, 0, cso_list);
+        query.addRecord(opt, Section.ADDITIONAL);
+        
+       
+           // Add ARecord Entry in the zone
+        InetAddress resolvedAddress = Inet4Address.getByName("192.168.8.9");
+        Record answer = new ARecord(name, DClass.IN, 12345L, resolvedAddress);
+        Record[] records = new Record[] {ar, ns, answer};
+        
+        Name m_an = Name.fromString("dns1.example.com.");
+        Zone zone = new Zone(m_an, records);
+       
+        DNSAccessRecord.Builder builder = new DNSAccessRecord.Builder(1L, 
client);
+
+        nameServer.setTrafficRouterManager(trafficRouterManager);
+        
nameServer.setEcsEnable(JsonUtils.optBoolean(trafficRouter.getCacheRegister().getConfig(),
 "ecsEnable", false)); // this mimics what happens in ConfigHandler
+       
+        // Following is needed to mock this call: zone = 
trafficRouterManager.getTrafficRouter().getZone(qname, qtype, clientAddress, 
dnssecRequest, builder);
+        
when(trafficRouterManager.getTrafficRouter()).thenReturn(trafficRouter);
+        when(trafficRouter.getZone(any(Name.class), any(int.class), 
eq(ipaddr2), any(boolean.class), 
any(DNSAccessRecord.Builder.class))).thenReturn(zone);
+
+        // The function call under test:
+        Message res = nameServer.query(query, client, builder);
+
+        
+        //Verification of response
+        OPTRecord qopt = res.getOPT();
+        List<EDNSOption> list = Collections.EMPTY_LIST;
+        list = qopt.getOptions(EDNSOption.Code.CLIENT_SUBNET);
+        assert (list != Collections.EMPTY_LIST);
+        ClientSubnetOption option = (ClientSubnetOption)list.get(0);
+        assertThat(1, equalTo(list.size()));
+        assertThat(nmask2, equalTo(option.getSourceNetmask()));
+        assertThat(nmask2, equalTo(option.getScopeNetmask()));
+        assertThat(ipaddr2, equalTo(option.getAddress()));
+        nameServer.setEcsEnable(false);
+    }
+   
+}


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
[email protected]


With regards,
Apache Git Services

Reply via email to