/* NAME : sockser.C TYPE : C++ SOURCE AUTHOR : Arunkumar Elango DESCRIPTION : This file contains the source code for the server. It takes the following command line as input : $ rpiped [-m maxconnections] [-p portnumber] It creates and binds a socket at the either the well known portnumber or at the portnumber given in the commandline. Then, it waits for a connection request from a client. When the request arrives, it forks a child process that handles the request. The child process first checks if the client has given the correct password and if it does, processes the request and exits. The parent doesnot fork a child if there are already maxconnections number of children. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const int MAXSTR=1024; // Maximum String length const long PORTNUM=54715; // The "Well-known" port number const int PASSWORD = 21; // The password that the server expects. void handleRequest(int); // Function to handle clients' request(s) int numberChildren; // The current number of child processes. void childExit(int); // signal handler for SIGCHLD main( int argc, char *argv[]) { int maxConnections,portNumber; char executableName[MAXSTR]; struct sigaction action; // Validate and read from the command line if ( (argc != 5) && (argc != 3) && (argc != 1) ) { cerr << "Invalid command line.\n"; exit(1); } if ( argc == 1 ) { maxConnections = 5; portNumber = PORTNUM; } else if (argc == 3 ) { if (!strcmp(argv[1],"-m")) { maxConnections = atoi(argv[2]); portNumber = PORTNUM; } else if (!strcmp(argv[1],"-p")) { portNumber = atoi(argv[2]); maxConnections = 5; } else { cerr << "Invalid command line.\n"; exit(1); } } else { if ((!strcmp(argv[1],"-m")) && (!strcmp(argv[3],"-p"))) { maxConnections = atoi(argv[2]); portNumber = atoi(argv[4]); } else { cerr << "Invalid command line.\n"; exit(1); } } if (portNumber < 10000) { cerr << "Port number has to be atleast 10000.\n"; exit(1); } // At this point, the command line has been validated. int sockfd,newsockfd,clilen,childpid; struct sockaddr_in serv_addr,cli_addr; // Set the signal handler for SIGCHLD. sigemptyset(&action.sa_mask); action.sa_handler = childExit; action.sa_flags = 0; if (sigaction(SIGCHLD,&action,0) == -1) { cerr <<" sigaction error.\n"; exit(1); } // Create a new TCP socket... if ((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0) { cerr << "Cant open stream socket.\n"; exit(0); } bzero((char *)&serv_addr,sizeof(serv_addr)) ; serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(portNumber); // Bind the socket to the server's ( this process ) address. if (bind(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0) { cerr << "Cant bind local address.\n"; exit(1); } // Listen for connections in this socket listen(sockfd,5); int status; cerr << "Max number of connections = " << maxConnections << " connected to port : " << portNumber << "\n"; char buf[MAXSTR],ack[MAXSTR]; // repeat forever.. while (1) { clilen = sizeof(cli_addr); // Accept a client's request, and get the client's address info into the local variable cli_addr. newsockfd = accept(sockfd,(struct sockaddr *)&cli_addr,&clilen); // After accepting the connection, all transaction with this client would happen with the new // socket descriptor - newsockfd. if ((newsockfd < 0) && (errno != EINTR)) cerr << " server : accept error.\n"; else if (newsockfd > 0) { if (numberChildren < maxConnections) { // Fork a new child to serve this client. if ((childpid = fork()) < 0) cerr << "server : fork error.\n"; else if (childpid == 0) { // The child server...first handles the client's request and then quits. close(sockfd); handleRequest(newsockfd); exit(0); } // The parent server, in the meantime, goes about serving other clients. numberChildren ++; } else { // There are already max no of clients being served. So send a negative ack to //this client, refusing connection. read(newsockfd,(void *)buf,MAXSTR); sprintf(ack,"NO"); ack[strlen(ack)] = '\0'; write(newsockfd,ack,strlen(ack)); } close(newsockfd); } waitpid(-1,&status,WNOHANG); } } void handleRequest(int newsockfd) { char buf[MAXSTR],execName[MAXSTR],ack[MAXSTR]; char data[MAXSTR]; int i=0,code; char ch='0'; int fd = open("dummy",O_RDWR | O_CREAT | O_TRUNC,0764); // Open a dummy file to store client's data, that would be needed to execute the requested // command. For example, the text after a wc command. if ( fd == -1) { cerr << "Error opening dummy file.\n"; exit(0); } // Read from the socket, for the password and the requested command. read(newsockfd,(void *)buf,MAXSTR); sscanf(buf,"%d %s\n",&code,execName); if (code != PASSWORD) { // If password is not valid, send a negative acknowledgement - refusing connection. cerr << "Invalid Password\n"; sprintf(ack,"NO"); ack[strlen(ack)] = '\0'; write(newsockfd,ack,strlen(ack)); exit(0); } // The client is authorized, so send a positive acknowledgement. cerr << "Processing " << execName << " for client.\n"; sprintf(ack,"OK"); ack[strlen(ack)] = '\0'; write(newsockfd,ack,strlen(ack)); // Get the addidtional data that the client has to send, like the text in a wc or cat command, //and then write it to the dummy file. read(newsockfd,(void *)data,MAXSTR); write(fd,data,strlen(data)); close(fd); /* Duplicate this process's Standard output into the socket's stream. Henceforth, anything that the process writes to the screen, will actually be written to the socket, so that the client can read from the socket and print to its local screen. Remember : This is the child server process, and hence only the child's STDOUT has been Duplicated. The parent server, still continues to write to the screen. */ dup2(newsockfd,STDOUT_FILENO); char command[MAXSTR]; sprintf(command,"cat dummy | %s",execName); command[strlen(command)] = '\0'; // execlp is a system call that executes a shell command from within a process. execlp("/bin/sh","sh","-c",command,0); } // Signal Handler for SIGCHLD void childExit(int signo) { // A child has just exitted, so just decrement the counter. numberChildren --; }