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.