Skip to content

Instantly share code, notes, and snippets.

@ql-owo-lp
Created March 3, 2015 00:05
Show Gist options
  • Save ql-owo-lp/eb4862d5dd0236ac252b to your computer and use it in GitHub Desktop.
Save ql-owo-lp/eb4862d5dd0236ac252b to your computer and use it in GitHub Desktop.
OpenDaylight DHCP and DNS packet parser
package com.kevinxw.net.packet;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.opendaylight.controller.sal.packet.BitBufferHelper;
import org.opendaylight.controller.sal.packet.BufferException;
import org.opendaylight.controller.sal.packet.Packet;
import org.opendaylight.controller.sal.packet.PacketException;
import org.opendaylight.controller.sal.utils.HexEncode;
import org.opendaylight.controller.sal.utils.NetUtils;
import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.util.*;
/**
* Here we defines a BOOTP packet, which includes DHCP packet
* About packet format, see http://www.networksorcery.com/enp/protocol/dhcp.htm for reference
* Field value dictionary also refers to https://github.com/FreeRADIUS/freeradius-server/blob/master/share/dictionary.dhcp
* About DHCP options see http://tools.ietf.org/html/rfc2132
* <p/>
* Created by kevin on 6/24/14.
*/
public class BOOTP extends Packet {
private static final String OPCODE = "OpCode";
private static final String HWTYPE = "HardwareType";
private static final String HWADDRLEN = "HardwareAddressLength";
private static final String HOPCOUNT = "HopCount";
private static final String TXID = "TransactionId";
private static final String NUMOFSEC = "NumberOfSecond";
private static final String FLAGS = "Flags";
private static final String CLIENTIPADDR = "ClientIpAddress";
private static final String YOURIPADDR = "YourIpAddress";
private static final String SRVIPADDR = "ServerIpAddress";
private static final String GWIPADDR = "GatewayIpAddress";
private static final String CLIENTHWADDR = "ClientHardwareAddress";
private static final String SRVHOSTNAME = "ServerHostName";
private static final String BOOTFILENAME = "BootFileName";
private static final String MAGICCOOKIE = "MagicCookie";
private static final String OPTIONS = "Options";
private static final Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
private static final long serialVersionUID = 1L;
{
put(OPCODE, new ImmutablePair<>(0, 8));
put(HWTYPE, new ImmutablePair<>(8, 8));
put(HWADDRLEN, new ImmutablePair<>(16, 8));
put(HOPCOUNT, new ImmutablePair<>(24, 8));
put(TXID, new ImmutablePair<>(32, 32));
put(NUMOFSEC, new ImmutablePair<>(64, 16));
put(FLAGS, new ImmutablePair<>(80, 16));
put(CLIENTIPADDR, new ImmutablePair<>(96, 32));
put(YOURIPADDR, new ImmutablePair<>(128, 32));
put(SRVIPADDR, new ImmutablePair<>(160, 32));
put(GWIPADDR, new ImmutablePair<>(192, 32));
put(CLIENTHWADDR, new ImmutablePair<>(224, 128)); // 16 bytes long
// we do not read server host name and boot file name here
// but just for us to have a correct header length
// we declare them as well
put(SRVHOSTNAME, new ImmutablePair<>(352, 512)); // 64 bytes
put(BOOTFILENAME, new ImmutablePair<>(864, 1024)); // 128 bytes
// magic cookie, start offset 236 bytes
put(MAGICCOOKIE, new ImmutablePair<>(1888, 32));
put(OPTIONS, new ImmutablePair<>(1920, 0));
}
};
private final Map<String, byte[]> fieldValues = new HashMap();
// DHCP option map
private final List<BootpOption> options = new ArrayList<>();
/**
* Default constructor that creates and sets the hash map values
*/
public BOOTP() {
super();
init();
}
/**
* Constructor that sets the access level for the packet
*/
public BOOTP(boolean writeAccess) {
super(writeAccess);
init();
}
/**
* Init the packet
*/
private void init() {
hdrFieldCoordMap = fieldCoordinates;
hdrFieldsMap = fieldValues;
// here we fill the Server Host Name with zero bytes
setHeaderField(SRVHOSTNAME, new byte[64]);
setHeaderField(BOOTFILENAME, new byte[128]);
}
@Override
public void setHeaderField(String headerField, byte[] readValue) {
if (headerField.equals(OPTIONS) &&
(readValue == null || readValue.length == 0)) {
hdrFieldsMap.remove(headerField);
return;
}
hdrFieldsMap.put(headerField, readValue);
}
@Override
protected void postDeserializeCustomOperation(byte[] data, int startBitOffset)
throws PacketException {
setOptions(rawPayload);
rawPayload = new byte[]{};
}
@Override
public int getfieldnumBits(String fieldName) {
if (OPTIONS.equals(fieldName)) {
byte[] opt = getOptions();
return opt == null ? 0 : opt.length * NetUtils.NumBitsInAByte;
} else
return hdrFieldCoordMap.get(fieldName).getRight();
}
/**
* Deserialize options
*/
private void deserializeOptions() throws PacketException {
options.clear(); // reset all options
byte[] optionsData = getOptions();
// the first byte is option code, second byte is option length
for (int offset = 0, len; offset < optionsData.length; offset += len + 2) {
// first, get option code
byte code = optionsData[offset];
BootpOption.BootpOptionCode optCode = BootpOption.BootpOptionCode.valueOf(code);
len = -1; // set to -1, in case we have zero length option
// some special cases
if (!BootpOption.BootpOptionCode.END.equals(optCode)
&& !BootpOption.BootpOptionCode.PAD.equals(optCode)) {
if (offset + 1 == optionsData.length) { // somethings is wrong
corrupted = true;
break;
}
len = optionsData[offset + 1] & 0xFF;
}
if (optCode == null) // unsupported option
continue;
BootpOption optPayload;
if (optCode.Clazz != null) {
try {
optPayload = optCode.Clazz.newInstance();
} catch (Exception e) {
throw new RuntimeException(
"Error parsing option payload for BootP packet", e);
}
} else {
optPayload = new BootpOption();
}
optPayload.deserialize(optionsData, offset * NetUtils.NumBitsInAByte, (len + 2) * NetUtils.NumBitsInAByte);
optPayload.setParent(this);
if (optCode.Clazz != null) {
optPayload.setRawPayload(new byte[]{}); // fully parsed option doesn't have any payload
} else {
byte[] _payload = Arrays.copyOfRange(optionsData, offset + 2, offset + len + 2);
optPayload.setRawPayload(_payload);
}
options.add(optPayload);
}
}
private void serializeOptions() throws PacketException {
int len = 0;
for (BootpOption opt : options) {
len += opt.getHeaderSize();
}
byte[] newOpt = new byte[len / NetUtils.NumBitsInAByte];
for (int i = 0, offset = 0; i < options.size(); i++) {
byte[] dat = options.get(i).serialize();
int numBits = dat.length * NetUtils.NumBitsInAByte;
try {
BitBufferHelper.setBytes(newOpt, dat, offset, numBits);
} catch (BufferException e) {
throw new PacketException(e.getMessage());
}
offset += numBits;
}
setOptions(newOpt); // update option
}
public byte getOpCode() {
return BitBufferHelper.getByte(fieldValues.get(OPCODE));
}
/**
* Set opcode
*
* @param opCode
* @return
*/
public BOOTP setOpCode(byte opCode) {
byte[] _opCode = BitBufferHelper.toByteArray(opCode);
setHeaderField(OPCODE, _opCode);
return this;
}
public byte getHwType() {
return BitBufferHelper.getByte(fieldValues.get(HWTYPE));
}
public BOOTP setHwType(byte hwType) {
byte[] _hwType = BitBufferHelper.toByteArray(hwType);
setHeaderField(HWTYPE, _hwType);
return this;
}
public byte getHwAddrLength() {
return BitBufferHelper.getByte(fieldValues.get(HWADDRLEN));
}
public BOOTP setHwAddrLength(byte len) {
byte[] _len = BitBufferHelper.toByteArray(len);
setHeaderField(HWADDRLEN, _len);
return this;
}
public byte getHopCount() {
return BitBufferHelper.getByte(fieldValues.get(HOPCOUNT));
}
public BOOTP setHopCount(byte hop) {
byte[] _hop = BitBufferHelper.toByteArray(hop);
setHeaderField(HOPCOUNT, _hop);
return this;
}
public int getTxId() {
return BitBufferHelper.getInt(fieldValues.get(TXID));
}
public BOOTP setTxId(int txId) {
byte[] _txId = BitBufferHelper.toByteArray(txId);
setHeaderField(TXID, _txId);
return this;
}
public short getNumOfSec() {
return BitBufferHelper.getShort(fieldValues.get(NUMOFSEC));
}
public BOOTP setNumOfSec(short sec) {
byte[] _sec = BitBufferHelper.toByteArray(sec);
setHeaderField(NUMOFSEC, _sec);
return this;
}
public short getFlags() {
return BitBufferHelper.getShort(fieldValues.get(FLAGS));
}
public BOOTP setFlags(short flags) {
byte[] _opCode = BitBufferHelper.toByteArray(flags);
setHeaderField(FLAGS, _opCode);
return this;
}
public int getClientIpAddr() {
return BitBufferHelper.getInt(fieldValues.get(CLIENTIPADDR));
}
public BOOTP setClientIpAddr(int addr) {
byte[] _addr = BitBufferHelper.toByteArray(addr);
setHeaderField(CLIENTIPADDR, _addr);
return this;
}
public int getYourIpAddr() {
return BitBufferHelper.getInt(fieldValues.get(YOURIPADDR));
}
public BOOTP setYourIpAddr(int addr) {
byte[] _addr = BitBufferHelper.toByteArray(addr);
setHeaderField(YOURIPADDR, _addr);
return this;
}
public int getSrvIpAddr() {
return BitBufferHelper.getInt(fieldValues.get(SRVIPADDR));
}
public BOOTP setSrvIpAddr(int addr) {
byte[] _addr = BitBufferHelper.toByteArray(addr);
setHeaderField(SRVIPADDR, _addr);
return this;
}
public int getGwIpAddr() {
return BitBufferHelper.getInt(fieldValues.get(GWIPADDR));
}
public BOOTP setGwIpAddr(int addr) {
byte[] _addr = BitBufferHelper.toByteArray(addr);
setHeaderField(GWIPADDR, _addr);
return this;
}
public byte[] getClientHwAddr() {
byte[] _addr = fieldValues.get(CLIENTHWADDR);
return Arrays.copyOfRange(_addr, 0, getHwAddrLength());
}
public BOOTP setClientHwAddr(byte[] addr) {
setHwAddrLength((byte) addr.length);
setHeaderField(CLIENTHWADDR, addr);
return this;
}
public int getMagicCookie() {
return BitBufferHelper.getInt(fieldValues.get(MAGICCOOKIE));
}
public BOOTP setMagicCookie(int cookie) {
byte[] _cookie = BitBufferHelper.toByteArray(cookie);
setHeaderField(MAGICCOOKIE, _cookie);
return this;
}
public BootpType getBootpType() {
return BootpType.valueOf(getMagicCookie());
}
/**
* gets the Options stored
*
* @return the options
*/
public byte[] getOptions() {
return fieldValues.get(OPTIONS);
}
public BOOTP setOptions(byte[] val) {
setHeaderField(OPTIONS, val);
try {
deserializeOptions();
} catch (PacketException e) {
corrupted = true;
throw new RuntimeException("Error setting BOOTP options.", e);
}
return this;
}
/**
* Get options' order
*
* @return
*/
public BootpOption[] getOptionsAsArray() {
BootpOption[] res = new BootpOption[options.size()];
return options.toArray(res);
}
/**
* Remove an option
*
* @param code optionCode
* @return
*/
public BOOTP removeOption(byte code) {
for (Iterator<BootpOption> it = options.iterator(); it.hasNext(); ) {
if (it.next().getOptCode() == code) {
it.remove(); // remove the first one merely
break;
}
}
try {
serializeOptions();
} catch (PacketException e) {
corrupted = true;
throw new RuntimeException("Error setting BOOTP options.", e);
}
return this;
}
public <T extends BootpOption> BOOTP insertOption(T option) {
byte code = option.getOptCode();
// by default we are going to insert option by code number
int i = 0;
for (byte curCode; i < options.size(); i++) {
curCode = options.get(i).getOptCode();
if (code <= curCode || curCode == -1)
break;
}
options.add(i, option);
try {
serializeOptions();
} catch (PacketException e) {
corrupted = true;
throw new RuntimeException("Error setting BOOTP options.", e);
}
return this;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Bootp Packet {")
.append("\n - OpCode: ").append(OpCode.valueOf(getOpCode()))
.append("\n - HardwareType: ").append(HardwareType.valueOf(getHwType()))
.append("\n - HardwareAddressLength: ").append(getHwAddrLength())
.append("\n - HopCount: ").append(getHopCount())
.append("\n - TransactionId: 0x").append(Integer.toHexString(getTxId()))
.append("\n - NumberOfSecond: ").append(getNumOfSec())
.append("\n - Flags: ").append(FlagType.valueOf(getFlags()))
.append("\n - ClientIpAddress: ").append(NetUtils.getInetAddress(getClientIpAddr()))
.append("\n - YourIpAddress: ").append(NetUtils.getInetAddress(getYourIpAddr()))
.append("\n - ServerIpAddress: ").append(NetUtils.getInetAddress(getSrvIpAddr()))
.append("\n - GatewayIpAddress: ").append(NetUtils.getInetAddress(getGwIpAddr()))
.append("\n - ClientHardwareAddress: ").append(HexEncode.bytesToHexStringFormat(getClientHwAddr()))
.append("\n - MagicCookie: ").append(BootpType.valueOf(getMagicCookie()))
.append("\n - OPTIONS [");
// append all the options
for (BootpOption opt : getOptionsAsArray()) {
// skip meaningless padding
if (BootpOption.BootpOptionCode.PAD.equals(opt.getOptCode()) || BootpOption.BootpOptionCode.END.equals(opt.getOptCode()))
continue;
sb.append("\n + ").append(opt.toString());
}
sb.append("\n ]")
.append("\n}");
return sb.toString();
}
/**
* Hardware Type field of DHCP packet
*/
public static enum HardwareType {
ETHERNET(1),
EXP_ETHERNET(2),
AX25(3),
PROTEON_TOKEN_RING(4),
CHAOS(5),
IEEE802(6),
ARCNET(7),
HYPERCHANNEL(8),
LANSTAR(9),
AUTONET_SHORT_ADDRESS(10),
LOCALTALK(11),
LOCALNET(12),
ULTRA_LINK(13),
SMDS(14),
FRAME_RELAY(15),
ATM_16(16), // NOTICE WE HAVE THREE ATM HERE 16, 19 & 21
HDLC(17),
FIBER_CHANNEL(18),
ATM_19(19),
SERIAL_LINE(20),
ATM_21(21),
MIL_STD_188_220(22),
METRICOM(23),
IEEE1394(24),
MAPOS(25),
TWINAXIAL(26),
EUI_64(27),
HIPARP(28),
IP_OVER_ISO_7816_3(29),
ARPSEC(30),
IPSEC_TUNNEL(31),
INFINIBAND(32),
CAI_TIA_102(33),
WIEGAND_INTERFACE(34),
PURE_IP(35),;
public final byte value;
private HardwareType(int val) {
value = (byte) val;
}
public static HardwareType valueOf(int val) {
return valueOf((byte) (val & 0xFF));
}
public static HardwareType valueOf(byte val) {
for (HardwareType c : values()) {
if ((c.value & val) != 0)
return c;
}
return null;
}
}
public static enum FlagType {
UNICAST(0x0000),
BROADCAST(0x8000);
public final short value;
private FlagType(int val) {
value = (short) val;
}
public static FlagType valueOf(int val) {
return valueOf((short) (val & 0xFFFF));
}
public static FlagType valueOf(short val) {
for (FlagType c : values()) {
if (c.value == val)
return c;
}
return null;
}
}
public static enum BootpType {
DHCP(1669485411);
public final int value;
private BootpType(int val) {
value = val;
}
public static BootpType valueOf(int value) {
for (BootpType c : values()) {
if (c.value == value)
return c;
}
return null; // null refers unknown
}
}
public static enum DhcpState {
AVAILABLE(1),
ACTIVE(2),
EXPIRED(3),
RELEASED(4),
ABANDONED(5),
RESET(6),
REMOTE(7),
TRANSITIONING(8),;
public final byte value;
private DhcpState(int val) {
value = (byte) val;
}
public static DhcpState valueOf(int value) {
return valueOf((byte) (value & 0xFF));
}
public static DhcpState valueOf(byte value) {
for (DhcpState c : values()) {
if (c.value == value)
return c;
}
return null;
}
}
/**
* DHCP OpCode
*/
public static enum OpCode {
BOOT_REQUEST(1),
BOOT_REPLY(2),;
public final byte value;
private OpCode(int val) {
value = (byte) val;
}
public static OpCode valueOf(int value) {
return valueOf((byte) (value & 0xFF));
}
public static OpCode valueOf(byte value) {
for (OpCode c : values()) {
if (c.value == value)
return c;
}
return null;
}
public boolean equals(byte value) {
return this.value == value;
}
}
/**
* Code = 1
*/
public static class SubnetMaskOption extends BootpOption {
private static final String OPTION_SUBNETMASK = "SubnetMask";
private static final Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
private static final long serialVersionUID = 1L;
{
put(OPTION_CODE, new ImmutablePair<>(0, 8));
put(OPTION_HEADERLENGTH, new ImmutablePair<>(8, 8));
put(OPTION_SUBNETMASK, new ImmutablePair<>(16, 32));
}
};
@Override
protected Map<String, Pair<Integer, Integer>> getFieldCoordinates() {
return fieldCoordinates;
}
public int getSubnetMask() {
return BitBufferHelper.getInt(fieldValues.get(OPTION_SUBNETMASK));
}
public void setSubnetMask(int mask) {
byte[] _mask = BitBufferHelper.toByteArray(mask);
setHeaderField(OPTION_SUBNETMASK, _mask);
}
@Override
public String toString() {
return new StringBuilder()
.append(super.toString())
.append(" ")
.append(NetUtils.getInetAddress(getSubnetMask()).getHostAddress())
.toString();
}
}
/**
* Code = 3
*/
public static class RouterAddressOption extends BootpOption {
private static final String OPTION_ROUTER_ADDRRESS = "RouterAddress";
private static final Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
private static final long serialVersionUID = 1L;
{
put(OPTION_CODE, new ImmutablePair<>(0, 8));
put(OPTION_HEADERLENGTH, new ImmutablePair<>(8, 8));
put(OPTION_ROUTER_ADDRRESS, new ImmutablePair<>(16, 0));
}
};
@Override
protected Map<String, Pair<Integer, Integer>> getFieldCoordinates() {
return fieldCoordinates;
}
@Override
public int getfieldnumBits(String fieldName) {
if (fieldName.equals(OPTION_ROUTER_ADDRRESS))
return getHeaderLen() * NetUtils.NumBitsInAByte;
else
return hdrFieldCoordMap.get(fieldName).getRight();
}
public byte[] getAddress() {
return fieldValues.get(OPTION_ROUTER_ADDRRESS);
}
public RouterAddressOption setAddress(byte[] val) {
if (val.length > 255 || val.length % 4 != 0)
throw new RuntimeException("Error setting address. Address's length should be a multiple of 4 and less than 255.");
setHeaderLen((byte) val.length);
setHeaderField(OPTION_ROUTER_ADDRRESS, val);
return this;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder()
.append(super.toString())
.append(" ");
byte[] addr = getAddress();
for (int i = 0; i < addr.length; i += 4) {
try {
sb
.append(Inet4Address.getByAddress(new byte[]{addr[i], addr[i + 1], addr[i + 2], addr[i + 3]}).getHostAddress())
.append(",");
} catch (UnknownHostException e) {
}
}
if (sb.charAt(sb.length() - 1) == ',')
sb.deleteCharAt(sb.length() - 1);
return sb.toString();
}
}
/**
* Code = 6
*/
public static class DomainNameServerOption extends BootpOption {
private static final String OPTION_DNS = "DomainNameServer";
private static final Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
private static final long serialVersionUID = 1L;
{
put(OPTION_CODE, new ImmutablePair<>(0, 8));
put(OPTION_HEADERLENGTH, new ImmutablePair<>(8, 8));
put(OPTION_DNS, new ImmutablePair<>(16, 0));
}
};
@Override
protected Map<String, Pair<Integer, Integer>> getFieldCoordinates() {
return fieldCoordinates;
}
public int[] getAddresses() {
byte[] _val = fieldValues.get(OPTION_DNS);
int[] addrs = new int[_val.length / 4];
for (int i = 0; i < addrs.length; i++) {
addrs[i] = BitBufferHelper.getInt(Arrays.copyOfRange(_val, i * 4, (i + 1) * 4));
}
return addrs;
}
public DomainNameServerOption setAddresses(int[] val) {
byte[] _val = new byte[val.length * 4];
for (int i = 0; i < val.length; i++) {
byte[] v = BitBufferHelper.toByteArray(val[i]);
for (int j = 0; j < 4; j++)
_val[i * 4 + j] = v[j];
}
setHeaderField(OPTION_DNS, _val);
return this;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder()
.append(super.toString())
.append(" ");
for (int addr : getAddresses())
sb.append(NetUtils.getInetAddress(addr).getHostAddress()).append(",");
if (sb.charAt(sb.length() - 1) == ',')
sb.deleteCharAt(sb.length() - 1);
return sb.toString();
}
}
/**
* Code = 12
*/
public static class HostNameOption extends BootpOption {
private static final String OPTION_HOST_NAME = "HostName";
private static final Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
private static final long serialVersionUID = 1L;
{
put(OPTION_CODE, new ImmutablePair<>(0, 8));
put(OPTION_HEADERLENGTH, new ImmutablePair<>(8, 8));
put(OPTION_HOST_NAME, new ImmutablePair<>(16, 0));
}
};
@Override
protected Map<String, Pair<Integer, Integer>> getFieldCoordinates() {
return fieldCoordinates;
}
public String getName() {
return new String(fieldValues.get(OPTION_HOST_NAME));
}
public HostNameOption setName(String val) {
byte[] _val = val.getBytes();
if (_val.length > 255)
throw new RuntimeException("Host name is longer than 255 characters.");
setHeaderLen((byte) _val.length);
setHeaderField(OPTION_HOST_NAME, _val);
return this;
}
@Override
public String toString() {
return new StringBuilder()
.append(super.toString())
.append(" ")
.append(getName())
.toString();
}
}
/**
* Code = 15
*/
public static class DomainNameOption extends BootpOption {
private static final String OPTION_DOMAIN_NAME = "DomainName";
private static final Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
private static final long serialVersionUID = 1L;
{
put(OPTION_CODE, new ImmutablePair<>(0, 8));
put(OPTION_HEADERLENGTH, new ImmutablePair<>(8, 8));
put(OPTION_DOMAIN_NAME, new ImmutablePair<>(16, 0));
}
};
@Override
protected Map<String, Pair<Integer, Integer>> getFieldCoordinates() {
return fieldCoordinates;
}
public String getName() {
return new String(fieldValues.get(OPTION_DOMAIN_NAME));
}
public DomainNameOption setName(String val) {
byte[] _val = val.getBytes();
if (_val.length > 255)
throw new RuntimeException("Domain name is longer than 255 characters.");
setHeaderLen((byte) _val.length);
setHeaderField(OPTION_DOMAIN_NAME, _val);
return this;
}
@Override
public String toString() {
return new StringBuilder()
.append(super.toString())
.append(" ")
.append(getName())
.toString();
}
}
/**
* Code = 50
*/
public static class RequestedIpAddressOption extends BootpOption {
private static final String OPTION_REQUESTED_IP_ADDRRESS = "RequestedIpAddress";
private static final Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
private static final long serialVersionUID = 1L;
{
put(OPTION_CODE, new ImmutablePair<>(0, 8));
put(OPTION_HEADERLENGTH, new ImmutablePair<>(8, 8));
put(OPTION_REQUESTED_IP_ADDRRESS, new ImmutablePair<>(16, 32));
}
};
@Override
protected Map<String, Pair<Integer, Integer>> getFieldCoordinates() {
return fieldCoordinates;
}
public int getAddress() {
return BitBufferHelper.getInt(fieldValues.get(OPTION_REQUESTED_IP_ADDRRESS));
}
public RequestedIpAddressOption setAddress(int val) {
byte[] _val = BitBufferHelper.toByteArray(val);
setHeaderField(OPTION_REQUESTED_IP_ADDRRESS, _val);
return this;
}
@Override
public String toString() {
return new StringBuilder()
.append(super.toString())
.append(" ")
.append(NetUtils.getInetAddress(getAddress()).getHostAddress())
.toString();
}
}
/**
* Code = 51
*/
public static class IpAddressLeaseTimeOption extends BootpOption {
private static final String OPTION_LEASETIME = "LeaseTime";
private static final Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
private static final long serialVersionUID = 1L;
{
put(OPTION_CODE, new ImmutablePair<>(0, 8));
put(OPTION_HEADERLENGTH, new ImmutablePair<>(8, 8));
put(OPTION_LEASETIME, new ImmutablePair<>(16, 32));
}
};
@Override
protected Map<String, Pair<Integer, Integer>> getFieldCoordinates() {
return fieldCoordinates;
}
public int getLeaseTime() {
return BitBufferHelper.getInt(fieldValues.get(OPTION_LEASETIME));
}
public IpAddressLeaseTimeOption setLeaseTime(int time) {
byte[] _time = BitBufferHelper.toByteArray(time);
setHeaderField(OPTION_LEASETIME, _time);
return this;
}
@Override
public String toString() {
return new StringBuilder()
.append(super.toString())
.append(" ")
.append(getLeaseTime())
.append(" sec")
.toString();
}
}
/**
* Code = 53
*/
public static class DhcpMessageTypeOption extends BootpOption {
private static final String OPTION_DHCP_MSG_TYPE = "DhcpMessageType";
private static final Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
private static final long serialVersionUID = 1L;
{
put(OPTION_CODE, new ImmutablePair<>(0, 8));
put(OPTION_HEADERLENGTH, new ImmutablePair<>(8, 8));
put(OPTION_DHCP_MSG_TYPE, new ImmutablePair<>(16, 8));
}
};
@Override
protected Map<String, Pair<Integer, Integer>> getFieldCoordinates() {
return fieldCoordinates;
}
public byte getType() {
return BitBufferHelper.getByte(fieldValues.get(OPTION_DHCP_MSG_TYPE));
}
public DhcpMessageTypeOption setType(byte val) {
byte[] _val = BitBufferHelper.toByteArray(val);
setHeaderField(OPTION_DHCP_MSG_TYPE, _val);
return this;
}
@Override
public String toString() {
return new StringBuilder()
.append(super.toString())
.append(" ")
.append(MessageType.valueOf(getType()))
.toString();
}
public static enum MessageType {
DO_NOT_RESPOND(0),
DISCOVER(1),
OFFER(2),
REQUEST(3),
DECLINE(4),
ACK(5),
NAK(6),
RELEASE(7),
INFORM(8),
FORCE_RENEW(9),
LEASE_QUERY(10),
LEASE_UNASSIGNED(11),
LEASE_UNKNOWN(12),
LEASE_ACTIVE(13),
BULK_LEASE_QUERY(14),
LEASE_QUERY_DONE(15),;
public final byte value;
private MessageType(int val) {
value = (byte) val;
}
public static MessageType valueOf(int value) {
return valueOf((byte) (value & 0xFF));
}
public static MessageType valueOf(byte value) {
for (MessageType c : values()) {
if (c.value == value)
return c;
}
return null;
}
public boolean equals(byte value) {
return this.value == value;
}
}
}
/**
* Code = 54
*/
public static class ServerIdentifierOption extends BootpOption {
private static final String OPTION_SRV_IDENT = "ServerIdentifier";
private static final Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
private static final long serialVersionUID = 1L;
{
put(OPTION_CODE, new ImmutablePair<>(0, 8));
put(OPTION_HEADERLENGTH, new ImmutablePair<>(8, 8));
put(OPTION_SRV_IDENT, new ImmutablePair<>(16, 32));
}
};
@Override
protected Map<String, Pair<Integer, Integer>> getFieldCoordinates() {
return fieldCoordinates;
}
public int getServerIdentifier() {
return BitBufferHelper.getInt(fieldValues.get(OPTION_SRV_IDENT));
}
public ServerIdentifierOption setSserverIdentifier(int val) {
byte[] _val = BitBufferHelper.toByteArray(val);
setHeaderField(OPTION_SRV_IDENT, _val);
return this;
}
@Override
public String toString() {
return new StringBuilder()
.append(super.toString())
.append(" ")
.append(NetUtils.getInetAddress(getServerIdentifier()).getHostAddress())
.toString();
}
}
/**
* Code = 55
*/
public static class ParameterListOption extends BootpOption {
private static final String OPTION_PARAM_LIST = "ParameterList";
private static final Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
private static final long serialVersionUID = 1L;
{
put(OPTION_CODE, new ImmutablePair<>(0, 8));
put(OPTION_HEADERLENGTH, new ImmutablePair<>(8, 8));
put(OPTION_PARAM_LIST, new ImmutablePair<>(16, 0));
}
};
@Override
protected Map<String, Pair<Integer, Integer>> getFieldCoordinates() {
return fieldCoordinates;
}
public byte[] getParameterList() {
return fieldValues.get(OPTION_PARAM_LIST);
}
public ParameterListOption setParameterList(byte[] params) {
if (params.length > 255)
throw new RuntimeException("Parameter list has more than 255 fields.");
setHeaderLen((byte) params.length);
setHeaderField(OPTION_PARAM_LIST, params);
return this;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder()
.append(super.toString())
.append(" ");
for (byte para : getParameterList()) {
BootpOptionCode opt = BootpOptionCode.valueOf(para);
sb.append(opt == null ? (int) (para & 0xFF) : opt).append(",");
}
if (sb.charAt(sb.length() - 1) == ',')
sb.deleteCharAt(sb.length() - 1);
return sb.toString();
}
}
/**
* Code = 58
*/
public static class RenewalTimeOption extends BootpOption {
private static final String OPTION_RENEWAL_TIME = "RenewalTime";
private static final Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
private static final long serialVersionUID = 1L;
{
put(OPTION_CODE, new ImmutablePair<>(0, 8));
put(OPTION_HEADERLENGTH, new ImmutablePair<>(8, 8));
put(OPTION_RENEWAL_TIME, new ImmutablePair<>(16, 32));
}
};
@Override
protected Map<String, Pair<Integer, Integer>> getFieldCoordinates() {
return fieldCoordinates;
}
public int getRenewalTime() {
return BitBufferHelper.getInt(fieldValues.get(OPTION_RENEWAL_TIME));
}
public RenewalTimeOption setRenewalTime(int time) {
byte[] _time = BitBufferHelper.toByteArray(time);
setHeaderField(OPTION_RENEWAL_TIME, _time);
return this;
}
@Override
public String toString() {
return new StringBuilder()
.append(super.toString())
.append(" ")
.append(getRenewalTime())
.append(" sec")
.toString();
}
}
/**
* Code = 59
*/
public static class RebindingTimeOption extends BootpOption {
private static final String OPTION_REBINDING_TIME = "RebindingTime";
private static final Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
private static final long serialVersionUID = 1L;
{
put(OPTION_CODE, new ImmutablePair<>(0, 8));
put(OPTION_HEADERLENGTH, new ImmutablePair<>(8, 8));
put(OPTION_REBINDING_TIME, new ImmutablePair<>(16, 32));
}
};
@Override
protected Map<String, Pair<Integer, Integer>> getFieldCoordinates() {
return fieldCoordinates;
}
public int getRebindingTime() {
return BitBufferHelper.getInt(fieldValues.get(OPTION_REBINDING_TIME));
}
public RebindingTimeOption setRebindingTime(int time) {
byte[] _time = BitBufferHelper.toByteArray(time);
setHeaderField(OPTION_REBINDING_TIME, _time);
return this;
}
@Override
public String toString() {
return new StringBuilder()
.append(super.toString())
.append(" ")
.append(getRebindingTime())
.append(" sec")
.toString();
}
}
/**
* Code = 61
*/
public static class ClientIdOption extends BootpOption {
private static final String OPTION_HW_TYPE = "HardwareType";
private static final String OPTION_CLIENT_ID = "ClientIdentifier";
private static final Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
private static final long serialVersionUID = 1L;
{
put(OPTION_CODE, new ImmutablePair<>(0, 8));
put(OPTION_HEADERLENGTH, new ImmutablePair<>(8, 8));
put(OPTION_HW_TYPE, new ImmutablePair<>(16, 8));
put(OPTION_CLIENT_ID, new ImmutablePair<>(24, 0));
}
};
@Override
protected Map<String, Pair<Integer, Integer>> getFieldCoordinates() {
return fieldCoordinates;
}
@Override
public int getfieldnumBits(String fieldName) {
if (OPTION_CLIENT_ID.equals(fieldName))
return (getHeaderLen() - 1) * NetUtils.NumBitsInAByte;
else
return fieldCoordinates.get(fieldName).getRight();
}
public byte getHwType() {
return BitBufferHelper.getByte(fieldValues.get(OPTION_HW_TYPE));
}
public ClientIdOption setHwType(byte type) {
byte[] _type = BitBufferHelper.toByteArray(type);
setHeaderField(OPTION_HW_TYPE, _type);
return this;
}
public byte[] getClientIdentifier() {
return fieldValues.get(OPTION_CLIENT_ID);
}
public ClientIdOption setClientIdentifier(byte[] val) {
if (val.length > 254)
throw new RuntimeException("Client Identifier is longer than 254.");
setHeaderLen((byte) (val.length + 1));
setHeaderField(OPTION_CLIENT_ID, val);
return this;
}
@Override
public String toString() {
return new StringBuilder()
.append(super.toString())
.append(" hardwareType=")
.append(HardwareType.valueOf(getHwType()))
.append(" id=")
.append(HexEncode.bytesToHexStringFormat(getClientIdentifier()))
.toString();
}
}
/**
* Code = 0
*/
public static class PadOption extends BootpOption {
private static final Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
private static final long serialVersionUID = 1L;
{
put(OPTION_CODE, new ImmutablePair<>(0, 8));
}
};
@Override
protected Map<String, Pair<Integer, Integer>> getFieldCoordinates() {
return fieldCoordinates;
}
}
/**
* Code = 255
*/
public static class EndOption extends BootpOption {
private static final Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
private static final long serialVersionUID = 1L;
{
// NOTE for PAD and END option, there is no length field
put(OPTION_CODE, new ImmutablePair<>(0, 8));
}
};
@Override
protected Map<String, Pair<Integer, Integer>> getFieldCoordinates() {
return fieldCoordinates;
}
}
/**
* Base class for all Bootp options
*/
public static class BootpOption extends Packet {
protected static final String OPTION_CODE = "OptionCode";
protected static final String OPTION_HEADERLENGTH = "HeaderLength";
private static final Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
private static final long serialVersionUID = 1L;
{
put(OPTION_CODE, new ImmutablePair<>(0, 8));
put(OPTION_HEADERLENGTH, new ImmutablePair<>(8, 8));
}
};
protected final Map<String, byte[]> fieldValues;
protected final BootpOptionCode optionCode;
protected final boolean isCodeOnlyOption;
/**
* Default constructor that creates and sets the hash map values
*/
public BootpOption() {
super();
fieldValues = new HashMap<>();
optionCode = BootpOptionCode.valueOf(this.getClass());
isCodeOnlyOption = BootpOptionCode.PAD.equals(optionCode) || BootpOptionCode.END.equals(optionCode);
init();
}
/**
* Constructor that sets the access level for the packet
*/
public BootpOption(boolean writeAccess) {
super(writeAccess);
fieldValues = new HashMap<>();
optionCode = BootpOptionCode.valueOf(this.getClass());
isCodeOnlyOption = BootpOptionCode.PAD.equals(optionCode) || BootpOptionCode.END.equals(optionCode);
init();
}
/**
* Init the packet
*/
protected void init() {
hdrFieldCoordMap = getFieldCoordinates();
hdrFieldsMap = fieldValues;
if (optionCode != null) {
setOptCode(optionCode.value);
if (!isCodeOnlyOption)
setHeaderLen(optionCode.defaultLenInByte);
}
}
/**
* Shoule be overridden
*
* @return
*/
protected Map<String, Pair<Integer, Integer>> getFieldCoordinates() {
return fieldCoordinates;
}
@Override
public int getHeaderSize() {
// additional two bytes, one byte for opcode, one byte for length
return isCodeOnlyOption ? NetUtils.NumBitsInAByte
: (getHeaderLen() + 2) * NetUtils.NumBitsInAByte;
}
@Override
public int getfieldnumBits(String fieldName) {
int len = hdrFieldCoordMap.get(fieldName).getRight();
return len != 0 ? len : getHeaderLen() * NetUtils.NumBitsInAByte;
}
@Override
protected void postDeserializeCustomOperation(byte[] data, int startBitOffset)
throws PacketException {
// option code mismatch
if (optionCode != null && data[startBitOffset / NetUtils.NumBitsInAByte] != optionCode.value)
corrupted = true;
}
public byte getHeaderLen() {
return isCodeOnlyOption ? 0 : BitBufferHelper.getByte(fieldValues.get(OPTION_HEADERLENGTH));
}
/**
* Notice change the header length here is very dangerous
* As for some options, their length is quite fixed
*
* @param len
* @param <T>
* @return
*/
protected <T extends BootpOption> T setHeaderLen(byte len) {
if (!isCodeOnlyOption) {
byte[] _len = BitBufferHelper.toByteArray(len);
setHeaderField(OPTION_HEADERLENGTH, _len);
}
return (T) this;
}
public byte getOptCode() {
return BitBufferHelper.getByte(fieldValues.get(OPTION_CODE));
}
/**
* It's unreasonable to change option code, as it will change the class
* of option instance. We leave it for internal use
*
* @param code
* @param <T>
* @return
*/
protected <T extends BootpOption> T setOptCode(byte code) {
byte[] _code = BitBufferHelper.toByteArray(code);
setHeaderField(OPTION_CODE, _code);
return (T) this;
}
@Override
public String toString() {
return new StringBuilder()
.append(BootpOptionCode.valueOf(getOptCode()))
.toString();
}
public static enum BootpOptionCode {
PAD(0, PadOption.class, 0),
SUBNET_MASK(1, SubnetMaskOption.class, 4), // subnet mask option must be inserted before router option
TIME_OFFSET(2),
ROUTER_ADDRESS(3, RouterAddressOption.class, 4),
TIME_SERVER(4),
IEN_116_NAME_SERVER(5),
DOMAIN_NAME_SERVER(6, DomainNameServerOption.class, 0),
LOG_SERVER(7),
QUOTES_SERVER(8),
LPR_SERVER(9),
IMPRESS_SERVER(10),
RLP_SERVER(11),
HOSTNAME(12, HostNameOption.class, 1), // min length is 1
BOOT_FILE_SIZE(13),
MERIT_DUMP_FILE(14),
DOMAIN_NAME(15, DomainNameOption.class, 1), // min length is 1
SWAP_SERVER(16),
ROOT_PATH(17),
BOOTP_EXTENSIONS_PATH(18),
IP_FORWARD_ENABLE(19),
SOURCE_ROUTE_ENABLE(20),
POLICY_FILTER(21),
MAX_DATAGRAM_REASSEMBLY_SZ(22),
DEFAULT_IP_TTL(23),
PATH_MTU_AGING_TIMEOUT(24),
PATH_MTU_PLATEAU_TABLE(25),
INTERFACE_MTU_SIZE(26),
ALL_SUBNETS_ARE_LOCAL(27),
BROADCAST_ADDRESS(28),
PERFORM_MASK_DISCOVERY(29),
PROVIDE_MASK_TO_OTHERS(30),
PERFORM_ROUTER_DISCOVERY(31),
ROUTER_SOLICITATION_ADDRESS(32),
STATIC_ROUTES(33),
TRAILER_ENCAPSULATION(34),
ARP_CACHE_TIMEOUT(35),
ETHERNET_ENCAPSULATION(36),
DEFAULT_TCP_TTL(37),
KEEP_ALIVE_INTERVAL(38),
KEEP_ALIVE_GARBAGE(39),
NIS_DOMAIN_NAME(40),
NIS_SERVERS(41),
NTP_SERVERS(42),
VENDOR(43),
NETBIOS_NAME_SERVERS(44),
NETBIOS_DGM_DIST_SERVERS(45),
NETBIOS_NODE_TYPE(46),
NETBIOS(47),
X_WINDOW_FONT_SERVER(48),
X_WINDOW_DISPLAY_MGR(49),
REQUESTED_IP_ADDRESS(50, RequestedIpAddressOption.class, 4),
IP_ADDRESS_LEASE_TIME(51, IpAddressLeaseTimeOption.class, 4),
OVERLOAD(52),
DHCP_MESSAGE_TYPE(53, DhcpMessageTypeOption.class, 1), // min length = 1
SERVER_IDENTIFIER(54, ServerIdentifierOption.class, 4),
PARAMETER_REQUEST_LIST(55, ParameterListOption.class, 1), // min length = 1
ERROR_MESSAGE(56),
MAXIMUM_DHCP_MSG_SIZE(57),
RENEWAL_TIME(58, RenewalTimeOption.class, 4),
REBINDING_TIME(59, RebindingTimeOption.class, 4),
CLASS_IDENTIFIER(60),
CLIENT_IDENTIFIER(61, ClientIdOption.class, 2), // min length = 2
NETWARE_DOMAIN_NAME(62),
NETWARE_SUB_OPTIONS(63),
NIS_CLIENT_DOMAIN_NAME(64),
NIS_SERVER_ADDRESS(65),
TFTP_SERVER_NAME(66),
BOOT_FILE_NAME(67),
HOME_AGENT_ADDRESS(68),
SMTP_SERVER_ADDRESS(69),
POP3_SERVER_ADDRESS(70),
NNTP_SERVER_ADDRESS(71),
WWW_SERVER_ADDRESS(72),
FINGER_SERVER_ADDRESS(73),
IRC_SERVER_ADDRESS(74),
STREETTALK_SERVER_ADDRESS(75),
STDA_SERVER_ADDRESS(76),
USER_CLASS(77),
DIRECTORY_AGENT(78),
SERVICE_SCOPE(79),
RAPID_COMMIT(80),
CLIENT_FQDN(81),
RELAY_AGENT_INFORMATION(82),
ISNS(83),
NDS_SERVERS(85),
NDS_TREE_NAME(86),
NDS_CONTEXT(87),
AUTHENTICATION(90),
CLIENT_LAST_TXN_TIME(91),
ASSOCIATED_IP(92),
CLIENT_SYSTEM(93),
CLIENT_NDI(94),
LDAP(95),
UUID_GUID(97),
USER_AUTH(98),
NETINFO_ADDRESS(112),
NETINFO_TAG(113),
URL(114),
AUTO_CONFIG(116),
NAME_SERVICE_SEARCH(117),
SUBNET_SELECTION_OPTION(118),
DOMAIN_SEARCH(119),
SIP_SERVERS_DHCP_OPTION(120),
CLASSLESS_STATIC_ROUTE(121),
CCC(122),
GEOCONF_OPTION(123),
V_I_VENDOR_CLASS(124),
V_I_VENDOR_SPECIFIC(125),
ETHERBOOT(128),
TFTP_SERVER_IP_ADDRESS(128),
CALL_SERVER_IP_ADDRESS(129),
ETHERNET_INTERFACE(130),
VENDOR_DISCRIMINATION_STR(130),
REMOTE_STATS_SVR_IP_ADDRESS(131),
IEEE_802_1P_VLAN_ID(132),
IEEE_802_1Q_L2_PRIORITY(133),
DIFFSERV_CODE_POINT(134),
HTTP_PROXY(135),
MICROSOFT_CLASSLESS_STATIC_ROUTE(249),
WEB_PROXY_AUTO_DISCOVERY(252),
END(255, EndOption.class, 0),;
public final byte value;
public final Class<? extends BootpOption> Clazz;
public final byte defaultLenInByte;
private BootpOptionCode(int val) {
value = (byte) val;
Clazz = null;
defaultLenInByte = 0;
}
private BootpOptionCode(int val, Class<? extends BootpOption> clazz, int len) {
value = (byte) val;
Clazz = clazz;
defaultLenInByte = (byte) (len & 0xFF);
}
public static BootpOptionCode valueOf(int value) {
return valueOf((byte) (value & 0xFF));
}
public static BootpOptionCode valueOf(byte value) {
for (BootpOptionCode c : values()) {
if (c.value == value)
return c;
}
return null;
}
public static BootpOptionCode valueOf(Class<? extends BootpOption> clazz) {
for (BootpOptionCode c : values()) {
if (c.Clazz == clazz)
return c;
}
return null;
}
public boolean equals(byte value) {
return this.value == value;
}
}
}
}
package com.kevinxw.net.packet;
import org.junit.Ignore;
import org.junit.Test;
import org.opendaylight.controller.sal.packet.Ethernet;
import org.opendaylight.controller.sal.packet.IPv4;
import org.opendaylight.controller.sal.packet.Packet;
import org.opendaylight.controller.sal.packet.UDP;
import org.opendaylight.controller.sal.utils.HexEncode;
import org.opendaylight.controller.sal.utils.NetUtils;
import java.util.ArrayList;
import static org.junit.Assert.*;
public class BOOTPTest {
/**
* Convert hex string to byte array
*
* @param s
* @return
*/
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i + 1), 16));
}
return data;
}
/**
* Create a DHCP payload. Notice this is payload merely, ethernet layer
* and ip layer are not included
*
* @param msgType Merely DHCP Discover, Request, Offer, ACK are supported
* @param txId
* @param clientHwAddr
* @return
*/
public static BOOTP createDhcpPayload(BOOTP.DhcpMessageTypeOption.MessageType msgType,
int txId, byte[] clientHwAddr, String hostName) {
BOOTP bootp = new BOOTP();
bootp.setOpCode(BOOTP.OpCode.BOOT_REQUEST.value);
bootp.setHwType(BOOTP.HardwareType.ETHERNET.value);
bootp.setHwAddrLength((byte) 6); // MAC address length
bootp.setTxId(txId);
bootp.setHopCount((byte) 0);
bootp.setNumOfSec((byte) 0);
bootp.setFlags(BOOTP.FlagType.UNICAST.value);
bootp.setClientIpAddr(0);
bootp.setSrvIpAddr(0);
bootp.setGwIpAddr(0);
// we do the client hardware address padding here
// this field requires 16 bytes, while normally ethernet addr has
// only 6 bytes
if (clientHwAddr.length < 16) {
byte[] tmpHwAddr = clientHwAddr;
clientHwAddr = new byte[16];
int i = 0;
for (; i < tmpHwAddr.length; i++)
clientHwAddr[i] = tmpHwAddr[i];
for (; i < 16; i++)
clientHwAddr[i] = 0;
}
bootp.setClientHwAddr(clientHwAddr);
bootp.setMagicCookie(BOOTP.BootpType.DHCP.value);
// DHCP message option
BOOTP.DhcpMessageTypeOption msgTypeOption = new BOOTP.DhcpMessageTypeOption();
msgTypeOption.setType(msgType.value);
bootp.insertOption(msgTypeOption);
// DHCP client identifier option
BOOTP.ClientIdOption clientIdOption = new BOOTP.ClientIdOption();
clientIdOption.setHwType(BOOTP.HardwareType.ETHERNET.value);
clientIdOption.setClientIdentifier(clientHwAddr);
bootp.insertOption(clientIdOption);
// DHCP requested IP address
BOOTP.RequestedIpAddressOption requestedIpAddressOption = new BOOTP.RequestedIpAddressOption();
requestedIpAddressOption.setAddress(0);
bootp.insertOption(requestedIpAddressOption);
// Parameter list option
BOOTP.ParameterListOption parameterListOption = new BOOTP.ParameterListOption();
parameterListOption.setParameterList(new byte[]{
BOOTP.BootpOption.BootpOptionCode.SUBNET_MASK.value,
BOOTP.BootpOption.BootpOptionCode.ROUTER_ADDRESS.value,
BOOTP.BootpOption.BootpOptionCode.DOMAIN_NAME_SERVER.value,
BOOTP.BootpOption.BootpOptionCode.NETBIOS_NAME_SERVERS.value,
BOOTP.BootpOption.BootpOptionCode.NETBIOS_NODE_TYPE.value,
BOOTP.BootpOption.BootpOptionCode.PERFORM_ROUTER_DISCOVERY.value,
BOOTP.BootpOption.BootpOptionCode.STATIC_ROUTES.value,
BOOTP.BootpOption.BootpOptionCode.CLASSLESS_STATIC_ROUTE.value,
});
bootp.insertOption(parameterListOption);
bootp.insertOption(new BOOTP.EndOption());
return bootp;
}
@Test
@Ignore
public void testToString() throws Exception {
final byte[] pkt = hexStringToByteArray("0101060000003d1e00000000000000000000000000" +
"00000000000000000b8201fc420000000000000000" +
"000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000" +
"0000000000638253633501033d0701000b8201fc42" +
"3204c0a8000a3604c0a8000137040103062aff00"
);
BOOTP bootp = new BOOTP();
bootp.deserialize(pkt, 0, pkt.length * NetUtils.NumBitsInAByte);
System.out.println(bootp.toString());
}
@Test
public void testSetOpCode() throws Exception {
BOOTP bootp = new BOOTP();
final byte val = BOOTP.OpCode.BOOT_REQUEST.value; // DHCPREQUEST
bootp.setOpCode(val);
assertEquals(val, bootp.getOpCode());
}
@Test
public void testSetHwType() throws Exception {
BOOTP bootp = new BOOTP();
final byte val = BOOTP.HardwareType.ETHERNET.value; // ETHERNET
bootp.setHwType(val);
assertEquals(val, bootp.getHwType());
}
@Test
public void testSetHwAddrLength() throws Exception {
BOOTP bootp = new BOOTP();
final byte val = NetUtils.MACAddrLengthInBytes; // ETH ADDR
bootp.setHwAddrLength(val);
assertEquals(val, bootp.getHwAddrLength());
}
@Test
public void testSetHopCount() throws Exception {
BOOTP bootp = new BOOTP();
final byte val = 3; // HOP COUNT
bootp.setHopCount(val);
assertEquals(val, bootp.getHopCount());
}
@Test
public void testSetTxId() throws Exception {
BOOTP bootp = new BOOTP();
final int val = 0x3903F326; // TRANSACTION ID
bootp.setTxId(val);
assertEquals(val, bootp.getTxId());
}
@Test
public void testSetNumOfSec() throws Exception {
BOOTP bootp = new BOOTP();
final short val = 4; // NORMALLY IT SHOULD BE ZERO, BUT FOR TESTING HERE
bootp.setNumOfSec(val);
assertEquals(val, bootp.getNumOfSec());
}
@Test
public void testSetFlags() throws Exception {
BOOTP bootp = new BOOTP();
final short val = BOOTP.FlagType.BROADCAST.value; // NORMALLY IT SHOULD BE ZERO, BUT FOR TESTING HERE
bootp.setFlags(val);
assertEquals(val, bootp.getFlags());
final short val2 = BOOTP.FlagType.UNICAST.value;
bootp.setFlags(val2);
assertEquals(val2, bootp.getFlags());
}
@Test
public void testSetClientIpAddr() throws Exception {
BOOTP bootp = new BOOTP();
final int val = 0xC0A80164; // CLIENT IP
bootp.setClientIpAddr(val);
assertEquals(val, bootp.getClientIpAddr());
}
@Test
public void testSetYourIpAddr() throws Exception {
BOOTP bootp = new BOOTP();
final int val = 0xC0A80102; // YOUR IP
bootp.setYourIpAddr(val);
assertEquals(val, bootp.getYourIpAddr());
}
@Test
public void testSetSrvIpAddr() throws Exception {
BOOTP bootp = new BOOTP();
final int val = 0x8D590000; // SERVER IP
bootp.setSrvIpAddr(val);
assertEquals(val, bootp.getSrvIpAddr());
}
@Test
public void testSetGwIpAddr() throws Exception {
BOOTP bootp = new BOOTP();
final int val = 0xC0A80101; // GATEWAY IP
bootp.setGwIpAddr(val);
assertEquals(val, bootp.getGwIpAddr());
}
@Test
public void testSetClientHwAddr() throws Exception {
BOOTP bootp = new BOOTP();
final byte[] val = new byte[]{(byte) 0x98, (byte) 0xfc,
(byte) 0x11, (byte) 0x93, (byte) 0x5c, (byte) 0xb8}; // MAGIC COOKIE
bootp.setClientHwAddr(val);
assertArrayEquals(val, bootp.getClientHwAddr());
}
@Test
public void testSetMagicCookie() throws Exception {
BOOTP bootp = new BOOTP();
final int val = 0x63825363, val2 = 0x0; // MAGIC COOKIE
assertEquals(val, BOOTP.BootpType.DHCP.value); // we are using DHCP's magic cookie
bootp.setMagicCookie(val);
assertEquals(val, bootp.getMagicCookie());
assertEquals(BOOTP.BootpType.DHCP, bootp.getBootpType());
// test special case for DHCP
bootp.setMagicCookie(val2);
assertEquals(val2, bootp.getMagicCookie());
assertNotEquals(BOOTP.BootpType.DHCP, bootp.getBootpType());
}
@Test
public void testSubnetMaskOption() throws Exception {
final byte[] rawOpt = hexStringToByteArray("0104ffffff00");
BOOTP.SubnetMaskOption option = new BOOTP.SubnetMaskOption();
option.deserialize(rawOpt, 0, rawOpt.length * NetUtils.NumBitsInAByte);
assertEquals(false, option.isCorrupted());
assertArrayEquals(new byte[]{(byte) 255, (byte) 255, (byte) 255, (byte) 0}, NetUtils.intToByteArray4(option.getSubnetMask()));
assertEquals(4, option.getHeaderLen());
}
@Test
public void testRouterAddressOption() throws Exception {
}
@Test
public void testDomainNameServerOption() throws Exception {
final byte[] rawOpt = hexStringToByteArray("3204c0a8000a");
BOOTP.RequestedIpAddressOption option = new BOOTP.RequestedIpAddressOption();
option.deserialize(rawOpt, 0, rawOpt.length * NetUtils.NumBitsInAByte);
assertEquals(false, option.isCorrupted());
assertArrayEquals(new byte[]{(byte) 192, (byte) 168, 0, (byte) 10}, NetUtils.intToByteArray4(option.getAddress()));
assertEquals(4, option.getHeaderLen());
}
@Test
public void testHostNameOption() throws Exception {
}
@Test
public void testDomainNameOption() throws Exception {
}
@Test
public void testRequestedIpAddressRequestOption() throws Exception {
final byte[] rawOpt = hexStringToByteArray("3204c0a8000a");
BOOTP.RequestedIpAddressOption option = new BOOTP.RequestedIpAddressOption();
option.deserialize(rawOpt, 0, rawOpt.length * NetUtils.NumBitsInAByte);
assertEquals(false, option.isCorrupted());
assertArrayEquals(new byte[]{(byte) 192, (byte) 168, 0, (byte) 10}, NetUtils.intToByteArray4(option.getAddress()));
assertEquals(4, option.getHeaderLen());
}
@Test
public void testIpAddressLeaseTimeOption() throws Exception {
final byte[] rawOpt = hexStringToByteArray("330400000e10");
BOOTP.IpAddressLeaseTimeOption option = new BOOTP.IpAddressLeaseTimeOption();
option.deserialize(rawOpt, 0, rawOpt.length * NetUtils.NumBitsInAByte);
assertEquals(false, option.isCorrupted());
assertEquals(3600, option.getLeaseTime());
assertEquals(4, option.getHeaderLen());
}
@Test
public void testDhcpMessageOption() throws Exception {
final byte[] rawOpt = hexStringToByteArray("350103");
BOOTP.DhcpMessageTypeOption option = new BOOTP.DhcpMessageTypeOption();
option.deserialize(rawOpt, 0, rawOpt.length * NetUtils.NumBitsInAByte);
assertEquals(false, option.isCorrupted());
assertEquals(BOOTP.DhcpMessageTypeOption.MessageType.REQUEST.value, option.getType());
assertEquals(1, option.getHeaderLen());
}
@Test
public void testServerIdentifierOption() throws Exception {
final byte[] rawOpt = hexStringToByteArray("3604c0a80001");
BOOTP.ServerIdentifierOption option = new BOOTP.ServerIdentifierOption();
option.deserialize(rawOpt, 0, rawOpt.length * NetUtils.NumBitsInAByte);
assertEquals(false, option.isCorrupted());
assertArrayEquals(new byte[]{(byte) 192, (byte) 168, 0, (byte) 1}, NetUtils.intToByteArray4(option.getServerIdentifier()));
assertEquals(4, option.getHeaderLen());
}
@Test
public void testParameterListOption() throws Exception {
final byte[] rawOpt = hexStringToByteArray("37040103062a");
BOOTP.ParameterListOption option = new BOOTP.ParameterListOption();
option.deserialize(rawOpt, 0, rawOpt.length * NetUtils.NumBitsInAByte);
assertEquals(false, option.isCorrupted());
byte[] list = new byte[]{
BOOTP.BootpOption.BootpOptionCode.SUBNET_MASK.value,
BOOTP.BootpOption.BootpOptionCode.ROUTER_ADDRESS.value,
BOOTP.BootpOption.BootpOptionCode.DOMAIN_NAME_SERVER.value,
BOOTP.BootpOption.BootpOptionCode.NTP_SERVERS.value
};
assertArrayEquals(
list, option.getParameterList()
);
assertEquals(4, option.getHeaderLen());
}
@Test
public void testRenewalTimeOption() throws Exception {
final byte[] rawOpt = hexStringToByteArray("3a0400000708");
BOOTP.RenewalTimeOption option = new BOOTP.RenewalTimeOption();
option.deserialize(rawOpt, 0, rawOpt.length * NetUtils.NumBitsInAByte);
assertEquals(false, option.isCorrupted());
assertEquals(1800, option.getRenewalTime());
assertEquals(4, option.getHeaderLen());
}
@Test
public void testRebindingTimeOption() throws Exception {
final byte[] rawOpt = hexStringToByteArray("3b0400000c4e");
BOOTP.RebindingTimeOption option = new BOOTP.RebindingTimeOption();
option.deserialize(rawOpt, 0, rawOpt.length * NetUtils.NumBitsInAByte);
assertEquals(false, option.isCorrupted());
assertEquals(3150, option.getRebindingTime());
assertEquals(4, option.getHeaderLen());
}
@Test
public void testClientIdOption() throws Exception {
final byte[] rawOpt = hexStringToByteArray("3d0701000b8201fc42");
BOOTP.ClientIdOption option = new BOOTP.ClientIdOption();
option.deserialize(rawOpt, 0, rawOpt.length * NetUtils.NumBitsInAByte);
assertEquals(false, option.isCorrupted());
assertEquals(BOOTP.HardwareType.ETHERNET.value, option.getHwType());
assertEquals(7, option.getHeaderLen());
assertArrayEquals(HexEncode.bytesFromHexString("00:0b:82:01:fc:42"), option.getClientIdentifier());
}
@Test
public void testPadOptionAndEndOption() throws Exception {
// test PAD
final byte[] rawPadOpt = hexStringToByteArray("00");
BOOTP.PadOption padOption = new BOOTP.PadOption();
padOption.deserialize(rawPadOpt, 0, rawPadOpt.length * NetUtils.NumBitsInAByte);
assertEquals(false, padOption.isCorrupted());
assertEquals(0, padOption.getHeaderLen());
assertArrayEquals(new byte[]{0}, padOption.serialize());
// test END
final byte[] rawEndOpt = hexStringToByteArray("ff");
BOOTP.EndOption endOption = new BOOTP.EndOption();
endOption.deserialize(rawEndOpt, 0, rawEndOpt.length * NetUtils.NumBitsInAByte);
assertEquals(false, endOption.isCorrupted());
assertEquals(0, endOption.getHeaderLen());
}
/**
* Here we test a corrupted option
*
* @throws Exception
*/
@Test
public void testCorruptedOption() throws Exception {
// we use option code 54 instead of 53 here
final byte[] rawOpt = hexStringToByteArray("360103");
BOOTP.DhcpMessageTypeOption option = new BOOTP.DhcpMessageTypeOption();
option.deserialize(rawOpt, 0, rawOpt.length * NetUtils.NumBitsInAByte);
assertEquals(true, option.isCorrupted());
}
@Test
public void testFullDHCP() throws Exception {
// DHCP Request packet
final byte[] pkt = hexStringToByteArray("ffffffffffff000b8201fc4208004" +
"500012ca8370000fa11178a000000" +
"00ffffffff0044004301189fbd010" +
"1060000003d1e0000000000000000" +
"000000000000000000000000000b8" +
"201fc420000000000000000000000" +
"00000000000000000000000000000" +
"00000000000000000000000000000" +
"00000000000000000000000000000" +
"00000000000000000000000000000" +
"00000000000000000000000000000" +
"00000000000000000000000000000" +
"00000000000000000000000000000" +
"00000000000000000000000000000" +
"00000000000000000000000000000" +
"00000000000000000000000000000" +
"00000000000000000000000000000" +
"00000000000000000000000000000" +
"00000000000000000000000000000" +
"00000638253633501033d0701000b" +
"8201fc423204c0a8000a3604c0a80" +
"00137040103062aff00"
);
// verify l2 payload
Ethernet ethPkt = new Ethernet();
ethPkt.deserialize(pkt, 0, pkt.length * NetUtils.NumBitsInAByte);
// verify l3 payload
Packet ipPkt = ethPkt.getPayload();
assertTrue(ipPkt instanceof IPv4);
// verify l4 payload
Packet udpPkt = ipPkt.getPayload();
assertTrue(udpPkt instanceof UDP);
// everything looks good by far, now read DHCP payload
byte[] rawPkt = udpPkt.getRawPayload();
BOOTP bootp = new BOOTP();
bootp.deserialize(rawPkt, 0, rawPkt.length * NetUtils.NumBitsInAByte);
// verify DHCP fields
assertEquals(BOOTP.OpCode.BOOT_REQUEST.value, bootp.getOpCode());
assertEquals(BOOTP.HardwareType.ETHERNET.value, bootp.getHwType());
assertEquals(NetUtils.MACAddrLengthInBytes, bootp.getHwAddrLength());
assertEquals(0, bootp.getHopCount());
assertEquals(0x3d1e, bootp.getTxId());
assertEquals(0, bootp.getNumOfSec());
assertEquals(BOOTP.FlagType.UNICAST.value, bootp.getFlags());
assertArrayEquals(new byte[]{0, 0, 0, 0}, NetUtils.intToByteArray4(bootp.getClientIpAddr()));
assertArrayEquals(new byte[]{0, 0, 0, 0}, NetUtils.intToByteArray4(bootp.getYourIpAddr()));
assertArrayEquals(new byte[]{0, 0, 0, 0}, NetUtils.intToByteArray4(bootp.getSrvIpAddr()));
assertArrayEquals(new byte[]{0, 0, 0, 0}, NetUtils.intToByteArray4(bootp.getGwIpAddr()));
assertArrayEquals(new byte[]{0, (byte) 0xb, (byte) 0x82, (byte) 0x1, (byte) 0xfc, (byte) 0x42}, bootp.getClientHwAddr());
assertEquals(BOOTP.BootpType.DHCP, bootp.getBootpType());
// now verify DHCP options
assertEquals("35:01:03:3d:07:01:00:0b:82:01:fc:42:32:04:c0:a8:00:0a:36:04:c0:a8:00:01:37:04:01:03:06:2a:ff:00", HexEncode.bytesToHexStringFormat(bootp.getOptions()));
BOOTP.BootpOption[] opts = bootp.getOptionsAsArray();
ArrayList<Byte> optCodes = new ArrayList<>();
for (int i = 0; i < opts.length; i++)
optCodes.add(opts[i].getOptCode());
assertArrayEquals(new Byte[]{53, 61, 50, 54, 55, -1, 0}, optCodes.toArray());
}
@Test
public void testFullDHCP_Request() throws Exception {
// DHCP Request packet, DHCP payload only
final byte[] pkt = hexStringToByteArray("0101060000003d1e00000000000000000000000000" +
"00000000000000000b8201fc420000000000000000" +
"000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000" +
"000000000000000000000000000000000000000000" +
"0000000000638253633501033d0701000b8201fc42" +
"3204c0a8000a3604c0a8000137040103062aff00"
);
BOOTP bootp = new BOOTP();
bootp.deserialize(pkt, 0, pkt.length * NetUtils.NumBitsInAByte);
// now verify basic DHCP fields
assertEquals(BOOTP.OpCode.BOOT_REQUEST.value, bootp.getOpCode());
assertEquals(BOOTP.HardwareType.ETHERNET.value, bootp.getHwType());
assertEquals(NetUtils.MACAddrLengthInBytes, bootp.getHwAddrLength());
assertEquals(0, bootp.getHopCount());
assertEquals(0x3d1e, bootp.getTxId());
assertEquals(0, bootp.getNumOfSec());
assertEquals(BOOTP.FlagType.UNICAST.value, bootp.getFlags());
assertArrayEquals(new byte[]{0, 0, 0, 0}, NetUtils.intToByteArray4(bootp.getClientIpAddr()));
assertArrayEquals(new byte[]{0, 0, 0, 0}, NetUtils.intToByteArray4(bootp.getYourIpAddr()));
assertArrayEquals(new byte[]{0, 0, 0, 0}, NetUtils.intToByteArray4(bootp.getSrvIpAddr()));
assertArrayEquals(new byte[]{0, 0, 0, 0}, NetUtils.intToByteArray4(bootp.getGwIpAddr()));
assertArrayEquals(new byte[]{0, (byte) 0xb, (byte) 0x82, (byte) 0x1, (byte) 0xfc, (byte) 0x42}, bootp.getClientHwAddr());
assertEquals(BOOTP.BootpType.DHCP, bootp.getBootpType());
// verify DHCP options
assertEquals("35:01:03:3d:07:01:00:0b:82:01:fc:42:32:04:c0:a8:00:0a:36:04:c0:a8:00:01:37:04:01:03:06:2a:ff:00", HexEncode.bytesToHexStringFormat(bootp.getOptions()));
BOOTP.BootpOption[] opts = bootp.getOptionsAsArray();
ArrayList<Byte> optCodes = new ArrayList<>();
for (int i = 0; i < opts.length; i++)
optCodes.add(opts[i].getOptCode());
assertArrayEquals(new Byte[]{53, 61, 50, 54, 55, -1, 0}, optCodes.toArray());
}
@Test
public void testFullDHCP_Discover() throws Exception {
// DHCP Discover packet, DHCP payload merely
final byte[] pkt = hexStringToByteArray("0101060000003d1d0000000000000000000000000000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501013d0701000b8201fc4232040000000037040103062aff00000000000000");
BOOTP bootp = new BOOTP();
bootp.deserialize(pkt, 0, pkt.length * NetUtils.NumBitsInAByte);
// now verify basic DHCP fields
assertEquals(BOOTP.OpCode.BOOT_REQUEST.value, bootp.getOpCode());
assertEquals(BOOTP.HardwareType.ETHERNET.value, bootp.getHwType());
assertEquals(NetUtils.MACAddrLengthInBytes, bootp.getHwAddrLength());
assertEquals(0, bootp.getHopCount());
assertEquals(0x3d1d, bootp.getTxId());
assertEquals(0, bootp.getNumOfSec());
assertEquals(BOOTP.FlagType.UNICAST.value, bootp.getFlags());
assertArrayEquals(new byte[]{0, 0, 0, 0}, NetUtils.intToByteArray4(bootp.getClientIpAddr()));
assertArrayEquals(new byte[]{0, 0, 0, 0}, NetUtils.intToByteArray4(bootp.getYourIpAddr()));
assertArrayEquals(new byte[]{0, 0, 0, 0}, NetUtils.intToByteArray4(bootp.getSrvIpAddr()));
assertArrayEquals(new byte[]{0, 0, 0, 0}, NetUtils.intToByteArray4(bootp.getGwIpAddr()));
assertArrayEquals(new byte[]{0, (byte) 0xb, (byte) 0x82, (byte) 0x1, (byte) 0xfc, (byte) 0x42}, bootp.getClientHwAddr());
assertEquals(BOOTP.BootpType.DHCP, bootp.getBootpType());
}
@Test
public void testFullDHCP_Offer() throws Exception {
// DHCP Offer packet, DHCP payload merely
final byte[] pkt = hexStringToByteArray("0201060000003d1d0000000000000000c0a8000ac0a8000100000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501020104ffffff003a04000007083b0400000c4e330400000e103604c0a80001ff0000000000000000000000000000000000000000000000000000");
BOOTP bootp = new BOOTP();
bootp.deserialize(pkt, 0, pkt.length * NetUtils.NumBitsInAByte);
// now verify basic DHCP fields
assertEquals(BOOTP.OpCode.BOOT_REPLY.value, bootp.getOpCode());
assertEquals(BOOTP.HardwareType.ETHERNET.value, bootp.getHwType());
assertEquals(NetUtils.MACAddrLengthInBytes, bootp.getHwAddrLength());
assertEquals(0, bootp.getHopCount());
assertEquals(0x3d1d, bootp.getTxId());
assertEquals(0, bootp.getNumOfSec());
assertEquals(BOOTP.FlagType.UNICAST.value, bootp.getFlags());
assertArrayEquals(new byte[]{0, 0, 0, 0}, NetUtils.intToByteArray4(bootp.getClientIpAddr()));
assertArrayEquals(new byte[]{(byte) 192, (byte) 168, 0, (byte) 10}, NetUtils.intToByteArray4(bootp.getYourIpAddr()));
assertArrayEquals(new byte[]{(byte) 192, (byte) 168, 0, (byte) 1}, NetUtils.intToByteArray4(bootp.getSrvIpAddr()));
assertArrayEquals(new byte[]{0, 0, 0, 0}, NetUtils.intToByteArray4(bootp.getGwIpAddr()));
assertArrayEquals(new byte[]{0, (byte) 0xb, (byte) 0x82, (byte) 0x1, (byte) 0xfc, (byte) 0x42}, bootp.getClientHwAddr());
assertEquals(BOOTP.BootpType.DHCP, bootp.getBootpType());
}
@Test
public void testFullDHCP_Ack() throws Exception {
// DHCP Ack packet, DHCP payload merely
final byte[] pkt = hexStringToByteArray("0201060000003d1e0000000000000000c0a8000a0000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501053a04000007083b0400000c4e330400000e103604c0a800010104ffffff00ff0000000000000000000000000000000000000000000000000000");
BOOTP bootp = new BOOTP();
bootp.deserialize(pkt, 0, pkt.length * NetUtils.NumBitsInAByte);
// now verify basic DHCP fields
assertEquals(BOOTP.OpCode.BOOT_REPLY.value, bootp.getOpCode());
assertEquals(BOOTP.HardwareType.ETHERNET.value, bootp.getHwType());
assertEquals(NetUtils.MACAddrLengthInBytes, bootp.getHwAddrLength());
assertEquals(0, bootp.getHopCount());
assertEquals(0x3d1e, bootp.getTxId());
assertEquals(0, bootp.getNumOfSec());
assertEquals(BOOTP.FlagType.UNICAST.value, bootp.getFlags());
assertArrayEquals(new byte[]{0, 0, 0, 0}, NetUtils.intToByteArray4(bootp.getClientIpAddr()));
assertArrayEquals(new byte[]{(byte) 192, (byte) 168, 0, (byte) 10}, NetUtils.intToByteArray4(bootp.getYourIpAddr()));
assertArrayEquals(new byte[]{0, 0, 0, 0}, NetUtils.intToByteArray4(bootp.getSrvIpAddr()));
assertArrayEquals(new byte[]{0, 0, 0, 0}, NetUtils.intToByteArray4(bootp.getGwIpAddr()));
assertArrayEquals(new byte[]{0, (byte) 0xb, (byte) 0x82, (byte) 0x1, (byte) 0xfc, (byte) 0x42}, bootp.getClientHwAddr());
assertEquals(BOOTP.BootpType.DHCP, bootp.getBootpType());
}
@Test
public void testFullDHCP2() throws Exception {
final byte[] pkt = HexEncode.bytesFromHexString("02:01:06:00:76:0a:bf:d5:00:00:00:00:00:00:00:00:0a:0a:01:22:00:00:00:00:00:00:00:00:3c:97:0e:dc:db:23:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:63:82:53:63:35:01:05:3a:04:00:05:46:00:3b:04:00:09:3a:80:33:04:00:0a:8c:00:36:04:0a:0a:00:0a:01:04:ff:ff:00:00:51:03:00:ff:ff:0f:0d:7a:61:6e:74:74:7a:2e:6c:6f:63:61:6c:00:03:04:0a:0a:00:01:06:08:0a:0a:00:0b:0a:0a:00:01:2c:04:0a:0a:00:0b:2e:01:08:ff");
BOOTP bootp = new BOOTP();
bootp.deserialize(pkt, 0, pkt.length * NetUtils.NumBitsInAByte);
assertEquals(BOOTP.BootpType.DHCP, bootp.getBootpType());
BOOTP.BootpOption[] options = bootp.getOptionsAsArray();
for (BOOTP.BootpOption option : options) {
if (BOOTP.BootpOption.BootpOptionCode.DHCP_MESSAGE_TYPE.equals(option.getOptCode())) {
assertTrue(BOOTP.DhcpMessageTypeOption.MessageType.ACK.equals(((BOOTP.DhcpMessageTypeOption) option).getType()));
}
}
}
@Test
public void testSerialize() throws Exception {
// DHCP Ack packet, DHCP payload merely
final byte[] pkt = hexStringToByteArray("0201060000003d1e0000000000000000c0a8000a0000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501053a04000007083b0400000c4e330400000e103604c0a800010104ffffff00ff0000000000000000000000000000000000000000000000000000");
BOOTP bootp = new BOOTP();
bootp.deserialize(pkt, 0, pkt.length * NetUtils.NumBitsInAByte);
assertArrayEquals(pkt, bootp.serialize());
}
@Test
public void testRemoveOption() throws Exception {
// DHCP Ack packet, DHCP payload merely
final byte[] pkt = hexStringToByteArray("0201060000003d1e0000000000000000c0a8000a0000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501053a04000007083b0400000c4e330400000e103604c0a800010104ffffff00ff0000000000000000000000000000000000000000000000000000");
final byte[] newPkt = hexStringToByteArray("0201060000003d1e0000000000000000c0a8000a0000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501053a04000007083b0400000c4e3604c0a800010104ffffff00ff0000000000000000000000000000000000000000000000000000");
BOOTP bootp = new BOOTP();
bootp.deserialize(pkt, 0, pkt.length * NetUtils.NumBitsInAByte);
bootp.removeOption((byte) 51); // we remove the IP address lease time option here
assertArrayEquals(newPkt, bootp.serialize());
}
@Test
public void testInsertOption() throws Exception {
// DHCP Ack packet, DHCP payload merely
final byte[] pkt = hexStringToByteArray("0201060000003d1e0000000000000000c0a8000a0000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501053a04000007083b0400000c4e330400000e103604c0a800010104ffffff00ff0000000000000000000000000000000000000000000000000000");
final byte[] newPkt = hexStringToByteArray("0201060000003d1e0000000000000000c0a8000a0000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501053a04000007083b0400000c4e330400000e103604c0a800010104ffffff003d0701000b8201fc42ff0000000000000000000000000000000000000000000000000000");
BOOTP bootp = new BOOTP();
bootp.deserialize(pkt, 0, pkt.length * NetUtils.NumBitsInAByte);
// a new client identifier option
final byte[] rawOpt = hexStringToByteArray("3d0701000b8201fc42");
BOOTP.ClientIdOption option = new BOOTP.ClientIdOption();
option.deserialize(rawOpt, 0, rawOpt.length * NetUtils.NumBitsInAByte);
// add to bootp
bootp.insertOption(option);
assertArrayEquals(newPkt, bootp.serialize());
}
}
package com.kevinxw.net.packet;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.opendaylight.controller.sal.packet.BitBufferHelper;
import org.opendaylight.controller.sal.packet.BufferException;
import org.opendaylight.controller.sal.packet.Packet;
import org.opendaylight.controller.sal.packet.PacketException;
import org.opendaylight.controller.sal.utils.HexEncode;
import org.opendaylight.controller.sal.utils.NetUtils;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Here we defines a DNS packet
* see http://www.tcpipguide.com/free/t_DNSMessageHeaderandQuestionSectionFormat.htm for reference
* also see http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
* http://tools.ietf.org/html/rfc2929
* <p/>
* Created by kevin on 6/24/14.
*/
public class DNS extends Packet {
public static final int HEADER_BIT_LENGTH = 96; // The header length (exclude record section) in bit
private static final String TXID = "TransactionId";
private static final String QRFLAG = "QRFlag";
private static final String OPCODE = "OpCode";
private static final String AUTHORITATIVE_ANSWER = "AuthoritativeAnswer";
private static final String TRUNCATION = "Truncation";
private static final String RECURSION_DESIRED = "RecursionDesired";
private static final String RECURSION_AVAILABLE = "RecursionAvailable";
private static final String Z = "Reserved";
private static final String RESPONSE_CODE = "ResponseCode";
private static final String QRCOUNT = "QueryCount";
private static final String ANSCOUNT = "AnswerRecordCount";
private static final String NSCOUNT = "AuthorityRecordCount";
private static final String ARCOUNT = "AdditionalRecordCount";
private static final String RECORD_DATA = "RecordData";
private static final Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
private static final long serialVersionUID = 1L;
{
// transaction ID
put(TXID, new ImmutablePair<>(0, 16));
// flags
put(QRFLAG, new ImmutablePair<>(16, 1));
put(OPCODE, new ImmutablePair<>(17, 4));
put(AUTHORITATIVE_ANSWER, new ImmutablePair<>(21, 1));
put(TRUNCATION, new ImmutablePair<>(22, 1));
put(RECURSION_DESIRED, new ImmutablePair<>(23, 1));
put(RECURSION_AVAILABLE, new ImmutablePair<>(24, 1));
put(Z, new ImmutablePair<>(25, 3));
put(RESPONSE_CODE, new ImmutablePair<>(28, 4));
// following several counts
put(QRCOUNT, new ImmutablePair<>(32, 16));
put(ANSCOUNT, new ImmutablePair<>(48, 16));
put(NSCOUNT, new ImmutablePair<>(64, 16));
put(ARCOUNT, new ImmutablePair<>(80, 16));
put(RECORD_DATA, new ImmutablePair<>(96, 0));
}
};
private final Map<String, byte[]> fieldValues = new HashMap<>();
// a domain name map indexed by offset, the value is domain name string section (content between dot and dot)
private final Map<Integer, String> stringDic = new HashMap<>();
private Question[] questions = null;
private ResourceRecord[] answerRRs = null;
private ResourceRecord[] authorityRRs = null;
private ResourceRecord[] additionalRRs = null;
/**
* Default constructor that creates and sets the hash map values
*/
public DNS() {
super();
init();
}
/**
* Constructor that sets the access level for the packet
*/
public DNS(boolean writeAccess) {
super(writeAccess);
init();
}
public static byte[] getDomainBytes(String str) {
String[] sections = str.split("\\.");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < sections.length; i++) {
if (sections[i].length() > 255)
throw new RuntimeException("Invalid domain string");
sb.append(Character.toChars(sections[i].length())).append(sections[i]);
}
sb.append("\0");
return sb.toString().getBytes();
}
private void init() {
hdrFieldCoordMap = fieldCoordinates;
hdrFieldsMap = fieldValues;
}
@Override
protected void postDeserializeCustomOperation(byte[] data, int startBitOffset)
throws PacketException {
// here we try to deserialize the questions and answers, which are stored in rawPayload
setRecordData(rawPayload);
rawPayload = new byte[]{};
}
@Override
public int getfieldnumBits(String fieldName) {
if (RECORD_DATA.equals(fieldName)) {
byte[] dat = getRecordData();
return dat == null ? 0 : dat.length * NetUtils.NumBitsInAByte;
} else
return hdrFieldCoordMap.get(fieldName).getRight();
}
@Override
public void setHeaderField(String headerField, byte[] readValue) {
if (headerField.equals(RECORD_DATA) &&
(readValue == null || readValue.length == 0)) {
hdrFieldsMap.remove(headerField);
return;
}
hdrFieldsMap.put(headerField, readValue);
}
private void deserializeRecordData() throws PacketException {
stringDic.clear();
questions = new Question[getQuestionCount()];
additionalRRs = new ResourceRecord[getAdditionalRRCount()];
answerRRs = new ResourceRecord[getAnswerRRCount()];
authorityRRs = new ResourceRecord[getAuthorityRRCount()];
if (questions.length + additionalRRs.length + answerRRs.length + authorityRRs.length < 1) {
return;
}
int offset = HEADER_BIT_LENGTH; // init offset for the first section (most likely question)
byte[] data = getRecordData();
// now try to peer them with buffer
// the order must not be changed
for (int i = 0; i < questions.length; i++) {
final Question question = new Question(this, offset);
int len = question.getHeaderSize();
question.deserialize(data, offset - HEADER_BIT_LENGTH, len);
questions[i] = question;
offset += len;
}
for (int i = 0; i < answerRRs.length; i++) {
final ResourceRecord answerRR = new ResourceRecord(this, offset);
int len = answerRR.getHeaderSize();
answerRR.deserialize(data, offset - HEADER_BIT_LENGTH, len);
answerRRs[i] = answerRR;
offset += len;
}
for (int i = 0; i < authorityRRs.length; i++) {
final ResourceRecord authorityRR = new ResourceRecord(this, offset);
int len = authorityRR.getHeaderSize();
authorityRR.deserialize(data, offset - HEADER_BIT_LENGTH, len);
authorityRRs[i] = authorityRR;
offset += len;
}
for (int i = 0; i < additionalRRs.length; i++) {
final ResourceRecord additionalRR = new ResourceRecord(this, offset);
int len = additionalRR.getHeaderSize();
additionalRR.deserialize(data, offset - HEADER_BIT_LENGTH, len);
additionalRRs[i] = additionalRR;
offset += len;
}
}
/**
* Serialize records to byte data
*/
private void serializeRecordData() throws PacketException {
int offset = 0;
int len = 0;
for (Question question : questions)
len += question.getHeaderSize();
for (ResourceRecord resourceRecord : additionalRRs)
len += resourceRecord.getHeaderSize();
for (ResourceRecord resourceRecord : answerRRs)
len += resourceRecord.getHeaderSize();
for (ResourceRecord resourceRecord : authorityRRs)
len += resourceRecord.getHeaderSize();
byte[] recordData = new byte[len / NetUtils.NumBitsInAByte];
for (int i = 0; i < questions.length; i++) {
byte[] data = questions[i].serialize();
}
for (int i = 0; i < answerRRs.length; i++) {
byte[] data = answerRRs[i].serialize();
}
for (int i = 0; i < authorityRRs.length; i++) {
byte[] data = authorityRRs[i].serialize();
}
for (int i = 0; i < additionalRRs.length; i++) {
byte[] data = additionalRRs[i].serialize();
}
}
/**
* Read domain from buffer
*
* @param bitOffset absolute offset, unit - bit
* @return
*/
private void readDomainAt(final int bitOffset) {
// if domain starting at this offset has already been read
if (stringDic.containsKey(bitOffset)) return;
int len, i = bitOffset - HEADER_BIT_LENGTH; // the unit of i is bit
byte[] data = getRecordData();
try {
while ((len = BitBufferHelper.getByte(BitBufferHelper.getBits(data, i, NetUtils.NumBitsInAByte)) & 0xFF) != 0 // it's not the end of a string
&& ((len & 0xC0) != 0xC0) // it's not a pointer
) {
byte[] chars = BitBufferHelper.getBits(data, i + NetUtils.NumBitsInAByte, len * NetUtils.NumBitsInAByte);
stringDic.put(i + HEADER_BIT_LENGTH, new String(chars));
i += (len + 1) * NetUtils.NumBitsInAByte; // skip the chars and length
}
if (len == 0) {
// put an end mark(null) here
stringDic.put(i + HEADER_BIT_LENGTH, null);
} else if ((len & 0xC0) == 0xC0) { // it's a pointer
// append pointed domain
int pointer = BitBufferHelper.getShort(BitBufferHelper.getBits(data, i, 2 * NetUtils.NumBitsInAByte)) & 0x3FFF;
if (pointer == bitOffset) return; // avoid infinite loop here
stringDic.put(i + HEADER_BIT_LENGTH, "#" + pointer * NetUtils.NumBitsInAByte); // mark as pointer, put an anchor here
// recursively read the pointer, notice the pointer is pointed to an absolute position
readDomainAt(pointer);
}
} catch (BufferException e) {
return;
}
}
/**
* Concatenate all the string section
*
* @param bitOffset absolute offset, unit - bit
* @return
*/
public String getDomainAt(int bitOffset) {
readDomainAt(bitOffset);
StringBuilder sb = new StringBuilder();
String str;
while (stringDic.containsKey(bitOffset) && (str = stringDic.get(bitOffset)) != null) {
if (str.startsWith("#")) { // if this is a pointer
bitOffset = Integer.parseInt(str.substring(1)); // jump to pointer
continue;
}
sb.append(str).append('.');
bitOffset += (str.length() + 1) * NetUtils.NumBitsInAByte;
}
if (sb.length() > 1)
sb.deleteCharAt(sb.length() - 1); // remove the additional dot
return sb.toString();
}
/**
* The input unit is bit. return -1 when there is an exception
*
* @param bitOffset absolute offset, unit - bit
* @return length of domain starting at specific offset
*/
public int getDomainLengthAt(int bitOffset) {
int len, i = 0;
bitOffset -= HEADER_BIT_LENGTH; // fix the offset to relative to RecordData
byte[] data = getRecordData();
try {
while ((len = BitBufferHelper.getByte(BitBufferHelper.getBits(data, i * NetUtils.NumBitsInAByte + bitOffset, NetUtils.NumBitsInAByte)) & 0xFF) != 0 // it's not the end of a string
&& ((len & 0xC0) != 0xC0) // it's not a pointer
) {
i += len + 1; // char bytes plus len byte
}
} catch (BufferException e) {
return -1;
}
if ((len & 0xC0) == 0xC0) { // it's a pointer
i++; // the pointer takes two bytes
}
i++; // as the i is index, plus one here
return i * NetUtils.NumBitsInAByte;
}
/**
* Returns the transaction ID of the current DNS packet
*
* @return The transaction ID of the current DNS packet
*/
public short getTxId() {
return BitBufferHelper.getShort(fieldValues.get(TXID));
}
/**
* Set the transaction ID for this DNS packet
*
* @param txId
* @return
*/
public DNS setTxId(short txId) {
byte[] _txId = BitBufferHelper.toByteArray(txId);
setHeaderField(TXID, _txId);
return this;
}
/**
* Returns the question flag
*
* @return
*/
public QRCode getQrFlag() {
return QRCode.valueOf(BitBufferHelper.getByte(fieldValues.get(QRFLAG))); // get the most significant bit
}
/**
* Set question flag
*
* @param flag
* @return
*/
public DNS setQrFlag(QRCode flag) {
byte[] _val = BitBufferHelper.toByteArray(flag.value);
setHeaderField(QRFLAG, _val);
return this;
}
/**
* @return
*/
public byte getOpCode() {
return BitBufferHelper.getByte(fieldValues.get(OPCODE));
}
/**
* @param code
* @return
*/
public DNS setOpCode(byte code) {
byte[] _val = BitBufferHelper.toByteArray((byte) (code & 0b1111));
setHeaderField(OPCODE, _val);
return this;
}
/**
* @return
*/
public byte getAuthAnswerFlag() {
return BitBufferHelper.getByte(fieldValues.get(AUTHORITATIVE_ANSWER));
}
/**
* @param val
* @return
*/
public DNS setAuthAnswerFlag(byte val) {
byte[] _val = BitBufferHelper.toByteArray((byte) (val & 1));
setHeaderField(AUTHORITATIVE_ANSWER, _val);
return this;
}
/**
* @return
*/
public byte getTruncationFlag() {
return BitBufferHelper.getByte(fieldValues.get(TRUNCATION));
}
/**
* @param val
* @return
*/
public DNS setTruncationFlag(byte val) {
byte[] _val = BitBufferHelper.toByteArray((byte) (val & 1));
setHeaderField(TRUNCATION, _val);
return this;
}
/**
* @return
*/
public byte getRecursionDesiredFlag() {
return BitBufferHelper.getByte(fieldValues.get(RECURSION_DESIRED));
}
/**
* @param val
* @return
*/
public DNS setRecursionDesiredFlag(byte val) {
byte[] _val = BitBufferHelper.toByteArray((byte) (val & 1));
setHeaderField(RECURSION_DESIRED, _val);
return this;
}
/**
* @return
*/
public byte getRecursionAvailableFlag() {
return BitBufferHelper.getByte(fieldValues.get(RECURSION_AVAILABLE));
}
/**
* @param val
* @return
*/
public DNS setRecursionAvailableFlag(byte val) {
byte[] _val = BitBufferHelper.toByteArray((byte) (val & 1));
setHeaderField(RECURSION_AVAILABLE, _val);
return this;
}
/**
* @return
*/
public byte getZCode() {
return BitBufferHelper.getByte(fieldValues.get(Z));
}
/**
* We provide this possibility, but remember, it should always be zero
* for correct usage
*
* @param val
* @return
*/
public DNS setZCode(byte val) {
byte[] _val = BitBufferHelper.toByteArray((byte) (val & 0b111));
setHeaderField(Z, _val);
return this;
}
/**
* @return
*/
public byte getResponseCode() {
return BitBufferHelper.getByte(fieldValues.get(RESPONSE_CODE));
}
/**
* @param val
* @return
*/
public DNS setResponseCode(byte val) {
byte[] _val = BitBufferHelper.toByteArray((byte) (val & 0b1111));
setHeaderField(RESPONSE_CODE, _val);
return this;
}
/**
* Get query count
*
* @return
*/
public short getQuestionCount() {
return BitBufferHelper.getShort(fieldValues.get(QRCOUNT));
}
public DNS setQuestionCount(short count) {
byte[] _count = BitBufferHelper.toByteArray(count);
setHeaderField(QRCOUNT, _count);
return this;
}
/**
* Return query answer count
*
* @return
*/
public short getAnswerRRCount() {
return BitBufferHelper.getShort(fieldValues.get(ANSCOUNT));
}
public DNS setAnswerRRCount(short count) {
byte[] _count = BitBufferHelper.toByteArray(count);
setHeaderField(ANSCOUNT, _count);
return this;
}
/**
* @return
*/
public short getAuthorityRRCount() {
return BitBufferHelper.getShort(fieldValues.get(NSCOUNT));
}
public DNS setAuthorityRRCount(short count) {
byte[] _count = BitBufferHelper.toByteArray(count);
setHeaderField(NSCOUNT, _count);
return this;
}
/**
* @return
*/
public short getAdditionalRRCount() {
return BitBufferHelper.getShort(fieldValues.get(ARCOUNT));
}
public DNS setAdditionalRRCount(short count) {
byte[] _count = BitBufferHelper.toByteArray(count);
setHeaderField(ARCOUNT, _count);
return this;
}
/**
* This is merely for private use. Not an official field of DNS
*
* @return
*/
private byte[] getRecordData() {
return fieldValues.get(RECORD_DATA);
}
private DNS setRecordData(byte[] data) throws PacketException {
setHeaderField(RECORD_DATA, data);
deserializeRecordData();
return this;
}
public Question[] getQuestions() {
return questions;
}
public ResourceRecord[] getAnswerRRs() {
return answerRRs;
}
public ResourceRecord[] getAuthorityRRs() {
return authorityRRs;
}
public ResourceRecord[] getAdditionalRRs() {
return additionalRRs;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("DNS Packet {")
.append("\n - TransactionId: 0x").append(Integer.toHexString(getTxId() & 0xFFFF))
.append("\n - Response: ").append("Message is a ").append(getQrFlag().toString())
.append("\n - Truncated: ").append(getTruncationFlag())
.append("\n - Recursion desired: ").append(getRecursionDesiredFlag())
.append("\n - Non-authenticated data acceptable: ").append(getAuthAnswerFlag())
.append("\n - Questions: ").append(getQuestionCount())
.append("\n - Answer RRs: ").append(getAnswerRRCount())
.append("\n - Authority RRs: ").append(getAuthorityRRCount())
.append("\n - Additional RRs:").append(getAdditionalRRCount())
;
for (Question question : getQuestions()) {
sb.append("\n + ").append(question.toString());
}
for (ResourceRecord record : getAnswerRRs()) {
sb.append("\n + Answer ").append(record.toString());
}
for (ResourceRecord record : getAuthorityRRs()) {
sb.append("\n + Authority ").append(record.toString());
}
for (ResourceRecord record : getAdditionalRRs()) {
sb.append("\n + Additional ").append(record.toString());
}
sb.append("\n}");
return sb.toString();
}
public static enum OpCode {
QUERY(0),
IQUERY(1), // inverse query, obsolete
STATUS(2),
// notice code 3 is unassigned / reserved
NOTIFY(4),
UPDATE(5);
public final byte value;
private OpCode(int value) {
this.value = (byte) value;
}
public static OpCode valueOf(int value) {
return valueOf((byte) value);
}
public static OpCode valueOf(byte value) {
for (OpCode code : values()) {
if (code.value == value)
return code;
}
return null;
}
public boolean equals(byte value) {
return this.value == value;
}
}
public static enum ClassType {
IN(1), CH(3), HS(4), QCLASS_NONE(254), QCLASS_ANY(255);
public final short value;
private ClassType(int value) {
this.value = (short) value;
}
public static ClassType valueOf(int value) {
return valueOf((short) (value & 0xFF));
}
public static ClassType valueOf(short value) {
for (ClassType type : values()) {
if (type.value == value)
return type;
}
return null;
}
public boolean equals(short value) {
return this.value == value;
}
}
/**
* Resource Record (RR) Types
*/
public static enum ResourceRecordType {
A(1), NS(2), MD(3), MF(4), CNAME(5), SOA(6), MB(7),
MG(8), MR(9), NULL(10), WKS(11), PTR(12), HINFO(13),
MINFO(14), MX(15), TXT(16), RP(17), AFSDB(18), X25(19),
ISDN(20), RT(21), NSAP(22), NSAP_PTR(23), SIG(24), KEY(25),
PX(26), GPOS(27), AAAA(28), LOC(29), NXT(30), EID(31),
NIMLOC(32), SRV(33), ATMA(34), NAPTR(35), KX(36), CERT(37),
A6(38), DNAME(39), SINK(40), OPT(41), APL(42), DS(43),
SSHFP(44), IPSECKEY(45), RRSIG(46), NSEC(47), DNSKEY(48),
DHCID(49), NSEC3(50), NSEC3PARAM(51), TLSA(52), HIP(55),
NINFO(56), RKEY(57), TALINK(58), CDS(59), CDNSKEY(60),
OPENPGPKEY(61), SPF(99), UINFO(100), UID(101), GID(102),
UNSPEC(103), NID(104), L32(105), L64(106), LP(107), EUI48(108),
EUI64(109), TKEY(249), TSIG(250), IXFR(251), AXFR(252),
MAILB(253), MAILA(254), ANY(255), URI(256), CAA(257), TA(32768),
DLV(32769),;
public final short value;
private ResourceRecordType(int value) {
this.value = (short) value;
}
public static ResourceRecordType valueOf(short value) {
for (ResourceRecordType type : values()) {
if (type.value == value)
return type;
}
return null;
}
public boolean equals(short value) {
return this.value == value;
}
}
public static enum QRCode {
REQUEST(0), RESPONSE(1);
public final byte value;
private QRCode(int value) {
this.value = (byte) value;
}
public static QRCode valueOf(int value) {
return valueOf((byte) value);
}
public static QRCode valueOf(byte value) {
for (QRCode code : values()) {
if (code.value == value)
return code;
}
return null;
}
public boolean equals(byte value) {
return this.value == value;
}
}
public static enum ResponseCode {
NO_ERROR(0),
FORMAT_ERROR(1),
SERVER_FAILURE(2),
NAME_ERROR(3),
NOT_IMPLEMENTED(4),
REFUSED(5),
YX_DOMAIN(6),
YX_RR_SET(7),
NX_RR_SET(8),
NOT_AUTH(9),
NOT_ZONE(10),
BAD_VERS_SIG(16), // bad OPT version or TSIG Signature Failure [RFC2845]
BADKEY(17),
BADTIME(18),
BADMODE(19),
BADNAME(20),
BADALG(21),
BADTRUNC(22),;
public final byte value;
private ResponseCode(int value) {
this.value = (byte) value;
}
public static ResponseCode valueOf(int value) {
return valueOf((byte) value);
}
public static ResponseCode valueOf(byte value) {
for (ResponseCode code : values()) {
if (code.value == value)
return code;
}
return null;
}
public boolean equals(byte value) {
return this.value == value;
}
}
/**
* Embed class for DNS query
*/
public static class Question extends Packet {
private static final String QNAME = "Name";
private static final String QTYPE = "Type";
private static final String QCLASS = "Class";
private static final Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
private static final long serialVersionUID = 1L;
{
// domain being queried
put(QNAME, new ImmutablePair<>(0, 0));
// query type, start offset depends on QNAME's length
put(QTYPE, new ImmutablePair<>(0, 16));
put(QCLASS, new ImmutablePair<>(16, 16));
}
};
private final Map<String, byte[]> fieldValues = new HashMap<>();
private int bitOffsetInParent = 0;
public Question(DNS dns, int bitOffsetInParent) {
super();
init(dns, bitOffsetInParent);
}
public Question(DNS dns, int bitOffsetInParent, boolean writeAccess) {
super(writeAccess);
init(dns, bitOffsetInParent);
}
private void init(DNS dns, int bitOffsetInParent) {
hdrFieldCoordMap = fieldCoordinates;
hdrFieldsMap = fieldValues;
setParent(dns);
setBitOffsetInParent(bitOffsetInParent);
}
@Override
public void setParent(Packet dns) {
if (dns == null)
throw new NullPointerException("Parent DNS packet cannot be null");
super.setParent(dns);
}
@Override
public int getfieldnumBits(String fieldName) {
if (QNAME.equals(fieldName))
return ((DNS) getParent()).getDomainLengthAt(getBitOffsetInParent());
else
return hdrFieldCoordMap.get(fieldName).getRight();
}
@Override
public int getfieldOffset(String fieldName) {
int offset = hdrFieldCoordMap.get(fieldName).getLeft();
if (!QNAME.equals(fieldName))
offset += ((DNS) getParent()).getDomainLengthAt(getBitOffsetInParent());
return offset;
}
/**
* Override this function so we can get header size before deserializing this packet
*
* @return Header size of this section
*/
@Override
public int getHeaderSize() {
int size = 0;
for (String field : hdrFieldCoordMap.keySet())
size += getfieldnumBits(field);
return size;
}
private int getBitOffsetInParent() {
return bitOffsetInParent;
}
private Question setBitOffsetInParent(int bitOffsetInParent) {
if (bitOffsetInParent < DNS.HEADER_BIT_LENGTH)
throw new RuntimeException("New offset is within parent DNS packet. Invalid offset.");
this.bitOffsetInParent = bitOffsetInParent;
return this;
}
public String getName() {
return ((DNS) getParent()).getDomainAt(getBitOffsetInParent());
}
// public Query setName(String name) {
// return this;
// }
public short getType() {
return BitBufferHelper.getShort(fieldValues.get(QTYPE));
}
public Question setType(short type) {
byte[] _type = BitBufferHelper.toByteArray(type);
setHeaderField(QTYPE, _type);
return this;
}
public short getClazz() {
return BitBufferHelper.getShort(fieldValues.get(QCLASS));
}
public Question setClazz(short clazz) {
byte[] _clazz = BitBufferHelper.toByteArray(clazz);
setHeaderField(QCLASS, _clazz);
return this;
}
@Override
public String toString() {
return new StringBuilder()
.append("Question: ")
.append(getName())
.append(" type ").append(ResourceRecordType.valueOf(getType()))
.append(" class ").append(ClassType.valueOf(getClazz()))
.toString();
}
}
/**
* Class for DNS Answer
*/
public static class ResourceRecord extends Packet {
private static final String NAME = "Name";
private static final String RTYPE = "Type";
private static final String RCLASS = "Class";
private static final String TTL = "TTL";
private static final String RDLENGTH = "RDataLength";
private static final String RDATA = "RData";
private static final Map<String, Pair<Integer, Integer>> fieldCoordinates = new LinkedHashMap<String, Pair<Integer, Integer>>() {
private static final long serialVersionUID = 1L;
{
// domain name
put(NAME, new ImmutablePair<>(0, 0));
// rdata type, start offset depends on name's length
put(RTYPE, new ImmutablePair<>(0, 16));
// rdata class
put(RCLASS, new ImmutablePair<>(16, 16));
put(TTL, new ImmutablePair<>(32, 32));
put(RDLENGTH, new ImmutablePair<>(64, 16));
put(RDATA, new ImmutablePair<>(80, 0));
}
};
private final Map<String, byte[]> fieldValues = new HashMap<>();
private int bitOffsetInParent = 0;
public ResourceRecord(DNS dns, int bitOffsetInParent) {
super();
init(dns, bitOffsetInParent);
}
public ResourceRecord(DNS dns, int bitOffsetInParent, boolean writeAccess) {
super(writeAccess);
init(dns, bitOffsetInParent);
}
private void init(DNS dns, int bitOffsetInParent) {
hdrFieldCoordMap = fieldCoordinates;
hdrFieldsMap = fieldValues;
setParent(dns);
setBitOffsetInParent(bitOffsetInParent);
}
@Override
public void setParent(Packet dns) {
if (dns == null)
throw new NullPointerException("Parent DNS packet cannot be null");
super.setParent(dns);
}
@Override
public int getfieldnumBits(String fieldName) {
if (NAME.equals(fieldName))
return ((DNS) getParent()).getDomainLengthAt(getBitOffsetInParent());
else if (RDATA.equals(fieldName)) {
return (getRdLengthFromParent() & 0xFFFF) * NetUtils.NumBitsInAByte;
} else
return hdrFieldCoordMap.get(fieldName).getRight();
}
@Override
public int getfieldOffset(String fieldName) {
int offset = hdrFieldCoordMap.get(fieldName).getLeft();
if (!NAME.equals(fieldName))
offset += ((DNS) getParent()).getDomainLengthAt(getBitOffsetInParent());
return offset;
}
/**
* Override this function so we can get header size before deserializing this packet
*
* @return Header size of this section
*/
@Override
public int getHeaderSize() {
int size = 0;
for (String field : hdrFieldCoordMap.keySet())
size += getfieldnumBits(field);
return size;
}
private int getBitOffsetInParent() {
return bitOffsetInParent;
}
private ResourceRecord setBitOffsetInParent(int bitOffsetInParent) {
if (bitOffsetInParent < DNS.HEADER_BIT_LENGTH)
throw new RuntimeException("New offset is within parent DNS packet. Invalid offset.");
this.bitOffsetInParent = bitOffsetInParent;
return this;
}
/**
* We can read the RDataLength value directly from parent
*
* @return
*/
private short getRdLengthFromParent() {
DNS dns = (DNS) getParent();
byte[] data = dns.getRecordData();
int offset = getBitOffsetInParent() - HEADER_BIT_LENGTH + getfieldOffset(RDLENGTH);
try {
return BitBufferHelper.getShort(BitBufferHelper.getBits(data, offset, 16));
} catch (BufferException e) {
return 0;
}
}
public String getName() {
return ((DNS) getParent()).getDomainAt(getBitOffsetInParent());
}
public short getRType() {
return BitBufferHelper.getShort(fieldValues.get(RTYPE));
}
public ResourceRecord setRType(short val) {
byte[] _val = BitBufferHelper.toByteArray(val);
setHeaderField(RTYPE, _val);
return this;
}
public short getRClass() {
return BitBufferHelper.getShort(fieldValues.get(RCLASS));
}
public ResourceRecord setRClass(short val) {
byte[] _val = BitBufferHelper.toByteArray(val);
setHeaderField(RCLASS, _val);
return this;
}
public int getTTL() {
return BitBufferHelper.getInt(fieldValues.get(TTL));
}
public ResourceRecord setTTL(int val) {
byte[] _val = BitBufferHelper.toByteArray(val);
setHeaderField(TTL, _val);
return this;
}
public short getRdLength() {
return BitBufferHelper.getShort(fieldValues.get(RDLENGTH));
}
// public ResourceRecord setRdLength(short val) {
// byte[] _val = BitBufferHelper.toByteArray(val);
// setHeaderField(RDLENGTH, _val);
// return this;
// }
/**
* Get resource record as raw bytes
*
* @return
*/
public byte[] getRData() {
return fieldValues.get(RDATA);
}
// do not support setting it yet
// public ResourceRecord setRData(byte[] val) {
// if (val.length > 65535)
// throw new RuntimeException("RData exceeds 65535 bytes.");
// setRdLength((short) (val.length & 0xFFFF));
// setHeaderField(RTYPE, val);
// return this;
// }
/**
* Return the RData as a string data
*
* @return
*/
public String getRDataAsString() {
ResourceRecordType rType = ResourceRecordType.valueOf(getRType());
if (rType == null)
return HexEncode.bytesToHexStringFormat(getRData());
String data;
try {
switch (rType) {
case A:
data = Inet4Address.getByAddress(getRData()).getHostAddress();
break;
case AAAA:
data = Inet6Address.getByAddress(getRData()).getHostAddress();
break;
case MX:
case TXT:
case PTR:
case CNAME:
data = ((DNS) getParent()).getDomainAt(getBitOffsetInParent() + 96); // 96 is offset of RData
break;
default:
data = HexEncode.bytesToHexStringFormat(getRData());
}
} catch (UnknownHostException e) {
data = "[INVALID ADDRESS] " + HexEncode.bytesToHexString(getRData());
}
return data;
}
@Override
public String toString() {
return new StringBuilder()
.append("Resource Record: ")
.append(getName())
.append(" type: ").append(ResourceRecordType.valueOf(getRType()))
.append(" class: ").append(ClassType.valueOf(getRClass()))
.append(" TTL: ").append(getTTL())
.append(" Data: ").append(getRDataAsString())
.toString()
;
}
}
}
package com.kevinxw.net.packet;
import org.junit.Ignore;
import org.junit.Test;
import org.opendaylight.controller.sal.utils.HexEncode;
import org.opendaylight.controller.sal.utils.NetUtils;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
public class DNSTest {
/**
* Convert hex string to byte array
*
* @param s
* @return
*/
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i + 1), 16));
}
return data;
}
@Test
public void testGetDomainBytes() throws Exception {
byte[] res = DNS.getDomainBytes("google-public-dns-a.google.com");
assertArrayEquals(hexStringToByteArray("13676f6f676c652d7075626c69632d646e732d6106676f6f676c6503636f6d00"), res);
}
@Test
public void testSetTxId() throws Exception {
DNS dns = new DNS();
final short val = (short) 0x8D59; // NORMALLY IT SHOULD BE ZERO, BUT FOR TESTING HERE
dns.setTxId(val);
assertEquals(val, dns.getTxId());
}
@Test
public void testQrFlag() throws Exception {
DNS dns = new DNS();
dns.setQrFlag(DNS.QRCode.RESPONSE);
assertEquals(DNS.QRCode.RESPONSE, dns.getQrFlag());
dns.setQrFlag(DNS.QRCode.REQUEST);
assertEquals(DNS.QRCode.REQUEST, dns.getQrFlag());
}
@Test
public void testOpCode() throws Exception {
DNS dns = new DNS();
dns.setOpCode(DNS.OpCode.NOTIFY.value);
assertEquals(DNS.OpCode.NOTIFY.value, dns.getOpCode());
dns.setOpCode(DNS.OpCode.IQUERY.value);
assertEquals(DNS.OpCode.IQUERY.value, dns.getOpCode());
dns.setOpCode(DNS.OpCode.QUERY.value);
assertEquals(DNS.OpCode.QUERY.value, dns.getOpCode());
dns.setOpCode(DNS.OpCode.STATUS.value);
assertEquals(DNS.OpCode.STATUS.value, dns.getOpCode());
dns.setOpCode(DNS.OpCode.UPDATE.value);
assertEquals(DNS.OpCode.UPDATE.value, dns.getOpCode());
}
@Test
public void testAaFlag() throws Exception {
DNS dns = new DNS();
final byte val = 1;
dns.setAuthAnswerFlag(val);
assertEquals(val, dns.getAuthAnswerFlag());
}
@Test
public void testTcFlag() throws Exception {
DNS dns = new DNS();
final byte val = 1;
dns.setTruncationFlag(val);
assertEquals(val, dns.getTruncationFlag());
}
@Test
public void testRdFlag() throws Exception {
DNS dns = new DNS();
final byte val = 1;
dns.setRecursionDesiredFlag(val);
assertEquals(val, dns.getRecursionDesiredFlag());
}
@Test
public void testRaFlag() throws Exception {
DNS dns = new DNS();
final byte val = 1;
dns.setRecursionAvailableFlag(val);
assertEquals(val, dns.getRecursionAvailableFlag());
}
@Test
public void testZCode() throws Exception {
DNS dns = new DNS();
final byte val = 0b101; // random value
dns.setZCode(val);
assertEquals(val, dns.getZCode());
}
@Test
public void testResponseCode() throws Exception {
DNS dns = new DNS();
final byte val = DNS.ResponseCode.NOT_ZONE.value;
dns.setResponseCode(val);
assertEquals(val, dns.getResponseCode());
}
@Test
public void testSetQrCount() throws Exception {
DNS dns = new DNS();
final short val = 999;
dns.setQuestionCount(val);
assertEquals(val, dns.getQuestionCount());
}
@Test
public void testSetAnCount() throws Exception {
DNS dns = new DNS();
final short val = 999;
dns.setAnswerRRCount(val);
assertEquals(val, dns.getAnswerRRCount());
}
@Test
public void testSetNsCount() throws Exception {
DNS dns = new DNS();
final short val = 999;
dns.setAuthorityRRCount(val);
assertEquals(val, dns.getAuthorityRRCount());
}
@Test
public void testSetArCount() throws Exception {
DNS dns = new DNS();
final short val = 999;
dns.setAdditionalRRCount(val);
assertEquals(val, dns.getAdditionalRRCount());
}
@Test
public void testFullDNS_Query() throws Exception {
final byte[] pkt = hexStringToByteArray("528e01000001000000000000013801380138013807696e2d61646472046172706100000c0001");
DNS dns = new DNS();
dns.deserialize(pkt, 0, pkt.length * NetUtils.NumBitsInAByte);
assertEquals(0x528e, dns.getTxId());
assertEquals(DNS.QRCode.REQUEST, dns.getQrFlag());
assertEquals(0, dns.getTruncationFlag());
assertEquals(1, dns.getRecursionDesiredFlag());
assertEquals(0, dns.getRecursionAvailableFlag());
assertEquals(0, dns.getZCode());
assertEquals(0, dns.getAuthAnswerFlag());
assertEquals(1, dns.getQuestionCount());
assertEquals(0, dns.getAnswerRRCount());
assertEquals(0, dns.getAuthorityRRCount());
assertEquals(0, dns.getAdditionalRRCount());
// now testing query attributes
DNS.Question[] queries = dns.getQuestions();
assertEquals(1, queries.length);
assertEquals("8.8.8.8.in-addr.arpa", queries[0].getName());
assertEquals(DNS.ResourceRecordType.PTR.value, queries[0].getType());
assertEquals(DNS.ClassType.IN.value, queries[0].getClazz());
}
@Test
public void testFullDNS_Response() throws Exception {
final byte[] pkt = hexStringToByteArray("528e81800001000100000000013801380138013807696e2d61646472046172706100000c0001c00c000c00010000acb7002013676f6f676c652d7075626c69632d646e732d6106676f6f676c6503636f6d00");
DNS dns = new DNS();
dns.deserialize(pkt, 0, pkt.length * NetUtils.NumBitsInAByte);
assertEquals(0x528e, dns.getTxId());
assertEquals(DNS.QRCode.RESPONSE, dns.getQrFlag());
assertEquals(0, dns.getTruncationFlag());
assertEquals(1, dns.getRecursionDesiredFlag());
assertEquals(1, dns.getRecursionAvailableFlag());
assertEquals(0, dns.getZCode());
assertEquals(0, dns.getAuthAnswerFlag());
assertEquals(1, dns.getQuestionCount());
assertEquals(1, dns.getAnswerRRCount());
assertEquals(0, dns.getAuthorityRRCount());
assertEquals(0, dns.getAdditionalRRCount());
// now testing query attributes
DNS.Question[] queries = dns.getQuestions();
assertEquals(1, queries.length);
assertEquals("8.8.8.8.in-addr.arpa", queries[0].getName());
assertEquals(DNS.ResourceRecordType.PTR.value, queries[0].getType());
assertEquals(DNS.ClassType.IN.value, queries[0].getClazz());
// test answer
DNS.ResourceRecord[] answers = dns.getAnswerRRs();
assertEquals(1, answers.length);
assertEquals("8.8.8.8.in-addr.arpa", answers[0].getName());
assertEquals(DNS.ResourceRecordType.PTR.value, answers[0].getRType());
assertEquals(DNS.ClassType.IN.value, answers[0].getRClass());
assertEquals(44215, answers[0].getTTL());
assertEquals(32, answers[0].getRdLength());
assertArrayEquals(DNS.getDomainBytes("google-public-dns-a.google.com"), answers[0].getRData());
assertEquals("google-public-dns-a.google.com", answers[0].getRDataAsString());
}
@Test
public void testGetDomainAt() throws Exception {
final byte[] pkt = hexStringToByteArray("528e01000001000000000000013801380138013807696e2d61646472046172706100000c0001");
// the domain in this query is "8.8.8.8.in-addr.arpa"
DNS dns = new DNS();
dns.deserialize(pkt, 0, pkt.length * NetUtils.NumBitsInAByte);
assertEquals(22 * NetUtils.NumBitsInAByte, dns.getDomainLengthAt(DNS.HEADER_BIT_LENGTH));
assertEquals("8.8.8.8.in-addr.arpa", dns.getDomainAt(DNS.HEADER_BIT_LENGTH));
final byte[] pkt2 = hexStringToByteArray("528e81800001000100000000013801380138013807696e2d61646472046172706100000c0001c00c000c00010000acb7002013676f6f676c652d7075626c69632d646e732d6106676f6f676c6503636f6d00");
// the domain in this response packet is "8.8.8.8.in-addr.arpa"
DNS dns2 = new DNS();
dns2.deserialize(pkt2, 0, pkt2.length * NetUtils.NumBitsInAByte);
// check the query section
assertEquals(22 * NetUtils.NumBitsInAByte, dns2.getDomainLengthAt(DNS.HEADER_BIT_LENGTH));
assertEquals("8.8.8.8.in-addr.arpa", dns2.getDomainAt(DNS.HEADER_BIT_LENGTH));
// check the response section
int offset = dns2.getQuestions()[0].getHeaderSize();
assertEquals(2 * NetUtils.NumBitsInAByte, dns2.getDomainLengthAt(DNS.HEADER_BIT_LENGTH + offset));
assertEquals("8.8.8.8.in-addr.arpa", dns2.getDomainAt(DNS.HEADER_BIT_LENGTH + offset));
}
@Test
@Ignore
public void printFullDNS() throws Exception {
final byte[] pkt = HexEncode.bytesFromHexString("ba:e9:81:80:00:01:00:05:00:00:00:00:03:77:77:77:06:67:6f:6f:67:6c:65:03:63:6f:6d:00:00:01:00:01:c0:0c:00:01:00:01:00:00:00:00:00:04:4a:7d:ef:90:c0:0c:00:01:00:01:00:00:00:00:00:04:4a:7d:ef:93:c0:0c:00:01:00:01:00:00:00:00:00:04:4a:7d:ef:91:c0:0c:00:01:00:01:00:00:00:00:00:04:4a:7d:ef:92:c0:0c:00:01:00:01:00:00:00:00:00:04:4a:7d:ef:94");
DNS dns = new DNS();
dns.deserialize(pkt, 0, pkt.length * NetUtils.NumBitsInAByte);
System.out.println(dns.toString());
}
@Test
public void testCNameDNS() throws Exception {
final byte[] pkt = hexStringToByteArray("2e7981800001000200000000037777770462696e6703636f6d0000010001c00c000500010000013d000b03616e790465646765c010c02a000100010000013d0004cc4fc5c8");
DNS dns = new DNS();
dns.deserialize(pkt, 0, pkt.length * NetUtils.NumBitsInAByte);
assertEquals(1, dns.getQuestionCount());
assertEquals(2, dns.getAnswerRRCount());
dns.toString();
System.out.println(dns.getAnswerRRs()[0].getName());
assertEquals("www.bing.com", dns.getAnswerRRs()[0].getName());
assertEquals("any.edge.bing.com", dns.getAnswerRRs()[0].getRDataAsString());
assertEquals("any.edge.bing.com", dns.getAnswerRRs()[1].getName());
assertEquals("204.79.197.200", dns.getAnswerRRs()[1].getRDataAsString());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment