Skip to content

Commit

Permalink
Add DNS CAA support (RFC6844)
Browse files Browse the repository at this point in the history
  • Loading branch information
fooinha committed Aug 16, 2017
1 parent db6a43f commit 273c435
Show file tree
Hide file tree
Showing 7 changed files with 327 additions and 3 deletions.
278 changes: 278 additions & 0 deletions pcap4j-core/src/main/java/org/pcap4j/packet/DnsRDataCaa.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
/*_##########################################################################
_##
_## Copyright (C) 2017 Pcap4J.org
_##
_##########################################################################
*/

package org.pcap4j.packet;

import org.pcap4j.packet.DnsResourceRecord.DnsRData;
import org.pcap4j.util.ByteArrays;

/**
* DNS CAA RDATA
*
* <pre style="white-space: pre;">
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* | FLAG |
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* / TAG /
* / /
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
* / VALUE /
* / /
* +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
*
* where:
*
* FLAG One octet containing bit flags for record. See rfc for details.
*
* TAG The property identifier, a sequence of US-ASCII characters.
*
* VALUE A sequence of octets representing the property value.
*
* </pre>
*
* @see <a href="https://tools.ietf.org/html/rfc6844">RFC 6844</a>
* @author Paulo Pacheco
* @since pcap4j 1.7.2
*/
public final class DnsRDataCaa implements DnsRData {

private final int flag;
private final String tag;
private final String value;

private static final int CAA_RR_MIN_LEN = 6 /* Do not accept empty tag values */;

/** A serial UID for serialization. */
private static final long serialVersionUID = -1015182073420031158L;

/**
* A static factory method.
* This method validates the arguments by {@link ByteArrays#validateBounds(byte[], int, int)},
* which may throw exceptions undocumented here.
*
* @param rawData rawData
* @param offset offset
* @param length length
* @return a new DnsRDataCaa object.
* @throws IllegalRawDataException if parsing the raw data fails.
*/
public static DnsRDataCaa newInstance(
byte[] rawData, int offset, int length
) throws IllegalRawDataException {
ByteArrays.validateBounds(rawData, offset, length);
return new DnsRDataCaa(rawData, offset, length);
}

private DnsRDataCaa(byte[] rawData, int offset, int length) throws IllegalRawDataException {

if (length < CAA_RR_MIN_LEN) {
throw new IllegalRawDataException("The data is too short to build a DnsRDataCaa");
}

this.flag = ByteArrays.getByte(rawData, offset) & 0xFF;

/* Reading single property entry consisting of a tag-value pair. */

/* Read tag */
int cursor = 1;
int tagLen = rawData[offset + cursor] & 0xFF;
cursor++; /* tag len */
this.tag = new String(rawData, offset + cursor, tagLen);
cursor+=tagLen;

/* Read value */
this.value = new String(rawData, offset + cursor, length - cursor);
}

private DnsRDataCaa(Builder builder) {
if (builder == null || builder.tag == null || builder.value == null) {
StringBuilder sb = new StringBuilder();
sb.append("builder: ").append(builder)
.append(" builder.tag: ").append(builder.tag)
.append(" builder.value: ").append(builder.value);
throw new NullPointerException(sb.toString());
}

if (builder.flag > 255 || builder.flag < 0) {
StringBuilder sb = new StringBuilder();
sb.append("Invalid value for flag: ").append(builder.flag);
throw new IllegalArgumentException(sb.toString());
}

this.flag = builder.flag;

/* TODO: validate if tag follows rfc rules.
Tag values MAY contain US-ASCII characters 'a' through 'z', 'A'
through 'Z', and the numbers 0 through 9. Tag values SHOULD NOT
contain any other characters. Matching of tag values is case
insensitive.
Tag values submitted for registration by IANA MUST NOT contain any
characters other than the (lowercase) US-ASCII characters 'a'
through 'z' and the numbers 0 through 9.
*/
this.tag = builder.tag;
this.value = builder.value;
}

@Override
public int length() {
return 1 /* flag */ + 1 /* tag len */ + tag.length() +
+ value.length();
}

@Override
public byte[] getRawData() {
int len = this.length();
byte rawData[] = new byte[len];

rawData[0] = (byte) this.flag;
rawData[1] = (byte) this.tag.length();
int cursor = 2;

System.arraycopy(this.tag.getBytes(), 0, rawData, cursor, this.tag.length());
cursor += this.tag.length();

System.arraycopy(this.value.getBytes(), 0, rawData, cursor, this.value.length());

return rawData;
}

/**
* @return a new Builder object populated with this object's fields.
*/
public Builder getBuilder() { return new Builder(this); }


@Override
public String toString(String indent) {

String ls = System.getProperty("line.separator");

StringBuilder sb = new StringBuilder();
sb.append(indent).append("CAA RDATA:").append(ls);
sb.append(indent);
sb.append(" CAA: ");
sb.append(this.flag);
sb.append(" ");
sb.append(this.tag);
sb.append(" ");
sb.append(this.value);
sb.append(ls);

return sb.toString();
}

@Override
public String toString() {
return toString("");
}

@Override
public String toString(String indent, byte[] headerRawData) {
// TODO Auto-generated method stub.
// I don't know where this is called with headerRawData
return toString(indent);
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + flag;
result = prime * result + ((tag == null) ? 0 : tag.hashCode());
result = prime * result + ((value == null) ? 0 : value.hashCode());
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DnsRDataCaa other = (DnsRDataCaa) obj;
if (flag != other.flag)
return false;
if (tag == null) {
if (other.tag != null)
return false;
} else if (!tag.equals(other.tag))
return false;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}

/**
* @author Paulo Pacheco
* @since pcap4j 1.7.2
*/
public static final class Builder {

private int flag;
private String tag;
private String value;

public Builder() { flag = 0; }

private Builder(DnsRDataCaa obj) {
this.flag = obj.flag;
this.tag = obj.tag;
this.value = obj.value;
}

/**
* @param flaf flag
* @return this Builder object for method chaining.
*/
public Builder flag(int flag) {
this.flag = flag;
return this;
}

/**
* @param flaf flag
* @return this Builder object for method chaining.
*/
public Builder flag(byte flag) {
this.flag = flag & 0xFF;
return this;
}

/**
* @param tag tag
* @return this Builder object for method chaining.
*/
public Builder tag(String tag) {
this.tag = tag;
return this;
}

/**
* @param value value
* @return this Builder object for method chaining.
*/
public Builder value(String value) {
this.value = value;
return this;
}

/**
* @return a new DnsRDataCaa object.
*/
public DnsRDataCaa build() {
return new DnsRDataCaa(this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ org.pcap4j.packet.DnsResourceRecord$DnsRData.classFor.org.pcap4j.packet.namednum
org.pcap4j.packet.DnsResourceRecord$DnsRData.classFor.org.pcap4j.packet.namednumber.DnsResourceRecordType.15 = org.pcap4j.packet.DnsRDataMx
org.pcap4j.packet.DnsResourceRecord$DnsRData.classFor.org.pcap4j.packet.namednumber.DnsResourceRecordType.16 = org.pcap4j.packet.DnsRDataTxt
org.pcap4j.packet.DnsResourceRecord$DnsRData.classFor.org.pcap4j.packet.namednumber.DnsResourceRecordType.28 = org.pcap4j.packet.DnsRDataAaaa
org.pcap4j.packet.DnsResourceRecord$DnsRData.classFor.org.pcap4j.packet.namednumber.DnsResourceRecordType.257 = org.pcap4j.packet.DnsRDataCaa
org.pcap4j.packet.DnsResourceRecord$DnsRData.classFor.unknownNumber = org.pcap4j.packet.UnknownDnsRData

#**************#
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import org.pcap4j.packet.DnsRDataA;
import org.pcap4j.packet.DnsRDataAaaa;
import org.pcap4j.packet.DnsRDataCaa;
import org.pcap4j.packet.DnsRDataCName;
import org.pcap4j.packet.DnsRDataHInfo;
import org.pcap4j.packet.DnsRDataMInfo;
Expand Down Expand Up @@ -282,6 +283,20 @@ public Class<DnsRDataAaaa> getTargetClass() {
}
}
);
instantiaters.put(
DnsResourceRecordType.CAA, new Instantiater() {
@Override
public DnsRData newInstance(
byte[] rawData, int offset, int length
) throws IllegalRawDataException {
return DnsRDataCaa.newInstance(rawData, offset, length);
}
@Override
public Class<DnsRDataCaa> getTargetClass() {
return DnsRDataCaa.class;
}
}
);
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public DnsPacketTest() throws Exception {
this.checkingDisabled = false;
this.rCode = DnsRCode.NOT_AUTH;
this.qdCount = 1;
this.anCount = 5;
this.anCount = 6;
this.nsCount = 6;
this.arCount = 8;
this.questions = new ArrayList<DnsQuestion>();
Expand Down Expand Up @@ -204,6 +204,27 @@ public DnsPacketTest() throws Exception {
.build();
answers.add(mbRR);

DnsResourceRecord aCaaRR
= new DnsResourceRecord.Builder()
.name(
new DnsDomainName.Builder()
.labels(hogeDomain)
.build()
)
.dataType(DnsResourceRecordType.CAA)
.dataClass(DnsClass.ANY)
.ttl(321321)
.rData(
new DnsRDataCaa.Builder()
.flag(0)
.tag("issue")
.value("ca.local")
.build()
)
.correctLengthAtBuild(true)
.build();
answers.add(aCaaRR);

DnsResourceRecord mdRR
= new DnsResourceRecord.Builder()
.name(
Expand Down
13 changes: 11 additions & 2 deletions pcap4j-packettest/src/test/resources/DnsPacketTest.log
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[DNS Header (1180 bytes)]
[DNS Header (1217 bytes)]
ID: 0x2fc9
QR: response
OPCODE: 2 (Status)
Expand All @@ -11,7 +11,7 @@
Checking Disabled: false
RCODE: 9 (Not Authorized)
QDCOUNT: 1
ANCOUNT: 5
ANCOUNT: 6
NSCOUNT: 6
ARCOUNT: 8
Question:
Expand Down Expand Up @@ -64,6 +64,15 @@
RDATA:
MB RDATA:
MADNAME: www.hoge.co.jp (name: www, pointer: 12)
Answer:
NAME: hoge.co.jp
TYPE: 257 (CAA (Certification Authority Restriction))
CLASS: 255 (ANY)
TTL: 321321
RDLENGTH: 15
RDATA:
CAA RDATA:
CAA: 0 issue ca.local
Authority:
NAME: hoge.co.jp
TYPE: 3 (MD (Mail destination))
Expand Down
Binary file modified pcap4j-packettest/src/test/resources/DnsPacketTest.obj
Binary file not shown.
Binary file modified pcap4j-packettest/src/test/resources/DnsPacketTest.pcap
Binary file not shown.

0 comments on commit 273c435

Please sign in to comment.