Sunday, November 28, 2010

Serialization using Google protobuf

Protocol buffers (that's what protobuf means :)) are a means to serialize structured data. Now you may be wondering what this 'serialize' means.

To be brief : Serialization is a process of packing the data structures or objects into sequence of bits so that they can be stored in a file or a buffer and transmitted across the network and then can be retrieved on the same or other system environment. Examples include XML, JSON, ASN.1, YAML, ProtoBuf etc.

Advantages :
1. Persistent object creation
2. RPC enabling
3. Are simple
4. Save space on disk
5. Very fast
6. Platform and language independent

ProtoBuf is available freely from Google (http://code.google.com/p/protobuf/). Procedure of installation and compilation is present there.

Everything that needs to be serialized is put in a protocol buffer message (a .proto file). This file tells the compiler how the data is to be serialized.

For each field there are setter and getter functions fieldname() is a getter and set_fieldname() is a setter. Remember to use these functions as the exact way it is told here.
The tutorials for the different languages can be found here

I present a small simple example to use the protobuf library and there is much more than that one can do with the protobuf :)

To make and run the example (on linux) follow the steps :
1. Download the tar.gz package from here
2. Login as root
3. Untar it in your home directory (say user1)
4. Go inside the protobuf directory and run ./configure
5. Add the path /usr/local/lib to the environment variable LD_LIBRARY_PATH
6. Then run :
make
make install
7. You are now ready to use the library.
8. To use protobuf you will need following files (I am explaining for c++ only):
.cc file
.proto file
Makefile
Rest of the files are generated on the fly.

Here is an example of a Vehicle parts which contains three fields , two mandatory and one optional.
-Part ID (mandatory)
-Part Name (mandatory)
-Vendor (optional)

We write this in .proto file

//partbook.proto
package tutorial;

message Part {
  required string name = 1;
  required int32 partid = 2;        // Unique ID number for this part
  optional string vendor = 3;

}

message PartDetails {
  repeated Part part = 1;
}


Then we write a c++ program to use the library and .proto to add serialized data to the data file.
/*add_part.cc*/
#include <iostream>
#include <fstream>
#include <string>
#include "partbook.pb.h"
using namespace std;

/* This function fills in a Part message based on user input.*/
void PromptForPart(tutorial::Part* part) {
  cout << "Enter part ID number: ";
  int partid;
  cin >> partid;
  part->set_partid(partid);
  cin.ignore(256, '\n');

  cout << "Enter part name: ";
  getline(cin, *part->mutable_name());

  cout << "Enter vendor name (blank for none): ";
  string vendor;
  getline(cin, vendor);
  if (!vendor.empty()) {
    part->set_vendor(vendor);
  }

}

/* Main function:  Reads the entire part list from a file,
 * adds one part based on user input, then writes it back out to the same
 *   file.
 */
int main(int argc, char* argv[]) {
  /* Verify that the version of the library that we linked against is
   * compatible with the version of the headers we compiled against.
   */
  GOOGLE_PROTOBUF_VERIFY_VERSION;

  if (argc != 2) {
    cerr << "Usage:  " << argv[0] << " PART_BOOK_FILE" << endl;
    return -1;
  }

  tutorial::PartDetails part_book;

  {
    // Read the existing part book.
    fstream input(argv[1], ios::in | ios::binary);
    if (!input) {
      cout << argv[1] << ": File not found.  Creating a new file." << endl;
    } else if (!part_book.ParseFromIstream(&input)) {
      cerr << "Failed to parse part book." << endl;
      return -1;
    }
  }

  // Add a new part.
  PromptForPart(part_book.add_part());

  {
    // Write the new part book back to disk.
    fstream output(argv[1], ios::out | ios::trunc | ios::binary);
    if (!part_book.SerializeToOstream(&output)) {
      cerr << "Failed to write part book." << endl;
      return -1;
    }
  }

  // Optional:  Delete all global objects allocated by libprotobuf.
  google::protobuf::ShutdownProtobufLibrary();

  return 0;
}


Then we write a c++ program to use the library and .proto to serialize the data.
/*list_parts.cc*/
#include <iostream>
#include <fstream>
#include <string>
#include "partbook.pb.h"
using namespace std;

// Iterates though all parts in the PartDetails and prints info about them.
void ListParts(const tutorial::PartDetails& part_book) {
  for (int i = 0; i < part_book.part_size(); i++) {
    const tutorial::Part& part = part_book.part(i);

    cout << "Part ID: " << part.partid() << endl;
    cout << "  Name: " << part.name() << endl;
    if (part.has_vendor()) {
      cout << "  Vendor: " << part.vendor() << endl;
    }

  }
}

// Main function:  Reads the entire part book from a file and prints all
//   the information inside.
int main(int argc, char* argv[]) {
  // Verify that the version of the library that we linked against is
  // compatible with the version of the headers we compiled against.
  GOOGLE_PROTOBUF_VERIFY_VERSION;

  if (argc != 2) {
    cerr << "Usage:  " << argv[0] << " PART_BOOK_FILE" << endl;
    return -1;
  }

  tutorial::PartDetails part_book;

  {
    // Read the existing data file.
    fstream input(argv[1], ios::in | ios::binary);
    if (!part_book.ParseFromIstream(&input)) {
      cerr << "Failed to parse part book." << endl;
      return -1;
    }
  }

  ListParts(part_book);

  // Optional:  Delete all global objects allocated by libprotobuf.
  google::protobuf::ShutdownProtobufLibrary();

  return 0;
}



#Makefile
#This creates executables for python cpp and java , to create only one language use #'make cpp' or 'make python' or 'make java'

.PHONY: all cpp java python clean

all: cpp java python

cpp:    add_part_cpp    list_parts_cpp
java:   add_part_java   list_parts_java
python: add_part_python list_parts_python

clean:
 rm -f add_part_cpp list_parts_cpp add_part_java list_parts_java add_part_python list_parts_python
 rm -f javac_middleman AddPart*.class ListParts*.class com/example/tutorial/*.class
 rm -f protoc_middleman partbook.pb.cc partbook.pb.h partbook.pb2.py com/example/tutorial/PartBookProtos.java
 rm -f *.pyc
 rmdir com/example/tutorial 2>/dev/null || true
 rmdir com/example 2>/dev/null || true
 rmdir com 2>/dev/null || true

protoc_middleman: partbook.proto
 protoc --cpp_out=. --java_out=. --python_out=. partbook.proto
 @touch protoc_middleman

add_part_cpp: add_part.cc protoc_middleman
 pkg-config --cflags protobuf  # fails if protobuf is not installed
 c++ add_part.cc partbook.pb.cc -o add_part_cpp `pkg-config --cflags --libs protobuf`

list_parts_cpp: list_parts.cc protoc_middleman
 pkg-config --cflags protobuf  # fails if protobuf is not installed
 c++ list_parts.cc partbook.pb.cc -o list_parts_cpp `pkg-config --cflags --libs protobuf`

javac_middleman: AddPart.java ListParts.java protoc_middleman
 javac AddPart.java ListParts.java com/example/tutorial/PartBookProtos.java
 @touch javac_middleman

add_part_java: javac_middleman
 @echo "Writing shortcut script add_part_java..."
 @echo '#! /bin/sh' > add_part_java
 @echo 'java -classpath .:$$CLASSPATH AddPart "$$@"' >> add_part_java
 @chmod +x add_part_java

list_parts_java: javac_middleman
 @echo "Writing shortcut script list_parts_java..."
 @echo '#! /bin/sh' > list_parts_java
 @echo 'java -classpath .:$$CLASSPATH ListParts "$$@"' >> list_parts_java
 @chmod +x list_parts_java

add_part_python: add_part.py protoc_middleman
 @echo "Writing shortcut script add_part_python..."
 @echo '#! /bin/sh' > add_part_python
 @echo './add_part.py "$$@"' >> add_part_python
 @chmod +x add_part_python

list_parts_python: list_parts.py protoc_middleman
 @echo "Writing shortcut script list_parts_python..."
 @echo '#! /bin/sh' > list_parts_python
 @echo './list_parts.py "$$@"' >> list_parts_python
 @chmod +x list_parts_python


Save all these files under <proto-installation-path>/examples/ and run make

To run the example
./add_part_cpp <data-filename>
./list_parts_cpp <data-filename>

Wednesday, November 24, 2010

Xdotool : Fake Keyboard/Mouse Input Tool

xdotool is a fake keyboard/mouse input tool for linux which is freely available on the internet.
So whatever you keep typing on the keyboard or kept clicking using the mouse , you can do it now with the help of a script.

One can get the tool downloaded from their website for all versions of linux:
http://www.semicomplete.com/projects/xdotool
There are too many uses you can use xdotool for. For example : If you need to open multiple tabs in a terminal you can use -
'xdotool key ctrl+shift+t' for as many times as the no. of tabs you need and to close down those tabs use 'xdotool ctrl+shift+w'

All this can be written on a script and when the script runs you can see all of that happening without even touching the keyboard or mouse.

The whole documentation is present here

Here are some examples :

Example: focus the firefox url bar

WID=`xdotool search "Mozilla Firefox" | head -1`
xdotool windowactivate --sync $WID
xdotool key --clearmodifiers ctrl+l


# As of version 2.20100623, you can do this simpler version of above:
xdotool search "Mozilla Firefox" windowactivate --sync key --clearmodifiers ctrl+l


Example: Resize all visible gnome-terminal windows
WIDS=`xdotool search --onlyvisible --name "gnome-terminal"`
for id in $WIDS; do
  xdotool windowsize $id 500 500
done

# As of version 2.20100623, you can do this simpler version of above:
xdotool search --onlyvisible --classname "gnome-terminal" windowsize %@ 500
500

Saturday, October 23, 2010

Create your own packet sniffer in C

A simple implementation of a packet sniffer in C on linux platform using the libpcap library. This packet sniffer currently sniffs IP , TCP , ICMP and UDP packets. It can be modified to any protocol as needed just by introducing the header information in it.

Certain filters can be used too like port number and specific host etc.
e.g.

Expression
Description
  ip
Capture all IP packets.
  tcp
Capture only TCP packets.
  tcp port 80
Capture only TCP packets with a port equal to 80.
  ip host 10.1.2.3
Capture all IP packets to or from host 10.1.2.3.

It is a little modified version of sniffer from tcpdump website.

Note : To run this code you require root permissions. 

Here's the code:

/*sniffer.c*/
//To compile : gcc -o sniffer sniffer.c -lpcap
//To run : ./sniffer [interface-name]

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

/* default snap length (maximum bytes per packet to capture) */
#define SNAP_LEN 1518

/* ethernet headers are always exactly 14 bytes [1] */
#define SIZE_ETHERNET 14

/* Ethernet addresses are 6 bytes */
#define ETHER_ADDR_LEN 6

/* Ethernet header */
struct sniff_ethernet {
        u_char  ether_dhost[ETHER_ADDR_LEN];    /* destination host address */
        u_char  ether_shost[ETHER_ADDR_LEN];    /* source host address */
        u_short ether_type;                     /* IP? ARP? RARP? etc */
};

/* IP header */
struct sniff_ip {
        u_char  ip_vhl;                 /* version << 4 | header length >> 2 */
        u_char  ip_tos;                 /* type of service */
        u_short ip_len;                 /* total length */
        u_short ip_id;                  /* identification */
        u_short ip_off;                 /* fragment offset field */
        #define IP_RF 0x8000            /* reserved fragment flag */
        #define IP_DF 0x4000            /* dont fragment flag */
        #define IP_MF 0x2000            /* more fragments flag */
        #define IP_OFFMASK 0x1fff       /* mask for fragmenting bits */
        u_char  ip_ttl;                 /* time to live */
        u_char  ip_p;                   /* protocol */
        u_short ip_sum;                 /* checksum */
        struct  in_addr ip_src,ip_dst;  /* source and dest address */
};
#define IP_HL(ip)               (((ip)->ip_vhl) & 0x0f)
#define IP_V(ip)                (((ip)->ip_vhl) >> 4)

/* TCP header */
typedef u_int tcp_seq;

struct sniff_tcp {
        u_short th_sport;               /* source port */
        u_short th_dport;               /* destination port */
        tcp_seq th_seq;                 /* sequence number */
        tcp_seq th_ack;                 /* acknowledgement number */
        u_char  th_offx2;               /* data offset, rsvd */
#define TH_OFF(th)      (((th)->th_offx2 & 0xf0) >> 4)
        u_char  th_flags;
        #define TH_FIN  0x01
        #define TH_SYN  0x02
        #define TH_RST  0x04
        #define TH_PUSH 0x08
        #define TH_ACK  0x10
        #define TH_URG  0x20
        #define TH_ECE  0x40
        #define TH_CWR  0x80
        #define TH_FLAGS        (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
        u_short th_win;                 /* window */
        u_short th_sum;                 /* checksum */
        u_short th_urp;                 /* urgent pointer */
};

void
got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);

