1package android.net.dhcp;
2
3import android.util.Log;
4
5import java.io.UnsupportedEncodingException;
6import java.net.InetAddress;
7import java.net.UnknownHostException;
8import java.nio.ByteBuffer;
9import java.nio.ByteOrder;
10import java.nio.charset.Charsets;
11import java.nio.ShortBuffer;
12
13import java.util.ArrayList;
14import 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 */
21abstract 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, Charsets.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