• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
doNextOp(DhcpStateMachine stateMachine)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      */
buildPacket(int encap, short destUdp, short srcUdp)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      */
finishPacket(ByteBuffer buffer)221     abstract void finishPacket(ByteBuffer buffer);
222 
DhcpPacket(int transId, InetAddress clientIp, InetAddress yourIp, InetAddress nextIp, InetAddress relayIp, byte[] clientMac, boolean broadcast)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      */
getTransactionId()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      */
fillInPacket(int encap, InetAddress destIp, InetAddress srcIp, short destUdp, short srcUdp, ByteBuffer buf, byte requestCode, boolean broadcast)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      */
intAbs(short v)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      */
checksum(ByteBuffer buf, int seed, int start, int end)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      */
addTlv(ByteBuffer buf, byte type, byte value)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      */
addTlv(ByteBuffer buf, byte type, byte[] payload)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      */
addTlv(ByteBuffer buf, byte type, InetAddress addr)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      */
addTlv(ByteBuffer buf, byte type, List<InetAddress> addrs)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      */
addTlv(ByteBuffer buf, byte type, Integer value)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      */
addTlv(ByteBuffer buf, byte type, String str)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      */
addTlvEnd(ByteBuffer buf)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      */
macToString(byte[] mac)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 
toString()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      */
readIpAddress(ByteBuffer packet)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      */
readAsciiString(ByteBuffer buf, int byteCount)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      */
decodeFullPacket(ByteBuffer packet, int pktType)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      */
decodeFullPacket(byte[] packet, int pktType)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      */
buildDiscoverPacket(int encap, int transactionId, byte[] clientMac, boolean broadcast, byte[] expectedParams)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      */
buildOfferPacket(int encap, int transactionId, boolean broadcast, InetAddress serverIpAddr, InetAddress clientIpAddr, byte[] mac, Integer timeout, InetAddress netMask, InetAddress bcAddr, InetAddress gateway, List<InetAddress> dnsServers, InetAddress dhcpServerIdentifier, String domainName)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      */
buildAckPacket(int encap, int transactionId, boolean broadcast, InetAddress serverIpAddr, InetAddress clientIpAddr, byte[] mac, Integer timeout, InetAddress netMask, InetAddress bcAddr, InetAddress gateway, List<InetAddress> dnsServers, InetAddress dhcpServerIdentifier, String domainName)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      */
buildNakPacket(int encap, int transactionId, InetAddress serverIpAddr, InetAddress clientIpAddr, byte[] mac)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      */
buildRequestPacket(int encap, int transactionId, InetAddress clientIp, boolean broadcast, byte[] clientMac, InetAddress requestedIpAddress, InetAddress serverIdentifier, byte[] requestedParams, String hostName)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