void
print_payload(const u_char *payload, int len);

void
print_hex_ascii_line(const u_char *payload, int len, int offset);



/*
 * print data in rows of 16 bytes: offset   hex   ascii
 *
 * 00000   47 45 54 20 2f 20 48 54  54 50 2f 31 2e 31 0d 0a   GET / HTTP/1.1..
 */
void
print_hex_ascii_line(const u_char *payload, int len, int offset)
{

 int i;
 int gap;
 const u_char *ch;

 /* offset */
 printf("%05d   ", offset);
 
 /* hex */
 ch = payload;
 for(i = 0; i < len; i++) {
  printf("%02x ", *ch);
  ch++;
  /* print extra space after 8th byte for visual aid */
  if (i == 7)
   printf(" ");
 }
 /* print space to handle line less than 8 bytes */
 if (len < 8)
  printf(" ");
 
 /* fill hex gap with spaces if not full line */
 if (len < 16) {
  gap = 16 - len;
  for (i = 0; i < gap; i++) {
   printf("   ");
  }
 }
 printf("   ");
 
 /* ascii (if printable) */
 ch = payload;
 for(i = 0; i < len; i++) {
  if (isprint(*ch))
   printf("%c", *ch);
  else
   printf(".");
  ch++;
 }

 printf("\n");

return;
}

/*
 * print packet payload data (avoid printing binary data)
 */
void
print_payload(const u_char *payload, int len)
{

 int len_rem = len;
 int line_width = 16;   /* number of bytes per line */
 int line_len;
 int offset = 0;     /* zero-based offset counter */
 const u_char *ch = payload;

 if (len <= 0)
  return;

 /* data fits on one line */
 if (len <= line_width) {
  print_hex_ascii_line(ch, len, offset);
  return;
 }

 /* data spans multiple lines */
 for ( ;; ) {
  /* compute current line length */
  line_len = line_width % len_rem;
  /* print line */
  print_hex_ascii_line(ch, line_len, offset);
  /* compute total remaining */
  len_rem = len_rem - line_len;
  /* shift pointer to remaining bytes to print */
  ch = ch + line_len;
  /* add offset */
  offset = offset + line_width;
  /* check if we have line width chars or less */
  if (len_rem <= line_width) {
   /* print last line and get out */
   print_hex_ascii_line(ch, len_rem, offset);
   break;
  }
 }

return;
}

/*
 * dissect/print packet
 */
void
got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{

 static int count = 1;                   /* packet counter */
 
 /* declare pointers to packet headers */
 const struct sniff_ethernet *ethernet;  /* The ethernet header [1] */
 const struct sniff_ip *ip;              /* The IP header */
 const struct sniff_tcp *tcp;            /* The TCP header */
 const char *payload;                    /* Packet payload */

 int size_ip;
 int size_tcp;
 int size_payload;
 
 printf("\nPacket number %d:\n", count);
 count++;
 
 /* define ethernet header */
 ethernet = (struct sniff_ethernet*)(packet);
 
 /* define/compute ip header offset */
 ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
 size_ip = IP_HL(ip)*4;
 if (size_ip < 20) {
  printf("   * Invalid IP header length: %u bytes\n", size_ip);
  return;
 }

 /* print source and destination IP addresses */
 printf("       From: %s\n", inet_ntoa(ip->ip_src));
 printf("         To: %s\n", inet_ntoa(ip->ip_dst));
 
 /* determine protocol */ 
 switch(ip->ip_p) {
  case IPPROTO_TCP:
   printf("   Protocol: TCP\n");
   break;
  case IPPROTO_UDP:
   printf("   Protocol: UDP\n");
   return;
  case IPPROTO_ICMP:
   printf("   Protocol: ICMP\n");
   return;
  case IPPROTO_IP:
   printf("   Protocol: IP\n");
   return;
  default:
   printf("   Protocol: unknown\n");
   return;
 }
 
 /*
  *  OK, this packet is TCP.
  */
 
 /* define/compute tcp header offset */
 tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);
 size_tcp = TH_OFF(tcp)*4;
 if (size_tcp < 20) {
  printf("   * Invalid TCP header length: %u bytes\n", size_tcp);
  return;
 }
 
 printf("   Src port: %d\n", ntohs(tcp->th_sport));
 printf("   Dst port: %d\n", ntohs(tcp->th_dport));
 
 /* define/compute tcp payload (segment) offset */
 payload = (u_char *)(packet + SIZE_ETHERNET + size_ip + size_tcp);
 
 /* compute tcp payload (segment) size */
 size_payload = ntohs(ip->ip_len) - (size_ip + size_tcp);
 
 /*
  * Print payload data; it might be binary, so don't just
  * treat it as a string.
  */
 if (size_payload > 0) {
  printf("   Payload (%d bytes):\n", size_payload);
  print_payload(payload, size_payload);
 }

