📄 dhcppacket.java
字号:
if (datagram == null) {
throw new IllegalArgumentException("datagram is null");
}
DHCPPacket packet = new DHCPPacket();
// all parameters are checked in marshall()
packet.marshall(datagram.getData(), datagram.getOffset(), datagram.getLength(),
datagram.getAddress(), datagram.getPort(),
true); // strict mode by default
return packet;
}
/**
* Factory for creating <tt>DHCPPacket</tt> objects by parsing a
* <tt>byte[]</tt> e.g. from a datagram.
*
* <p>This method allows you to specify non-strict mode which is much more
* tolerant for packet options. By default, any problem seen during DHCP option
* parsing causes a DHCPBadPacketException to be thrown.
*
* @param buf buffer for holding the incoming datagram.
* @param offset the offset for the buffer.
* @param length the number of bytes to read.
* @param strict do we parse in strict mode?
* @return the newly create <tt>DHCPPacket</tt> instance
* @throws DHCPBadPacketException the datagram is malformed.
*/
public static DHCPPacket getPacket(byte[] buf, int offset, int length, boolean strict) throws DHCPBadPacketException {
DHCPPacket packet = new DHCPPacket();
// all parameters are checked in marshall()
packet.marshall(buf, offset, length, null, 0, strict);
return packet;
}
/**
* Returns a copy of this <tt>DHCPPacket</tt>.
*
* <p>The <tt>truncated</tt> flag is reset.
*
* @return a copy of the <tt>DHCPPacket</tt> instance.
*/
@Override
public DHCPPacket clone() {
try {
DHCPPacket p = (DHCPPacket) super.clone();
// specifically cloning arrays to avoid side-effects
p.ciaddr = this.ciaddr.clone();
p.yiaddr = this.yiaddr.clone();
p.siaddr = this.siaddr.clone();
p.giaddr = this.giaddr.clone();
p.chaddr = this.chaddr.clone();
p.sname = this.sname .clone();
p.file = this.file .clone();
//p.options = this.options.clone();
p.options = new LinkedHashMap<Byte, DHCPOption>(this.options);
p.padding = this.padding.clone();
p.truncated = false; // freshly new object, it is not considered as corrupt
return p;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}
/**
* Returns true if 2 instances of <tt>DHCPPacket</tt> represent the same DHCP packet.
*
* <p>This is a field by field comparison, except <tt>truncated</tt> which is ignored.
*/
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof DHCPPacket)) {
return false;
}
DHCPPacket p = (DHCPPacket) o;
boolean b;
b = (this.comment.equals(p.comment));
b &= (this.op == p.op);
b &= (this.htype == p.htype);
b &= (this.hlen == p.hlen);
b &= (this.hops == p.hops);
b &= (this.xid == p.xid);
b &= (this.secs == p.secs);
b &= (this.flags == p.flags);
b &= (Arrays.equals(this.ciaddr, p.ciaddr));
b &= (Arrays.equals(this.yiaddr, p.yiaddr));
b &= (Arrays.equals(this.siaddr, p.siaddr));
b &= (Arrays.equals(this.giaddr, p.giaddr));
b &= (Arrays.equals(this.chaddr, p.chaddr));
b &= (Arrays.equals(this.sname, p.sname));
b &= (Arrays.equals(this.file, p.file));
b &= (this.options.equals(p.options));
b &= (this.isDhcp == p.isDhcp);
// we deliberately ignore "truncated" since it is reset when cloning
b &= (Arrays.equals(this.padding, p.padding));
b &= (equalsStatic (this.address, p.address));
b &= (this.port == p.port);
return b;
}
/**
* Returns a hash code value for the object.
*/
@Override
public int hashCode() {
int h = -1;
h ^= this.comment.hashCode();
h += this.op;
h += this.htype;
h += this.hlen;
h += this.hops;
h += this.xid;
h += this.secs;
h ^= this.flags;
h ^= Arrays.hashCode(this.ciaddr);
h ^= Arrays.hashCode(this.yiaddr);
h ^= Arrays.hashCode(this.siaddr);
h ^= Arrays.hashCode(this.giaddr);
h ^= Arrays.hashCode(this.chaddr);
h ^= Arrays.hashCode(this.sname);
h ^= Arrays.hashCode(this.file);
h ^= this.options.hashCode();
h += this.isDhcp ? 1 : 0;
// h += this.truncated ? 1 : 0;
h ^= Arrays.hashCode(this.padding);
h ^= (this.address != null) ? this.address.hashCode() : 0;
h += this.port;
return h;
}
private static boolean equalsStatic(Object a, Object b) {
return ((a == null) ? (b == null) : a.equals(b));
}
/**
* Assert all the invariants of the object. For debug purpose only.
*
*/
private void assertInvariants() {
assert(this.comment != null);
assert(this.ciaddr != null);
assert(this.ciaddr.length == 4);
assert(this.yiaddr != null);
assert(this.yiaddr.length == 4);
assert(this.siaddr != null);
assert(this.siaddr.length == 4);
assert(this.giaddr != null);
assert(this.giaddr.length == 4);
// strings
assert(this.chaddr != null);
assert(this.chaddr.length == 16);
assert(this.sname != null);
assert(this.sname.length == 64);
assert(this.file != null);
assert(this.file.length == 128);
assert(this.padding != null); // length is free for padding
// options
assert(this.options != null);
for (Map.Entry<Byte, DHCPOption> mapEntry : this.options.entrySet()) {
Byte key = mapEntry.getKey();
DHCPOption opt = mapEntry.getValue();
assert(key != null);
assert(key != DHO_PAD);
assert(key != DHO_END);
assert(opt != null);
assert(opt.getCode() == key);
assert(opt.getValueFast() != null);
}
}
/**
* Convert a specified byte array containing a DHCP message into a
* DHCPMessage object.
*
* @return a DHCPMessage object with information from byte array.
* @param buffer byte array to convert to a DHCPMessage object
* @param offset starting offset for the buffer
* @param length length of the buffer
* @param address0 the address from which the packet was sent, or <tt>null</tt>
* @param port0 the port from which the packet was sent
* @param strict do we read in strict mode?
* @throws IllegalArgumentException if buffer is <tt>null</tt>...
* @throws IndexOutOfBoundsException offset..offset+length is out of buffer bounds
* @throws DHCPBadPacketException datagram is malformed
*/
protected DHCPPacket marshall(byte[] buffer, int offset, int length,
InetAddress address0, int port0, boolean strict) {
// do some basic sanity checks
// ibuff, offset & length are valid?
if (buffer == null) {
throw new IllegalArgumentException("null buffer not allowed");
}
if (offset < 0) {
throw new IndexOutOfBoundsException("negative offset not allowed");
}
if (length < 0) {
throw new IllegalArgumentException("negative length not allowed");
}
if (buffer.length < offset + length) {
throw new IndexOutOfBoundsException("offset+length exceeds buffer length");
}
// absolute minimum size for a valid packet
if (length < _BOOTP_ABSOLUTE_MIN_LEN) {
throw new DHCPBadPacketException("DHCP Packet too small (" + length +
") absolute minimum is " + _BOOTP_ABSOLUTE_MIN_LEN);
}
// maximum size for a valid DHCP packet
if (length > _DHCP_MAX_MTU) {
throw new DHCPBadPacketException("DHCP Packet too big (" + length +
") max MTU is " + _DHCP_MAX_MTU);
}
// copy address and port
this.address = address0; // no need to clone, InetAddress is immutable
this.port = port0;
try {
// turn buffer into a readable stream
ByteArrayInputStream inBStream = new ByteArrayInputStream(buffer, offset, length);
DataInputStream inStream = new DataInputStream(inBStream);
// parse static part of packet
this.op = inStream.readByte ();
this.htype = inStream.readByte ();
this.hlen = inStream.readByte ();
this.hops = inStream.readByte ();
this.xid = inStream.readInt ();
this.secs = inStream.readShort();
this.flags = inStream.readShort();
inStream.readFully(this.ciaddr, 0, 4);
inStream.readFully(this.yiaddr, 0, 4);
inStream.readFully(this.siaddr, 0, 4);
inStream.readFully(this.giaddr, 0, 4);
inStream.readFully(this.chaddr, 0, 16);
inStream.readFully(this.sname, 0, 64);
inStream.readFully(this.file, 0, 128);
// check for DHCP MAGIC_COOKIE
this.isDhcp = true;
inBStream.mark(4); // read ahead 4 bytes
if (inStream.readInt() != _MAGIC_COOKIE) {
this.isDhcp = false;
inBStream.reset(); // re-read the 4 bytes
}
if (this.isDhcp) { // is it a full DHCP packet or a simple BOOTP?
// DHCP Packet: parsing options
int type = 0;
while (true) {
int r = inBStream.read();
if (r < 0) { break; } // EOF
type = (byte) r;
if (type == DHO_PAD) { continue; } // skip Padding
if (type == DHO_END) { break; } // break if end of options
r = inBStream.read();
if (r < 0) { break; } // EOF
int len = Math.min(r, inBStream.available());
byte[] unit_opt = new byte[len];
inBStream.read(unit_opt);
this.setOption(new DHCPOption((byte) type, unit_opt)); // store option
}
this.truncated = (type != DHO_END); // truncated options?
if (strict && this.truncated) {
throw new DHCPBadPacketException("Packet seams to be truncated");
}
}
// put the remaining in padding
this.padding = new byte[inBStream.available()];
inBStream.read(this.padding);
// final verifications (if assertions are activated)
this.assertInvariants();
return this;
} catch (IOException e) {
// unlikely with ByteArrayInputStream
throw new DHCPBadPacketException("IOException: "+e.toString(), e);
}
}
/**
* Converts the object to a byte array ready to be sent on the wire.
*
* <p>Default max size of resulting packet is 576, which is the maximum
* size a client can accept without explicit notice (option XXX)
*
* @return a byte array with information from DHCPMessage object.
* @throws DHCPBadPacketException the datagram would be malformed (too small, too big...)
*/
public byte[] serialize() {
int minLen = _BOOTP_ABSOLUTE_MIN_LEN;
if (this.isDhcp) {
// most other DHCP software seems to ensure that the BOOTP 'vend'
// field is padded to at least 64 bytes
minLen += _BOOTP_VEND_SIZE;
}
return serialize(minLen, _DHCP_DEFAULT_MAX_LEN);
}
/**
* Converts the object to a byte array ready to be sent on the wire.
*
* @param maxSize the maximum buffer size in bytes
* @return a byte array with information from DHCPMessage object.
* @throws DHCPBadPacketException the datagram would be malformed (too small, too big...)
*/
public byte[] serialize(int minSize, int maxSize) {
this.assertInvariants();
// prepare output buffer, pre-sized to maximum buffer length
// default buffer is half the maximum size of possible packet
// (this seams reasonable for most uses, worst case only doubles the buffer size once
ByteArrayOutputStream outBStream = new ByteArrayOutputStream(_DHCP_MAX_MTU / 2);
DataOutputStream outStream = new DataOutputStream(outBStream);
try {
outStream.writeByte (this.op);
outStream.writeByte (this.htype);
outStream.writeByte (this.hlen);
outStream.writeByte (this.hops);
outStream.writeInt (this.xid);
outStream.writeShort(this.secs);
outStream.writeShort(this.flags);
outStream.write(this.ciaddr, 0, 4);
outStream.write(this.yiaddr, 0, 4);
outStream.write(this.siaddr, 0, 4);
outStream.write(this.giaddr, 0, 4);
outStream.write(this.chaddr, 0, 16);
outStream.write(this.sname, 0, 64);
outStream.write(this.file, 0, 128);
if (this.isDhcp) {
// DHCP and not BOOTP -> magic cookie required
outStream.writeInt(_MAGIC_COOKIE);
// parse output options in creation order (LinkedHashMap)
for (DHCPOption opt : this.getOptionsCollection()) {
assert (opt != null);
assert (opt.getCode() != DHO_PAD);
assert (opt.getCode() != DHO_END);
assert (opt.getValueFast() != null);
int size = opt.getValueFast().length;
assert (size >= 0);
if (size > 255) {
throw new DHCPBadPacketException("Options larger than 255 bytes are not yet supported");
}
outStream.writeByte(opt.getCode()); // output option code
outStream.writeByte(size); // output option length
outStream.write(opt.getValueFast()); // output option data
}
// mark end of options
outStream.writeByte(DHO_END);
}
// write padding
outStream.write(this.padding);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -