1package android.net.dhcp;
2
3import java.net.InetAddress;
4import java.net.UnknownHostException;
5import java.nio.ByteBuffer;
6import java.nio.ByteOrder;
7import java.nio.charset.StandardCharsets;
8import java.nio.ShortBuffer;
9
10import java.util.ArrayList;
11import 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 */
18abstract 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