return;
}

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

 char *dev = NULL;   /* capture device name */
 char errbuf[PCAP_ERRBUF_SIZE];  /* error buffer */
 pcap_t *handle;    /* packet capture handle */

 char filter_exp[] = "ip";  /* filter expression */
 struct bpf_program fp;   /* compiled filter program (expression) */
 bpf_u_int32 mask;   /* subnet mask */
 bpf_u_int32 net;   /* ip */
 int num_packets ;   /* number of packets to capture */

 /* check for capture device name on command-line */
 if (argc == 2) {
  dev = argv[1];
 }
 else if (argc > 3) {
  fprintf(stderr, "error: unrecognized command-line options\n\n");
 printf("Usage: %s [interface]\n", argv[0]);
 printf("\n");
 printf("Options:\n");
 printf("    interface    Listen on <interface> for packets.\n");
 printf("\n");
  exit(EXIT_FAILURE);
 }
 else {
  /* find a capture device if not specified on command-line */
  dev = pcap_lookupdev(errbuf);
  if (dev == NULL) {
   fprintf(stderr, "Couldn't find default device: %s\n",
       errbuf);
   exit(EXIT_FAILURE);
  }
 }
 printf("\nEnter no. of packets you want to capture: ");
        scanf("%d",&num_packets);
        printf("\nWhich kind of packets you want to capture : ");
        scanf("%s",filter_exp);
 /* get network number and mask associated with capture device */
 if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
  fprintf(stderr, "Couldn't get netmask for device %s: %s\n",
      dev, errbuf);
  net = 0;
  mask = 0;
 }

 /* print capture info */
 printf("Device: %s\n", dev);
 printf("Number of packets: %d\n", num_packets);
 printf("Filter expression: %s\n", filter_exp);

 /* open capture device */
 handle = pcap_open_live(dev, SNAP_LEN, 1, 1000, errbuf);
 if (handle == NULL) {
  fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
  exit(EXIT_FAILURE);
 }

 /* make sure we're capturing on an Ethernet device [2] */
 if (pcap_datalink(handle) != DLT_EN10MB) {
  fprintf(stderr, "%s is not an Ethernet\n", dev);
  exit(EXIT_FAILURE);
 }

 /* compile the filter expression */
 if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
  fprintf(stderr, "Couldn't parse filter %s: %s\n",
      filter_exp, pcap_geterr(handle));
  exit(EXIT_FAILURE);
 }

 /* apply the compiled filter */
 if (pcap_setfilter(handle, &fp) == -1) {
  fprintf(stderr, "Couldn't install filter %s: %s\n",
      filter_exp, pcap_geterr(handle));
  exit(EXIT_FAILURE);
 }

 /* now we can set our callback function */
 pcap_loop(handle, num_packets, got_packet, NULL);

 /* cleanup */
 pcap_freecode(&fp);
 pcap_close(handle);

 printf("\nCapture complete.\n");

return 0;
}


Tuesday, October 19, 2010

Vector Write in C

Simple implementation of vector writes in unix. This example uses writev system call to write into a file. The writev call enables you to write multiple discontiguous regions of memory to a file descriptor in a single operation.This is called a vector write.

We use iovec structure for the purpose of filling up the vector.Each
element specifies one region of memory to write; the fields iov_base and iov_len
specify the address of the start of the region and the length of the region, respectively.

This program takes command line arguments to fill up the vector.
Anything after the second argument will go to the file.

//vector-write.c
//To compile : gcc -o vwrite vector-write.c
//To run : ./vwrite file-name argument1 argument2 ..

