You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by el...@apache.org on 2018/03/08 15:47:46 UTC

[incubator-trafficcontrol] 01/04: Experimental support for Client Subnet in DNS Queries RFC7871

This is an automated email from the ASF dual-hosted git repository.

elsloo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-trafficcontrol.git

commit d47ce5918a9bff617cdb094ed5345b4682b29b87
Author: kapsharm <ka...@cisco.com>
AuthorDate: Wed Jun 21 09:20:33 2017 -0400

    Experimental support for Client Subnet in DNS Queries RFC7871
    
    (cherry picked from commit 34e122e932d409c24a26101149a8a50a3a1a1a28)
---
 .../traffic_router/core/dns/NameServer.java        |  52 ++++++-
 .../traffic_router/core/dns/NameServerTest.java    | 171 +++++++++++++++++++++
 2 files changed, 217 insertions(+), 6 deletions(-)

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 1bf3c2e..e8a0ab7 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
@@ -22,6 +22,7 @@ import java.util.Iterator;
 import java.util.List;
 
 import org.apache.log4j.Logger;
+import org.json.JSONObject;
 import org.xbill.DNS.DClass;
 import org.xbill.DNS.ExtendedFlags;
 import org.xbill.DNS.Flags;
@@ -36,7 +37,10 @@ import org.xbill.DNS.Section;
 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.cache.CacheRegister;
 import com.comcast.cdn.traffic_control.traffic_router.core.router.TrafficRouterManager;
 
 public class NameServer {
@@ -74,7 +78,7 @@ public class NameServer {
 		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,12 @@ public class NameServer {
 			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();
-
+			final CacheRegister data = trafficRouterManager.getTrafficRouter().getCacheRegister();
+			final JSONObject config = data.getConfig();
+			final boolean ecsEnable = config.optBoolean("ecsEnable", false);
 			int flags = 0;
 
 			if ((qopt != null) && (qopt.getVersion() > MAX_SUPPORTED_EDNS_VERS)) {
@@ -108,13 +115,46 @@ public class NameServer {
 				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 (ecsEnable) {
+				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) && (ecsEnable)) {
+				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) && (ecsEnable)) {	
+				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);
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 0000000..9ce13ac
--- /dev/null
+++ b/traffic_router/core/src/test/java/com/comcast/cdn/traffic_control/traffic_router/core/dns/NameServerTest.java
@@ -0,0 +1,171 @@
+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 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 org.json.JSONObject;
+
+
+
+@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();
+        JSONObject js = new JSONObject().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);
+	
+        // 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()));
+    }
+    
+    @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);
+	
+        // 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()));
+    }
+   
+}

-- 
To stop receiving notification emails like this one, please contact
elsloo@apache.org.