Home | History | Annotate | Download | only in dhcp
      1 package android.net.dhcp;
      2 
      3 import java.net.InetAddress;
      4 import java.net.UnknownHostException;
      5 import java.nio.ByteBuffer;
      6 import java.nio.ByteOrder;
      7 import java.nio.charset.StandardCharsets;
      8 import java.nio.ShortBuffer;
      9 
     10 import java.util.ArrayList;
     11 import java.util.List;
     12 
     13 /**
     14  * Defines basic data and operations needed to build and use packets for the
     15  * DHCP protocol.  Subclasses create the specific packets used at each
     16  * stage of the negotiation.
     17  */
     18 abstract class DhcpPacket {
     19     protected static final String TAG = "DhcpPacket";
     20 
     21     /**
     22      * Packet encapsulations.
     23      */
     24     public static final int ENCAP_L2 = 0;    // EthernetII header included
     25     public static final int ENCAP_L3 = 1;    // IP/UDP header included
     26     public static final int ENCAP_BOOTP = 2; // BOOTP contents only
     27 
     28     /**
     29      * IP layer definitions.
     30      */
     31     private static final byte IP_TYPE_UDP = (byte) 0x11;
     32 
     33     /**
     34      * IP: Version 4, Header Length 20 bytes
     35      */
     36     private static final byte IP_VERSION_HEADER_LEN = (byte) 0x45;
     37 
     38     /**
     39      * IP: Flags 0, Fragment Offset 0, Don't Fragment
     40      */
     41     private static final short IP_FLAGS_OFFSET = (short) 0x4000;
     42 
     43     /**
     44      * IP: TOS
     45      */
     46     private static final byte IP_TOS_LOWDELAY = (byte) 0x10;
     47 
     48     /**
     49      * IP: TTL -- use default 64 from RFC1340
     50      */
     51     private static final byte IP_TTL = (byte) 0x40;
     52 
     53     /**
     54      * The client DHCP port.
     55      */
     56     static final short DHCP_CLIENT = (short) 68;
     57 
     58     /**
     59      * The server DHCP port.
     60      */
     61     static final short DHCP_SERVER = (short) 67;
     62 
     63     /**
     64      * The message op code indicating a request from a client.
     65      */
     66     protected static final byte DHCP_BOOTREQUEST = (byte) 1;
     67 
     68     /**
     69      * The message op code indicating a response from the server.
     70      */
     71     protected static final byte DHCP_BOOTREPLY = (byte) 2;
     72 
     73     /**
     74      * The code type used to identify an Ethernet MAC address in the
     75      * Client-ID field.
     76      */
     77     protected static final byte CLIENT_ID_ETHER = (byte) 1;
     78 
     79     /**
     80      * The maximum length of a packet that can be constructed.
     81      */
     82     protected static final int MAX_LENGTH = 1500;
     83 
     84     /**
     85      * DHCP Optional Type: DHCP Subnet Mask
     86      */
     87     protected static final byte DHCP_SUBNET_MASK = 1;
     88     protected InetAddress mSubnetMask;
     89 
     90     /**
     91      * DHCP Optional Type: DHCP Router
     92      */
     93     protected static final byte DHCP_ROUTER = 3;
     94     protected InetAddress mGateway;
     95 
     96     /**
     97      * DHCP Optional Type: DHCP DNS Server
     98      */
     99     protected static final byte DHCP_DNS_SERVER = 6;
    100     protected List<InetAddress> mDnsServers;
    101 
    102     /**
    103      * DHCP Optional Type: DHCP Host Name
    104      */
    105     protected static final byte DHCP_HOST_NAME = 12;
    106     protected String mHostName;
    107 
    108     /**
    109      * DHCP Optional Type: DHCP DOMAIN NAME
    110      */
    111     protected static final byte DHCP_DOMAIN_NAME = 15;
    112     protected String mDomainName;
    113 
    114     /**
    115      * DHCP Optional Type: DHCP BROADCAST ADDRESS
    116      */
    117     protected static final byte DHCP_BROADCAST_ADDRESS = 28;
    118     protected InetAddress mBroadcastAddress;
    119 
    120     /**
    121      * DHCP Optional Type: DHCP Requested IP Address
    122      */
    123     protected static final byte DHCP_REQUESTED_IP = 50;
    124     protected InetAddress mRequestedIp;
    125 
    126     /**
    127      * DHCP Optional Type: DHCP Lease Time
    128      */
    129     protected static final byte DHCP_LEASE_TIME = 51;
    130     protected Integer mLeaseTime;
    131 
    132     /**
    133      * DHCP Optional Type: DHCP Message Type
    134      */
    135     protected static final byte DHCP_MESSAGE_TYPE = 53;
    136     // the actual type values
    137     protected static final byte DHCP_MESSAGE_TYPE_DISCOVER = 1;
    138     protected static final byte DHCP_MESSAGE_TYPE_OFFER = 2;
    139     protected static final byte DHCP_MESSAGE_TYPE_REQUEST = 3;
    140     protected static final byte DHCP_MESSAGE_TYPE_DECLINE = 4;
    141     protected static final byte DHCP_MESSAGE_TYPE_ACK = 5;
    142     protected static final byte DHCP_MESSAGE_TYPE_NAK = 6;
    143     protected static final byte DHCP_MESSAGE_TYPE_INFORM = 8;
    144 
    145     /**
    146      * DHCP Optional Type: DHCP Server Identifier
    147      */
    148     protected static final byte DHCP_SERVER_IDENTIFIER = 54;
    149     protected InetAddress mServerIdentifier;
    150 
    151     /**
    152      * DHCP Optional Type: DHCP Parameter List
    153      */
    154     protected static final byte DHCP_PARAMETER_LIST = 55;
    155     protected byte[] mRequestedParams;
    156 
    157     /**
    158      * DHCP Optional Type: DHCP MESSAGE
    159      */
    160     protected static final byte DHCP_MESSAGE = 56;
    161     protected String mMessage;
    162 
    163     /**
    164      * DHCP Optional Type: DHCP Renewal Time Value
    165      */
    166     protected static final byte DHCP_RENEWAL_TIME = 58;
    167 
    168     /**
    169      * DHCP Optional Type: Vendor Class Identifier
    170      */
    171     protected static final byte DHCP_VENDOR_CLASS_ID = 60;
    172 
    173     /**
    174      * DHCP Optional Type: DHCP Client Identifier
    175      */
    176     protected static final byte DHCP_CLIENT_IDENTIFIER = 61;
    177 
    178     /**
    179      * The transaction identifier used in this particular DHCP negotiation
    180      */
    181     protected final int mTransId;
    182 
    183     /**
    184      * The IP address of the client host.  This address is typically
    185      * proposed by the client (from an earlier DHCP negotiation) or
    186      * supplied by the server.
    187      */
    188     protected final InetAddress mClientIp;
    189     protected final InetAddress mYourIp;
    190     private final InetAddress mNextIp;
    191     private final InetAddress mRelayIp;
    192 
    193     /**
    194      * Does the client request a broadcast response?
    195      */
    196     protected boolean mBroadcast;
    197 
    198     /**
    199      * The six-octet MAC of the client.
    200      */
    201     protected final byte[] mClientMac;
    202 
    203     /**
    204      * Asks the packet object to signal the next operation in the DHCP
    205      * protocol.  The available actions are methods defined in the
    206      * DhcpStateMachine interface.
    207      */
    208     public abstract void doNextOp(DhcpStateMachine stateMachine);
    209 
    210     /**
    211      * Asks the packet object to create a ByteBuffer serialization of
    212      * the packet for transmission.
    213      */
    214     public abstract ByteBuffer buildPacket(int encap, short destUdp,
    215         short srcUdp);
    216 
    217     /**
    218      * Allows the concrete class to fill in packet-type-specific details,
    219      * typically optional parameters at the end of the packet.
    220      */
    221     abstract void finishPacket(ByteBuffer buffer);
    222 
    223     protected DhcpPacket(int transId, InetAddress clientIp, InetAddress yourIp,
    224                          InetAddress nextIp, InetAddress relayIp,
    225                          byte[] clientMac, boolean broadcast) {
    226         mTransId = transId;
    227         mClientIp = clientIp;
    228         mYourIp = yourIp;
    229         mNextIp = nextIp;
    230         mRelayIp = relayIp;
    231         mClientMac = clientMac;
    232         mBroadcast = broadcast;
    233     }
    234 
    235     /**
    236      * Returns the transaction ID.
    237      */
    238     public int getTransactionId() {
    239         return mTransId;
    240     }
    241 
    242     /**
    243      * Creates a new L3 packet (including IP header) containing the
    244      * DHCP udp packet.  This method relies upon the delegated method
    245      * finishPacket() to insert the per-packet contents.
    246      */
    247     protected void fillInPacket(int encap, InetAddress destIp,
    248         InetAddress srcIp, short destUdp, short srcUdp, ByteBuffer buf,
    249         byte requestCode, boolean broadcast) {
    250         byte[] destIpArray = destIp.getAddress();
    251         byte[] srcIpArray = srcIp.getAddress();
    252         int ipLengthOffset = 0;
    253         int ipChecksumOffset = 0;
    254         int endIpHeader = 0;
    255         int udpHeaderOffset = 0;
    256         int udpLengthOffset = 0;
    257         int udpChecksumOffset = 0;
    258 
    259         buf.clear();
    260         buf.order(ByteOrder.BIG_ENDIAN);
    261 
    262         // if a full IP packet needs to be generated, put the IP & UDP
    263         // headers in place, and pre-populate with artificial values
    264         // needed to seed the IP checksum.
    265         if (encap == ENCAP_L3) {
    266             // fake IP header, used in the IP-header checksum
    267             buf.put(IP_VERSION_HEADER_LEN);
    268             buf.put(IP_TOS_LOWDELAY);    // tos: IPTOS_LOWDELAY
    269             ipLengthOffset = buf.position();
    270             buf.putShort((short)0);  // length
    271             buf.putShort((short)0);  // id
    272             buf.putShort(IP_FLAGS_OFFSET); // ip offset: don't fragment
    273             buf.put(IP_TTL);    // TTL: use default 64 from RFC1340
    274             buf.put(IP_TYPE_UDP);
    275             ipChecksumOffset = buf.position();
    276             buf.putShort((short) 0); // checksum
    277 
    278             buf.put(srcIpArray);
    279             buf.put(destIpArray);
    280             endIpHeader = buf.position();
    281 
    282             // UDP header
    283             udpHeaderOffset = buf.position();
    284             buf.putShort(srcUdp);
    285             buf.putShort(destUdp);
    286             udpLengthOffset = buf.position();
    287             buf.putShort((short) 0); // length
    288             udpChecksumOffset = buf.position();
    289             buf.putShort((short) 0); // UDP checksum -- initially zero
    290         }
    291 
    292         // DHCP payload
    293         buf.put(requestCode);
    294         buf.put((byte) 1); // Hardware Type: Ethernet
    295         buf.put((byte) mClientMac.length); // Hardware Address Length
    296         buf.put((byte) 0); // Hop Count
    297         buf.putInt(mTransId);  // Transaction ID
    298         buf.putShort((short) 0); // Elapsed Seconds
    299 
    300         if (broadcast) {
    301             buf.putShort((short) 0x8000); // Flags
    302         } else {
    303             buf.putShort((short) 0x0000); // Flags
    304         }
    305 
    306         buf.put(mClientIp.getAddress());
    307         buf.put(mYourIp.getAddress());
    308         buf.put(mNextIp.getAddress());
    309         buf.put(mRelayIp.getAddress());
    310         buf.put(mClientMac);
    311         buf.position(buf.position() +
    312                      (16 - mClientMac.length) // pad addr to 16 bytes
    313                      + 64     // empty server host name (64 bytes)
    314                      + 128);  // empty boot file name (128 bytes)
    315         buf.putInt(0x63825363); // magic number
    316         finishPacket(buf);
    317 
    318         // round up to an even number of octets
    319         if ((buf.position() & 1) == 1) {
    320             buf.put((byte) 0);
    321         }
    322 
    323         // If an IP packet is being built, the IP & UDP checksums must be
    324         // computed.
    325         if (encap == ENCAP_L3) {
    326             // fix UDP header: insert length
    327             short udpLen = (short)(buf.position() - udpHeaderOffset);
    328             buf.putShort(udpLengthOffset, udpLen);
    329             // fix UDP header: checksum
    330             // checksum for UDP at udpChecksumOffset
    331             int udpSeed = 0;
    332 
    333             // apply IPv4 pseudo-header.  Read IP address src and destination
    334             // values from the IP header and accumulate checksum.
    335             udpSeed += intAbs(buf.getShort(ipChecksumOffset + 2));
    336             udpSeed += intAbs(buf.getShort(ipChecksumOffset + 4));
    337             udpSeed += intAbs(buf.getShort(ipChecksumOffset + 6));
    338             udpSeed += intAbs(buf.getShort(ipChecksumOffset + 8));
    339 
    340             // accumulate extra data for the pseudo-header
    341             udpSeed += IP_TYPE_UDP;
    342             udpSeed += udpLen;
    343             // and compute UDP checksum
    344             buf.putShort(udpChecksumOffset, (short) checksum(buf, udpSeed,
    345                                                              udpHeaderOffset,
    346                                                              buf.position()));
    347             // fix IP header: insert length
    348             buf.putShort(ipLengthOffset, (short)buf.position());
    349             // fixup IP-header checksum
    350             buf.putShort(ipChecksumOffset,
    351                          (short) checksum(buf, 0, 0, endIpHeader));
    352         }
    353     }
    354 
    355     /**
    356      * Converts a signed short value to an unsigned int value.  Needed
    357      * because Java does not have unsigned types.
    358      */
    359     private int intAbs(short v) {
    360         if (v < 0) {
    361             int r = v + 65536;
    362             return r;
    363         } else {
    364             return(v);
    365         }
    366     }
    367 
    368     /**
    369      * Performs an IP checksum (used in IP header and across UDP
    370      * payload) on the specified portion of a ByteBuffer.  The seed
    371      * allows the checksum to commence with a specified value.
    372      */
    373     private int checksum(ByteBuffer buf, int seed, int start, int end) {
    374         int sum = seed;
    375         int bufPosition = buf.position();
    376 
    377         // set position of original ByteBuffer, so that the ShortBuffer
    378         // will be correctly initialized
    379         buf.position(start);
    380         ShortBuffer shortBuf = buf.asShortBuffer();
    381 
    382         // re-set ByteBuffer position
    383         buf.position(bufPosition);
    384 
    385         short[] shortArray = new short[(end - start) / 2];
    386         shortBuf.get(shortArray);
    387 
    388         for (short s : shortArray) {
    389             sum += intAbs(s);
    390         }
    391 
    392         start += shortArray.length * 2;
    393 
    394         // see if a singleton byte remains
    395         if (end != start) {
    396             short b = buf.get(start);
    397 
    398             // make it unsigned
    399             if (b < 0) {
    400                 b += 256;
    401             }
    402 
    403             sum += b * 256;
    404         }
    405 
    406         sum = ((sum >> 16) & 0xFFFF) + (sum & 0xFFFF);
    407         sum = ((sum + ((sum >> 16) & 0xFFFF)) & 0xFFFF);
    408         int negated = ~sum;
    409         return intAbs((short) negated);
    410     }
    411 
    412     /**
    413      * Adds an optional parameter containing a single byte value.
    414      */
    415     protected void addTlv(ByteBuffer buf, byte type, byte value) {
    416         buf.put(type);
    417         buf.put((byte) 1);
    418         buf.put(value);
    419     }
    420 
    421     /**
    422      * Adds an optional parameter containing an array of bytes.
    423      */
    424     protected void addTlv(ByteBuffer buf, byte type, byte[] payload) {
    425         if (payload != null) {
    426             buf.put(type);
    427             buf.put((byte) payload.length);
    428             buf.put(payload);
    429         }
    430     }
    431 
    432     /**
    433      * Adds an optional parameter containing an IP address.
    434      */
    435     protected void addTlv(ByteBuffer buf, byte type, InetAddress addr) {
    436         if (addr != null) {
    437             addTlv(buf, type, addr.getAddress());
    438         }
    439     }
    440 
    441     /**
    442      * Adds an optional parameter containing a list of IP addresses.
    443      */
    444     protected void addTlv(ByteBuffer buf, byte type, List<InetAddress> addrs) {
    445         if (addrs != null && addrs.size() > 0) {
    446             buf.put(type);
    447             buf.put((byte)(4 * addrs.size()));
    448 
    449             for (InetAddress addr : addrs) {
    450                 buf.put(addr.getAddress());
    451             }
    452         }
    453     }
    454 
    455     /**
    456      * Adds an optional parameter containing a simple integer
    457      */
    458     protected void addTlv(ByteBuffer buf, byte type, Integer value) {
    459         if (value != null) {
    460             buf.put(type);
    461             buf.put((byte) 4);
    462             buf.putInt(value.intValue());
    463         }
    464     }
    465 
    466     /**
    467      * Adds an optional parameter containing and ASCII string.
    468      */
    469     protected void addTlv(ByteBuffer buf, byte type, String str) {
    470         if (str != null) {
    471             buf.put(type);
    472             buf.put((byte) str.length());
    473 
    474             for (int i = 0; i < str.length(); i++) {
    475                 buf.put((byte) str.charAt(i));
    476             }
    477         }
    478     }
    479 
    480     /**
    481      * Adds the special end-of-optional-parameters indicator.
    482      */
    483     protected void addTlvEnd(ByteBuffer buf) {
    484         buf.put((byte) 0xFF);
    485     }
    486 
    487     /**
    488      * Converts a MAC from an array of octets to an ASCII string.
    489      */
    490     public static String macToString(byte[] mac) {
    491         String macAddr = "";
    492 
    493         for (int i = 0; i < mac.length; i++) {
    494             String hexString = "0" + Integer.toHexString(mac[i]);
    495 
    496             // substring operation grabs the last 2 digits: this
    497             // allows signed bytes to be converted correctly.
    498             macAddr += hexString.substring(hexString.length() - 2);
    499 
    500             if (i != (mac.length - 1)) {
    501                 macAddr += ":";
    502             }
    503         }
    504 
    505         return macAddr;
    506     }
    507 
    508     public String toString() {
    509         String macAddr = macToString(mClientMac);
    510 
    511         return macAddr;
    512     }
    513 
    514     /**
    515      * Reads a four-octet value from a ByteBuffer and construct
    516      * an IPv4 address from that value.
    517      */
    518     private static InetAddress readIpAddress(ByteBuffer packet) {
    519         InetAddress result = null;
    520         byte[] ipAddr = new byte[4];
    521         packet.get(ipAddr);
    522 
    523         try {
    524             result = InetAddress.getByAddress(ipAddr);
    525         } catch (UnknownHostException ex) {
    526             // ipAddr is numeric, so this should not be
    527             // triggered.  However, if it is, just nullify
    528             result = null;
    529         }
    530 
    531         return result;
    532     }
    533 
    534     /**
    535      * Reads a string of specified length from the buffer.
    536      */
    537     private static String readAsciiString(ByteBuffer buf, int byteCount) {
    538         byte[] bytes = new byte[byteCount];
    539         buf.get(bytes);
    540         return new String(bytes, 0, bytes.length, StandardCharsets.US_ASCII);
    541     }
    542 
    543     /**
    544      * Creates a concrete DhcpPacket from the supplied ByteBuffer.  The
    545      * buffer may have an L2 encapsulation (which is the full EthernetII
    546      * format starting with the source-address MAC) or an L3 encapsulation
    547      * (which starts with the IP header).
    548      * <br>
    549      * A subset of the optional parameters are parsed and are stored
    550      * in object fields.
    551      */
    552     public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType)
    553     {
    554         // bootp parameters
    555         int transactionId;
    556         InetAddress clientIp;
    557         InetAddress yourIp;
    558         InetAddress nextIp;
    559         InetAddress relayIp;
    560         byte[] clientMac;
    561         List<InetAddress> dnsServers = new ArrayList<InetAddress>();
    562         InetAddress gateway = null; // aka router
    563         Integer leaseTime = null;
    564         InetAddress serverIdentifier = null;
    565         InetAddress netMask = null;
    566         String message = null;
    567         String vendorId = null;
    568         byte[] expectedParams = null;
    569         String hostName = null;
    570         String domainName = null;
    571         InetAddress ipSrc = null;
    572         InetAddress ipDst = null;
    573         InetAddress bcAddr = null;
    574         InetAddress requestedIp = null;
    575 
    576         // dhcp options
    577         byte dhcpType = (byte) 0xFF;
    578 
    579         packet.order(ByteOrder.BIG_ENDIAN);
    580 
    581         // check to see if we need to parse L2, IP, and UDP encaps
    582         if (pktType == ENCAP_L2) {
    583             // System.out.println("buffer len " + packet.limit());
    584             byte[] l2dst = new byte[6];
    585             byte[] l2src = new byte[6];
    586 
    587             packet.get(l2dst);
    588             packet.get(l2src);
    589 
    590             short l2type = packet.getShort();
    591 
    592             if (l2type != 0x0800)
    593                 return null;
    594         }
    595 
    596         if ((pktType == ENCAP_L2) || (pktType == ENCAP_L3)) {
    597             // assume l2type is 0x0800, i.e. IP
    598             byte ipType = packet.get();
    599             // System.out.println("ipType is " + ipType);
    600             byte ipDiffServicesField = packet.get();
    601             short ipTotalLength = packet.getShort();
    602             short ipIdentification = packet.getShort();
    603             byte ipFlags = packet.get();
    604             byte ipFragOffset = packet.get();
    605             byte ipTTL = packet.get();
    606             byte ipProto = packet.get();
    607             short ipChksm = packet.getShort();
    608 
    609             ipSrc = readIpAddress(packet);
    610             ipDst = readIpAddress(packet);
    611 
    612             if (ipProto != IP_TYPE_UDP) // UDP
    613                 return null;
    614 
    615             // assume UDP
    616             short udpSrcPort = packet.getShort();
    617             short udpDstPort = packet.getShort();
    618             short udpLen = packet.getShort();
    619             short udpChkSum = packet.getShort();
    620 
    621             if ((udpSrcPort != DHCP_SERVER) && (udpSrcPort != DHCP_CLIENT))
    622                 return null;
    623         }
    624 
    625         // assume bootp
    626         byte type = packet.get();
    627         byte hwType = packet.get();
    628         byte addrLen = packet.get();
    629         byte hops = packet.get();
    630         transactionId = packet.getInt();
    631         short elapsed = packet.getShort();
    632         short bootpFlags = packet.getShort();
    633         boolean broadcast = (bootpFlags & 0x8000) != 0;
    634         byte[] ipv4addr = new byte[4];
    635 
    636         try {
    637             packet.get(ipv4addr);
    638             clientIp = InetAddress.getByAddress(ipv4addr);
    639             packet.get(ipv4addr);
    640             yourIp = InetAddress.getByAddress(ipv4addr);
    641             packet.get(ipv4addr);
    642             nextIp = InetAddress.getByAddress(ipv4addr);
    643             packet.get(ipv4addr);
    644             relayIp = InetAddress.getByAddress(ipv4addr);
    645         } catch (UnknownHostException ex) {
    646             return null;
    647         }
    648 
    649         clientMac = new byte[addrLen];
    650         packet.get(clientMac);
    651 
    652         // skip over address padding (16 octets allocated)
    653         packet.position(packet.position() + (16 - addrLen)
    654                         + 64    // skip server host name (64 chars)
    655                         + 128); // skip boot file name (128 chars)
    656 
    657         int dhcpMagicCookie = packet.getInt();
    658 
    659         if (dhcpMagicCookie !=  0x63825363)
    660             return null;
    661 
    662         // parse options
    663         boolean notFinishedOptions = true;
    664 
    665         while ((packet.position() < packet.limit()) && notFinishedOptions) {
    666             byte optionType = packet.get();
    667 
    668             if (optionType == (byte) 0xFF) {
    669                 notFinishedOptions = false;
    670             } else {
    671                 byte optionLen = packet.get();
    672                 int expectedLen = 0;
    673 
    674                 switch(optionType) {
    675                     case DHCP_SUBNET_MASK:
    676                         netMask = readIpAddress(packet);
    677                         expectedLen = 4;
    678                         break;
    679                     case DHCP_ROUTER:
    680                         gateway = readIpAddress(packet);
    681                         expectedLen = 4;
    682                         break;
    683                     case DHCP_DNS_SERVER:
    684                         expectedLen = 0;
    685 
    686                         for (expectedLen = 0; expectedLen < optionLen;
    687                              expectedLen += 4) {
    688                             dnsServers.add(readIpAddress(packet));
    689                         }
    690                         break;
    691                     case DHCP_HOST_NAME:
    692                         expectedLen = optionLen;
    693                         hostName = readAsciiString(packet, optionLen);
    694                         break;
    695                     case DHCP_DOMAIN_NAME:
    696                         expectedLen = optionLen;
    697                         domainName = readAsciiString(packet, optionLen);
    698                         break;
    699                     case DHCP_BROADCAST_ADDRESS:
    700                         bcAddr = readIpAddress(packet);
    701                         expectedLen = 4;
    702                         break;
    703                     case DHCP_REQUESTED_IP:
    704                         requestedIp = readIpAddress(packet);
    705                         expectedLen = 4;
    706                         break;
    707                     case DHCP_LEASE_TIME:
    708                         leaseTime = Integer.valueOf(packet.getInt());
    709                         expectedLen = 4;
    710                         break;
    711                     case DHCP_MESSAGE_TYPE:
    712                         dhcpType = packet.get();
    713                         expectedLen = 1;
    714                         break;
    715                     case DHCP_SERVER_IDENTIFIER:
    716                         serverIdentifier = readIpAddress(packet);
    717                         expectedLen = 4;
    718                         break;
    719                     case DHCP_PARAMETER_LIST:
    720                         expectedParams = new byte[optionLen];
    721                         packet.get(expectedParams);
    722                         expectedLen = optionLen;
    723                         break;
    724                     case DHCP_MESSAGE:
    725                         expectedLen = optionLen;
    726                         message = readAsciiString(packet, optionLen);
    727                         break;
    728                     case DHCP_VENDOR_CLASS_ID:
    729                         expectedLen = optionLen;
    730                         vendorId = readAsciiString(packet, optionLen);
    731                         break;
    732                     case DHCP_CLIENT_IDENTIFIER: { // Client identifier
    733                         byte[] id = new byte[optionLen];
    734                         packet.get(id);
    735                         expectedLen = optionLen;
    736                     } break;
    737                     default:
    738                         // ignore any other parameters
    739                         for (int i = 0; i < optionLen; i++) {
    740                             expectedLen++;
    741                             byte throwaway = packet.get();
    742                         }
    743                 }
    744 
    745                 if (expectedLen != optionLen) {
    746                     return null;
    747                 }
    748             }
    749         }
    750 
    751         DhcpPacket newPacket;
    752 
    753         switch(dhcpType) {
    754             case -1: return null;
    755             case DHCP_MESSAGE_TYPE_DISCOVER:
    756                 newPacket = new DhcpDiscoverPacket(
    757                     transactionId, clientMac, broadcast);
    758                 break;
    759             case DHCP_MESSAGE_TYPE_OFFER:
    760                 newPacket = new DhcpOfferPacket(
    761                     transactionId, broadcast, ipSrc, yourIp, clientMac);
    762                 break;
    763             case DHCP_MESSAGE_TYPE_REQUEST:
    764                 newPacket = new DhcpRequestPacket(
    765                     transactionId, clientIp, clientMac, broadcast);
    766                 break;
    767             case DHCP_MESSAGE_TYPE_DECLINE:
    768                 newPacket = new DhcpDeclinePacket(
    769                     transactionId, clientIp, yourIp, nextIp, relayIp,
    770                     clientMac);
    771                 break;
    772             case DHCP_MESSAGE_TYPE_ACK:
    773                 newPacket = new DhcpAckPacket(
    774                     transactionId, broadcast, ipSrc, yourIp, clientMac);
    775                 break;
    776             case DHCP_MESSAGE_TYPE_NAK:
    777                 newPacket = new DhcpNakPacket(
    778                     transactionId, clientIp, yourIp, nextIp, relayIp,
    779                     clientMac);
    780                 break;
    781             case DHCP_MESSAGE_TYPE_INFORM:
    782                 newPacket = new DhcpInformPacket(
    783                     transactionId, clientIp, yourIp, nextIp, relayIp,
    784                     clientMac);
    785                 break;
    786             default:
    787                 System.out.println("Unimplemented type: " + dhcpType);
    788                 return null;
    789         }
    790 
    791         newPacket.mBroadcastAddress = bcAddr;
    792         newPacket.mDnsServers = dnsServers;
    793         newPacket.mDomainName = domainName;
    794         newPacket.mGateway = gateway;
    795         newPacket.mHostName = hostName;
    796         newPacket.mLeaseTime = leaseTime;
    797         newPacket.mMessage = message;
    798         newPacket.mRequestedIp = requestedIp;
    799         newPacket.mRequestedParams = expectedParams;
    800         newPacket.mServerIdentifier = serverIdentifier;
    801         newPacket.mSubnetMask = netMask;
    802         return newPacket;
    803     }
    804 
    805     /**
    806      * Parse a packet from an array of bytes.
    807      */
    808     public static DhcpPacket decodeFullPacket(byte[] packet, int pktType)
    809     {
    810         ByteBuffer buffer = ByteBuffer.wrap(packet).order(ByteOrder.BIG_ENDIAN);
    811         return decodeFullPacket(buffer, pktType);
    812     }
    813 
    814     /**
    815      * Builds a DHCP-DISCOVER packet from the required specified
    816      * parameters.
    817      */
    818     public static ByteBuffer buildDiscoverPacket(int encap, int transactionId,
    819         byte[] clientMac, boolean broadcast, byte[] expectedParams) {
    820         DhcpPacket pkt = new DhcpDiscoverPacket(
    821             transactionId, clientMac, broadcast);
    822         pkt.mRequestedParams = expectedParams;
    823         return pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT);
    824     }
    825 
    826     /**
    827      * Builds a DHCP-OFFER packet from the required specified
    828      * parameters.
    829      */
    830     public static ByteBuffer buildOfferPacket(int encap, int transactionId,
    831         boolean broadcast, InetAddress serverIpAddr, InetAddress clientIpAddr,
    832         byte[] mac, Integer timeout, InetAddress netMask, InetAddress bcAddr,
    833         InetAddress gateway, List<InetAddress> dnsServers,
    834         InetAddress dhcpServerIdentifier, String domainName) {
    835         DhcpPacket pkt = new DhcpOfferPacket(
    836             transactionId, broadcast, serverIpAddr, clientIpAddr, mac);
    837         pkt.mGateway = gateway;
    838         pkt.mDnsServers = dnsServers;
    839         pkt.mLeaseTime = timeout;
    840         pkt.mDomainName = domainName;
    841         pkt.mServerIdentifier = dhcpServerIdentifier;
    842         pkt.mSubnetMask = netMask;
    843         pkt.mBroadcastAddress = bcAddr;
    844         return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
    845     }
    846 
    847     /**
    848      * Builds a DHCP-ACK packet from the required specified parameters.
    849      */
    850     public static ByteBuffer buildAckPacket(int encap, int transactionId,
    851         boolean broadcast, InetAddress serverIpAddr, InetAddress clientIpAddr,
    852         byte[] mac, Integer timeout, InetAddress netMask, InetAddress bcAddr,
    853         InetAddress gateway, List<InetAddress> dnsServers,
    854         InetAddress dhcpServerIdentifier, String domainName) {
    855         DhcpPacket pkt = new DhcpAckPacket(
    856             transactionId, broadcast, serverIpAddr, clientIpAddr, mac);
    857         pkt.mGateway = gateway;
    858         pkt.mDnsServers = dnsServers;
    859         pkt.mLeaseTime = timeout;
    860         pkt.mDomainName = domainName;
    861         pkt.mSubnetMask = netMask;
    862         pkt.mServerIdentifier = dhcpServerIdentifier;
    863         pkt.mBroadcastAddress = bcAddr;
    864         return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
    865     }
    866 
    867     /**
    868      * Builds a DHCP-NAK packet from the required specified parameters.
    869      */
    870     public static ByteBuffer buildNakPacket(int encap, int transactionId,
    871         InetAddress serverIpAddr, InetAddress clientIpAddr, byte[] mac) {
    872         DhcpPacket pkt = new DhcpNakPacket(transactionId, clientIpAddr,
    873             serverIpAddr, serverIpAddr, serverIpAddr, mac);
    874         pkt.mMessage = "requested address not available";
    875         pkt.mRequestedIp = clientIpAddr;
    876         return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
    877     }
    878 
    879     /**
    880      * Builds a DHCP-REQUEST packet from the required specified parameters.
    881      */
    882     public static ByteBuffer buildRequestPacket(int encap,
    883         int transactionId, InetAddress clientIp, boolean broadcast,
    884         byte[] clientMac, InetAddress requestedIpAddress,
    885         InetAddress serverIdentifier, byte[] requestedParams, String hostName) {
    886         DhcpPacket pkt = new DhcpRequestPacket(transactionId, clientIp,
    887             clientMac, broadcast);
    888         pkt.mRequestedIp = requestedIpAddress;
    889         pkt.mServerIdentifier = serverIdentifier;
    890         pkt.mHostName = hostName;
    891         pkt.mRequestedParams = requestedParams;
    892         ByteBuffer result = pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT);
    893         return result;
    894     }
    895 }
    896