#include <fcntl.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
int main (int argc, char* argv[])
{
  int fd;
  struct iovec* vec;
  struct iovec* vec_next;
  int i;
  
  if(argc < 2 )
  {
    printf("\nUsage : %s <filename> [args...]\n",argv[0]);
    exit(1);
  }
  
  char newline = '\n';
  
  char* filename = argv[1];
  
  argc -= 2;
  argv += 2;
  
  /* Allocate an array of iovec elements. We.ll need two for each
   * element of the argument list, one for the text itself, and one for
   * a newline. */
  
  vec = (struct iovec*) malloc (2 * argc * sizeof (struct iovec));
  
  /* Fill the iovec entries. */
  
  vec_next = vec;
  for (i = 0; i < argc; ++i) {
    
    /* The first element is the text of the argument itself. */
    
    vec_next->iov_base = argv[i];
    vec_next->iov_len = strlen (argv[i]);
    ++vec_next;
    
    /* The second element is a single newline character. It.s okay for
     * multiple elements of the struct iovec array to point to the
     * same region of memory. */
    
    vec_next->iov_base = &newline;
    vec_next->iov_len = 1;
    ++vec_next;
  }
  
  /* Write the to a file. */
  
  fd = open (filename, O_WRONLY | O_CREAT);
  writev (fd, vec, 2 * argc);
  close (fd);
  free (vec);
  return 0;
}

Wednesday, October 13, 2010

IPC Memory Map implementation in C

Using memory mapped file two processes can both open the same file and both read and write from it, thus sharing the information.It is easier to map a section of the file to memory, and get a pointer to it rather than doing fseek .

A memory mapped file is very easy to use as we can perform simple arithmetic operations to get and set data from a file.

Here is a simple example of creating and using memory mapped files.It takes a file and offset as input and tells what data is present at that offset.


//memorymap.c
//To compile : gcc -o mmap memorymap.c
//To run : ./mmap file-name offset

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>

