Sockets Redirection problem
- From: pradqdo@xxxxxxxxx
- Date: 18 Feb 2007 14:05:33 -0800
Hi guys,
I am trying to build a simple server that can execute local commands
and return the output of the execution to the client. For example
executing ls, cd, find and all of the normal unix utility programs.
Here is my program's server source:
serv.c:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/wait.h>
#define MYPORT 1111
#define BACKLOG 10
#define MAXDATASIZE 100
#define COMMANDBUFFER 100000
void sigchld_handler(int s)
{
while (waitpid(-1, NULL, WNOHANG) > 0);
}
int check_token(char *buf, FILE *fp, char *match); // check for valid
usernames and passwords
int execute(int sockfd);
int main (void)
{
int sockfd, new_fd;
struct sockaddr_in my_addr;
struct sockaddr_in their_addr;
socklen_t sin_size;
struct sigaction sa;
int yes = 1, numbytes;
char buf[MAXDATASIZE];
FILE *user;
char match[50]; // used to check usernames or passwords
if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int))
== -1) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(MYPORT);
my_addr.sin_addr.s_addr = INADDR_ANY;
memset(&(my_addr.sin_zero), '\0', 8);
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct
sockaddr)) == -1) {
perror("bind");
exit(EXIT_FAILURE);
}
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(EXIT_FAILURE);
}
sa.sa_handler = sigchld_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(EXIT_FAILURE);
}
while (1)
{
sin_size = sizeof(struct sockaddr_in);
if ( (new_fd = accept(sockfd, (struct sockaddr *)&their_addr,
&sin_size)) == -1 ) {
perror("accept");
continue;
}
printf("Server got connection from: %s\n",
inet_ntoa(their_addr.sin_addr));
if (!fork())
{
close(sockfd);
if (send(new_fd, "Stan's Server v0.1", 18, 0) == -1)
perror("send banner");
if ( (numbytes = recv(new_fd, buf, MAXDATASIZE - 1, 0)) == -1)
perror("recv username");
buf[numbytes - 1] = '\0'; // get rid of the \n
if ( (user = fopen("users", "r")) == NULL) {
fprintf(stderr, "Error opening username file\n");
if (send(new_fd, "2", 1, 0) == -1)
perror("send response");
}
if (check_token(buf, user, NULL) == 1)
{
printf("%s provided valid username.\n",
inet_ntoa(their_addr.sin_addr));
if (send(new_fd, "1", 1, 0) == -1)
perror("send response");
strcpy(match, buf); // store the valid user name to be compared
with a valid
// password for that username
if ( (numbytes = recv(new_fd, buf, MAXDATASIZE - 1, 0)) == -1)
perror("recv passwrd");
buf[numbytes - 1] = '\0'; // get rid of \n
if(check_token(buf, user, match) == 1)
{
printf("%s provided valid password.\nAuthentication of %s
successful!\n", inet_ntoa(their_addr.sin_addr),
inet_ntoa(their_addr.sin_addr));
if (send(new_fd, "1", 1, 0) == -1)
perror("send response");
execute(new_fd); // authentication successful so we start
executing commands
}
else
if (send(sockfd, "0", 1, 0) == -1)
perror("send resopnse");
}
else
if (send(new_fd, "0", 1, 0) == -1)
perror("send bad_username");
close(new_fd);
exit(0);
}
close(new_fd);
}
return 0;
}
int check_token(char *buf, FILE *fp, char *match)
{
char temp[128];
char *tok;
rewind(fp);
while (!feof(fp))
{
fgets(temp, 128, fp);
tok = strtok(temp, ":");
if (match == NULL) {
if (!strcmp(tok, buf))
return 1;
else
continue;
}
else // we have already a username that was checked for
correction so we have
// to check the password
{
if (!strcmp(tok, match))
{
tok = strtok(NULL, ":");
tok[strlen(tok) - 1] = '\0'; // remove newline character \n
if (!strcmp(tok, buf))
return 1;
}
}
}
return 0;
}
int execute(int sockfd)
{
int redir_stdout[2]; // pipe used for redirection of commands :)
char pipe_transffer[4096];
char buf[MAXDATASIZE * 5];
int numbytes;
char *command[MAXDATASIZE];
char *temp;
int counter = 0;
int status; // close child
pipe(redir_stdout);
while (1)
{
counter = 0;
if ( (numbytes = recv(sockfd, buf, MAXDATASIZE * 5 - 1, 0)) ==
-1) {
perror("recv command");
return -1;
}
buf[numbytes] = '\0';
/* sPLITTING THE COMMAND */
temp = strtok(buf, " \n");
if (!strcmp(temp, "quit"))
break;
while (temp)
{
command[counter] = (char *)malloc(sizeof(char) * strlen(temp));
strcpy(command[counter++], temp);
temp = strtok(NULL, " \n");
}
command[counter] = NULL;
printf("Executing: ");
counter = 0;
while(command[counter])
printf("%s ", command[counter++]);
if (!fork()) // child
{
close(1);
close(redir_stdout[0]);
dup2(redir_stdout[1], fileno(stdout));
execvp (command[0], command);
exit (EXIT_SUCCESS);
}
// parent
while(1)
{
numbytes = read(redir_stdout[0], pipe_transffer,
strlen(pipe_transffer));
if (send(sockfd, pipe_transffer, strlen(pipe_transffer), 0) == -1)
{
perror("send pipe_redirection");
return -1;
}
printf("Numbytes sent: %d\n", numbytes);
// See if there is more data to be sent or there is none so we can
escape blocking
// in case there is nothing in the pipe and we try to read
if (numbytes < )
{
if (send(sockfd, "0", 1, 0) == -1)
perror("send end_of_data");
break;
}
else
{
if (send(sockfd, "1", 1, 0) == -1)
perror("send more_data");
continue;
}
}
wait(&status);
}
return 1;
}
Here is my client source:
cli.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#define PORT 1111
#define MAXDATASIZE 100
void bye (char *buf);
int command(int sockfd);
int main (int argc, char **argv)
{
int sockfd, numbytes;
char buf[MAXDATASIZE];
struct hostent *he;
struct sockaddr_in their_addr;
struct timeval timeout;
if (argc != 2) {
fprintf(stderr, "Usage: %s server_hostname\n", argv[0]);
exit(EXIT_FAILURE);
}
if ( (he = gethostbyname(argv[1])) == NULL) {
herror("gethostbyname");
exit(EXIT_FAILURE);
}
if ( (sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
bye("socket");
//Set timeout
bzero (&timeout, sizeof(timeout));
timeout.tv_sec = 2;
if (setsockopt (sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout,
sizeof(timeout))) {
fprintf(stderr, "Error setting timeout option!\n");
exit(EXIT_FAILURE);
}
their_addr.sin_family = AF_INET;
their_addr.sin_port = htons(PORT);
their_addr.sin_addr = *((struct in_addr *)he->h_addr);
memset(&(their_addr.sin_zero), '\0', 8);
if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct
sockaddr)) == -1)
bye("connect");
/* Get banner */
if ( (numbytes = recv(sockfd, buf, MAXDATASIZE - 1, 0)) == -1)
perror("recv banner");
buf[numbytes] = '\0';
printf("%s\n", buf);
/*-------------------------*/
/* Authenticate */
printf("Enter username: ");
fgets(buf, MAXDATASIZE, stdin);
if (send(sockfd, buf, strlen(buf), 0) == -1)
perror("send username");
/* Recieve response */
if ( (numbytes = recv(sockfd, buf, MAXDATASIZE - 1, 0)) == -1)
bye("recv response");
buf[numbytes] = '\0';
if (atoi(buf) == 1) // username is valid
{
/* Send password */
printf("Enter password: ");
fgets(buf, MAXDATASIZE, stdin);
if (send(sockfd, buf, strlen(buf), 0) == -1)
bye("send password");
/* Receive response */
if ( (numbytes = recv(sockfd, buf, MAXDATASIZE - 1, 0)) == -1)
bye("recv response");
buf[numbytes] = '\0';
if (atoi(buf) == 1)
{
printf("Password accepted! Enjoy your stay!\n");
command(sockfd);
}
else
printf("Wrong password!\n");
}
else if (atoi(buf) == 2)
printf("Username/Password Database error!\n");
else
printf("Wrong username!\n");
close(sockfd);
return 0;
}
void bye (char *buf)
{
perror(buf);
exit(EXIT_FAILURE);
}
int command(int sockfd)
{
char buf[MAXDATASIZE * 50];
int numbytes;
while(1)
{
printf("Command: ");
fgets(buf, MAXDATASIZE * 5, stdin);
if (buf[0] == '\n')
continue;
if (!strcmp(strtok(buf, "\n"), "quit"))
break;
printf("Sending command: %s\n", buf);
if ((numbytes = send(sockfd, buf, strlen(buf), 0)) == -1) {
perror("send command");
return -1;
}
while (1)
{
if ((numbytes = recv(sockfd, buf, MAXDATASIZE * 50 - 1, 0)) == -1)
{ // PIPE SIZE ON THE SERVER
perror("recv response");
return -1;
}
buf[numbytes] = '\0';
printf("%s", buf);
// Get information if there is more data to follow
if ( (numbytes = recv(sockfd, buf, 101, 0)) == -1)
perror("recv response for continuity");
printf("Continue: %s\n", buf);
if (atoi(buf) == 0)
break;
}
}
return 1;
}
As you can see I am trying to do something very simple but I have a
couple of questions:
1. When I redirect the standard output to a pipe from the fork in the
child process on the server side and then when I try to read after
that in the parent in order to send the output of the command that was
executed how can I know in advance how much data it is needed to be
sent to the client because I know that pipes have a limitation of 4096
and if I do a ls /usr/bin it will produce much larger stream of data
so I have to resend the information. If I read too much data from the
pipe it blocks when there is no more data - how can I check if there
is more data to be sent in order to avoid blocking. And on the client
side - how can I check if there is more to be received.
I tried to use a lame method as you can see in the source code but it
is not working! I tried first to check the data read from the pipe is
less then 4096 - that means that the pipe is not full so there is no
more data and if the whole capacity of the pipe is full 4096 then it
is probably going to have more data. But I couldn't make that method
to work.
2. The other strange problem that I have is that when I place a
breakpoint in gdb in execute() function gdb doesn't stop - that is
completely strange for me - I am using cygwin under windows. I can
follow the code through stepping until the the call to execute(int
sockfd) function - after that it simply doesn't break.
3. Is there any way that I can use some sort of progress indication in
the form of wget if I decide to implement transfer file option to my
client and server programs. I don't really want to use ncurses but if
that is the only way I guess I will have to read the documentation.
I am a complete newbie in socket programming and IPC so I will
appreciate any help in the filed and especially redirecting.
I guess I am trying to build a simple shell but the idea behind is to
use system() for executing commands on the server so it can work on
any platform using the specific platform's commands. If you have any
information on programming simple shells I will be glad to check it
out.
Thank You in Advance
.
- Follow-Ups:
- Re: Sockets Redirection problem
- From: Rainer Weikusat
- Re: Sockets Redirection problem
- From: Lars Rune Nøstdal
- Re: Sockets Redirection problem
- From: Bin Chen
- Re: Sockets Redirection problem
- From: Dan Mills
- Re: Sockets Redirection problem
- Prev by Date: Asterisk:
- Next by Date: Re: Sockets Redirection problem
- Previous by thread: Asterisk:
- Next by thread: Re: Sockets Redirection problem
- Index(es):
Relevant Pages
|