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