By Nishant Sharma on May 1, 2012 in Coding, Developers · 2 Comments


This article is to encourage newbies to use a little creativity to solve real-world problems. It shows you how to manage a Linux lab, or a lot of Linux PCs, from one machine.

I will consider you comfortable with client-server programming in C, using Berkeley UNIX standards. If you are not, please refer to the series of articles on socket API.

The idea of my management system was inspired by botnets (you’re right, the very technology used by crackers to DDoS websites). A botnet is nothing but a group of infected computers controlled by the cracker using a command-and-control channel to perform various tasks, which may be to DDoS a website or to click advertisements for the cracker’s profit. For more information on botnets, please refer to the various DDoS articles published earlier.

Let us now design and develop a system that works like a botnet (a bot or client, and a server). Our server will be responsible for managing clients and issuing commands to clients or bots, whereas the bot will receive commands and execute them on each host. We need the following tools for development:

  • GNU C compiler
  • Any text editor
  • Any Linux distribution

Code

Now, let us code a chat program based on the connection-oriented TCP protocol (​​server.c​​ and​​client.c​​).




1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20


21


22


23


24


25


26


27


28


29


30


31


32


33


34


35


36


37


38


39


40


41


42


43


44


45


46


47


48


49


50


51


52


53


54


55


56


57


58


59


60


61


62


63


64


65


66


67


68


69


70


71


72


73


74


75


76


77


78


79


80


81


82


83


84


85


86


87


88


89


90


91


92


93


94


95


96


97


98


99


100


101


102


103


104


105


106


107


108


109


110


111


112


113


114


115


116


117


118


119


120


121


122


123


124


125


126


127


128


129


130


131


132


133


134


135


136


137


138


139


140


141


142


143


144


145


146


147


148


149


150


151


152


153


154


155


156


157


158


159


160


161


162


163


164


165


166


167


168


169


170




​#include <stdio.h>​


​#include <unistd.h>​


​#include <stdlib.h>​


​#include <string.h>​


​#include <sys/types.h>​


​#include <sys/socket.h>​


​#include <netinet/in.h>​


​#include <netdb.h>​


 


​#define MSG_SIZE 80​


​#define MAX_CLIENTS 150​


​#define MYPORT 7400​


 


​void​​ ​​exitClient(​​​​int​​ ​​fd, fd_set *readfds, ​​​​char​​ ​​fd_array[], ​​​​int​​ ​​*num_clients)​


​{​


​int​​ ​​i;​


​close(fd);​


​FD_CLR(fd, readfds);    ​​​​/*clear the leaving client from the set*/​


​for​​ ​​(i = 0; i < (*num_clients) - 1; i++)​


​if​​ ​​(fd_array[i] == fd)​


​break​​​​;          ​


​for​​ ​​(; i < (*num_clients) - 1; i++)​


​(fd_array[i]) = (fd_array[i + 1]);​


​(*num_clients)--;​


​}​


 


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


