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 <kapsh...@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 els...@apache.org.