/*++

Copyright (c) 2000 Microsoft Corporation

Module Name:

    dhcpdump.c

Abstract:

    Dump DHCP message content to the debugger

Revision History:

    05/25/2000 davidx
        Created it.

--*/

#include "precomp.h"

//
// Whether to dump incoming and outgoing DHCP messages
//
#if DBG

INT _dhcpDumpFlag = FALSE;

PRIVATE VOID
DhcpDumpByteArray(
    const BYTE* buf,
    UINT buflen
    )

/*++

Routine Description:

    Dump a byte array in hexdecimal format

Arguments:

    buf - Points to the data buffer
    buflen - Buffer length

Return Value:

    NONE

--*/

{
    for ( ; buflen--; buf++) {
        DbgPrint("%02x", *buf);
    }
}


PRIVATE VOID
DhcpDumpOption(
    IN const BYTE* buf,
    IN UINT buflen,
    OUT BYTE* overload
    )

/*++

Routine Description:

    Dump DHCP options

Arguments:

    buf - Points to the option data buffer
    buflen - Data buffer length
    overload - Returns the option overload flags

Return Value:

    NONE

--*/

{
    static PCSTR dhcpMessageTypeStrs[] = {
        "***",
        "DHCPDISCOVER",
        "DHCPOFFER",
        "DHCPREQUEST",
        "DHCPDECLINE",
        "DHCPACK",
        "DHCPNAK",
        "DHCPRELEASE",
        "DHCPINFORM"
    };

    UINT tag, len, val;

    while (buflen && *buf != DHCPOPT_END) {
        // Special case for DHCPOPT_PAD - single byte
        if (*buf == DHCPOPT_PAD) {
            DbgPrint("    PAD\n");
            buflen--, buf++;
            continue;
        }

        // Check option length
        if (buflen < 2 || buflen-2 < (len = buf[1])) goto badopt;

        switch (tag = buf[0]) {
        case DHCPOPT_DHCP_MESSAGE_TYPE:
            if (len != 1) goto badopt;
            val = buf[2];
            if (val > 0 && val < ARRAYCOUNT(dhcpMessageTypeStrs)) {
                DbgPrint("    %s", dhcpMessageTypeStrs[val]);
            } else {
                DbgPrint("    Unknown DHCP message type: %d", val);
            }
            break;

        case DHCPOPT_FIELD_OVERLOAD:
            if (len != 1) goto badopt;
            DbgPrint("    OVERLOAD: %d", buf[2]);
            if (overload)
                *overload = buf[2];
            else
                DbgPrint(" !!!");
            break;

        case DHCPOPT_REQUESTED_IPADDR:
        case DHCPOPT_SERVERID:
            if (len != 4) goto badopt;
            DbgPrint("    %s %d.%d.%d.%d",
                (tag == DHCPOPT_SERVERID) ? "SERVERID" : "REQUEST IP ADDR",
                buf[2], buf[3], buf[4], buf[5]);
            break;

        default:
            DbgPrint("    %d - ", tag);
            DhcpDumpByteArray(buf+2, len);
            break;
        }

        DbgPrint("\n");
        buf += len+2;
        buflen -= len+2;
    }

    if (buflen == 0) {
        DbgPrint("!!! Missing 'end' option\n");
    } else {
        do {
            buflen--, buf++;
        } while (buflen && *buf == 0);

        if (buflen != 0)
            DbgPrint("!!! Extra data after 'end' option\n");
    }
    return;

badopt:
    DbgPrint("!!! Bad DHCP option data\n");
}


VOID
DhcpDumpMessage(
    const DhcpMessage* msg,
    UINT msglen
    )

/*++

Routine Description:

    Dump the content of a DHCP message

Arguments:

    msg - Points to the DHCP message
    msglen - Message length

Return Value:

    NONE

--*/

{
    const BYTE* option;
    BYTE overload = 0;

    if (!_dhcpDumpFlag) return;

    //
    // Sanity check
    //
    if (msglen < DHCPHDRLEN) {
        DbgPrint("!!! DHCP message too small: %d bytes\n", msglen);
        return;
    }

    //
    // Dump out fixed header information
    //
    DbgPrint("DHCP message: ");
    if (msg->op == BOOTREQUEST)
        DbgPrint("BOOTREQUEST\n");
    else if (msg->op == BOOTREPLY)
        DbgPrint("BOOTREPLY\n");
    else
        DbgPrint("%d\n", msg->op);

    DbgPrint("  htype: %d\n", msg->htype);
    if (msg->hlen > sizeof(msg->chaddr)) {
        DbgPrint("!!! Invalid hardware address length: %d\n", msg->hlen);
    } else if (msg->hlen) {
        DbgPrint("  chaddr: ");
        DhcpDumpByteArray(msg->chaddr, msg->hlen);
        DbgPrint("\n");
    }

    DbgPrint("  hops: %d\n", msg->hops);
    DbgPrint("  xid: 0x%08x\n", NTOHL(msg->xid));
    DbgPrint("  secs: %d\n", NTOHS(msg->secs));
    DbgPrint("  flags: 0x%04x\n", NTOHS(msg->flags));
    DbgPrint("  ciaddr: %s\n", IPADDRSTR(msg->ciaddr));
    DbgPrint("  yiaddr: %s\n", IPADDRSTR(msg->yiaddr));
    DbgPrint("  siaddr: %s\n", IPADDRSTR(msg->siaddr));
    DbgPrint("  giaddr: %s\n", IPADDRSTR(msg->giaddr));

    //
    // Dump options
    //
    option = msg->options;
    msglen -= DHCPHDRLEN;

    if (msglen < DHCPCOOKIELEN ||
        !EqualMem(option, DhcpMagicCookie, DHCPCOOKIELEN)) {
        DbgPrint("!!! Invalid DHCP magic cookie\n");
        return;
    }
    msglen -= DHCPCOOKIELEN;
    option += DHCPCOOKIELEN;

    DbgPrint("  options:\n");
    DhcpDumpOption(option, msglen, &overload);

    if (overload & 1) {
        DbgPrint("  overload options (file):\n");
        DhcpDumpOption(msg->file, sizeof(msg->file), NULL);
    }

    if (overload & 2) {
        DbgPrint("  overload options (sname):\n");
        DhcpDumpOption(msg->sname, sizeof(msg->sname), NULL);
    }
}

#endif // DBG

