📄 dhcppacket.java
字号:
/*
* This file is part of dhcp4java, a DHCP API for the Java language.
* (c) 2006 Stephan Hadinger
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.dhcp4java;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.net.DatagramPacket;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.dhcp4java.DHCPConstants.*;
/**
* The basic class for manipulating DHCP packets.
*
* @author Stephan Hadinger
* @version 1.00
*
* <p>There are two basic ways to build a new DHCPPacket object.
* <p>First one is to build an object from scratch using the constructor and setters.
* If you need to set repeatedly the same set of parameters and options,
* you can create a "master" object and clone it many times.
*
* <pre>
* DHCPPacket discover = new DHCPPacket();
* discover.setOp(DHCPPacket.BOOTREQUEST);
* discover.setHtype(DHCPPacket.HTYPE_ETHER);
* discover.setHlen((byte) 6);
* discover.setHops((byte) 0);
* discover.setXid( (new Random()).nextInt() );
* ...
* </pre>
* Second is to decode a DHCP datagram received from the network.
* In this case, the object is created through a factory.
*
* <p>Example: simple DHCP sniffer
* <pre>
* DatagramSocket socket = new DatagramSocket(67);
* while (true) {
* DatagramPacket pac = new DatagramPacket(new byte[1500], 1500);
* socket.receive(pac);
* DHCPPacket dhcp = DHCPPacket.getPacket(pac);
* System.out.println(dhcp.toString());
* }
* </pre>
* In this second way, beware that a <tt>BadPacketExpcetion</tt> is thrown
* if the datagram contains invalid DHCP data.
*
*
* <p><b>Getters and Setters</b>: methods are provided with high-level data structures
* wherever it is possible (String, InetAddress...). However there are also low-overhead
* version (suffix <tt>Raw</tt>) dealing directly with <tt>byte[]</tt> for maximum performance.
* They are useful in servers for copying parameters in a servers from a request to a response without
* any type conversion. All parameters are copies, you may modify them as you like without
* any side-effect on the <tt>DHCPPacket</tt> object.
*
* <h4>DHCP datagram format description:</h4>
* <blockquote><table cellspacing=2>
* <tr><th>Field</th><th>Octets</th><th>Description</th></tr>
* <tr><td valign=top><tt>op</tt></td><td valign=top>1</td>
* <td>Message op code / message type.<br>
* use constants
* <tt>BOOTREQUEST</tt>,
* <tt>BOOTREPLY</tt></td></tr>
* <tr><td valign=top><tt>htype</tt></td>
* <td valign=top>1</td><td>Hardware address type, see ARP section in
* "Assigned Numbers" RFC<br>
* use constants
* <tt>HTYPE_ETHER</tt>,
* <tt>HTYPE_IEEE802</tt>,
* <tt>HTYPE_FDDI</tt></td></tr>
* <tr><td valign=top><tt>hlen</tt></td><td>1</td><td>Hardware address length
* (e.g. '6' for ethernet).</td></tr>
* <tr><td valign=top><tt>hops</tt></td><td valign=top>1</td><td>Client sets to zero, optionally used
* by relay agents when booting via a relay agent.</td></tr>
* <tr><td valign=top><tt>xid</tt></td><td valign=top>4</td>
* <td>Transaction ID, a random number chosen by the
* client, used by the client and server to associate
* messages and responses between a client and a
* server.</td></tr>
* <tr><td valign=top><tt>secs</tt></td><td valign=top>2</td>
* <td>Filled in by client, seconds elapsed since client
* began address acquisition or renewal process.</td></tr>
* <tr><td valign=top><tt>flags</tt></td><td valign=top>2</td>
* <td>Flags (see below).</td></tr>
* <tr><td valign=top><tt>ciaddr</tt></td><td valign=top>4</td>
* <td>Client IP address; only filled in if client is in
* BOUND, RENEW or REBINDING state and can respond
* to ARP requests.</td></tr>
* <tr><td valign=top><tt>yiaddr</tt></td><td valign=top>4</td>
* <td>'your' (client) IP address.</td></tr>
* <tr><td valign=top><tt>siaddr</tt></td><td valign=top>4</td>
* <td>IP address of next server to use in bootstrap;
* returned in DHCPOFFER, DHCPACK by server.</td></tr>
* <tr><td valign=top><tt>giaddr</tt></td><td valign=top>4</td>
* <td>Relay agent IP address, used in booting via a
* relay agent.</td></tr>
* <tr><td valign=top><tt>chaddr</tt></td><td valign=top>16</td>
* <td>Client hardware address.</td></tr>
* <tr><td valign=top><tt>sname</tt></td><td valign=top>64</td>
* <td>Optional server host name, null terminated string.</td></tr>
* <tr><td valign=top><tt>file</tt></td><td valign=top>128</td>
* <td>Boot file name, null terminated string; "generic"
* name or null in DHCPDISCOVER, fully qualified
* directory-path name in DHCPOFFER.</td></tr>
* <tr><td valign=top><tt>isDhcp</tt></td><td valign=top>4</td>
* <td>Controls whether the packet is BOOTP or DHCP.
* DHCP contains the "magic cookie" of 4 bytes.
* 0x63 0x82 0x53 0x63.</td></tr>
* <tr><td valign=top><tt>DHO_*code*</tt></td><td valign=top>*</td>
* <td>Optional parameters field. See the options
* documents for a list of defined options. See below.</td></tr>
* <tr><td valign=top><tt>padding</tt></td><td valign=top>*</td>
* <td>Optional padding at the end of the packet.</td></tr>
* </table></blockquote>
*
* <h4>DHCP Option</h4>
*
* The following options are codes are supported:
* <pre>
* DHO_SUBNET_MASK(1)
* DHO_TIME_OFFSET(2)
* DHO_ROUTERS(3)
* DHO_TIME_SERVERS(4)
* DHO_NAME_SERVERS(5)
* DHO_DOMAIN_NAME_SERVERS(6)
* DHO_LOG_SERVERS(7)
* DHO_COOKIE_SERVERS(8)
* DHO_LPR_SERVERS(9)
* DHO_IMPRESS_SERVERS(10)
* DHO_RESOURCE_LOCATION_SERVERS(11)
* DHO_HOST_NAME(12)
* DHO_BOOT_SIZE(13)
* DHO_MERIT_DUMP(14)
* DHO_DOMAIN_NAME(15)
* DHO_SWAP_SERVER(16)
* DHO_ROOT_PATH(17)
* DHO_EXTENSIONS_PATH(18)
* DHO_IP_FORWARDING(19)
* DHO_NON_LOCAL_SOURCE_ROUTING(20)
* DHO_POLICY_FILTER(21)
* DHO_MAX_DGRAM_REASSEMBLY(22)
* DHO_DEFAULT_IP_TTL(23)
* DHO_PATH_MTU_AGING_TIMEOUT(24)
* DHO_PATH_MTU_PLATEAU_TABLE(25)
* DHO_INTERFACE_MTU(26)
* DHO_ALL_SUBNETS_LOCAL(27)
* DHO_BROADCAST_ADDRESS(28)
* DHO_PERFORM_MASK_DISCOVERY(29)
* DHO_MASK_SUPPLIER(30)
* DHO_ROUTER_DISCOVERY(31)
* DHO_ROUTER_SOLICITATION_ADDRESS(32)
* DHO_STATIC_ROUTES(33)
* DHO_TRAILER_ENCAPSULATION(34)
* DHO_ARP_CACHE_TIMEOUT(35)
* DHO_IEEE802_3_ENCAPSULATION(36)
* DHO_DEFAULT_TCP_TTL(37)
* DHO_TCP_KEEPALIVE_INTERVAL(38)
* DHO_TCP_KEEPALIVE_GARBAGE(39)
* DHO_NIS_SERVERS(41)
* DHO_NTP_SERVERS(42)
* DHO_VENDOR_ENCAPSULATED_OPTIONS(43)
* DHO_NETBIOS_NAME_SERVERS(44)
* DHO_NETBIOS_DD_SERVER(45)
* DHO_NETBIOS_NODE_TYPE(46)
* DHO_NETBIOS_SCOPE(47)
* DHO_FONT_SERVERS(48)
* DHO_X_DISPLAY_MANAGER(49)
* DHO_DHCP_REQUESTED_ADDRESS(50)
* DHO_DHCP_LEASE_TIME(51)
* DHO_DHCP_OPTION_OVERLOAD(52)
* DHO_DHCP_MESSAGE_TYPE(53)
* DHO_DHCP_SERVER_IDENTIFIER(54)
* DHO_DHCP_PARAMETER_REQUEST_LIST(55)
* DHO_DHCP_MESSAGE(56)
* DHO_DHCP_MAX_MESSAGE_SIZE(57)
* DHO_DHCP_RENEWAL_TIME(58)
* DHO_DHCP_REBINDING_TIME(59)
* DHO_VENDOR_CLASS_IDENTIFIER(60)
* DHO_DHCP_CLIENT_IDENTIFIER(61)
* DHO_NWIP_DOMAIN_NAME(62)
* DHO_NWIP_SUBOPTIONS(63)
* DHO_NIS_DOMAIN(64)
* DHO_NIS_SERVER(65)
* DHO_TFTP_SERVER(66)
* DHO_BOOTFILE(67)
* DHO_MOBILE_IP_HOME_AGENT(68)
* DHO_SMTP_SERVER(69)
* DHO_POP3_SERVER(70)
* DHO_NNTP_SERVER(71)
* DHO_WWW_SERVER(72)
* DHO_FINGER_SERVER(73)
* DHO_IRC_SERVER(74)
* DHO_STREETTALK_SERVER(75)
* DHO_STDA_SERVER(76)
* DHO_USER_CLASS(77)
* DHO_FQDN(81)
* DHO_DHCP_AGENT_OPTIONS(82)
* DHO_NDS_SERVERS(85)
* DHO_NDS_TREE_NAME(86)
* DHO_USER_AUTHENTICATION_PROTOCOL(98)
* DHO_AUTO_CONFIGURE(116)
* DHO_NAME_SERVICE_SEARCH(117)
* DHO_SUBNET_SELECTION(118)
* </pre>
*
* <p>These options can be set and get through basic low-level <tt>getOptionRaw</tt> and
* <tt>setOptionRaw</tt> passing <tt>byte[]</tt> structures. Using these functions, data formats
* are under your responsibility. Arrays are always passed by copies (clones) so you can modify
* them freely without side-effects. These functions allow maximum performance, especially
* when copying options from a request datagram to a response datagram.
*
* <h4>Special case: DHO_DHCP_MESSAGE_TYPE</h4>
* The DHCP Message Type (option 53) is supported for the following values
* <pre>
* DHCPDISCOVER(1)
* DHCPOFFER(2)
* DHCPREQUEST(3)
* DHCPDECLINE(4)
* DHCPACK(5)
* DHCPNAK(6)
* DHCPRELEASE(7)
* DHCPINFORM(8)
* DHCPFORCERENEW(9)
* DHCPLEASEQUERY(13)
* </pre>
*
* <h4>DHCP option formats</h4>
*
* A limited set of higher level data-structures are supported. Type checking is enforced
* according to rfc 2132. Check corresponding methods for a list of option codes allowed for
* each datatype.
*
* <blockquote>
* <br>Inet (4 bytes - IPv4 address)
* <br>Inets (X*4 bytes - list of IPv4 addresses)
* <br>Short (2 bytes - short)
* <br>Shorts (X*2 bytes - list of shorts)
* <br>Byte (1 byte)
* <br>Bytes (X bytes - list of 1 byte parameters)
* <br>String (X bytes - ASCII string)
* <br>
* </blockquote>
*
*
* <p><b>Note</b>: this class is not synchronized for maximum performance.
* However, it is unlikely that the same <tt>DHCPPacket</tt> is used in two different
* threads in real life DHPC servers or clients. Multi-threading acces
* to an instance of this class is at your own risk.
*
* <p><b>Limitations</b>: this class doesn't support spanned options or options longer than 256 bytes.
* It does not support options stored in <tt>sname</tt> or <tt>file</tt> fields.
*
* <p>This API is originally a port from my PERL
* <tt><a href="http://search.cpan.org/~shadinger/">Net::DHCP</a></tt> api.
*
* <p><b>Future extensions</b>: IPv6 support, extended data structure TODO...
*
*/
public class DHCPPacket implements Cloneable, Serializable {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(DHCPPacket.class.getName().toLowerCase());
// ----------------------------------------------------------------------
// user defined comment
private String comment; // Free user-defined comment
// ----------------------------------------------------------------------
// static structure of the packet
private byte op; // Op code
private byte htype; // HW address Type
private byte hlen; // hardware address length
private byte hops; // Hw options
private int xid; // transaction id
private short secs; // elapsed time from trying to boot
private short flags; // flags
private byte[] ciaddr; // client IP
private byte[] yiaddr; // your client IP
private byte[] siaddr; // Server IP
private byte[] giaddr; // relay agent IP
private byte[] chaddr; // Client HW address
private byte[] sname; // Optional server host name
private byte[] file; // Boot file name
// ----------------------------------------------------------------------
// options part of the packet
// DHCP options
// Invariant 1: K is identical to V.getCode()
// Invariant 2: V.value is never <tt>null</tt>
// Invariant 3; K is not 0 (PAD) and not -1 (END)
private Map<Byte, DHCPOption> options;
private boolean isDhcp; // well-formed DHCP Packet ?
private boolean truncated; // are the option truncated
// ----------------------------------------------------------------------
// extra bytes for padding
private byte[] padding; // end of packet padding
// ----------------------------------------------------------------------
// Address/port address of the machine, which this datagram is being sent to
// or received from.
private InetAddress address;
private int port;
/**
* Constructor for the <tt>DHCPPacket</tt> class.
*
* <p>This creates an empty <tt>DHCPPacket</tt> datagram.
* All data is default values and the packet is still lacking key data
* to be sent on the wire.
*/
public DHCPPacket() {
this.comment = "";
this.op = BOOTREPLY;
this.htype = HTYPE_ETHER;
this.hlen = 6;
this.ciaddr = new byte[ 4];
this.yiaddr = new byte[ 4];
this.siaddr = new byte[ 4];
this.giaddr = new byte[ 4];
this.chaddr = new byte[ 16];
this.sname = new byte[ 64];
this.file = new byte[128];
this.padding = new byte[0];
this.isDhcp = true;
this.options = new LinkedHashMap<Byte, DHCPOption>();
}
/**
* Factory for creating <tt>DHCPPacket</tt> objects by parsing a
* <tt>DatagramPacket</tt> object.
*
* @param datagram the UDP datagram received to be parsed
* @return the newly create <tt>DHCPPacket</tt> instance
* @throws DHCPBadPacketException the datagram is malformed and cannot be parsed properly.
* @throws IllegalArgumentException datagram is <tt>null</tt>
* @throws IOException
*/
public static DHCPPacket getPacket(DatagramPacket datagram) throws DHCPBadPacketException {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -