/**************************************************************************** 
** File: dhcp.c
**
** Author: Mike Borella
**
** Comments: Dump L2TP header information.  See RFCs 2131 and 2132.
**
*****************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include "config.h"
#include "dhcp.h"

extern u_char *packet_end;

/*----------------------------------------------------------------------------
**
** dump_dhcp()
**
** Parse DHCP packet and dump fields
**
**----------------------------------------------------------------------------
*/

void dump_dhcp(u_char *bp, int length)
{
  u_char *ep = bp + length;
  DHCPHdr *dhcp;
  int end_pad_cnt = 0;
  int i;
  int c;
  int j;
  int opt;
  int done = 0;
  u_int16_t s;
  char hostname [DHCP_MAX_HOSTNAME];
  struct in_addr in_holder;

  /*
   * Make sure we don't run off the end of the packet
   */

  if (ep > packet_end) 
    ep = packet_end;

  printf("-----------------------------------------------------------------\n");
  printf("                        DHCP Header\n");
  printf("-----------------------------------------------------------------\n");

  dhcp = (DHCPHdr *) bp;

  printf("OP                      %d ", (int) dhcp->op);
  switch (dhcp->op)
    {
    case DHCP_BOOTREQUEST:
      printf("(boot request)\n");
      break;
    case DHCP_BOOTREPLY:
      printf("(boot reply)\n");
      break;
    default:
      printf("(unknown)\n");
    }
  printf("Hardware addr type:     %d\n", (int) dhcp->htype);
  printf("Hardware addr length:   %d\n", (int) dhcp->hlen);
  printf("Hops:                   %d\n", (int) dhcp->hops); 
  printf("Transaction ID:         %x\n", ntohl(dhcp->xid));
  printf("Seconds:                %d\n", ntohs(dhcp->secs));
  printf("Flags:                  %x\n", ntohs(dhcp->flags));

  memcpy((void *) &in_holder, (void *) &dhcp->ciaddr, sizeof(struct in_addr));
  printf("Client addr:            %s\n", inet_ntoa(in_holder));

  memcpy((void *) &in_holder, (void *) &dhcp->yiaddr, sizeof(struct in_addr));
  printf("Your addr:              %s\n", inet_ntoa(in_holder));

  memcpy((void *) &in_holder, (void *) &dhcp->siaddr, sizeof(struct in_addr));
  printf("Next server addr:       %s\n", inet_ntoa(in_holder));

  memcpy((void *) &in_holder, (void *) &dhcp->giaddr, sizeof(struct in_addr));
  printf("Relay agent addr:       %s\n", inet_ntoa(in_holder));

  printf("Client hardware addr:   \n");
  printf("Server host name:       %s\n", dhcp->sname);
  printf("Boot file name:         %s\n", dhcp->file);
  
  /*
   * Look for BOOTP cookie
   */

  bp = bp + sizeof(DHCPHdr);
  i = htonl(BOOTP_COOKIE);
  memcpy((void *) &c, bp, 4);

  if (i == c)
    {
      printf("BOOTP Cookie:           %x\n", i);
      bp = bp + sizeof(BOOTP_COOKIE);
    }

  /*
   * Parse the options list
   */

  while (bp < ep)
    {     
      int len=0;
      opt = (int) *bp;
      if (opt > 0 && opt < 255)
	len = *++bp;
      if (!done) 
	printf("Option:                 %d ", opt);
      bp++;

      switch(opt)
	{
	case DHCP_OPT_PAD:  /* 0 */
	  if (!done) 
	    printf("(pad)\n");
	  else
	    end_pad_cnt++;
	  break;

	case DHCP_OPT_NETMASK: /* 1 */
	  printf("(netmask)\n");
	  printf("  Length:               %d\n", len);
	  memcpy((void *) &in_holder, bp, sizeof(struct in_addr));	  
	  printf("  Mask:                 %s\n", inet_ntoa(in_holder));
	  bp = bp + sizeof(struct in_addr);
	  break;

	case DHCP_OPT_TIMEOFFSET: /* 2 */
	  printf("(time offset)\n");
	  printf("  Length:               %d\n", len);
	  memcpy((void *) &i, bp, sizeof(int));
	  printf("  Offset:               %d\n", ntohl(i));
	  bp = bp + sizeof(u_int32_t);
	  break;

	case DHCP_OPT_ROUTER: /* 3 */
	  printf("(routers)\n");
	  printf("  Length:               %d\n", len);
	  j = 0;
	  while (j < len / 4)
	    {
	      memcpy((void *) &in_holder, bp, sizeof(struct in_addr));
	      printf("  Address:              %s\n", inet_ntoa(in_holder));
	      bp = bp + sizeof(struct in_addr);
	      j++;
	    }
	  break;

	case DHCP_OPT_DNS: /* 6 */
	  printf("(DNS)\n");
	  printf("  Length:               %d\n", len);
	  j = 0;
	  while (j < len / 4)
	    {
	      memcpy((void *) &in_holder, bp, sizeof(struct in_addr));
	      printf("  Address:              %s\n", inet_ntoa(in_holder));
	      bp = bp + sizeof(struct in_addr);
	      j++;
	    }
	  break;

	case DHCP_OPT_HOSTNAME: /* 12 */
	  printf("(host name)\n");
	  printf("  Length:               %d\n", len);
	  memcpy(hostname, bp, len);
	  hostname[len] = '\0';
	  printf("  Host name:            %s\n", hostname);
	  bp = bp + len;
	  break;

	case DHCP_OPT_DOMAINNAME: /* 15 */
	  printf("(domain name)\n");
	  printf("  Length:               %d\n", len);
	  memcpy(hostname, bp, len);
	  hostname[len] = '\0';
	  printf("  Domain name:          %s\n", hostname);
	  bp = bp + len;
	  break;

	case DHCP_OPT_VENDORSPECIFIC: /* 43 */
	  printf("(vendor specific parameters)\n");
	  printf("  Length:               %d\n", len);
	  printf("  Parameters:           ");
	  for (i=0; i<len; i++)
	    {
	      printf("%x ", *bp);
	      bp++;
	    }
	  printf("\n");
	  break;

	case DHCP_OPT_NETBIOSNAMESERV: /* 44 */
	  printf("(netbios name servers)\n");
	  printf("  Length:               %d\n", len);
	  j = 0;
	  while (j < len / 4)
	    {
	      memcpy((void *) &in_holder, bp, sizeof(struct in_addr));
	      printf("  Address:              %s\n", inet_ntoa(in_holder));
	      bp = bp + sizeof(struct in_addr);
	      j++;
	    }
	  break;

	case DHCP_OPT_NETBIOSNODETYPE: /* 46 */
	  printf("(netbios node type)\n");
	  printf("  Length:               %d\n", len);
	  printf("  Node type:            ");
	  switch(*bp)
	    {
	    case 0x1: 
	      printf("B\n");
	      break;
	    case 0x2:
	      printf("P\n");
	      break;
	    case 0x4:
	      printf("M\n");
	      break;
	    case 0x8:
	      printf("H\n");
	      break;
	    }
	  bp++;
	  break;

	case DHCP_OPT_REQUESTEDIPADDR: /* 50 */
	  printf("(requested IP address)\n");
	  printf("  Length:               %d\n", len);
	  memcpy((void *) &in_holder, bp, sizeof(struct in_addr));	  
	  printf("  Address:              %s\n", inet_ntoa(in_holder));
	  bp = bp + sizeof(struct in_addr);
	  break;

	case DHCP_OPT_IPADDRLEASE: /* 51 */
	  printf("(IP address lease time)\n");
	  printf("  Length:               %d\n", len);
	  memcpy((void *) &i, bp, sizeof(int));
	  printf("  Lease time:           %d\n", ntohl(i));
	  bp = bp + sizeof(u_int32_t);
	  break;

	case DHCP_OPT_MESSAGETYPE: /* 53 */
	  printf("(message type)\n");
	  printf("  Length:               %d\n", len);
	  printf("  Type:                 ");
	  switch(*bp)
	    {
	    case 1:
	      printf("DHCPDISCOVER\n");
	      break;
	    case 2: 
	      printf("DHCPOFFER\n");
	      break;
	    case 3:
	      printf("DHCPREQUEST\n");
	      break;
	    case 4: 
	      printf("DHCPDECLINE\n");
	      break;
	    case 5:
	      printf("DHCPACK\n");
	      break;
	    case 6: 
	      printf("DHCPNAK\n");
	      break;
	    case 7: 
	      printf("DHCPRELEASE\n");
	      break;
	    case 8:
	      printf("DHCPINFORM\n");
	      break;
	    default: 
	      printf("unknown\n");
	      break;
	    }
	  bp++;
	  break;

	case DHCP_OPT_SERVERID: /* 54 */
	  printf("(server ID)\n");
	  printf("  Length:               %d\n", len);
	  memcpy((void *) &in_holder, bp, sizeof(struct in_addr));	  
	  printf("  Address:              %s\n", inet_ntoa(in_holder));
	  bp = bp + sizeof(struct in_addr);
	  break;

	case DHCP_OPT_PARAMREQLIST: /* 55 */
	  printf("(DHCP options requested)\n");
	  printf("  Length:               %d\n", len);
	  for (i=0; i < len; i++)
	    {
	      printf("  Option requested:     %d\n", (u_int32_t) *bp);
	      bp++;
	    }
	  break;

	case DHCP_OPT_MAXDHCPMSGSIZE: /* 57 */
	  printf("(max message size)\n");
	  printf("  Length:               %d\n", len);
	  memcpy((void *) &s, bp, sizeof(u_int16_t));
	  printf("  Size:                 %d\n", ntohs(s));
	  bp = bp + sizeof(u_int16_t);
	  break;

	case DHCP_OPT_RENEWALTIME: /* 58 */
	  printf("(renewal time)\n");
	  printf("  Length:               %d\n", len);
	  memcpy((void *) &i, bp, sizeof(u_int32_t));
	  printf("  Renewal time:         %d\n", ntohl(i));
	  bp = bp + sizeof(u_int32_t);
	  break;

	case DHCP_OPT_REBINDINGTIME: /* 59 */
	  printf("(rebinding time)\n");
	  printf("  Length:               %d\n", len);
	  memcpy((void *) &i, bp, sizeof(u_int32_t));
	  printf("  Rebinding time:       %d\n", ntohl(i));
	  bp = bp + sizeof(u_int32_t);
	  break;

	case DHCP_OPT_VENDORCLASSID: /* 60 */
	  printf("(vendor class ID)\n");
	  printf("  Length:               %d\n", len);
	  printf("  Parameters:           ");
	  for (i=0; i<len; i++)
	    {
	      printf("%x ", *bp);
	      bp++;
	    }
	  printf("\n");
	  break;

	case DHCP_OPT_CLIENTID: /* 61 */
	  printf("(client ID)\n");
	  printf("  Length:               %d\n", len);
	  printf("  Parameters:           ");
	  for (i=0; i<len; i++)
	    {
	      printf("%x ", *bp);
	      bp++;
	    }
	  printf("\n");
	  break;

	case DHCP_OPT_END: /* 255 */
	  printf("(end of options)\n");
	  done = 1;
	  bp++;
	  break;

	default:
	  printf("(unknown)\n");
	  printf("  Length:               %d\n", len);
	  bp = bp + len;
	} /* switch */

    } /* while */
  
  printf("%d padding bytes\n", end_pad_cnt);

}