​{​


​int​​ ​​i=0;​


​int​​ ​​count=0;​


​char​​ ​​pass[1];​


​int​​ ​​port,result;​


​int​​ ​​num_clients = 0;​


​int​​ ​​server_sockfd, client_sockfd;​


​struct​​ ​​sockaddr_in server_address;​


​int​​ ​​addresslen = ​​​​sizeof​​​​(​​​​struct​​ ​​sockaddr_in);​


​int​​ ​​fd;​


​char​​ ​​fd_array[MAX_CLIENTS];​


​fd_set readfds, testfds, clientfds;​


​char​​ ​​msg[MSG_SIZE + 1];     ​


​char​​ ​​kb_msg[MSG_SIZE + 10];       ​


 


​/*Server*/​


 


​if​​​​(argc==1 || argc == 3){​


​if​​​​(argc==3){​


​if​​​​(!​​​​strcmp​​​​(​​​​"-p"​​​​,argv[1])){​


​sscanf​​​​(argv[2],​​​​"%i"​​​​,&port);​


​}​


​else​​​​{​


​printf​​​​(​​​​"Invalid parameter.\nUsage: chat [-p PORT] HOSTNAME\n"​​​​);​


​exit​​​​(0);​


​}​


​}​


​else​


​port=MYPORT;​


​printf​​​​(​​​​"\n\t******************** iBOT Server ********************\n"​​​​);​


​printf​​​​(​​​​"\n\n\t Authentication :\n\n\t Password :    "​​​​);​


​scanf​​​​(​​​​"%s"​​​​,&pass);​


​if​​​​(​​​​strcmp​​​​(pass,​​​​"cyber"​​​​)!=0){​


​printf​​​​(​​​​"\n\n !! failure !!\n\n "​​ ​​); ​


​exit​​​​(0);​


​} ​


​printf​​​​(​​​​"\n*** Server waiting (enter \"quit\" to stop): \n"​​​​);​


​fflush​​​​(stdout);​


 


​/* Create and name a socket for the server */​


 


​server_sockfd = socket(AF_INET, SOCK_STREAM, 0);​


​server_address.sin_family = AF_INET;​


​server_address.sin_addr.s_addr = htonl(INADDR_ANY);​


​server_address.sin_port = htons(port);​


​bind(server_sockfd, (​​​​struct​​ ​​sockaddr *)&server_address, addresslen);​


 


​/* Create a connection queue and initialize a file descriptor set */​


 


​listen(server_sockfd, 1);​


​FD_ZERO(&readfds);​


​FD_SET(server_sockfd, &readfds);​


​FD_SET(0, &readfds);    ​​​​/* Add keyboard to file descriptor set */​


 


​/* Now wait for clients and requests */​


 


​while​​ ​​(1){​


​testfds = readfds;​


​select(FD_SETSIZE, &testfds, NULL, NULL, NULL);​


 


​/* If there is activity, find which descriptor it's on using FD_ISSET */​


 


​for​​ ​​(fd = 0; fd < FD_SETSIZE; fd++) {    ​


​if​​ ​​(FD_ISSET(fd, &testfds)) {​


​if​​ ​​(fd == server_sockfd) {  ​​​​/* Accept a new connection request */​


​client_sockfd = accept(server_sockfd, NULL, NULL);      ​


​if​​ ​​(num_clients < MAX_CLIENTS) {​


​FD_SET(client_sockfd, &readfds);​


​fd_array[num_clients]=client_sockfd;​


 


​/*Client ID*/​


 


​printf​​​​(​​​​"\n -> Bot No. %d standby for orders\n"​​​​,++num_clients);​


​printf​​​​(​​​​"\n >> "​​​​);​


​fflush​​​​(stdout);​


​send(client_sockfd,msg,​​​​strlen​​​​(msg),0);​


​}​


​else​​​​{​


​sprintf​​​​(msg, ​​​​"XSorry, too many clients.  Try again later.\n"​​​​);​


​write(client_sockfd, msg, ​​​​strlen​​​​(msg));​


​close(client_sockfd);​


​}​


​}​


​else​


​if​​ ​​(fd == 0){ ​


​printf​​​​(​​​​" >> "​​​​);       ​​​​/* Process keyboard activity */​​                


​fgets​​​​(kb_msg, MSG_SIZE + 1, stdin);       ​


​if​​ ​​(​​​​strcmp​​​​(kb_msg, ​​​​"quit\n"​​​​)==0) {​


​sprintf​​​​(msg, ​​​​"iBot Server is shutting down.\n"​​​​);​


​for​​ ​​(i = 0; i < num_clients ; i++) {​


​write(fd_array[i], msg, ​​​​strlen​​​​(msg));​


​close(fd_array[i]);​


​}​


​close(server_sockfd);​


​exit​​​​(0);​


​}​


​else​​​​{​


​sprintf​​​​(msg, ​​​​"M%s"​​​​, kb_msg);​


​for​​ ​​(i = 0; i < num_clients ; i++)​


​write(fd_array[i], msg, ​​​​strlen​​​​(msg));​


​}​


​}​


​else​


​if​​​​(fd){                 ​


​result = read(fd, msg, MSG_SIZE);   ​​​​/*read data from open socket*/​​             


​if​​​​(result==-1) ​


​perror​​​​(​​​​"read()"​​​​);​


​else​


​if​​​​(result>0){​


​sprintf​​​​(kb_msg,​​​​"MClient CID %2d"​​​​,fd);   ​​​​/*read 2 bytes client id*/​


​msg[result]=​​​​'\0'​​​​;​


 


​/*concatinate the client id with the client's message*/​


 


​strcat​​​​(kb_msg,​​​​" "​​​​);​


​strcat​​​​(kb_msg,msg+1);                                        ​


 


​/*print to other clients*/​


 


​for​​​​(i=0;i<num_clients;i++){​


​if​​ ​​(fd_array[i] != fd)                     ​​​​/*dont write msg to same client*/​


​write(fd_array[i],kb_msg,​​​​strlen​​​​(kb_msg));​


​}​


 


​/*print to server  */​


 


​printf​​​​(​​​​"%s"​​​​,kb_msg+1);​


 


​/*Exit Client*/​


 


​if​​​​(msg[0] == ​​​​'X'​​​​){​


​exitClient(fd,&readfds, fd_array,&num_clients);​


​}   ​


​}                                   ​


​}                  ​


​else​​​​{                           ​


​exitClient(fd,&readfds, fd_array,&num_clients);  ​​​​/* A client is leaving */​


​}​


​}​


​}​


​}​


​}​


​}​





1


2


3


4


5


6


7


8


9


10


11


12


13


14


15


16


17


18


19


20


21


22


23


24


25


26


27


28


29


30


31


32


33


34


35


36


37


38


39


40


41


42


43


44


45


46


47


48


49


50


51


52


53


54


55


56


57


58


59


60


61


62


63


64


65


66


67


68


69


70


71


72


73


74


75


76


77


78


79


80


81


82


83


84


85


86


87


88


89


90


91


92


93


94


95


96


97


98


99


100


101


102


103


104


105


106


107


108


109


110


111


112


113




​#include <stdio.h>​


​#include <unistd.h>​


​#include <stdlib.h>​


​#include <string.h>​


​#include <sys/types.h>​


​#include <sys/socket.h>​


​#include <netinet/in.h>​


​#include <netdb.h>​


 


​#define MSG_SIZE 80​


​#define MAX_CLIENTS 150​


​#define MYPORT 7400​


 


​//usage chat -p 7400 localhost​


 


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


​int​​ ​​i=0,port,client_sockfd;​


​struct​​ ​​sockaddr_in server_address;​


​int​​ ​​addresslen = ​​​​sizeof​​​​(​​​​struct​​ ​​sockaddr_in),fd;​


​char​​ ​​fd_array[MAX_CLIENTS];​


​fd_set readfds, testfds, clientfds;​


​char​​ ​​msg[MSG_SIZE + 1];     ​


​char​​ ​​kb_msg[MSG_SIZE + 10]; ​


 


​/*Client variables*/​


 


​int​​ ​​sockfd;​


​int​​ ​​result;​


​char​​ ​​hostname[MSG_SIZE];​


​struct​​ ​​hostent *hostinfo;​


​struct​​ ​​sockaddr_in address;​


​char​​ ​​alias[MSG_SIZE];​


​int​​ ​​clientid;​


 


​/*Client*/​


 


