views:

53

answers:

2

Hello! I'm making a network sniffer for my college project using the libpcap library. Majority of the code works without any problem, however, I'm stuck with the following issue. I've added five command line options using getopt_long() function, but one option doesn't work as expected.

The option is -d (--device_info) and is used to print the I.P address and netmask associated with an interface. It takes one argument, which is the name of the interface for which the information is to be printed.

The necessary portion of the code from the program is attached below. My problem is that even though I'm explicitly assigning the name of the interface to char *device (line 35) and then passing that as an argument to print_device_info() (line 36), when print_device_info() executes, it always executes the if block (line 4) i.e char *device is always equal to NULL in print_device_info() even though I pass it as an argument.

I'm a beginner programmer and this is the first "real" project I'm developing. I guess it's a pointer issue but I'm not sure.

Kindly, also suggest some method to solve it.

1)   //Prints information associated with an interface.
 2)   void print_device_info(char *device)
 3)   { 
      ...
      ...
      ...

 4)     if(device == NULL);
 5)      {
 6)       fprintf(stdout, "No interface specified \n");
 7)       exit(1);
 8)      }
      ...
      ...
      ...
 9)    }

 10)   int main(int argc, char *argv[])
 11)   { 
 12)     int next_option; // Options

 13)     char *device=NULL; // NIC to sniff packet from. 

 14)     // A string listing valid short options letters.
 15)     const char* const short_options = "ho:i:d:p";

 16)     /* An array describing valid long options. */
 17)     const struct option long_options[] = 
 18)      {
 19)       { "help",        0, NULL, 'h' },
 20)       { "output",      1, NULL, 'o' },
 21)       { "interface",   1, NULL, 'i' },
 22)       { "device_info", 1, NULL, 'd' },
 23)       { "promisc_off", 0, NULL, 'p' },
 24)       { NULL,          0, NULL,  0  } /* Required at end of array. */
 25)      };

 26)     //Check the arguments and perform their respective functions
 27)   do {
 28)     next_option = getopt_long (argc, argv, short_options,
 29)                                 long_options, NULL);
 30)     switch (next_option)
 31)     {
 32)       case 'd':    /* -d or --device_info */
 33)        /* User has requested information associated an interface. Print it to standard
 34)           output, and exit with exit code zero (normal termination). */
 35)        device = optarg;
 36)        print_device_info (device);
 37)     }
 38)     }while (next_option != -1);
       }

Edit: Here I'm posting an edited version of the original program. This edited version is a complete program in itself, so should be sufficient for any future queries.

#include <pcap.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

//Prints information associated with an interface.
void print_device_info(char *device)
 { 

  struct in_addr addr_struct; //Stores I.P address.

  char *ipaddr=NULL; //Points to I.P address in dotted-decimal form.

  char *netmask=NULL; //Points to  Netmask in dotted-decimal form.

  int ret; //Return code (Useful for error detection).

  bpf_u_int32 tmpip; // Stores I.P address temporarily in host-byte (little-endian) order.

  bpf_u_int32 tmpmask;// Stores Netmask temporarily in host-byte (little-endian) order.

  char errbuf[PCAP_ERRBUF_SIZE]; // Stores error messages.

   printf("%s\n",device);

  if(device==NULL);
   {
    fprintf(stdout, "No interface specified \n");
    exit(1);
   }

/*Lookup device info and store the return value in ret */
  ret = pcap_lookupnet(device, &tmpip, &tmpmask, errbuf);

//error checking
 if(ret==-1)
  {
   fprintf(stderr, "Couldn't find IP address and subnet mask: %s\n", errbuf);
   exit(1);
  }

//Calculate IP address
  addr_struct.s_addr=tmpip;
  ipaddr=inet_ntoa(addr_struct);

//error checking.
  if(ipaddr==NULL)
   {
    perror("Error in locating the I.P address");
    exit(1);
   }

//Print the IP address
  printf("IP address: %s\n",ipaddr);

//Calculate subnet mask
  addr_struct.s_addr=tmpmask;
  netmask=inet_ntoa(addr_struct);

//error checking.
  if(netmask==NULL)
   {
    perror("Error in locating the subnet mask");
    exit(1);
   }

//Print the subnet mask 
  printf("Subnet mask: %s\n",netmask);

  exit(0);
}

