From f889399ecfe94373b8adb3546f09564f71194bfe Mon Sep 17 00:00:00 2001 From: nathan Date: Wed, 28 Mar 2018 14:27:37 +0200 Subject: [PATCH] WIP cleaning up DNS flags --- .../network/dns/constants/ExtendedFlags.java | 81 ++++++++++++ src/dorkbox/network/dns/constants/Flags.java | 120 ++++++++++++------ .../network/dns/records/ExtendedFlags.java | 51 -------- src/dorkbox/network/dns/records/Header.java | 62 +++++---- .../network/dns/ExtendedFlagsTest.java | 6 +- test/dorkbox/network/dns/FlagsTest.java | 57 ++++++--- .../dorkbox/network/dns/ZoneDatabaseTest.java | 56 ++++++++ .../network/dns/records/HeaderTest.java | 46 +++---- 8 files changed, 324 insertions(+), 155 deletions(-) create mode 100644 src/dorkbox/network/dns/constants/ExtendedFlags.java delete mode 100644 src/dorkbox/network/dns/records/ExtendedFlags.java create mode 100644 test/dorkbox/network/dns/ZoneDatabaseTest.java diff --git a/src/dorkbox/network/dns/constants/ExtendedFlags.java b/src/dorkbox/network/dns/constants/ExtendedFlags.java new file mode 100644 index 00000000..f5ad500d --- /dev/null +++ b/src/dorkbox/network/dns/constants/ExtendedFlags.java @@ -0,0 +1,81 @@ +/* + * Copyright 2018 dorkbox, llc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dorkbox.network.dns.constants; + +import dorkbox.network.dns.Mnemonic; + +/** + * Constants and functions relating to EDNS flags. + * + * @author Brian Wellington + */ + +public +enum ExtendedFlags { + + + /** + * dnssec ok + */ + DO(0x8000, "do"); + + private static Mnemonic extflags = new Mnemonic("EDNS Flag", Mnemonic.CASE_LOWER); + static { + extflags.setMaximum(0xFFFF); + extflags.setPrefix("FLAG"); + extflags.setNumericAllowed(true); + + extflags.add(DO.flagValue, "do"); + } + + + private final byte flagValue; + private final String textValue; + + ExtendedFlags(final int flagValue, final String textValue) { + this.flagValue = (byte) flagValue; + this.textValue = textValue; + } + + public + byte value() { + return flagValue; + } + + public + String string() { + return textValue; + } + + /** + * Converts a numeric extended flag into a String + */ + public static + String string(int i) { + return extflags.getText(i); + } + + /** + * Converts a textual representation of an extended flag into its numeric + * value + */ + public static + int value(String s) { + return extflags.getValue(s); + } + +} diff --git a/src/dorkbox/network/dns/constants/Flags.java b/src/dorkbox/network/dns/constants/Flags.java index ec655054..e3ab0980 100644 --- a/src/dorkbox/network/dns/constants/Flags.java +++ b/src/dorkbox/network/dns/constants/Flags.java @@ -3,103 +3,147 @@ package dorkbox.network.dns.constants; import dorkbox.network.dns.Mnemonic; -import dorkbox.network.dns.records.ExtendedFlags; /** * Constants and functions relating to flags in the DNS header. * - * @author Brian Wellington + * In DNS query header there is a flag field in the second 16 bit word in query from bit 5 through bit 11 ([RFC1035] section 4.1.1) + * + * https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-12 */ -public final -class Flags { - - private static Mnemonic flags = new Mnemonic("DNS Header Flag", Mnemonic.CASE_LOWER); +public +enum Flags { /** * query/response */ - public static final byte QR = 0; + QR(0, "qr"), /** * authoritative answer */ - public static final byte AA = 5; + AA(5, "aa"), /** * truncated */ - public static final byte TC = 6; + TC(6, "tc"), /** * recursion desired */ - public static final byte RD = 7; + RD(7, "rd"), /** * recursion available */ - public static final byte RA = 8; + RA(8, "ra"), + + /** + * RESERVED + */ + RESERVED(9, "__"), /** * authenticated data */ - public static final byte AD = 10; + AD(10, "ad"), /** * (security) checking disabled */ - public static final byte CD = 11; + CD(11, "cd"), /** * dnssec ok (extended) */ - public static final int DO = ExtendedFlags.DO; + DO(ExtendedFlags.DO.value(), ExtendedFlags.DO.string()); + + private static Mnemonic flags = new Mnemonic("DNS Header Flag", Mnemonic.CASE_LOWER); static { flags.setMaximum(0xF); flags.setPrefix("FLAG"); flags.setNumericAllowed(true); - flags.add(QR, "qr"); - flags.add(AA, "aa"); - flags.add(TC, "tc"); - flags.add(RD, "rd"); - flags.add(RA, "ra"); - flags.add(AD, "ad"); - flags.add(CD, "cd"); + flags.add(QR.flagValue, "qr"); + flags.add(AA.flagValue, "aa"); + flags.add(TC.flagValue, "tc"); + flags.add(RD.flagValue, "rd"); + flags.add(RA.flagValue, "ra"); + flags.add(AD.flagValue, "ad"); + flags.add(CD.flagValue, "cd"); + } + + private final byte flagValue; + private final String textValue; + + Flags(final int flagValue, final String textValue) { + this.flagValue = (byte) flagValue; + this.textValue = textValue; + } + + public + byte value() { + return flagValue; + } + + public + String string() { + return textValue; } - private - Flags() {} - /** - * Converts a numeric Flag into a String - */ public static - String string(int i) { - return flags.getText(i); + Flags toFlag(final int flagBit) { + for (Flags flag : values()) { + if (flag.value() == flagBit) { + return flag; + } + } + + throw new IllegalArgumentException("Invalid flag " + flagBit); } - /** - * Converts a String representation of an Flag into its numeric value - */ public static - int value(String s) { - return flags.getValue(s); + Flags toFlag(final String flagName) { + for (Flags flag : values()) { + if (flag.string().equals(flagName)) { + return flag; + } + } + + throw new IllegalArgumentException("Invalid flag " + flagName); } + + // /** + // * Converts a numeric Flag into a String + // */ + // public static + // String string(int i) { + // return flags.getText(i); + // } + // + // /** + // * Converts a String representation of an Flag into its numeric value + // */ + // public static + // int value(String s) { + // return flags.getValue(s); + // } + /** - * Indicates if a bit in the flags field is a flag or not. If it's part of - * the rcode or opcode, it's not. + * Indicates if a bit in the flags field is a flag or not. If it's part of the rcode or opcode, it's not. */ public static boolean isFlag(int index) { - flags.check(index); - if ((index >= 1 && index <= 4) || (index >= 12)) { + // Checks that a numeric value is within the range + if (index < 0 || index > 0xF || (index >= 1 && index <= 4) || (index >= 12)) { return false; } + return true; } - } diff --git a/src/dorkbox/network/dns/records/ExtendedFlags.java b/src/dorkbox/network/dns/records/ExtendedFlags.java deleted file mode 100644 index 244d5575..00000000 --- a/src/dorkbox/network/dns/records/ExtendedFlags.java +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2004 Brian Wellington (bwelling@xbill.org) - -package dorkbox.network.dns.records; - -import dorkbox.network.dns.Mnemonic; - -/** - * Constants and functions relating to EDNS flags. - * - * @author Brian Wellington - */ - -public final -class ExtendedFlags { - - private static Mnemonic extflags = new Mnemonic("EDNS Flag", Mnemonic.CASE_LOWER); - - /** - * dnssec ok - */ - public static final int DO = 0x8000; - - static { - extflags.setMaximum(0xFFFF); - extflags.setPrefix("FLAG"); - extflags.setNumericAllowed(true); - - extflags.add(DO, "do"); - } - - private - ExtendedFlags() {} - - /** - * Converts a numeric extended flag into a String - */ - public static - String string(int i) { - return extflags.getText(i); - } - - /** - * Converts a textual representation of an extended flag into its numeric - * value - */ - public static - int value(String s) { - return extflags.getValue(s); - } - -} diff --git a/src/dorkbox/network/dns/records/Header.java b/src/dorkbox/network/dns/records/Header.java index 740a2672..3ff02dd9 100644 --- a/src/dorkbox/network/dns/records/Header.java +++ b/src/dorkbox/network/dns/records/Header.java @@ -138,33 +138,40 @@ class Header implements Cloneable { * @see Flags */ public - void setFlag(int bit) { - checkFlag(bit); - flags = setFlag(flags, bit, true); + void setFlag(Flags flag) { + checkFlag(flag); + flags = setFlag(flags, flag, true); } static private - void checkFlag(int bit) { - if (!validFlag(bit)) { - throw new IllegalArgumentException("invalid flag bit " + bit); + void checkFlag(int flag) { + if (!Flags.isFlag(flag)) { + throw new IllegalArgumentException("invalid flag bit " + flag); } } static private - boolean validFlag(int bit) { - return (bit >= 0 && bit <= 0xF && Flags.isFlag(bit)); + void checkFlag(Flags flag) { + if (!validFlag(flag)) { + throw new IllegalArgumentException("invalid flag bit " + flag); + } + } + + static private + boolean validFlag(Flags flag) { + return flag != null && (flag.value() >= 0 && flag.value() <= 0xF && Flags.isFlag(flag.value())); } static - int setFlag(int flags, int bit, boolean value) { - checkFlag(bit); + int setFlag(int flags, Flags flag, boolean value) { + checkFlag(flag); // bits are indexed from left to right if (value) { - return flags |= (1 << (15 - bit)); + return flags |= (1 << (15 - flag.value())); } else { - return flags &= ~(1 << (15 - bit)); + return flags &= ~(1 << (15 - flag.value())); } } @@ -174,15 +181,15 @@ class Header implements Cloneable { * @see Flags */ public - void unsetFlag(int bit) { - checkFlag(bit); - flags = setFlag(flags, bit, false); + void unsetFlag(Flags flag) { + checkFlag(flag); + flags = setFlag(flags, flag, false); } boolean[] getFlags() { boolean[] array = new boolean[16]; for (int i = 0; i < array.length; i++) { - if (validFlag(i)) { + if (Flags.isFlag(i)) { array[i] = getFlag(i); } } @@ -195,10 +202,21 @@ class Header implements Cloneable { * @see Flags */ public - boolean getFlag(int bit) { - checkFlag(bit); + boolean getFlag(Flags flag) { + // bit s are indexed from left to right + return (flags & (1 << (15 - flag.value()))) != 0; + } + + /** + * Retrieves a flag. + * + * @param flagValue ALWAYS checked before using, so additional checks are not necessary + * @see Flags + */ + private + boolean getFlag(int flagValue) { // bits are indexed from left to right - return (flags & (1 << (15 - bit))) != 0; + return (flags & (1 << (15 - flagValue))) != 0; } void setCount(int field, int value) { @@ -328,13 +346,13 @@ class Header implements Cloneable { /** * Converts the header's flags into a String */ - public String printFlags() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < 16; i++) { - if (validFlag(i) && getFlag(i)) { - sb.append(Flags.string(i)); + if (Flags.isFlag(i) && getFlag(i)) { + Flags flag = Flags.toFlag(i); + sb.append(flag.string()); sb.append(" "); } } diff --git a/test/dorkbox/network/dns/ExtendedFlagsTest.java b/test/dorkbox/network/dns/ExtendedFlagsTest.java index 477d6391..c9e741fe 100644 --- a/test/dorkbox/network/dns/ExtendedFlagsTest.java +++ b/test/dorkbox/network/dns/ExtendedFlagsTest.java @@ -34,7 +34,7 @@ // package dorkbox.network.dns; -import dorkbox.network.dns.records.ExtendedFlags; +import dorkbox.network.dns.constants.ExtendedFlags; import junit.framework.TestCase; public @@ -42,7 +42,7 @@ class ExtendedFlagsTest extends TestCase { public void test_string() { // a regular one - assertEquals("do", ExtendedFlags.string(ExtendedFlags.DO)); + assertEquals("do", ExtendedFlags.DO.string()); // one that doesn't exist assertTrue(ExtendedFlags.string(1) @@ -65,7 +65,7 @@ class ExtendedFlagsTest extends TestCase { public void test_value() { // regular one - assertEquals(ExtendedFlags.DO, ExtendedFlags.value("do")); + assertEquals(ExtendedFlags.DO.value(), ExtendedFlags.value("do")); // one thats undefined but within range assertEquals(16, ExtendedFlags.value("FLAG16")); diff --git a/test/dorkbox/network/dns/FlagsTest.java b/test/dorkbox/network/dns/FlagsTest.java index 54cfaead..d66f90aa 100644 --- a/test/dorkbox/network/dns/FlagsTest.java +++ b/test/dorkbox/network/dns/FlagsTest.java @@ -42,42 +42,61 @@ class FlagsTest extends TestCase { public void test_string() { // a regular one - assertEquals("aa", Flags.string(Flags.AA)); + assertEquals("aa", Flags.AA.string()); // one that doesn't exist - assertTrue(Flags.string(12) - .startsWith("flag")); + try { + Flags.toFlag(12); + fail("IllegalArgumentException not thrown"); + } catch (IllegalArgumentException ignored) { + } try { - Flags.string(-1); + Flags.toFlag(-1); fail("IllegalArgumentException not thrown"); - } catch (IllegalArgumentException e) { + } catch (IllegalArgumentException ignored) { } // (max is 0xF) try { - Flags.string(0x10); + Flags.toFlag(0x10); fail("IllegalArgumentException not thrown"); - } catch (IllegalArgumentException e) { + } catch (IllegalArgumentException ignored) { } } public void test_value() { // regular one - assertEquals(Flags.CD, Flags.value("cd")); + assertEquals(Flags.CD, Flags.toFlag("cd")); - // one thats undefined but within range - assertEquals(13, Flags.value("FLAG13")); + // one that's undefined but within range + try { + Flags.toFlag("FLAG13"); + fail("IllegalArgumentException not thrown"); + } catch (IllegalArgumentException ignored) { + } - // one thats undefined but out of range - assertEquals(-1, Flags.value("FLAG" + 0x10)); + // one that's undefined but out of range + try { + Flags.toFlag("FLAG" + 0x10); + fail("IllegalArgumentException not thrown"); + } catch (IllegalArgumentException ignored) { + } - // something that unknown - assertEquals(-1, Flags.value("THIS IS DEFINITELY UNKNOWN")); + // something that's unknown + try { + Flags.toFlag("THIS IS DEFINITELY UNKNOWN"); + fail("IllegalArgumentException not thrown"); + } catch (IllegalArgumentException ignored) { + } // empty string - assertEquals(-1, Flags.value("")); + try { + Flags.toFlag(""); + fail("IllegalArgumentException not thrown"); + } catch (IllegalArgumentException ignored) { + } } public @@ -85,8 +104,9 @@ class FlagsTest extends TestCase { try { Flags.isFlag(-1); fail("IllegalArgumentException not thrown"); - } catch (IllegalArgumentException e) { + } catch (IllegalArgumentException ignored) { } + assertTrue(Flags.isFlag(0)); assertFalse(Flags.isFlag(1)); // opcode assertFalse(Flags.isFlag(2)); @@ -102,11 +122,12 @@ class FlagsTest extends TestCase { assertFalse(Flags.isFlag(12)); assertFalse(Flags.isFlag(13)); assertFalse(Flags.isFlag(14)); - assertFalse(Flags.isFlag(14)); + assertFalse(Flags.isFlag(15)); + try { Flags.isFlag(16); fail("IllegalArgumentException not thrown"); - } catch (IllegalArgumentException e) { + } catch (IllegalArgumentException ignored) { } } } diff --git a/test/dorkbox/network/dns/ZoneDatabaseTest.java b/test/dorkbox/network/dns/ZoneDatabaseTest.java new file mode 100644 index 00000000..9cda933c --- /dev/null +++ b/test/dorkbox/network/dns/ZoneDatabaseTest.java @@ -0,0 +1,56 @@ +/* + * Copyright 2018 dorkbox, llc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.network.dns; + +import org.junit.Test; + +import dorkbox.network.dns.constants.DnsClass; +import dorkbox.network.dns.exceptions.TextParseException; +import dorkbox.network.dns.server.Response; +import dorkbox.network.dns.zone.AbstractZone; +import dorkbox.network.dns.zone.ZoneDatabase; +import dorkbox.network.dns.zone.ZoneType; +import junit.framework.TestCase; + +public +class ZoneDatabaseTest extends TestCase { + + class TestZone extends AbstractZone { + public + TestZone(String name) throws TextParseException { + super(ZoneType.master, Name.fromString(name)); + } + + @Override + public + Response find(final Name qname, final int recordType) { + return null; + } + } + + @Test + public + void testFind() throws TextParseException { + ZoneDatabase db = new ZoneDatabase(); + db.add(new TestZone("example.com.")); + db.add(new TestZone("example.co.jp.")); + db.add(new TestZone("jp.")); + db.add(new TestZone("ne.jp.")); + + assertNotNull(db.prepare(Name.fromString("jp."), DnsClass.IN)); + assertNull(db.prepare(Name.fromString("com."), DnsClass.IN)); + } +} diff --git a/test/dorkbox/network/dns/records/HeaderTest.java b/test/dorkbox/network/dns/records/HeaderTest.java index c2f1ea4b..99576253 100644 --- a/test/dorkbox/network/dns/records/HeaderTest.java +++ b/test/dorkbox/network/dns/records/HeaderTest.java @@ -165,23 +165,23 @@ class HeaderTest extends TestCase { public void test_flags() { - m_h.setFlag(0); - m_h.setFlag(5); - assertTrue(m_h.getFlag(0)); + m_h.setFlag(Flags.toFlag(0)); + m_h.setFlag(Flags.toFlag(5)); + assertTrue(m_h.getFlag(Flags.toFlag(0))); assertTrue(m_h.getFlags()[0]); - assertTrue(m_h.getFlag(5)); + assertTrue(m_h.getFlag(Flags.toFlag(5))); assertTrue(m_h.getFlags()[5]); - m_h.unsetFlag(0); - assertFalse(m_h.getFlag(0)); + m_h.unsetFlag(Flags.toFlag(0)); + assertFalse(m_h.getFlag(Flags.toFlag(0))); assertFalse(m_h.getFlags()[0]); - assertTrue(m_h.getFlag(5)); + assertTrue(m_h.getFlag(Flags.toFlag(5))); assertTrue(m_h.getFlags()[5]); - m_h.unsetFlag(5); - assertFalse(m_h.getFlag(0)); + m_h.unsetFlag(Flags.toFlag(5)); + assertFalse(m_h.getFlag(Flags.toFlag(0))); assertFalse(m_h.getFlags()[0]); - assertFalse(m_h.getFlag(5)); + assertFalse(m_h.getFlag(Flags.toFlag(5))); assertFalse(m_h.getFlags()[5]); boolean[] flags = m_h.getFlags(); @@ -196,47 +196,47 @@ class HeaderTest extends TestCase { public void test_flags_invalid() { try { - m_h.setFlag(-1); + m_h.setFlag(Flags.toFlag(-1)); fail("IllegalArgumentException not thrown"); } catch (IllegalArgumentException e) { } try { - m_h.setFlag(1); + m_h.setFlag(Flags.toFlag(1)); fail("IllegalArgumentException not thrown"); } catch (IllegalArgumentException e) { } try { - m_h.setFlag(16); + m_h.setFlag(Flags.toFlag(16)); fail("IllegalArgumentException not thrown"); } catch (IllegalArgumentException e) { } try { - m_h.unsetFlag(-1); + m_h.unsetFlag(Flags.toFlag(-1)); fail("IllegalArgumentException not thrown"); } catch (IllegalArgumentException e) { } try { - m_h.unsetFlag(13); + m_h.unsetFlag(Flags.toFlag(13)); fail("IllegalArgumentException not thrown"); } catch (IllegalArgumentException e) { } try { - m_h.unsetFlag(16); + m_h.unsetFlag(Flags.toFlag(16)); fail("IllegalArgumentException not thrown"); } catch (IllegalArgumentException e) { } try { - m_h.getFlag(-1); + m_h.getFlag(Flags.toFlag(-1)); fail("IllegalArgumentException not thrown"); } catch (IllegalArgumentException e) { } try { - m_h.getFlag(4); + m_h.getFlag(Flags.toFlag(4)); fail("IllegalArgumentException not thrown"); } catch (IllegalArgumentException e) { } try { - m_h.getFlag(16); + m_h.getFlag(Flags.toFlag(16)); fail("IllegalArgumentException not thrown"); } catch (IllegalArgumentException e) { } @@ -280,7 +280,7 @@ class HeaderTest extends TestCase { if ((i > 0 && i < 5) || i > 11) { continue; } - assertFalse(m_h.getFlag(i)); + assertFalse(m_h.getFlag(Flags.toFlag(i))); } } @@ -305,9 +305,9 @@ class HeaderTest extends TestCase { m_h.setOpcode(0xE); // 1110 assertEquals(0xE, m_h.getOpcode()); - assertFalse(m_h.getFlag(0)); + assertFalse(m_h.getFlag(Flags.toFlag(0))); for (int i = 5; i < 12; ++i) { - assertFalse(m_h.getFlag(i)); + assertFalse(m_h.getFlag(Flags.toFlag(i))); } assertEquals(0, m_h.getRcode()); } @@ -446,7 +446,7 @@ class HeaderTest extends TestCase { if ((i > 0 && i < 5) || i > 11) { continue; } - assertEquals(m_h.getFlag(i), h2.getFlag(i)); + assertEquals(m_h.getFlag(Flags.toFlag(i)), h2.getFlag(Flags.toFlag(i))); } for (int i = 0; i < 4; ++i) { assertEquals(m_h.getCount(i), h2.getCount(i));