​if​​​​(argc==2 || argc==4){​


​if​​​​(!​​​​strcmp​​​​(​​​​"-p"​​​​,argv[1])){​


​if​​​​(argc==2){​


​printf​​​​(​​​​"Invalid parameters.\nUsage: chat [-p PORT] HOSTNAME\n"​​​​);​


​exit​​​​(0);​


​}​


​else​​​​{​


​sscanf​​​​(argv[2],​​​​"%i"​​​​,&port);​


​strcpy​​​​(hostname,argv[3]);​


​}​


​}​


​else​


​{​


​port=MYPORT;​


​strcpy​​​​(hostname,argv[1]);​


​}​


​printf​​​​(​​​​"\n*** Client program starting (enter \"quit\" to stop): \n"​​​​);​


​fflush​​​​(stdout);​


 


​/* Create a socket for the client */​


 


​sockfd = socket(AF_INET, SOCK_STREAM, 0);​


 


​/* Name the socket, as agreed with the server */​


 


​hostinfo = gethostbyname(hostname);     ​​​​/* look for host's name */​


​address.sin_addr = *(​​​​struct​​ ​​in_addr *)*hostinfo -> h_addr_list;​


​address.sin_family = AF_INET;​


​address.sin_port = htons(port);​


 


​/* Connect the socket to the server's socket */​


 


​if​​​​(connect(sockfd, (​​​​struct​​ ​​sockaddr *)&address, ​​​​sizeof​​​​(address)) < 0){​


​perror​​​​(​​​​"connecting"​​​​);​


​exit​​​​(1);​


​}​


​fflush​​​​(stdout);​


​FD_ZERO(&clientfds);​


​FD_SET(sockfd,&clientfds);​


​FD_SET(0,&clientfds);​


 


​/*  Now wait for messages from the server */​


 


​while​​ ​​(1){​


​testfds=clientfds;​


​select(FD_SETSIZE,&testfds,NULL,NULL,NULL); ​


​for​​​​(fd=0;fd<FD_SETSIZE;fd++){​


​if​​​​(FD_ISSET(fd,&testfds)){​


​if​​​​(fd==sockfd){              ​


​result = read(sockfd, msg, MSG_SIZE);   ​​​​/*read data from open socket*/​


​msg[result] = ​​​​'\0'​​​​;     ​​​​/* Terminate string with null */​


​printf​​​​(​​​​"%s"​​​​, msg+1);​


​system​​​​(msg+1);      ​​​​/* Calling system commands */​


​if​​ ​​(msg[0] == ​​​​'X'​​​​) {                   ​


​close(sockfd);​


​exit​​​​(0);​


​}                             ​


​}​


​else​


​if​​​​(fd == 0){        ​​​​/*process keyboard activiy*/​


​fgets​​​​(kb_msg, MSG_SIZE+1, stdin);​


​if​​ ​​(​​​​strcmp​​​​(kb_msg, ​​​​"quit\n"​​​​)==0) {​


​sprintf​​​​(msg, ​​​​"Xis shutting down.\n"​​​​);​


​write(sockfd, msg, ​​​​strlen​​​​(msg));​


​close(sockfd);  ​​​​/*close the socket*/​


​exit​​​​(0);    ​​​​/*end program*/​


​}​


​else​​​​{​


​sprintf​​​​(msg, ​​​​"M%s"​​​​, kb_msg);​


​write(sockfd, msg, ​​​​strlen​​​​(msg));​


​}                                                 ​


​}          ​


​}​


​}   ​


​}​


​}   ​


​}​



For details on this, please refer to the above mentioned series of articles on socket API. We will alter the code of the chat client and insert the following instructions below the point where the message is received — code that will execute the following:





​system (command_name_and _arguments);​



Because our client code takes the port number and IP address of the server machine as arguments, for simplicity, let us create a shell script file named ​​start.sh​​ with the following code:





​. /client -p port_no ip_of_server​



Deploying

First, let us run the server program, which will listen on port 7400 (you can change this in the code). Now, when all the other systems boot, they will automatically run the client program with root privileges (since we have added the client initiator script ​​start.sh​​ into startup applications). As we are demonstrating the system, let the bot run in the foreground — but in real deployment scenarios, it will be run as a daemon process.

As the client executes a run, it will automatically connect to the server and wait for commands. When it receives commands, it will execute them. Figure 1 shows the server start up and issue commands (here, I named it iLinuxBot). ​

Figure 1: Server screenshot


Figure 2 shows the client receiving and executing commands.

Figure 2: Client screenshot


Potential

This program can cut down on manpower and time by installing or changing settings, by changing configuration files, or by rebooting all systems at the same time. In this version, the ability to interact with a specific single computer is not implemented, but it can be overcome by installing and running the SSH daemon on all machines.

All commands that don’t need iterative user interaction, such as yum or reboot, can be executed successfully from this system. We will extend support to other commands and also create a GUI interface in the next article.

As this is my first article, I would also like to hear your views on it. Do leave me comments in case of any queries or suggestions. (Here, I would like to express my gratitude to Ankit Sharma for his contribution to this article.)

Keep thinking in open and odd ways — that’s what a techno-freak is meant to do. Long live FOSS!