All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.pcap4j.packet.DnsDomainName Maven / Gradle / Ivy

There is a newer version: 2.0.0-alpha.6
Show newest version
/*_##########################################################################
  _##
  _##  Copyright (C) 2016-2017  Pcap4J.org
  _##
  _##########################################################################
*/

package org.pcap4j.packet;

import static org.pcap4j.util.ByteArrays.*;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.pcap4j.util.ByteArrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * DNS domain name
 *
 * 
 * labels:
 *     1            len             1            len
 * +-------+-------+-//-+-------+-------+-------+-//-+-------+--//--+-------+
 * |  len  |       label        |  len  |       label        |      |len (0)|
 * +-------+-------+-//-+-------+-------+-------+-//-+-------+--//--+-------+
 *
 * pointer:
 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 * | 1  1|                OFFSET                   |
 * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
 * 
* * @see RFC 1035 * @author Kaito Yamada * @since pcap4j 1.7.1 */ public final class DnsDomainName implements Serializable { /** */ private static final long serialVersionUID = -9123494137779222577L; private static final Logger LOG = LoggerFactory.getLogger(DnsDomainName.class); /** The root domain (zero) */ public static final DnsDomainName ROOT_DOMAIN; static { try { ROOT_DOMAIN = new DnsDomainName(new byte[] {0}, 0, 1); } catch (IllegalRawDataException e) { throw new AssertionError("Never get here."); } } private final List labels; private final String name; private final Short pointer; /** * 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 DnsDomainName object. * @throws IllegalRawDataException if parsing the raw data fails. */ public static DnsDomainName newInstance(byte[] rawData, int offset, int length) throws IllegalRawDataException { ByteArrays.validateBounds(rawData, offset, length); return new DnsDomainName(rawData, offset, length); } private DnsDomainName(byte[] rawData, int offset, int length) throws IllegalRawDataException { this.labels = new ArrayList(); int cursor = 0; Short foundPointer = null; boolean terminated = false; while (cursor < length) { int len = rawData[offset + cursor] & 0xFF; int flag = len & 0xC0; if (flag == 0x00) { if (len == 0) { terminated = true; break; } cursor++; if (length - cursor < len) { StringBuilder sb = new StringBuilder(200); sb.append("The data is too short to build a DnsDomainName. data: ") .append(ByteArrays.toHexString(rawData, " ")) .append(", offset: ") .append(offset) .append(", length: ") .append(length) .append(", cursor: ") .append(cursor); throw new IllegalRawDataException(sb.toString()); } labels.add(new String(rawData, offset + cursor, len)); cursor += len; continue; } else if (flag == 0xC0) { if (length - cursor < SHORT_SIZE_IN_BYTES) { StringBuilder sb = new StringBuilder(200); sb.append("The data is too short to build a DnsDomainName. data: ") .append(ByteArrays.toHexString(rawData, " ")) .append(", offset: ") .append(offset) .append(", length: ") .append(length) .append(", cursor: ") .append(cursor); throw new IllegalRawDataException(sb.toString()); } foundPointer = (short) (ByteArrays.getShort(rawData, offset + cursor) & 0x3FFF); terminated = true; break; } else { StringBuilder sb = new StringBuilder(200); sb.append("A label must start with 00 or 11. data: ") .append(ByteArrays.toHexString(rawData, " ")) .append(", offset: ") .append(offset) .append(", length: ") .append(length); throw new IllegalRawDataException(sb.toString()); } } this.pointer = foundPointer; if (!terminated) { StringBuilder sb = new StringBuilder(200); sb.append("No null termination nor pointer. data: ") .append(ByteArrays.toHexString(rawData, " ")) .append(", offset: ") .append(offset) .append(", length: ") .append(length); throw new IllegalRawDataException(sb.toString()); } this.name = joinLabels(labels); } private DnsDomainName(Builder builder) { if (builder == null || builder.labels == null) { StringBuilder sb = new StringBuilder(); sb.append("builder").append(builder).append(" builder.labels: ").append(builder.labels); throw new NullPointerException(sb.toString()); } for (String label : builder.labels) { if (label.getBytes().length > 63) { throw new IllegalArgumentException( "Length of a label must be less than 64. label: " + label); } } if (builder.pointer != null && (builder.pointer & 0xC000) != 0) { throw new IllegalArgumentException( "(builder.pointer & 0xC000) must be zero. builder.pointer: " + builder.pointer); } this.labels = new ArrayList(builder.labels); this.name = joinLabels(labels); this.pointer = builder.pointer; } private String joinLabels(List lbls) { if (lbls.size() == 0) { return ""; } StringBuilder sb = new StringBuilder(); Iterator iter = lbls.iterator(); while (true) { sb.append(iter.next()); if (iter.hasNext()) { sb.append("."); } else { break; } } return sb.toString(); } /** @return labels */ public List getLabels() { return new ArrayList(labels); } /** @return name, which is made by joining labels with "." */ public String getName() { return name; } /** @return pointer (0 - 16383 (inclusive)). May be null. */ public Short getPointer() { return pointer; } /** @return pointer (0 - 16383 (inclusive)). May be null. */ public Integer getPointerAsInt() { if (pointer != null) { return (int) pointer; } else { return null; } } /** * @param headerRawData the raw data of the DNS header including this domain name. * @return decompressed name. * @throws IllegalRawDataException if an error occurred during decompression or circular reference * is detected. */ public String decompress(byte[] headerRawData) throws IllegalRawDataException { if (headerRawData == null) { throw new NullPointerException("headerRawData is null."); } return decompress(headerRawData, new ArrayList()); } private String decompress(byte[] headerRawData, List pointers) throws IllegalRawDataException { if (pointer == null) { return name; } else { if (pointers.contains(pointer)) { StringBuilder sb = new StringBuilder(200); sb.append("Circular reference detected. data: ") .append(ByteArrays.toHexString(headerRawData, " ")) .append(", offset: ") .append(pointer) .append(", name: ") .append(name); throw new IllegalRawDataException(sb.toString()); } pointers.add(pointer); StringBuilder sb = new StringBuilder(); sb.append(name) .append(".") .append( new DnsDomainName(headerRawData, pointer, headerRawData.length - pointer) .decompress(headerRawData, pointers)); return sb.toString(); } } /** @return a new Builder object populated with this object's fields. */ public Builder getBuilder() { return new Builder(this); } /** @return the raw data. */ public byte[] getRawData() { byte[] data = new byte[length()]; int cursor = 0; for (String label : labels) { byte[] labelBytes = label.getBytes(); data[cursor] = (byte) labelBytes.length; cursor++; System.arraycopy(labelBytes, 0, data, cursor, labelBytes.length); cursor += labelBytes.length; } if (pointer != null) { byte[] offsetBytes = ByteArrays.toByteArray(pointer); offsetBytes[0] |= 0xC0; System.arraycopy(offsetBytes, 0, data, cursor, offsetBytes.length); } return data; } /** @return length */ public int length() { int len = 0; for (String label : labels) { len += label.length() + 1; } if (pointer != null) { len += 2; } else { len++; } return len; } @Override public String toString() { if (labels.size() == 0 && pointer == null) { return ""; } if (pointer == null) { return name; } else { StringBuilder sb = new StringBuilder(); sb.append("[name: ").append(name).append(", pointer: ").append(pointer).append("]"); return sb.toString(); } } /** * Convert this object to string representation including all fields info and decompressed domain * name. * * @param headerRawData the raw data of the DNS header including this domain name. * @return string representation of this object. */ public String toString(byte[] headerRawData) { if (labels.size() == 0 && pointer == null) { return ""; } if (pointer == null) { return name; } else { String decompressedName; try { decompressedName = decompress(headerRawData); } catch (IllegalRawDataException e) { LOG.error("Error occurred during building complete name.", e); decompressedName = "Error occurred during building complete name"; } StringBuilder sb = new StringBuilder(); sb.append(decompressedName) .append(" (name: ") .append(name) .append(", pointer: ") .append(pointer) .append(")"); return sb.toString(); } } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + name.hashCode(); result = prime * result + ((pointer == null) ? 0 : pointer.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; } DnsDomainName other = (DnsDomainName) obj; if (!name.equals(other.name)) { return false; } if (pointer == null) { if (other.pointer != null) { return false; } } else if (!pointer.equals(other.pointer)) { return false; } return true; } /** * @author Kaito Yamada * @since pcap4j 1.7.1 */ public static final class Builder { private List labels; private Short pointer = null; /** */ public Builder() {} private Builder(DnsDomainName obj) { this.labels = obj.labels; this.pointer = obj.pointer; } /** * @param labels labels * @return this Builder object for method chaining. */ public Builder labels(List labels) { this.labels = labels; return this; } /** * @param labels labels * @return this Builder object for method chaining. */ public Builder labels(String[] labels) { this.labels = Arrays.asList(labels); return this; } /** * @param pointer pointer * @return this Builder object for method chaining. */ public Builder pointer(Short pointer) { this.pointer = pointer; return this; } /** @return a new DnsDomainName object. */ public DnsDomainName build() { return new DnsDomainName(this); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy