⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dhcppacket.java

📁 DHCP 的JAVA实现
💻 JAVA
📖 第 1 页 / 共 5 页
字号:
    	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 + -