// DHCPDUMP
//
// Usage: tcpdump -s 1518 -lenx port bootps or port bootpc | dhcpdump
//
// note 1: how does this work for FDDI / PPP links?
// note 2: what is this number 14?
//
// $Id: dhcpdump.c,v 1.12 2004/10/31 11:22:58 mavetju Exp $
//
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <regex.h>
#include "config.h"
#include "dhcp_options.h"
#ifndef HAVE_STRSEP
#include "strsep.c"
#endif
#define bool int
#define TRUE (1)
#define FALSE (0)
#define LARGESTRING 1024
#define uchar unsigned char
// header variables
uchar timestamp[40]; // timestamp on header
uchar mac_origin[40]; // mac address of origin
uchar mac_destination[40]; // mac address of destination
uchar ip_origin[40]; // ip address of origin
uchar ip_destination[40]; // ip address of destination
int max_data_len; // maximum size of a packet
int tcpdump_style=-1;
int check_ch(uchar *data,int data_len,regex_t *preg);
int readheader(uchar *buf);
int readdata(uchar *buf,uchar *data,int *data_len);
int printdata(uchar *data,int data_len);
void printIPaddress(uchar *data);
void printIPaddressAddress(uchar *data);
void printIPaddressMask(uchar *data);
void print8bits(uchar *data);
void print16bits(uchar *data);
void print32bits(uchar *data);
void printTime8(uchar *data);
void printTime32(uchar *data);
void printReqParmList(uchar *data,int len);
void printHexColon(uchar *data,int len);
void printHex(uchar *data,int len);
void printHexString(uchar *data,int len);
int main(int argc,char **argv) {
char *hmask=NULL;
regex_t preg;
int i;
// data variables
uchar data[LARGESTRING]; // data of the udp packet
int data_len=0; // length of the packet
uchar buf[LARGESTRING]; // buffer from input line
for (i=1;i<argc;i++) {
if (argv[i]==NULL || argv[i][0]!='-') break;
switch (argv[i][1]) {
case 'h':
hmask=argv[++i];
break;
default:
fprintf(stderr,"%s: %c: uknown option\n",argv[0],argv[i][1]);
exit(2);
}
}
if (hmask) regcomp(&preg,hmask,REG_EXTENDED | REG_ICASE | REG_NOSUB);
while (!feof(stdin)) {
if (fgets(buf,LARGESTRING,stdin)==NULL)
return 1;
if (isdigit(buf[0])) {
//
// this is a header, salvage the information needed and go on:
// - time
// - mac origin
// - mac destination
// - ip origin
// - ip destination
//
readheader(buf);
data_len=0;
} else if (buf[0]=='\t') {
if (readdata(buf,data,&data_len)==1
&& ( !hmask || !check_ch(data,data_len,&preg)))
printdata(data,data_len);
}
}
return 0;
}
// check for matching CHADDR (Peter Apian-Bennewitz <apian@ise.fhg.de>)
int check_ch(uchar *data,int data_len,regex_t *preg) {
char ch_ip[50];
if (data_len<43) return(0);
sprintf(ch_ip,
"%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
data[28],data[29],data[30],data[31],
data[32],data[33],data[34],data[35],
data[36],data[37],data[38],data[39],
data[40],data[41],data[42],data[43]);
return (regexec(preg,ch_ip,0,NULL,0));
}
// print the data as an IP address
void printIPaddress(uchar *data) {
printf("%d.%d.%d.%d",
data[0],data[1],data[2],data[3]);
}
// print the data as an IP address and an IP address
void printIPaddressAddress(uchar *data) {
printf("%d.%d.%d.%d %d.%d.%d.%d",
data[0],data[1],data[2],data[3],
data[4],data[5],data[6],data[7]);
}
// print the data as an IP address and mask
void printIPaddressMask(uchar *data) {
printf("%d.%d.%d.%d/%d.%d.%d.%d",
data[0],data[1],data[2],data[3],
data[4],data[5],data[6],data[7]);
}
// prints a value of 8 bits (1 byte)
void print8bits(uchar *data) {
printf("%d",data[0]);
}
// prints a value of 16 bits (2 bytes)
void print16bits(uchar *data) {
printf("%d",(data[0]<<8)+data[1]);
}
// prints a value of 32 bits (4 bytes)
void print32bits(uchar *data) {
printf("%d",(data[0]<<24)+(data[1]<<16)+(data[2]<<8)+data[3]);
}
// print the data as a 8bits time-value
void printTime8(uchar *data) {
int t=data[0];
printf("%d (",t);
if (t>7*24*3600) { printf("%dw",t/(7*24*3600));t%=7*24*3600; }
if (t>24*3600) { printf("%dd",t/(24*3600));t%=24*3600; }
if (t>3600) { printf("%dh",t/3600);t%=3600; }
if (t>60) { printf("%dm",t/60);t%=60; }
if (t>0) printf("%ds",t);
printf(")");
}
// print the data as a 32bits time-value
void printTime32(uchar *data) {
int t=(data[0]<<24)+(data[1]<<16)+(data[2]<<8)+data[3];
printf("%d (",t);
if (t>7*24*3600) { printf("%dw",t/(7*24*3600));t%=7*24*3600; }
if (t>24*3600) { printf("%dd",t/(24*3600));t%=24*3600; }
if (t>3600) { printf("%dh",t/3600);t%=3600; }
if (t>60) { printf("%dm",t/60);t%=60; }
if (t>0) printf("%ds",t);
printf(")");
}
// print the data as a hex-list, with the translation into ascii behind it
void printHexString(uchar *data,int len) {
int i,j,k;
for (i=0;i<=len/8;i++) {
for (j=0;j<8;j++) {
if (i*8+j>=len) break;
printf("%02x",data[i*8+j]);
}
for (k=j;k<8;k++)
printf(" ");
printf(" ");
for (j=0;j<8;j++) {
char c=data[i*8+j];
if (i*8+j>=len) break;
printf("%c",isprint(c)?c:'.');
}
if (i*8+j<len) printf("\n\t\t\t\t\t ");
}
}
// print the data as a hex-list, without the translation into ascii behind it
void printHex(uchar *data,int len) {
int i,j;
for (i=0;i<=len/8;i++) {
for (j=0;j<8;j++) {
if (i*8+j>=len) break;
printf("%02x",data[i*8+j]);
}
if (i*8+j<len) printf("\n\t\t\t\t\t ");
}
}
// print the data as a hex-list seperated by colons
void printHexColon(uchar *data,int len) {
int i;
for (i=0;i<len;i++) {
if (i!=0) printf(":");
printf("%02x",data[i]);
}
}
// print the list of requested parameters
void printReqParmList(uchar *data,int len) {
int i;
for (i=0;i<len;i++) {
printf("%3d (%s)\n",data[i],dhcp_options[data[i]]);
printf("\t\t\t\t\t ");
}
}
// print the header and the options.
int printdata(uchar *data,int data_len) {
int j,i;
uchar buf[LARGESTRING];
if (data_len==0)
return 0;
// Skip the ethernet header. Is there a way to do this better?
data+=28; // note 1
printf( " TIME: %s\n",timestamp);
printf( " IP: %s (%s) > %s (%s)\n",
ip_origin,mac_origin,ip_destination,mac_destination);
printf( " OP: %d (%s)\n",data[0],operands[data[0]]);
printf( " HTYPE: %d (%s)\n",data[1],htypes[data[1]]);
printf( " HLEN: %d\n",data[2]);
printf( " HOPS: %d\n",data[3]);
printf( " XID: %02x%02x%02x%02x\n",
data[4],data[5],data[6],data[7]);
printf( " SECS: ");print16bits(data+8);//,255*data[8]+data[9]);
printf("\n FLAGS: %x\n",255*data[10]+data[11]);
printf( "CIADDR: ");printIPaddress(data+12);
printf("\nYIADDR: ");printIPaddress(data+16);
printf("\nSIADDR: ");printIPaddress(data+20);
printf("\nGIADDR: ");printIPaddress(data+24);
printf("\nCHADDR: ");printHexColon(data+28,16);
printf("\n SNAME: %s.\n",data+44);
printf( " FNAME: %s.\n",data+108);
j=236;
j+=4; /* cookie */
while (j<data_len && data[j]!=255) {
printf("OPTION: %3d (%3d) %-26s",data[j],data[j+1],dhcp_options[data[j]]);
switch (data[j]) {
default:
printHexString(data+j+2,data[j+1]);
break;
case 0: // pad
break;
case 1: // Subnetmask
case 3: // Routers
case 16: // Swap server
case 28: // Broadcast address
case 32: // Router solicitation
case 50: // Requested IP address
case 54: // Server identifier
printIPaddress(data+j+2);
break;
case 12: // Hostname
case 14: // Merit dump file
case 15: // Domain name
case 17: // Root Path
case 18: // Extensions path
case 40: // NIS domain
case 56: // Message
case 62: // Netware/IP domain name
case 64: // NIS+ domain
case 66: // TFTP server name
case 67: // bootfile name
case 60: // Domain name
case 86: // NDS Tree name
case 87: // NDS context
strncpy(buf,&data[j+2],data[j+1]);
buf[data[j+1]]=0;
printf("%s",buf);
break;
case 4: // Time servers
case 5: // Name servers
case 6: // DNS server
case 7: // Log server
case 8: // Cookie server
case 9: // LPR server
case 10: // Impress server
case 11: // Resource location server
case 41: // NIS servers
case 42: // NTP servers
case 44: // NetBIOS name server
case 45: // NetBIOS datagram distribution server
case 48: // X Window System font server
case 49: // X Window System display server
case 65: // NIS+ servers
case 68: // Mobile IP home agent
case 69: // SMTP server
case 70: // POP3 server
case 71: // NNTP server
case 72: // WWW server
case 73: // Finger server
case 74: // IRC server
case 75: // StreetTalk server
case 76: // StreetTalk directory assistance server
case 85: // NDS server
for (i=0;i<data[j+1]/4;i++) {
if (i!=0) printf(",");
printIPaddress(data+j+2+i*4);
}
break;
case 21: // Policy filter
for (i=0;i<data[j+1]/8;i++) {
if (i!=0) printf(",");
printIPaddressMask(data+j+2+i*8);
}
break;
case 33: // Static route
for (i=0;i<data[j+1]/8;i++) {
if (i!=0) printf(",");
printIPaddressAddress(data+j+2+i*8);
}
break;
case 25: // Path MTU plateau table
for (i=0;i<data[j+1]/2;i++) {
if (i!=0) printf(",");
print16bits(data+j+2+i*2);
}
break;
case 13: // bootfile size
case 22: // Maximum datagram reassembly size
case 26: // Interface MTU
case 57: // Maximum DHCP message size
print16bits(data+j+2);
break;
case 19: // IP forwarding enabled/disable
case 20: // Non-local source routing
case 27: // All subnets local
case 29: // Perform mask discovery
case 30: // Mask supplier
case 31: // Perform router discovery
case 34: // Trailer encapsulation
case 39: // TCP keepalive garbage
printf("%d (%s)",data[j+2],enabledisable[data[j+2]]);
break;
case 23: // Default IP TTL
printTime8(data+j+2);
break;
case 37: // TCP default TTL
print8bits(data+j+2);
break;
case 43: // Vendor specific info
case 47: // NetBIOS scope (no idea how it looks like)
printHexString(data+j+2,data[j+1]);
break;
case 46: // NetBIOS over TCP/IP node type
printf("%d (%s)",
data[j+2],netbios_node_type[data[j+2]]);
break;
case 2: // Time offset
case 24: // Path MTU aging timeout
case 35: // ARP cache timeout
case 38: // TCP keepalive interval
case 51: // IP address leasetime
case 58: // T1
case 59: // T2
printTime32(data+j+2);
break;
case 36: // Ethernet encapsulation
printf("%d (%s)",
data[j+2],
data[j+2]>sizeof(ethernet_encapsulation)?
"*wrong value*":
ethernet_encapsulation[data[j+2]]);
break;
case 52: // Option overload
printf("%d (%s)",
data[j+2],
data[j+2]>sizeof(option_overload)?
"*wrong value*":
option_overload[data[j+2]]);
break;
case 53: // DHCP message type
printf("%d (%s)",
data[j+2],
data[j+2]>sizeof(dhcp_message_types)?
"*wrong value*":
dhcp_message_types[data[j+2]]);
break;
case 55: // Parameter Request List
printReqParmList(data+j+2,data[j+1]);
break;
case 63: // Netware/IP domain information
printHex(data+j+2,data[j+1]);
break;
case 61: // Client identifier
printHexColon(data+j+2,data[j+1]);
break;
case 81: // Client FQDN
print8bits(data+j+2);
printf("-");
print8bits(data+j+3);
printf("-");
print8bits(data+j+4);
printf(" ");
strncpy(buf,&data[j+5],data[j+1]-3);
buf[data[j+1-3]]=0;
printf("%s",buf);
break;
case 82: // Relay Agent Information
printf("\n");
for (i=j+2;i<j+data[j+1];) {
printf("%-17s %-13s ", " ",
data[i]>sizeof(relayagent_suboptions)?
"*wrong value*":
relayagent_suboptions[data[i]]);
if (i+data[i+1]>j+data[j+1]) {
printf("*MALFORMED -- TOO LARGE*\n");
break;
}
printHexColon(data+i+2,data[i+1]);
i+=data[i+1];
}
break;
}
printf("\n");
/*
// This might go wrong if a mallformed packet is received.
// Maybe from a bogus server which is instructed to reply
// with invalid data and thus causing an exploit.
// My head hurts... but I think it's solved by the checking
// for j<data_len at the begin of the while-loop.
*/
if (data[j]==0) // padding
j++;
else
j+=data[j+1]+2;
}
printf("---------------------------------------------------------------------------\n");
fflush(stdout);
return 0;
}
//
// read the data of the packet, which is a bunch of hexdigits like:
// ffff ffff 0043 0044 013f 2432 0201 0600.
//
// For tcpdump 3.8.3, it is:
// 0x0110: 04c0 a801 0133 0400 0002 5801 04ff ffff .....3....X.....
//
int readdata(uchar *buf,uchar *data,int *data_len) {
int i,length;
bool first=TRUE;
int prev=0;
if (tcpdump_style==0) {
length=strlen(buf);
for (i=0;i<length;i++) {
if (buf[i]==' ') continue;
if (buf[i]=='\t') continue;
if (buf[i]=='\r') continue;
if (buf[i]=='\n') continue;
if (isxdigit(buf[i])) {
if (buf[i]<='9') {
if (first) {
prev=buf[i]-'0'; first=FALSE;
} else {
data[(*data_len)++]=prev*16+buf[i]-'0'; first=TRUE;
}
} else {
buf[i]=tolower(buf[i]);
if (first) {
prev=buf[i]-'a'+10; first=FALSE;
} else {
data[(*data_len)++]=prev*16+buf[i]-'a'+10; first=TRUE;
}
}
continue;
}
fprintf(stderr,"Error in packet: offset: %d, character %c\n",i,buf[i]);
}
if (*data_len>=max_data_len)
return 1;
}
if (tcpdump_style==1) {
bool foundcolon=FALSE;
bool founddata=FALSE;
bool foundspace=FALSE;
int count=0;
length=strlen(buf);
for (i=0;i<length;i++) {
if (buf[i]==' ') {
if (founddata && foundspace)
foundcolon=FALSE;
else
foundspace=TRUE;
continue;
}
foundspace=FALSE;
if (buf[i]=='\t') continue;
if (buf[i]=='\r') { count=0; continue; }
if (buf[i]=='\n') { count=0; continue; }
if (buf[i]==':') { foundcolon=TRUE; continue; }
if (!foundcolon) continue;
if (count==32) continue;
if (isxdigit(buf[i])) {
if (buf[i]<='9') {
if (first) {
prev=buf[i]-'0'; first=FALSE;
} else {
data[(*data_len)++]=prev*16+buf[i]-'0'; first=TRUE;
}
} else {
buf[i]=tolower(buf[i]);
if (first) {
prev=buf[i]-'a'+10; first=FALSE;
} else {
data[(*data_len)++]=prev*16+buf[i]-'a'+10; first=TRUE;
}
}
count++;
founddata++;
continue;
}
fprintf(stderr,"Error in packet: offset: %d, character %c\n",i,buf[i]);
}
if (*data_len>=max_data_len)
return 1;
}
return 0;
}
// read the header of the packet, which should look like this:
// 14:06:20.149959 0:80:5f:c1:71:f ff:ff:ff:ff:ff:ff 0800 353:
// 130.139.64.101.67 > 255.255.255.255.68:
// field 1: timestamp
// field 2: mac address origin
// field 3: mac address destination
// field 5: length of IP packets + 14
// field 6: ip address origin
// field 8: ip address destination
//
// tcpdump 3.8.3 has this as header:
// 18:19:30.618569 00:0b:82:01:b5:e3 > ff:ff:ff:ff:ff:ff, ethertype IPv4
// (0x0800), length 342: IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP,
// Request from 00:0b:82:01:b5:e3, length: 300
// field 1: timestamp
// field 2: mac address origin
// field 4: mac address destination
// field 9: length of IP packets + 14
// field 11: ip address origin
// field 13: ip address destination
//
//
int readheader(uchar *lbuf) {
int n;
char **ap;
char *argv[16];
char max_data_str[20];
char *buf=(char *)lbuf;
if (tcpdump_style==-1) {
char *b=(char *)malloc(LARGESTRING);
strcpy(b,buf);
tcpdump_style=0;
for (ap=argv,n=0;(*ap=strsep(&b," \t"))!=NULL;n++) {
if (n==2) {
if (ap[0][0]=='>') tcpdump_style=1;
break;
}
}
if (tcpdump_style==0)
fprintf(stderr,"Old-style tcpdump output\n");
if (tcpdump_style==1)
fprintf(stderr,"TCPdump 3.8.x output\n");
// XXX yeah yeah *b is a memory leak.
}
if (tcpdump_style==0) {
buf=(char *)lbuf;
for (ap=argv,n=0;(*ap=strsep(&buf," \t"))!=NULL;n++)
if (**ap!='\0') {
if (++ap>=&argv[8])
break;
switch(n) {
default:
break;
case 0: // timestamp
strcpy(timestamp,argv[0]);
break;
case 1: // mac origin
strcpy(mac_origin,argv[1]);
break;
case 2: // mac destination
strcpy(mac_destination,argv[2]);
break;
case 4: // size of packet
strcpy(max_data_str,argv[4]);
max_data_str[strlen(max_data_str)-1]=0;
max_data_len=atoi(max_data_str)-14; // note 2 *************
break;
case 5: // ip origin
strcpy(ip_origin,argv[5]);
break;
case 7: // ip destination
strcpy(ip_destination,argv[7]);
ip_destination[strlen(ip_destination)-1]=0;
break;
}
}
return 0;
}
if (tcpdump_style==1) {
// tcpdump 3.8.3 has this as header:
// 18:19:30.618569 00:0b:82:01:b5:e3 > ff:ff:ff:ff:ff:ff, ethertype IPv4
// (0x0800), length 342: IP 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP,
// Request from 00:0b:82:01:b5:e3, length: 300
// field 1: timestamp
// field 2: mac address origin
// field 4: mac address destination
// field 9: length of IP packets + 14
// field 11: ip address origin
// field 13: ip address destination
buf=(char *)lbuf;
for (ap=argv,n=0;(*ap=strsep(&buf," \t"))!=NULL;n++) {
//fprintf(stderr,"n: %d\n",n);
if (**ap!='\0') {
if (++ap>=&argv[13])
break;
switch(n) {
default:
break;
case 0: // timestamp
strcpy(timestamp,argv[0]);
//fprintf(stderr,"timestamp: %s\n",timestamp);
break;
case 1: // mac origin
strcpy(mac_origin,argv[1]);
//fprintf(stderr,"mac origin: %s\n",mac_origin);
break;
case 3: // mac destination
strcpy(mac_destination,argv[3]);
mac_destination[strlen(mac_destination)-1]=0;
//fprintf(stderr,"mac destination: %s\n",mac_destination);
break;
case 8: // size of packet
strcpy(max_data_str,argv[8]);
max_data_str[strlen(max_data_str)-1]=0;
max_data_len=atoi(max_data_str)-14; // note 2 *************
//fprintf(stderr,"maxdatalen: %d\n",max_data_len);
break;
case 10: // ip origin
strcpy(ip_origin,argv[10]);
//fprintf(stderr,"ip origin: %s\n",ip_origin);
break;
case 12: // ip destination
strcpy(ip_destination,argv[12]);
ip_destination[strlen(ip_destination)-1]=0;
//fprintf(stderr,"ip destination: %s\n",ip_destination);
break;
}
}
}
//fprintf(stderr,"%d\n",n);
return 0;
}
return 1;
}
syntax highlighted by Code2HTML, v. 0.9.1