#include <sys/stat.h>
#include <errno.h>
int main(int argc, char *argv[])
{
    int fd, offset;
    char *data;
    struct stat sbuf;
    if (argc != 3)
    {
        fprintf(stderr, "usage: %s <file-name> offset\n",argv[0]);
        exit(1);
    }
    if ((fd = open(argv[1], O_RDONLY)) == -1)
    {
        perror("open");
        exit(1);
    }
    /*argv[1] is the file name to be examined*/
    if (stat(argv[1], &sbuf) == -1)
    {
        perror("stat");
        exit(1);
    }
    offset = atoi(argv[2]);
    if (offset < 0 || offset > sbuf.st_size-1)
    {
        printf("%s: offset must be in the range 0-%d\n",argv[0],sbuf.st_size-1);
        exit(1);
    }
    data = mmap((caddr_t)0, sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
    {
        if (data == (caddr_t)(-1))
        {
            perror("mmap");
            exit(1);
        }
        printf("Byte at offset %d is '%c'\n", offset, data[offset]);
        return 0;
    }
}

Tuesday, October 12, 2010

How to create Static and Shared Libraries out of your source


Here is a simple method to create a static as well as shared or dynamic library in C.

There are two ways of creating libraries

1. Static Libraries

2. Shared or dynamic libraries


Difference between the two



Static Library
Shared Library
1. Static libraries are compiled into the program itself1. Shared libraries are compiled separately and referenced by the program
2. Program Size increases2. Program size is smaller but shared libraries are required at the runtime.
3. Every program has its own static library3. Shared library has only one copy and referenced by different programs

  Consider some files which we need to include in library

  1. logger.c*
  2. logger.h*
Process to create static library
  1. gcc -c logger.c -o logger.o 
  2. ar rcs liblogger.a logger.o
Process to create shared library
  1. gcc -c -fPIC logger.c -o logger.o 
  2. gcc -shared -Wl,-soname,liblogger.so.1 -o liblogger.so.1.0.1 logger.o
 Note:  lib is mandatory as the prefix to the library name.

Tar the source (if needed) using this command

     tar -zcvf liblogger.tar.gz liblogger/ 
where liblogger is the folder which contains all the source and library files.

 
Using test.c* as the test C file to use the library.


How to use static library :

Compile :gcc -static test.c -L. -llogger -o staticlog
Run : ./staticlog

How to use dynamic/shared library :

            Compile :gcc test.c -o dynamiclog -L. –llogger
            Run : ./dynamiclog


  We use llogger as the linker option since the the library name is liblogger

  So you must have understood how the things work !!

  * The source of these files is present at the link pointed by the file names.

IPC Semaphores implementation in C

Here is a simple example to show how the IPC semaphores work.
In this example two processes (parent and child) are communicating between each other using semaphores.

Each process access the same track thrice based on which process gets the access.
Once the process gets the access of the track it sleeps for 5 seconds (in this section user can do some useful work instead of sleeping !) and after that it releases the track using sem_op function.

Instead of communicating between the parent and child process we can have two different processes running on the same machine also.

#include <stdio.h>
//ipcsemaphore.c
//To compile : gcc -o sem ipcsemaphore.c
//To run : ./sem
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

void die(char *msg)
{
  perror(msg);
  exit(1);
}

int main()
{
    int i,j;
    int pid;
    int semid; /* semid of semaphore set */
    key_t key ; /* key to pass to semget() */
    int semflg = IPC_CREAT | 0666; /* semflg to pass to semget() */
    int nsems = 1; /* nsems to pass to semget() */
    int nsops; /* number of operations to do */
    struct sembuf *sops = (struct sembuf *) malloc(2*sizeof(struct sembuf));
    /* ptr to operations to perform */

    /* generate key */
    if ((key = ftok("ipcsemaphore.c", 'Q')) == -1) 
       die("ftok");
    
    /* set up semaphore */

    printf("\nsemget: Setting up semaphore: semget(%#lx, %\
                    %#o)\n",key, nsems, semflg);
    if ((semid = semget(key, nsems, semflg)) == -1)
        die("semget: semget failed");


    if ((pid = fork()) < 0)
        die("fork");

    if (pid == 0)
    {
        /* child */
        i = 0;


        while (i  < 3)  /* allow for 3 semaphore sets */
        {

            nsops = 2;

            /* wait for semaphore to reach zero */

            sops[0].sem_num = 0; /* We only use one track */
            sops[0].sem_op = 0; /* wait for semaphore flag to become zero */
            sops[0].sem_flg = SEM_UNDO; /* take off semaphore asynchronous  */


            sops[1].sem_num = 0;
            sops[1].sem_op = 1; /* increment semaphore -- take control of track */
            sops[1].sem_flg = SEM_UNDO | IPC_NOWAIT; /* take off semaphore */

            /* Recap the call to be made. */

            printf("\nsemop:Child  Calling semop(%d, &sops, %d) with:", semid, nsops);
            for (j = 0; j < nsops; j++)
            {
                 printf("\n\tsops[%d].sem_num = %d, ", j, sops[j].sem_num);
                 printf("sem_op = %d, ", sops[j].sem_op);
                 printf("sem_flg = %#o\n", sops[j].sem_flg);
            }

            /* Make the semop() call and report the results. */
            if ((j = semop(semid, sops, nsops)) == -1)
            {
                perror("semop: semop failed");
            }
            else
            {
                printf("\n\nChild Process Taking Control of Track: %d/3 times\n", i+1);
                sleep(5); /* DO Nothing for 5 seconds */

                nsops = 1;

                /* wait for semaphore to reach zero */
                sops[0].sem_num = 0;
                sops[0].sem_op = -1; /* Give UP COntrol of track */
                sops[0].sem_flg = SEM_UNDO | IPC_NOWAIT; /* take off semaphore, asynchronous  */


                if ((j = semop(semid, sops, nsops)) == -1)
                {
                    perror("semop: semop failed");
                }
                else
                     printf("Child Process Giving up Control of Track: %d/3 times\n", i+1);
                sleep(5); /* halt process to allow parent to catch semaphore change first */
            }
            ++i;
        }

    }
    else /* parent */
    {
        i = 0;

        while (i  < 3)   /* allow for 3 semaphore sets */
        {

            nsops = 2;

            /* wait for semaphore to reach zero */
            sops[0].sem_num = 0;
            sops[0].sem_op = 0; /* wait for semaphore flag to become zero */
            sops[0].sem_flg = SEM_UNDO; /* take off semaphore asynchronous  */


            sops[1].sem_num = 0;
            sops[1].sem_op = 1; /* increment semaphore -- take control of track */
            sops[1].sem_flg = SEM_UNDO | IPC_NOWAIT; /* take off semaphore */

            /* Recap the call to be made. */

             printf("\nsemop:Parent Calling semop(%d, &sops, %d) with:", semid, nsops);
            for (j = 0; j < nsops; j++)
            {
                printf("\n\tsops[%d].sem_num = %d, ", j, sops[j].sem_num);
                printf("sem_op = %d, ", sops[j].sem_op);
                printf("sem_flg = %#o\n", sops[j].sem_flg);
            }

            /* Make the semop() call and report the results. */
            if ((j = semop(semid, sops, nsops)) == -1)
            {
                perror("semop: semop failed");
            }
            else
            {
                printf("Parent Process Taking Control of Track: %d/3 times\n", i+1);
                sleep(5); /* Sleep for 5 seconds */

                nsops = 1;

                /* wait for semaphore to reach zero */
                sops[0].sem_num = 0;
                sops[0].sem_op = -1; /* Give UP Control of track */
                sops[0].sem_flg = SEM_UNDO | IPC_NOWAIT; /* take off semaphore, asynchronous  */

                if ((j = semop(semid, sops, nsops)) == -1)
                {
                    perror("semop: semop failed");
                }
                else
                    printf("Parent Process Giving up Control of Track: %d/3 times\n", i+1);
                sleep(5); /* halt process to allow child to catch semaphore change first */
            }
            ++i;

        }

    }
}


References : http://www.cs.cf.ac.uk

Friday, October 8, 2010

TCP header format

TCP segments are sent as internet datagrams. The Internet Protocol header carries several information fields, including the source and destination host addresses. A TCP header follows the internet header, supplying information specific to the TCP protocol. This division allows for the existence of host level protocols other  than TCP.

TCP Header Format



//TCP Header structure as per RFC 793
struct tcphdr {
 u_short th_sport;  /* source port */
 u_short th_dport;  /* destination port */
 tcp_seq th_seq;   /* sequence number */
 tcp_seq th_ack;   /* acknowledgement number */
#if BYTE_ORDER == LITTLE_ENDIAN
 u_int th_x2:4,  /* (unused) */
  th_off:4;  /* data offset */
#endif
#if BYTE_ORDER == BIG_ENDIAN
 u_int th_off:4,  /* data offset */
  th_x2:4;  /* (unused) */
#endif
 u_char th_flags;
#define TH_FIN 0x01
#define TH_SYN 0x02
#define TH_RST 0x04
#define TH_PUSH 0x08
#define TH_ACK 0x10
#define TH_URG 0x20
#define TH_ECE 0x40
#define TH_CWR 0x80
#define TH_FLAGS (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)

 u_short th_win;   /* window */
 u_short th_sum;   /* checksum */
 u_short th_urp;   /* urgent pointer */
};

Note that one tick mark represents one bit position.


Source Port: 16 bits

The source port number.


Destination Port: 16 bits

The destination port number.

Sequence Number: 32 bits

The sequence number of the first data octet in this segment (except when SYN is present). If SYN is present the sequence number is the initial sequence number (ISN) and the first data octet is ISN+1.

Acknowledgment Number: 32 bits

If the ACK control bit is set this field contains the value of the next sequence number the sender of the segment is expecting to receive. Once a connection is established this is always sent.

Data Offset: 4 bits

The number of 32 bit words in the TCP Header. This indicates where the data begins. The TCP header (even one including options) is an integral number of 32 bits long.

Reserved: 6 bits

Reserved for future use. Must be zero.

Control Bits: 6 bits (from left to right):

URG: Urgent Pointer field significant
ACK: Acknowledgment field significant
PSH: Push Function
RST: Reset the connection
SYN: Synchronize sequence numbers
FIN: No more data from sender

Window: 16 bits

The number of data octets beginning with the one indicated in the acknowledgment field which the sender of this segment is willing to accept.

Checksum: 16 bits

The checksum field is the 16 bit one's complement of the one's complement sum of all 16 bit words in the header and text. If a segment contains an odd number of header and text octets to be checksummed, the last octet is padded on the right with zeros to form a 16 bit word for checksum purposes. The pad is not transmitted as part of the segment. While computing the checksum, the checksum field itself is replaced with zeros.

The checksum also covers a 96 bit pseudo header conceptually prefixed to the TCP header. This pseudo header contains the Source Address, the Destination Address, the Protocol, and TCP length. This gives the TCP protection against misrouted segments. This information is carried in the Internet Protocol and is transferred across the TCP/Network interface in the arguments or results of calls by the TCP on the IP.
 

Source Address


Destination Address


zero


PTCL

TCP Length

The TCP Length is the TCP header length plus the data length in octets (this is not an explicitly transmitted quantity, but is computed), and it does not count the 12 octets of the pseudo header.

Urgent Pointer: 16 bits

This field communicates the current value of the urgent pointer as a positive offset from the sequence number in this segment. The urgent pointer points to the sequence number of the octet following the urgent data. This field is only be interpreted in segments with the URG control bit set.

Options: variable

Options may occupy space at the end of the TCP header and are a multiple of 8 bits in length. All options are included in the checksum. An option may begin on any octet boundary. There are two cases for the format of an option:

Case 1: A single octet of option-kind.

Case 2: An octet of option-kind, an octet of option-length, and the actual option-data octets.

The option-length counts the two octets of option-kind and option-length as well as the option-data octets.

Note that the list of options may be shorter than the data offset field might imply. The content of the header beyond the End-of-Option option must be header padding (i.e., zero).

A TCP must implement all options.

Currently defined options include (kind indicated in octal):

Kind
Length
Meaning
0
-
End of option list.
1
-
No-Operation.
2
4
Maximum Segment Size.


Specific Option Definitions :

End of Option List

00000000

Kind=0

This option code indicates the end of the option list. This might not coincide with the end of the TCP header according to the Data Offset field. This is used at the end of all options, not the end of each option, and need only be used if the end of the options would not otherwise coincide with the end of the TCP header.

No-Operation


00000001

Kind=1

This option code may be used between options, for example, to align the beginning of a subsequent option on a word boundary. There is no guarantee that senders will use this option, so receivers must be prepared to process options even if they do not begin on a word boundary.

Maximum Segment Size

00000010
00000000
max seg size


Kind=2 Length=4

Maximum Segment Size Option Data: 16 bits

If this option is present, then it communicates the maximum receive segment size at the TCP which sends this segment. This field must only be sent in the initial connection request (i.e., in segments with the SYN control bit set). If this option is not used, any segment size is allowed.

Padding: variable

The TCP header padding is used to ensure that the TCP header ends and data begins on a 32 bit boundary. The padding is composed of zeros.

References : RFC 793

SCTP Packet Format

    The SCTP packet format is shown below:
  •  An SCTP packet is composed of a common header and chunks. A chunk contains either control information or user data.
  •  Multiple chunks can be bundled into one SCTP packet up to the MTU size, except for the INIT, INIT ACK, and SHUTDOWN COMPLETE chunks.
  • These chunks MUST NOT be bundled with any other chunk in a packet.
  • If a user data message doesn't fit into one SCTP packet it can befragmented into multiple chunks.
  • All integer fields in an SCTP packet MUST be transmitted in network byte order, unless otherwise stated.


SCTP Common Header Format




/* SCTP common header */
typedef struct sctp_hdr {
 uint16_t sh_sport;
 uint16_t sh_dport;
 uint32_t sh_verf;
 uint32_t sh_chksum;
} sctp_hdr_t;

Source Port Number: 16 bits (unsigned integer)


This is the SCTP sender's port number. It can be used by the receiver in combination with the source IP address, the SCTP destination port and possibly the destination IP address to identify the association to which this packet belongs.

Destination Port Number: 16 bits (unsigned integer)

This is the SCTP port number to which this packet is destined. The receiving host will use this port number to de-multiplex the SCTP packet to the correct receiving endpoint/application.

Verification Tag: 32 bits (unsigned integer)

The receiver of this packet uses the Verification Tag to validate the sender of this SCTP packet. On transmit, the value of this Verification Tag MUST be set to the value of the Initiate Tag received from the peer endpoint during the association initialization, with the following exceptions:

  • A packet containing an INIT chunk MUST have a zero Verification Tag.
  • A packet containing a SHUTDOWN-COMPLETE chunk with the T-bit set MUST have the Verification Tag copied from the packet with the SHUTDOWN-ACK chunk.
  • A packet containing an ABORT chunk may have the verification tag copied from the packet which caused the ABORT to be sent.
  • An INIT chunk MUST be the only chunk in the SCTP packet carrying it.

Checksum: 32 bits (unsigned integer)

This field contains the checksum of this SCTP packet. Its calculation uses the Adler- 32 for calculating the checksum


Chunk Field Descriptions

The figure below illustrates the field format for the chunks to be transmitted in the SCTP packet. Each chunk is formatted with a Chunk Type field, a chunk-specific Flag field, a Chunk Length field, and a
Value field.




/* Common chunk header */
typedef struct sctp_chunk_hdr {
 uint8_t  sch_id;
 uint8_t  sch_flags;
 uint16_t sch_len;
} sctp_chunk_hdr_t;
Chunk Type: 8 bits (unsigned integer)


This field identifies the type of information contained in the Chunk Value field. It takes a value from 0 to 254. The value of 255 is reserved for future use as an extension field.
 
The values of Chunk Types are defined as follows:
/* Chunk IDs */
typedef enum {
 CHUNK_DATA,
 CHUNK_INIT,
 CHUNK_INIT_ACK,
 CHUNK_SACK,
 CHUNK_HEARTBEAT,
 CHUNK_HEARTBEAT_ACK,
 CHUNK_ABORT,
 CHUNK_SHUTDOWN,
 CHUNK_SHUTDOWN_ACK,
 CHUNK_ERROR,
 CHUNK_COOKIE,
 CHUNK_COOKIE_ACK,
 CHUNK_ECNE,
 CHUNK_CWR,
 CHUNK_SHUTDOWN_COMPLETE,
 CHUNK_ASCONF_ACK = 128,
 CHUNK_FORWARD_TSN = 192,
 CHUNK_ASCONF = 193
} sctp_chunk_id_t;

ID
Value Chunk Type
0
Payload Data (DATA)
1
Initiation (INIT)
2
Initiation Acknowledgement (INIT ACK)
3
Selective Acknowledgement (SACK)
4
Heartbeat Request (HEARTBEAT)
5
Heartbeat Acknowledgement (HEARTBEAT ACK)
6
Abort (ABORT)
7
Shutdown (SHUTDOWN)
8
Shutdown Acknowledgement (SHUTDOWN ACK)
9
Operation Error (ERROR)
10
State Cookie (COOKIE ECHO)
11
Cookie Acknowledgement (COOKIE ACK)
12
Reserved for Explicit Congestion Notification Echo (ECNE)
13
Reserved for Congestion Window Reduced (CWR)
14
Shutdown Complete (SHUTDOWN COMPLETE)
15 to 62
reserved by IETF
63
IETF-defined Chunk Extensions
64 to 126
reserved by IETF
127
IETF-defined Chunk Extensions
128 to 190
reserved by IETF
191
IETF-defined Chunk Extensions
192 to 254
reserved by IETF
255
IETF-defined Chunk Extensions

Chunk Types are encoded such that the highest-order two bits specify the action that must be taken if the processing endpoint does not recognize the Chunk Type.

00 - Stop processing this SCTP packet and discard it, do not process any further chunks within it.

01 - Stop processing this SCTP packet and discard it, do not process any further chunks within it, and report the unrecognized parameter in an 'Unrecognized Parameter Type' (in either an ERROR or in the INIT ACK).

10 - Skip this chunk and continue processing.

11 - Skip this chunk and continue processing, but report in an ERROR Chunk using the 'Unrecognized Chunk Type' cause of error.

Note: The ECNE and CWR chunk types are reserved for future use of Explicit Congestion Notification (ECN).
 
Chunk Flags: 8 bits

The usage of these bits depends on the chunk type as given by the Chunk Type. Unless otherwise specified, they are set to zero on transmit and are ignored on receipt.

Chunk Length: 16 bits (unsigned integer)

This value represents the size of the chunk in bytes including the Chunk Type, Chunk Flags, Chunk Length, and Chunk Value fields.
Therefore, if the Chunk Value field is zero-length, the Length field will be set to 4. The Chunk Length field does not count any padding.


Chunk Value: variable length

The Chunk Value field contains the actual information to be transferred in the chunk. The usage and format of this field is dependent on the Chunk Type.

The total length of a chunk (including Type, Length and Value fields) MUST be a multiple of 4 bytes. If the length of the chunk is not a multiple of 4 bytes, the sender MUST pad the chunk with all zero bytes and this padding is not included in the chunk length field. The sender should never pad with more than 3 bytes. The receiver MUST ignore the padding bytes.

References : RFC 4960