int main(int argc, char *argv[])
{ 

//A) Declarations -----------------------------------------------------------------------------------------------------
  int next_option; // Options

  char *device=NULL; // NIC to sniff packet from. 

  char errbuf[PCAP_ERRBUF_SIZE]; // Stores error messages.

// A string listing valid short options letters.
  const char* const short_options = "i:d:";

/* An array describing valid long options. */
  const struct option long_options[] = 
   {
    { "interface",   1, NULL, 'i' },
    { "device_info", 1, NULL, 'd' },
    { NULL,          0, NULL,  0  } /* Required at end of array. */
   };

//Check the arguments and perform their respective functions
do {
  next_option = getopt_long (argc, argv, short_options,
                              long_options, NULL);
  switch (next_option)
  {

  case 'i':    /* -i or  --interface */
     /* User has specified the NIC device to sniff from */
     device = optarg;
     break;

  case 'd':    /* -d or --device_info */
     /* User has requested information associated an interface. Print it to standard
        output, and exit with exit code zero (normal termination). */
     device = optarg;
     printf("%s\n",device);
     print_device_info (device);

  case -1:     /* Done with options.  */
     break;

  default:     /* Something else: unexpected.  */
     abort ();
  }
}
while (next_option != -1);

//B)Lookup device
  if (device == NULL)
  {
    device=pcap_lookupdev(errbuf);  

     //Check for error  
     if(device==NULL)
      {
        fprintf(stderr, "Couldn't find device %s\n",errbuf);
        return(2);
      }
  }

//Print Device
  printf("Device: %s\n", device);

}
+2  A: 

I've modified your code to break out of arg-reading loop and it works exactly as I would expect:

$ ./longopt
(null)
$ ./longopt -d eth0
eth0

Here's the code:

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>


int main(int argc, char *argv[])
{ 
    int next_option; // Options
    char *device=NULL; // NIC to sniff packet from.

    // A string listing valid short options letters.
    const char* const short_options = "ho:i:d:p";

    /* An array describing valid long options. */
    const struct option long_options[] = 
        {
            { "help",        0, NULL, 'h' },
            { "output",      1, NULL, 'o' },
            { "interface",   1, NULL, 'i' },
            { "device_info", 1, NULL, 'd' },
            { "promisc_off", 0, NULL, 'p' },
            { NULL,          0, NULL,  0  } /* Required at end of array. */
        };

    //Check the arguments and perform their respective functions
    while(1) {
        next_option = getopt_long (argc, argv, short_options,
                                   long_options, NULL);
        if (next_option == -1)
            break;


        switch (next_option)
        {
            case 'd':    /* -d or --device_info */
                /* User has requested information associated an interface. Print it to standard
                   output, and exit with exit code zero (normal termination). */
                device = optarg;


        }
    } 

    printf("%s\n", device);

    return(0);

}
SteveRawlinson
Firstly, I'm sorry that I forgot to add the "while" condition of the "do-while" in the above edited code (I've corrected it). However, "while" condition is present in the original code. What you're doing is assigning an interface to "char *device". However, this isn't the issue. As I already stated in my response to Gauthier, that I already have another option "-i" used to set the interface code exactly as the "case 'd'" block of your code. Also, if I add another statement "printf("%s\n",device)" after line 35 in my code and then call "print_device_info()", value of "device" is what I
Naruto Uzumaki
value of "device" is what I enter via the command line "./mysniffer -d etho". It is when "print_device_info(device)" is called that, for some strange reason, "if" block is always executed. I also added another "printf("%s\n",device)" statement, right before the "if" block and I can see that "device" is "eth0" even in the "print_device_info(device)" function, but "if" block is still executing.
Naruto Uzumaki
I see this is now solved - but one more thing. That do .. while loop will execute once *after* next_option == -1 because the test is at the bottom. Put the condition at the top: while(next_option != -1)
SteveRawlinson
+3  A: 

You have a stray semicolon (;) at the end of your test.

This means that the only thing controlled by the test is an empty expression and the block following the if statement is just a nested block (will always be executed).

Bart van Ingen Schenau
good catch.. sharp eye :-)
Naveen
@Naruto Uzumaki: Unrelated but aren't you missing a `:` in short_options string ?
Naveen
Naruto Uzumaki just killed himself. He couldn't stand that a stray semicolon could consume 2 hrs. of his useless life. This is a bot programmed by him which is executed when someone posts a correct answer to this problem. I'm sure he is rotting in hell. He says thank you BTW :P
Naruto Uzumaki
@Naveen It's still Naruto's bot. He says that a colon `:` is added in `short_options` string if that option accepts an argument. In the above case only options "o","i" and "d" accept arguments, hence the colon.
Naruto Uzumaki