WIP cleaning up DNS flags

This commit is contained in:
nathan 2018-03-28 14:27:37 +02:00
parent c69d048a75
commit f889399ecf
8 changed files with 324 additions and 155 deletions

View File

@ -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);
}
}

View File

@ -3,103 +3,147 @@
package dorkbox.network.dns.constants; package dorkbox.network.dns.constants;
import dorkbox.network.dns.Mnemonic; import dorkbox.network.dns.Mnemonic;
import dorkbox.network.dns.records.ExtendedFlags;
/** /**
* Constants and functions relating to flags in the DNS header. * 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 public
class Flags { enum Flags {
private static Mnemonic flags = new Mnemonic("DNS Header Flag", Mnemonic.CASE_LOWER);
/** /**
* query/response * query/response
*/ */
public static final byte QR = 0; QR(0, "qr"),
/** /**
* authoritative answer * authoritative answer
*/ */
public static final byte AA = 5; AA(5, "aa"),
/** /**
* truncated * truncated
*/ */
public static final byte TC = 6; TC(6, "tc"),
/** /**
* recursion desired * recursion desired
*/ */
public static final byte RD = 7; RD(7, "rd"),
/** /**
* recursion available * recursion available
*/ */
public static final byte RA = 8; RA(8, "ra"),
/**
* RESERVED
*/
RESERVED(9, "__"),
/** /**
* authenticated data * authenticated data
*/ */
public static final byte AD = 10; AD(10, "ad"),
/** /**
* (security) checking disabled * (security) checking disabled
*/ */
public static final byte CD = 11; CD(11, "cd"),
/** /**
* dnssec ok (extended) * 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 { static {
flags.setMaximum(0xF); flags.setMaximum(0xF);
flags.setPrefix("FLAG"); flags.setPrefix("FLAG");
flags.setNumericAllowed(true); flags.setNumericAllowed(true);
flags.add(QR, "qr"); flags.add(QR.flagValue, "qr");
flags.add(AA, "aa"); flags.add(AA.flagValue, "aa");
flags.add(TC, "tc"); flags.add(TC.flagValue, "tc");
flags.add(RD, "rd"); flags.add(RD.flagValue, "rd");
flags.add(RA, "ra"); flags.add(RA.flagValue, "ra");
flags.add(AD, "ad"); flags.add(AD.flagValue, "ad");
flags.add(CD, "cd"); 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 public static
String string(int i) { Flags toFlag(final int flagBit) {
return flags.getText(i); 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 public static
int value(String s) { Flags toFlag(final String flagName) {
return flags.getValue(s); 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 * 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.
* the rcode or opcode, it's not.
*/ */
public static public static
boolean isFlag(int index) { boolean isFlag(int index) {
flags.check(index); // Checks that a numeric value is within the range
if ((index >= 1 && index <= 4) || (index >= 12)) { if (index < 0 || index > 0xF || (index >= 1 && index <= 4) || (index >= 12)) {
return false; return false;
} }
return true; return true;
} }
} }

View File

@ -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);
}
}

View File

@ -138,33 +138,40 @@ class Header implements Cloneable {
* @see Flags * @see Flags
*/ */
public public
void setFlag(int bit) { void setFlag(Flags flag) {
checkFlag(bit); checkFlag(flag);
flags = setFlag(flags, bit, true); flags = setFlag(flags, flag, true);
} }
static private static private
void checkFlag(int bit) { void checkFlag(int flag) {
if (!validFlag(bit)) { if (!Flags.isFlag(flag)) {
throw new IllegalArgumentException("invalid flag bit " + bit); throw new IllegalArgumentException("invalid flag bit " + flag);
} }
} }
static private static private
boolean validFlag(int bit) { void checkFlag(Flags flag) {
return (bit >= 0 && bit <= 0xF && Flags.isFlag(bit)); 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 static
int setFlag(int flags, int bit, boolean value) { int setFlag(int flags, Flags flag, boolean value) {
checkFlag(bit); checkFlag(flag);
// bits are indexed from left to right // bits are indexed from left to right
if (value) { if (value) {
return flags |= (1 << (15 - bit)); return flags |= (1 << (15 - flag.value()));
} }
else { else {
return flags &= ~(1 << (15 - bit)); return flags &= ~(1 << (15 - flag.value()));
} }
} }
@ -174,15 +181,15 @@ class Header implements Cloneable {
* @see Flags * @see Flags
*/ */
public public
void unsetFlag(int bit) { void unsetFlag(Flags flag) {
checkFlag(bit); checkFlag(flag);
flags = setFlag(flags, bit, false); flags = setFlag(flags, flag, false);
} }
boolean[] getFlags() { boolean[] getFlags() {
boolean[] array = new boolean[16]; boolean[] array = new boolean[16];
for (int i = 0; i < array.length; i++) { for (int i = 0; i < array.length; i++) {
if (validFlag(i)) { if (Flags.isFlag(i)) {
array[i] = getFlag(i); array[i] = getFlag(i);
} }
} }
@ -195,10 +202,21 @@ class Header implements Cloneable {
* @see Flags * @see Flags
*/ */
public public
boolean getFlag(int bit) { boolean getFlag(Flags flag) {
checkFlag(bit); // 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 // 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) { void setCount(int field, int value) {
@ -328,13 +346,13 @@ class Header implements Cloneable {
/** /**
* Converts the header's flags into a String * Converts the header's flags into a String
*/ */
public
String printFlags() { String printFlags() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
if (validFlag(i) && getFlag(i)) { if (Flags.isFlag(i) && getFlag(i)) {
sb.append(Flags.string(i)); Flags flag = Flags.toFlag(i);
sb.append(flag.string());
sb.append(" "); sb.append(" ");
} }
} }

View File

@ -34,7 +34,7 @@
// //
package dorkbox.network.dns; package dorkbox.network.dns;
import dorkbox.network.dns.records.ExtendedFlags; import dorkbox.network.dns.constants.ExtendedFlags;
import junit.framework.TestCase; import junit.framework.TestCase;
public public
@ -42,7 +42,7 @@ class ExtendedFlagsTest extends TestCase {
public public
void test_string() { void test_string() {
// a regular one // a regular one
assertEquals("do", ExtendedFlags.string(ExtendedFlags.DO)); assertEquals("do", ExtendedFlags.DO.string());
// one that doesn't exist // one that doesn't exist
assertTrue(ExtendedFlags.string(1) assertTrue(ExtendedFlags.string(1)
@ -65,7 +65,7 @@ class ExtendedFlagsTest extends TestCase {
public public
void test_value() { void test_value() {
// regular one // regular one
assertEquals(ExtendedFlags.DO, ExtendedFlags.value("do")); assertEquals(ExtendedFlags.DO.value(), ExtendedFlags.value("do"));
// one thats undefined but within range // one thats undefined but within range
assertEquals(16, ExtendedFlags.value("FLAG16")); assertEquals(16, ExtendedFlags.value("FLAG16"));

View File

@ -42,42 +42,61 @@ class FlagsTest extends TestCase {
public public
void test_string() { void test_string() {
// a regular one // a regular one
assertEquals("aa", Flags.string(Flags.AA)); assertEquals("aa", Flags.AA.string());
// one that doesn't exist // one that doesn't exist
assertTrue(Flags.string(12) try {
.startsWith("flag")); Flags.toFlag(12);
fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException ignored) {
}
try { try {
Flags.string(-1); Flags.toFlag(-1);
fail("IllegalArgumentException not thrown"); fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException ignored) {
} }
// (max is 0xF) // (max is 0xF)
try { try {
Flags.string(0x10); Flags.toFlag(0x10);
fail("IllegalArgumentException not thrown"); fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException ignored) {
} }
} }
public public
void test_value() { void test_value() {
// regular one // regular one
assertEquals(Flags.CD, Flags.value("cd")); assertEquals(Flags.CD, Flags.toFlag("cd"));
// one thats undefined but within range // one that's undefined but within range
assertEquals(13, Flags.value("FLAG13")); try {
Flags.toFlag("FLAG13");
fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException ignored) {
}
// one thats undefined but out of range // one that's undefined but out of range
assertEquals(-1, Flags.value("FLAG" + 0x10)); try {
Flags.toFlag("FLAG" + 0x10);
fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException ignored) {
}
// something that unknown // something that's unknown
assertEquals(-1, Flags.value("THIS IS DEFINITELY UNKNOWN")); try {
Flags.toFlag("THIS IS DEFINITELY UNKNOWN");
fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException ignored) {
}
// empty string // empty string
assertEquals(-1, Flags.value("")); try {
Flags.toFlag("");
fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException ignored) {
}
} }
public public
@ -85,8 +104,9 @@ class FlagsTest extends TestCase {
try { try {
Flags.isFlag(-1); Flags.isFlag(-1);
fail("IllegalArgumentException not thrown"); fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException ignored) {
} }
assertTrue(Flags.isFlag(0)); assertTrue(Flags.isFlag(0));
assertFalse(Flags.isFlag(1)); // opcode assertFalse(Flags.isFlag(1)); // opcode
assertFalse(Flags.isFlag(2)); assertFalse(Flags.isFlag(2));
@ -102,11 +122,12 @@ class FlagsTest extends TestCase {
assertFalse(Flags.isFlag(12)); assertFalse(Flags.isFlag(12));
assertFalse(Flags.isFlag(13)); assertFalse(Flags.isFlag(13));
assertFalse(Flags.isFlag(14)); assertFalse(Flags.isFlag(14));
assertFalse(Flags.isFlag(14)); assertFalse(Flags.isFlag(15));
try { try {
Flags.isFlag(16); Flags.isFlag(16);
fail("IllegalArgumentException not thrown"); fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException ignored) {
} }
} }
} }

View File

@ -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));
}
}

View File

@ -165,23 +165,23 @@ class HeaderTest extends TestCase {
public public
void test_flags() { void test_flags() {
m_h.setFlag(0); m_h.setFlag(Flags.toFlag(0));
m_h.setFlag(5); m_h.setFlag(Flags.toFlag(5));
assertTrue(m_h.getFlag(0)); assertTrue(m_h.getFlag(Flags.toFlag(0)));
assertTrue(m_h.getFlags()[0]); assertTrue(m_h.getFlags()[0]);
assertTrue(m_h.getFlag(5)); assertTrue(m_h.getFlag(Flags.toFlag(5)));
assertTrue(m_h.getFlags()[5]); assertTrue(m_h.getFlags()[5]);
m_h.unsetFlag(0); m_h.unsetFlag(Flags.toFlag(0));
assertFalse(m_h.getFlag(0)); assertFalse(m_h.getFlag(Flags.toFlag(0)));
assertFalse(m_h.getFlags()[0]); assertFalse(m_h.getFlags()[0]);
assertTrue(m_h.getFlag(5)); assertTrue(m_h.getFlag(Flags.toFlag(5)));
assertTrue(m_h.getFlags()[5]); assertTrue(m_h.getFlags()[5]);
m_h.unsetFlag(5); m_h.unsetFlag(Flags.toFlag(5));
assertFalse(m_h.getFlag(0)); assertFalse(m_h.getFlag(Flags.toFlag(0)));
assertFalse(m_h.getFlags()[0]); assertFalse(m_h.getFlags()[0]);
assertFalse(m_h.getFlag(5)); assertFalse(m_h.getFlag(Flags.toFlag(5)));
assertFalse(m_h.getFlags()[5]); assertFalse(m_h.getFlags()[5]);
boolean[] flags = m_h.getFlags(); boolean[] flags = m_h.getFlags();
@ -196,47 +196,47 @@ class HeaderTest extends TestCase {
public public
void test_flags_invalid() { void test_flags_invalid() {
try { try {
m_h.setFlag(-1); m_h.setFlag(Flags.toFlag(-1));
fail("IllegalArgumentException not thrown"); fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
} }
try { try {
m_h.setFlag(1); m_h.setFlag(Flags.toFlag(1));
fail("IllegalArgumentException not thrown"); fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
} }
try { try {
m_h.setFlag(16); m_h.setFlag(Flags.toFlag(16));
fail("IllegalArgumentException not thrown"); fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
} }
try { try {
m_h.unsetFlag(-1); m_h.unsetFlag(Flags.toFlag(-1));
fail("IllegalArgumentException not thrown"); fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
} }
try { try {
m_h.unsetFlag(13); m_h.unsetFlag(Flags.toFlag(13));
fail("IllegalArgumentException not thrown"); fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
} }
try { try {
m_h.unsetFlag(16); m_h.unsetFlag(Flags.toFlag(16));
fail("IllegalArgumentException not thrown"); fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
} }
try { try {
m_h.getFlag(-1); m_h.getFlag(Flags.toFlag(-1));
fail("IllegalArgumentException not thrown"); fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
} }
try { try {
m_h.getFlag(4); m_h.getFlag(Flags.toFlag(4));
fail("IllegalArgumentException not thrown"); fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
} }
try { try {
m_h.getFlag(16); m_h.getFlag(Flags.toFlag(16));
fail("IllegalArgumentException not thrown"); fail("IllegalArgumentException not thrown");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
} }
@ -280,7 +280,7 @@ class HeaderTest extends TestCase {
if ((i > 0 && i < 5) || i > 11) { if ((i > 0 && i < 5) || i > 11) {
continue; 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 m_h.setOpcode(0xE); // 1110
assertEquals(0xE, m_h.getOpcode()); assertEquals(0xE, m_h.getOpcode());
assertFalse(m_h.getFlag(0)); assertFalse(m_h.getFlag(Flags.toFlag(0)));
for (int i = 5; i < 12; ++i) { for (int i = 5; i < 12; ++i) {
assertFalse(m_h.getFlag(i)); assertFalse(m_h.getFlag(Flags.toFlag(i)));
} }
assertEquals(0, m_h.getRcode()); assertEquals(0, m_h.getRcode());
} }
@ -446,7 +446,7 @@ class HeaderTest extends TestCase {
if ((i > 0 && i < 5) || i > 11) { if ((i > 0 && i < 5) || i > 11) {
continue; 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) { for (int i = 0; i < 4; ++i) {
assertEquals(m_h.getCount(i), h2.getCount(i)); assertEquals(m_h.getCount(i), h2.getCount(i));