I'm writing a program that parses tcpdump output files. I have a tab delimited text file that contains MAC addresses and hostnames.

How can I have the program search this file for a MAC address and then return the corresponding hostname?

Any help would be appreciated.

Thanks.

This is the basic idea, you might have to modify it to suit yourself, such as do error checking and trim whitespace from end of the mac address after extracting it from the line.

std::string foo(std::string search)
{

std::string line;
ifstream in("filename.txt");
while( getline(in, line) )
{
   size_t pos = line.find('\t');
   std::string mac = line.substr(0,line);
   if( mac == search )
   {
      return line.substr(pos+1);
   }
}

It works, but I'm having a strange issue when I print the results.

Here's what I have:

cout << "    Eth:    From:    " << source << "    (" << search_eaddr(source) << ")" << endl;

Here's what it looks like when it prints on the screen:

)       Eth:    From:    08:00:20:78:97:b2    (prime

Why is that second parenthesis going to the beginning of the line?

Thanks.

Please post an example of your input file.

This is all that's in the input file that I'm using for the search:

00:00:0c:76:3d:c6    sisko2

00:00:c0:95:57:4b    mach

00:00:d0:00:80:05    oucsnet

00:00:e8:a0:06:e2    br209a

00:00:e8:a0:06:f3    kitten24

00:00:e8:a0:07:44    br209c

00:00:e8:a0:07:4a    puppy01

00:00:e8:a0:07:ce    puppy24

00:00:e8:a0:0a:30    puppy10

00:00:e8:a0:0c:64    puppy12

00:00:e8:a0:0d:31    cub11

00:00:e8:a0:0e:4a    puppy03

00:00:e8:a0:0f:b5    107mort1

00:00:e8:a0:0f:d0    puppy05

00:00:e8:a0:10:2d    puppy14

00:00:e8:a0:12:75    puppy21

00:00:e8:a0:13:69    br206b

00:00:e8:a0:14:05    puppy08

00:00:e8:a0:14:9c    br208a

00:00:e8:a0:14:ad    puppy18

00:00:e8:a0:16:92    puppy16

00:00:e8:a0:16:df    puppy11

00:00:e8:a0:17:83    puppy02

I just did a copy and paste.

In the actual file, there is not gap between lines.

Please post the FULL source code you are using.

Here's the search code:

string search_eaddr(string search)
{
    string line;
    ifstream in("eaddr.txt");
    while (!in.eof())
    {
        getline(in,line);
        size_t pos = line.find('\t');
        string mac = line.substr(0, pos);

        if(mac == search)
            return line.substr(pos + 1);    
    }
    return "";
}

Here is where the search string is generated and the output is printed:

char source[17], dest[17];

    sprintf(source, "%02x:%02x:%02x:%02x:%02x:%02x", eth.esrc[0], eth.esrc[1], \
        eth.esrc[2], eth.esrc[3], eth.esrc[4], eth.esrc[5]);
    
    sprintf(dest, "%02x:%02x:%02x:%02x:%02x:%02x", eth.edst[0], eth.edst[1], \
        eth.edst[2], eth.edst[3], eth.edst[4], eth.edst[5]);

    cout << "    Eth:    From:    " << source << "    (" << search_eaddr(source) << ")" << endl;
    cout << "        To:    " << dest << "    (" << search_eaddr(dest) << ")" << endl;

why do instructors insist on teaching while( !in.eof()) instead of while( getline(in, line) ) ? eof() doesn't work like that and cause the while loop to loop too many times.


You should probably attach the input file rather than trying to paste its contents into the editor because the editor. The file contents you posted does not contain tabs -- just spaces.

Here is the full code, and the test file used for the search is attached.

#include <iostream>
#include <fstream>
#include <sstream>
#include <time.h>
#include <cstring>
#include "pdump.h"

using namespace std;

struct dump_file_header hdr;        // Initialize Dump File Header Structure 
struct dump_header dhdr;        // Initialize Dump Header Structure 
struct ether_header eth;        // Initialize Ethernet Header Structure
struct arp_header arp;            // Initialize ARP Header Structure
struct ip_header ip;            // Initialize Internet Protocol Structure

void frame_info();            // Declare function to print frame information
void eth_hdr();                // Declare function to print ethernet header
void arp_hdr();                // Declare function to print ARP header
void ip_hdr();                // Declare function to print IP header
void clear_eth_only_buffer();        // Declare function to clear buffer after printing ethernet header only
void clear_arp_buffer();        // Declare function to clear buffer after printing ARP header
void clear_ip_buffer();            // Declare function to clear buffer after printing IP header
string search_eaddr(string, string);    // Declare function to perform MAC to hostname lookup

FILE *input, *input2;            // Declare global variables for reading input files

unsigned char buff, array[1500];    // Declare global variables that will be used to clear buffer

int bit_swap;                // Declare global variable to indicate "endian" of dump file
string hostfile;            // Declare global variable to store name of file containing MAC to hostname list

int main(int argc, char *argv[])
{
    int count = 0;

    if (argc < 2 || argc > 3)    // Checks the number of arguments entered by user
    {
        // If too many or too few arguments provided, program usage will be displayed.
        printf("\n    Usage: %s <dump_filename> (host_filename) \n\n", argv[0]);
        return 1;
    }
    
    if (argc == 3)            // Checks if MAC to hostname file provided
    {
        input2 = fopen(argv[2], "r");    // Verifies that MAC to hostname file exists and can be opened.
        if(fopen == NULL)
        {
            printf("Cannot open host list\n");
            return 1;
        }
        else
        {
            hostfile = argv[2];
            fclose(input2);        // Closes MAC to hostname file after verification
        }
    }

    input = fopen(argv[1], "rb");    // Verifies that dump file can be opened

    if(fopen == NULL)
    {
        printf("Cannot open saved dump file\n");
        return 1;
    }
    else
    {
        fread((char *) &hdr, sizeof(hdr), 1, input);    // Reads packet header information
    
        if (hdr.magic != TCPDUMP_MAGIC && hdr.magic != TCPDUMP_MAGIC_BIG)    // Checks for Magic Number
        {
            printf("Invalid Magic Number.  Verify that supplied file is a valid tcpdump file.");
            return 1;
        }
        else if (hdr.magic == TCPDUMP_MAGIC_BIG)    // Checks endian of file by looking at Magic Number
        {
            bit_swap = 1;                // Flag tells program that bit swapping is necessary
        }
                    
        while(fread((char *) &dhdr, sizeof(dhdr), 1, input))    // Use While loop used to set the packet boundary
        {
            ++count;    // Increments count variable

            printf("Packet %d \n", count);     // Displays packet number
            
            frame_info();        // Calls function to display frame information
            eth_hdr();        // Calls function to display ethernet header information
                          
            printf("\n ");        // Adds blank line in between packet information
               
        } // end while loop
    } // end main else
          
    fclose(input); // Closes dump file
             
    return 0;
}

void frame_info()    // Function for displaying frame information
{
    if (bit_swap == 0)    // Checks endian flag to see if bit swapping is necessary
    {
        printf("    Packet Length %u (%u) \n", dhdr.caplen, dhdr.len);    // Prints packet length
                                            // captured size (off wire packet size)

        time_t newtime;                    // Creates new time_t variable for converting timestamp
        newtime = time_t (dhdr.ts_secs);        // Copies timestamp (in seconds) to time_t variable

        struct tm * timeinfo;                // Creates structure for new date/time format
        timeinfo = localtime (&newtime);        // Converts timestamp in seconds since epoch to date/time format

        char buffer[30];
        strftime(buffer, 30, "    Collected: %a %b %d %X", timeinfo);    // Formats date/time for output

        cout << buffer << "." << dhdr.ts_usecs << endl;        // Prints timestamp in formated form, including milliseconds
                            
    }
    else
    {
        printf("    Packet Length %u (%u) \n", SWAPLONG(dhdr.caplen), SWAPLONG(dhdr.len));    // Performs bit swap before 
                                                    // printing packet length

        time_t newtime;                    // Creates new time_t variable for converting timestamp
        newtime = time_t (SWAPLONG(dhdr.ts_secs));    // Performs bit swap, then copies timestamp (in seconds) to time_t variable

        struct tm * timeinfo;                // Creates structure for new date/time format
        timeinfo = localtime (&newtime);        // Converts timestamp in seconds since epoch to date/time format

        char buffer[80];                
        strftime(buffer, 80, "    Collected: %a %b %d %X", timeinfo);    // Formats date/time for output

        cout << buffer << "." << SWAPLONG(dhdr.ts_usecs) << endl;    // Performs bit swap on milliseconds variable
                            //Prints timestamp in formated form, including milliseconds                
    }
}

void eth_hdr()        // Function for printing ethernet header information
{

    string shost = "", dhost = "";

    fread((char *) &eth, sizeof(eth), 1, input);    // Read ethernet header information

    char source[17], dest[17];

    sprintf(source, "%02x:%02x:%02x:%02x:%02x:%02x", eth.esrc[0], eth.esrc[1], \
        eth.esrc[2], eth.esrc[3], eth.esrc[4], eth.esrc[5]);    // Concatenates seperate ethernet address variables into a single string
    
    sprintf(dest, "%02x:%02x:%02x:%02x:%02x:%02x", eth.edst[0], eth.edst[1], \
        eth.edst[2], eth.edst[3], eth.edst[4], eth.edst[5]);    // Concatenates seperate ethernet address variables into a single string

    if (hostfile != "")    // Checks if a MAC to hostname list file was provided
    {
        shost = search_eaddr(source, hostfile);        // Performs lookup against list file to display hostnames
        dhost = search_eaddr(dest, hostfile);
    }

    cout << "    Eth:    From:    " << source << "    (" << shost << ")" << endl;    // Prints ethernet source address
    cout << "        To:    " << dest << "    (" << dhost << ")" << endl;        // Prints ethernet destination address

    eth.etype = SWAPSHORT(eth.etype);    // Performs bit swap (regardless of endian) in preparation to display ethernet type

    switch (eth.etype)
    {
        case ETHERTYPE_IP:        // If type IP, runs function to print IP header information
            printf("        Type:    %#04x    (IP) \n", eth.etype);
            ip_hdr();
            break;
        case ETHERTYPE_ARP:        // If type ARP, runs function to print ARP header information
            printf("        Type:    %#04x    (ARP) \n", eth.etype);
            arp_hdr();
            break;
        case ETHERTYPE_RARP:        // If type RARP, runs function to print ARP header information
            printf("        Type:    %#04x     (RARP) \n", eth.etype);
            arp_hdr();
            break;
        default:            // If any other type, only prints ethernet header information and move to next packet
            printf("        Type:    %#04x    (OTHER) \n", eth.etype);
            clear_eth_only_buffer();
            break;
    }

    
}

void arp_hdr()        // Function to print ARP header information
{
    string shost = "", dhost = "";

    fread((char *) &arp, sizeof(arp), 1, input);    // Reads ARP header

    printf("    ARP:    HW type: %x \n", arp.arp_htype[1]);    // Prints ARP hardware type number

    switch (arp.arp_oper[1])    // Checks ARP operation
    {
        case 1:        // Operation type 1 displays "request"
            printf("        OP: %x    (request) \n", arp.arp_oper[1]);
            break;
        case 2:        // Operation type 2 displays "reply"
            printf("        OP: %x    (reply) \n", arp.arp_oper[1]);
            break;
        default:    // Any other operation type is invalid, but number will display for debugging purposes
            printf("        OP: %x    (invalid operation) \n", arp.arp_oper[1]);
            break;
    }

    char source[17], dest[17];

    sprintf(source, "%02x:%02x:%02x:%02x:%02x:%02x", arp.arp_hwsrc[0], arp.arp_hwsrc[1], \
        arp.arp_hwsrc[2], arp.arp_hwsrc[3], arp.arp_hwsrc[4], arp.arp_hwsrc[5]);
                            // Concatenates seperate ethernet address variables into a single string
    
    sprintf(dest, "%02x:%02x:%02x:%02x:%02x:%02x", arp.arp_hwdst[0], arp.arp_hwdst[1], \
        arp.arp_hwdst[2], arp.arp_hwdst[3], arp.arp_hwdst[4], arp.arp_hwdst[5]);
                            // Concatenates seperate ethernet address variables into a single string

    if (hostfile != "")    // Checks if a MAC to hostname list file was provided
    {
        shost = search_eaddr(source, hostfile);        // Performs lookup against list file to display hostnames
        dhost = search_eaddr(dest, hostfile);
    }

    cout << "        Hardware:    " << source << "    (" << shost << ")" << endl;    // Prints source hardware address
    cout << "            ==>    " << dest << "    (" << dhost << ")" << endl;    // Prints destination hardware address

    printf("        Protocol:    %d.%d.%d.%d \n", arp.arp_prosrc[0], arp.arp_prosrc[1], \
        arp.arp_prosrc[2], arp.arp_prosrc[3]);        // Prints source protocol address
    printf("            ==>    %d.%d.%d.%d \n", arp.arp_prodst[0], arp.arp_prodst[1], \
        arp.arp_prodst[2], arp.arp_prodst[3]);        // Prints destination protocol address

    clear_arp_buffer();    // Calls function to clear remainder of packet from buffer
}


void ip_hdr()        // Function to print IP header information
{
    fread((char *) &ip, sizeof(ip), 1, input);    // Reads IP header

    printf("    IP:    Vers:    %x \n", IP_V(&ip));    // Prints IP version 
    printf("        Hlen:    %d \n", IP_HL(&ip)*4);    // Prints IP header length

    printf("        Src:    %d.%d.%d.%d \n", ip.ip_src[0], ip.ip_src[1], \
        ip.ip_src[2], ip.ip_src[3]);    // Prints source IP address
    printf("        Dest:    %d.%d.%d.%d \n", ip.ip_dst[0], ip.ip_dst[1], \
        ip.ip_dst[2], ip.ip_dst[3]);    // Prints destination IP address

    printf("        TTL:    %d \n", ip.ip_ttl);    // Prints IP Time to Live (TTL)

    switch (ip.ip_proto)        // Determines type of information in IP packet
    {
        case IPPROTO_ICMP:    // For ICMP packets
            printf("        Type:    %#x    (ICMP) \n", ip.ip_proto);
            break;
        case IPPROTO_TCP:    // For TCP packets
            printf("        Type:    %#x    (TCP) \n", ip.ip_proto);
            break;
        case IPPROTO_UDP:    // For UDP packets
            printf("        Type:    %#x    (UDP) \n", ip.ip_proto);
            break;
        default:        // Any other types, only type number will be displayed.
            printf("        Type:    %#x \n", ip.ip_proto);
            break;
    }
    
    clear_ip_buffer();    // Calls function to clear remainder of packet from buffer
            
}

void clear_eth_only_buffer()    // Function to clear packet from buffer after only displaying ethernet header
{
    if (bit_swap == 0)
    {
        for (int i = 0; i < dhdr.caplen -14; i++)    // Remaining packet size is total length minus ethernet header size
               {
            fread((char *) &buff, sizeof(buff), 1 , input);
                    array[i] = buff;        // Empties buffer into a character array
               }
    }
    else
    {
        for (int i = 0; i < SWAPLONG(dhdr.caplen) -14; i++)    // Performs bit swap before determining packet size
               {
            fread((char *) &buff, sizeof(buff), 1 , input);
                    array[i] = buff;        // Empties buffer into a character array
               }
    }
}

void clear_arp_buffer()        // Function to clear packet from buffer after displaying ethernet header and ARP header
{

    if (bit_swap == 0)
    {
              for (int i = 0; i < dhdr.caplen -42; i++)    // Remaining packet size is total length minus ethernet header + ARP header
               {
            fread((char *) &buff, sizeof(buff), 1 , input);
                    array[i] = buff;        // Empties buffer into a character array
               }
    }
    else
    {
              for (int i = 0; i < SWAPLONG(dhdr.caplen) -42; i++)    // Performs bit swap before determining packet size
               {
            fread((char *) &buff, sizeof(buff), 1 , input);
                    array[i] = buff;        // Empties buffer into a character array
               }
    }
}

void clear_ip_buffer()        // Funcation to clear packet from buffer after displaying ethernet header and IP header
{

    if (bit_swap == 0)
    {
              for (int i = 0; i < dhdr.caplen -34; i++)    // Remaining packet size is total length minus ethernet header + IP header
               {
            fread((char *) &buff, sizeof(buff), 1 , input);
                    array[i] = buff;        // Empties buffer into a character array
               }
    }
    else
    {
              for (int i = 0; i < SWAPLONG(dhdr.caplen) -34; i++)    // Performs bit swap before determining packet size
               {
            fread((char *) &buff, sizeof(buff), 1 , input);
                    array[i] = buff;        // Empties buffer into a character array
               }
    }
}

string search_eaddr(string search, string hostfile)    // Function to perform lookup in provided MAC to hostname file
{
    string line;
    ifstream in(hostfile.c_str());

    while (getline(in,line))    // Reads each line of the file
    {
        size_t pos = line.find('\t');    // Finds tab delimiter
        string mac = line.substr(0, pos);    // Sets MAC address as portion before tab

        if(mac == search)    // Compares MAC address with search criteria
            return line.substr(pos+1);    // If found, will return the hostname after the tab
    }
    return "";    // If no match found, will return an empty string
}

A suggestion, you might use stringstream for extracting the two fields, that would work for both TAB and SPACE -separated fields.
That would be along the lines of ..

#include <sstream> // for stringstream

// Function to perform lookup in provided MAC to hostname file
string search_eaddr(const string & search, const string & hostfile)
{
  ifstream in(hostfile.c_str());

  if(!in)
  {
    // Error, do something here ...
  }

  string line;
  while (getline(in,line))
  {
    // Stuff the line into the stream
    stringstream ss(line);
    string mac, host;

    // Extract, note that any trailing whitespace will NOT end up in 'host'
    if(ss >> mac >> host)
    {
      if(mac == search)
        return host;
    }
  }
  return "";    // If no match found, will return an empty string
}

Then a couple of things .. this

if (argc < 2 || argc > 3)    // Checks the number of arguments entered by user
{
  // If too many or too few arguments provided, program usage will be displayed.
  printf("\n    Usage: %s <dump_filename> (host_filename) \n\n", argv[0]);
  return 1;
}
if (argc == 3) // Checks if MAC to hostname file provided
{
   ...

could be simplified to

// Checks the number of arguments entered by user
if(argc != 3)
{
  // complain here and return 
  return 1;
}

Then instead of

input2 = fopen(argv[2], "r");    // Verifies that MAC to hostname file exists and can be opened.
if(fopen == NULL)
{
  printf("Cannot open host list\n");
  return 1;
}

you definitely want to have

input2 = fopen(argv[2], "r");    // Verifies that MAC to hostname file exists and can be opened.
// Check input2, NOT fopen
if(input2 == NULL)
{
  printf("Cannot open host list\n");
  return 1;
}

Then

  • Your source and dest buffers are too small, you need more than 17 bytes, don't forget the terminating '\0' added by sprintf()
  • You might check that every fread() call actually reads the expected amount of data.
  • Magic numbers (14, 32, 42) could be avoided.

Changing the code to using stringstream fixed my strange parenthesis issue.

I also updated the code per your suggestions.

Thanks for your help!

A suggestion, you might use stringstream for extracting the two fields, that would work for both TAB and SPACE -separated fields.
That would be along the lines of ..

#include <sstream> // for stringstream

// Function to perform lookup in provided MAC to hostname file
string search_eaddr(const string & search, const string & hostfile)
{
  ifstream in(hostfile.c_str());

  if(!in)
  {
    // Error, do something here ...
  }

  string line;
  while (getline(in,line))
  {
    // Stuff the line into the stream
    stringstream ss(line);
    string mac, host;

    // Extract, note that any trailing whitespace will NOT end up in 'host'
    if(ss >> mac >> host)
    {
      if(mac == search)
        return host;
    }
  }
  return "";    // If no match found, will return an empty string
}