Thread: Server deadlocks, Nonblocking IO, or Timeout?

  1. #1
    Registered User
    Join Date
    Jan 2005
    Posts
    6

    Server deadlocks, Nonblocking IO, or Timeout?

    Hi i'm trying to code a program in c that will essentially transfer commands (using netcat) recieved from a client to another computer running a command prompt or a bash shell or something...

    Problem:
    my problem is that when a client connects it can connect to the service and send messages like an ls command or a ps -aux command and it works just perfectly fine. but when i do something like cd <dir> it deadlocks... i'm almost 100% sure that the server running the shell and my program deadlock because the server does not reply with any data on that command. i've tried using the MSG_DONTWAIT flag on the recv() function thinking that nonblocking IO would fix it (not to familar with nonblocking IO) but when i do this it requires the user to hit <enter> twice after each command before the results are displayed...

    so my question is: is there a way to put a timeout on just the recv() function if no data is recieved? or is there another way to fix this up?

    Thanks for any help

    Code:
    /* Author:  0xception
     * Date:    Win, 2004
     * Title:   Pogo
     * File:    pogo.c
     * Purpose: To allow a client to connect to this server 
     *          and then pogo will forward any incoming 
     *          shell commands to the target computer running
     *          a shell, bash, command prompt. 
     */ 
    #include <stdio.h>
    #include <sys/socket.h>
    #include <sys/wait.h>
    #include <arpa/inet.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <signal.h>
    
    #define MAXBUFSIZE 65535
    #define MAXPENDING 5      
    
    void pogo(int,int);
    
    int main(int argc, char*argv[])
    {  
       int servSock;
       int clntSock;
       int trgtSock;
       int bytesRcvd;  
       int setFork = 0;
       struct sockaddr_in serv;
       struct sockaddr_in clnt;
       struct sockaddr_in trgt;
       unsigned short servPort;
       unsigned short trgtPort;
       unsigned int clntLen;
       unsigned int trgtLen;
       unsigned int childProcCount = 0; 
       char *trgtIP;
       pid_t pid;
     
       int flag;   
       while((flag = getopt(argc,argv,"fp:t:r:")) != -1)
       {  switch(flag)
          {  case 'f':
                setFork = 1;
                break;
             case 'p':
                servPort = atoi(optarg);
    /* DEBUG /* printf("\t -p: %d\n",servPort); */
                break;
             case 't':
                trgtIP = optarg;
    /* DEBUG /* printf("\t -t: %s\n",trgtIP); */
                break;
             case 'r':
                trgtPort = atoi(optarg);
    /* DEBUG /* printf("\t -r: %d\n",trgtPort); */
                break;
             default:
                fprintf(stderr, "Usage: %s [-f] -p <Server Port> -t <Target IP> -r <Target remote Port> \n\t See Also: README\n",argv[0]);
                exit(1);
          }
       }
    
      /*
       * Server Setup 
       */ 
       if((servSock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0)
       {  fprintf(stderr, "ERROR: Incomming socket Error\n");
          exit(1);
       }
       
       memset(&serv,0,sizeof(serv));
       serv.sin_family = AF_INET;
       serv.sin_addr.s_addr = htonl(INADDR_ANY);
       serv.sin_port = htons(servPort);
    
       if(bind(servSock, (struct sockaddr*)&serv, sizeof(serv)) < 0)
       {  fprintf(stderr, "Error: bind() faild\n");
          exit(1);
       }
      
      /*
       * Server Listen
       */
       if(listen(servSock,MAXPENDING) < 0)
       {  fprintf(stderr, "Error: listen() failed\n");
          exit(1);
       }
       
       for(;;)
       {  clntLen = sizeof(clnt);
          
          if((clntSock = accept(servSock, (struct sockaddr*)&clnt, &clntLen)) < 0)
          {  fprintf(stderr, "Error: accept() failed\n");
             exit(1);
          }
          
          printf("Client Connected: %s\n",inet_ntoa(clnt.sin_addr));
        
          if(setFork == 1)
          { /* Fork the process to allow multiple clients IE: Threading */
             if((pid = fork()) < 0)
             {  fprintf(stderr, "Error: fork() failed\n");
                exit(1);
             }
             else if(pid == 0)
             {  close(servSock);     /* Child process closes listening socket */
             
               /*
                * Target Setup
                */
                if((trgtSock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0)
                {  fprintf(stderr,"Error: target.socket() failed\n");
                   exit(1);
                }
                
                memset(&trgt,0,sizeof(trgt));
                trgt.sin_family = AF_INET;
                trgt.sin_addr.s_addr = inet_addr(trgtIP);
                trgt.sin_port = htons(trgtPort); 
              
               /*
                * Connects to target server
                */ 
                if(connect(trgtSock,(struct sockaddr*)&trgt,sizeof(trgt)) < 0)
                {  fprintf(stderr,"Error: target.connect() failed\n");
                   exit(1);
                }
             
                pogo(clntSock, trgtSock);
                exit(0);       
             } /* END ELSE IF */
             
             
             printf("Child Process: %d\n", (int)pid);
             
            /* Close Current sockets */ 
             close(clntSock);
             printf("\tConnection Closed\n");   
             
             childProcCount++;
              
            /* Clean up all zombie processes */
             while(childProcCount)
             {  pid = waitpid((pid_t) -1,NULL,WNOHANG);
                if(pid < 0) 
                {  fprintf(stderr,"Error: waitpid() failed\n");
                   exit(1);
                }
                else if(pid == 0)
                {  break;  }
                else
                {  childProcCount--;  }
             } 
          } /* END IF */
          else
          {  /*
              * Target Setup
              */
              if((trgtSock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0)
              {  fprintf(stderr,"Error: target.socket() failed\n");
                 exit(1);
              }
       
              memset(&trgt,0,sizeof(trgt));
              trgt.sin_family = AF_INET;
              trgt.sin_addr.s_addr = inet_addr(trgtIP);
              trgt.sin_port = htons(trgtPort); 
     
             /*
              * Connects to target server
              */ 
              if(connect(trgtSock,(struct sockaddr*)&trgt,sizeof(trgt)) < 0)
              {  fprintf(stderr,"Error: target.connect() failed\n");
                 exit(1);
              }
            
              pogo(clntSock, trgtSock);
              exit(0);
    
          } /* END ELSE */
       } /*END WHILE */
        
       close(servSock);
       close(clntSock);
       close(trgtSock);
       return 0;
    } /* END MAIN */
    
    void pogo(int clntSock, int trgtSock)
    {  
       char *buffer;
       buffer = (char*)calloc(1,MAXBUFSIZE);
       int recvMsgSize;
       int lcv; 
       int wait = 0;
       pid_t cPID = getpid();
     
      /* Grab first client request */ 
       if((recvMsgSize = recv(clntSock,buffer,MAXBUFSIZE,0)) < 0)
       {  fprintf(stderr,"\tError: [%d] client.recv() failed\n",(int)cPID);
          printf("buffer: \n %s",buffer);
          exit(1);
       }
       
       while(recvMsgSize > 0)   
       { 
                 
    /* DEBUG */ printf("\tclnt-trgt Buffer: %s",buffer); 
    
         /* Sends the Target the Clients message */ 
          if(send(trgtSock,buffer,recvMsgSize,0) != recvMsgSize)
          {  fprintf(stderr,"\tError: [%d] target.send() failed\n",(int)cPID);
             exit(1);
          }
    
    /* MAY NEED TO CLEAR BUFFER HERE */
          free(buffer);
          buffer = (char*)calloc(1,MAXBUFSIZE);
         
          while(recvMsgSize > 0)
          { /* Recv message from the target */  
             if((recvMsgSize = recv(trgtSock,buffer,MAXBUFSIZE,MSG_DONTWAIT)) < 0)
             {  printf("\ttrgt-clnt Buffer in if: %s\n",buffer);
                fprintf(stderr,"\t\tError: [%d] target.recv() failed\n",(int)cPID); 
             }
             else
             { 
    /* DEBUG /* printf("\ttrgt-clnt Buffer in else: %d, size: %s",strlen(buffer),buffer);*/
               
               /* Sends the client the message recieved from the target */
                if((send(clntSock,buffer,recvMsgSize,0) != recvMsgSize))
                {  fprintf(stderr,"\t\tError: [%d] client.send() failed\n",(int)cPID);
                   exit(1);
                }
                
                free(buffer);
                buffer = (char*)calloc(1,MAXBUFSIZE);
                printf("\tbuffer size: %d\n",strlen(buffer));
              }
     
           } /* END WHILE */
    
         /* Grab first client request */ 
          if((recvMsgSize = recv(clntSock,buffer,MAXBUFSIZE,0)) < 0)
          {  fprintf(stderr,"\tError: [%d] client.recv() failed\n",(int)cPID);
             exit(1);
          } 
        
       } /* END WHILE */
    } /* END POGO */
    Last edited by 0xception; 01-03-2005 at 03:02 AM.

  2. #2
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    so my question is: is there a way to put a timeout on just the recv() function if no data is recieved?
    Use the select function.

  3. #3
    Registered User
    Join Date
    Jan 2005
    Posts
    6
    i'm not very familar with the select() function but doesn't it just poll multiple socket for sockets ready for IO? even if it is just used on one socket it will signal when the socket is ready for IO or when it has information to return but when a command like cd is given wont' it just deadlock up again because cd will never return data?

    but i'll read up on that function some and see if it works... thanks for the help

  4. #4
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    The select function will tell you if there is data to receive. If the cd command is given, then select will time out, and you wouldn't call recv(). Look at the example code in the link I gave you to see how to set the timeout value, and call select on the correct file descripter.

  5. #5
    Registered User
    Join Date
    Jan 2005
    Posts
    6
    ok i've tried this out but i can't seem to get it to work... just wanted to make sure this is what you meant. it seems to be that every time it gets to the select() it times out.

    Code:
    void pogo(int clntSock, int trgtSock)
    {  
       char *buffer;
       buffer = (char*)calloc(1,MAXBUFSIZE);
       struct timeval tv;
       fd_set sockSet; 
       int recvMsgSize;
       int lcv; 
       int retval;
       pid_t cPID = getpid();
        
      /* Select() Setup */
       FD_ZERO(&sockSet);
       FD_SET(trgtSock,&sockSet);
       tv.tv_sec = 5;
       tv.tv_usec = 0;
       
      /* Grab first client request */ 
       if((recvMsgSize = recv(clntSock,buffer,MAXBUFSIZE,0)) < 0)
       {  fprintf(stderr,"\tError: [%d] client.recv() failed\n",(int)cPID);
          printf("buffer: \n %s",buffer);
          exit(1);
       }
       
       while(recvMsgSize > 0)   
       { 
         /* Sends the Target the Clients message */ 
          if(send(trgtSock,buffer,recvMsgSize,0) != recvMsgSize)
          {  fprintf(stderr,"\tError: [%d] target.send() failed\n",(int)cPID);
             exit(1);
          }
    
          free(buffer);
          buffer = (char*)calloc(1,MAXBUFSIZE);
        
          if(select(1, &sockSet, NULL, NULL, &tv) == 0)
          {  fprintf(stderr,"WARNING: select() timed out");
          }
          else
          {  /* Recv message from the target */  
             if((recvMsgSize = recv(trgtSock,buffer,MAXBUFSIZE,0)) < 0)
             {  fprintf(stderr,"\t\tError: [%d] target.recv() failed\n",(int)cPID); 
                 exit(1);
             }
    
            /* Sends the client the message recieved from the target */
             if((send(clntSock,buffer,recvMsgSize,0) != recvMsgSize))
             {  fprintf(stderr,"\t\tError: [%d] client.send() failed\n",(int)cPID);
                exit(1);
             }
          }   
          
          free(buffer);
          buffer = (char*)calloc(1,MAXBUFSIZE);
          printf("\tbuffer size: %d\n",strlen(buffer));
              
         /* Grab first client request */ 
          if((recvMsgSize = recv(clntSock,buffer,MAXBUFSIZE,0)) < 0)
          {  fprintf(stderr,"\tError: [%d] client.recv() failed\n",(int)cPID);
             exit(1);
          } 
        
       } /* END WHILE */
    } /* END POGO */

  6. #6
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    Your buffer managment is pretty horrible - and it isn't even C

    > char *buffer;
    > buffer = (char*)calloc(1,MAXBUFSIZE);
    > struct timeval tv;
    1. do not cast the result of malloc (and by implication calloc and realloc) in C FAQ
    2. Mixing declarations with statements is a feature of C++, C99 (the new C standard) or some non-standard compiler extension. If you want the code to be the most portable, then stick to C89 constructs.
    3. If doesn't even guarantee that you'll end up with a \0 terminated character string anyway, since if the recv() completely fills the buffer, all your \0 will have been overwritten.

    > recvMsgSize = recv(clntSock,buffer,MAXBUFSIZE,0)
    If the receive fills the buffer completely, there will be no \0 at the end and therefore printing it with printf("buffer: \n %s",buffer);
    Not only that, you attempt to print the buffer contents on an error, when the contents of the buffer are undefined.

    > free(buffer);
    > buffer = (char*)calloc(1,MAXBUFSIZE);
    And this is simply a horrible way of attempting to get a clean buffer.

    All you need is these 3 elements
    Code:
    char buff[MAXBUFSIZE];  /* a buffer */
    recvMsgSize = recv(clntSock,buff,MAXBUFSIZE-1,0); /* receive a message, with room for a \0 */
    if ( recvMsgSize >= 0 ) buff[recvMsgSize] = '\0';  /* append a \0 */
    Then you can dispense with all that calloc/free stuff.

    Futhermore, since you've set up a TCP connection, you'll need to do some packet reassembly at some point.
    For example, if you send "hello world\n" as a single message via send(), there is no guarantee that you'll get "hello world\n" back in a single recv() call. Sometimes you might end up with just "hello" and then later on you get " world\n".

    > void pogo(int clntSock, int trgtSock)
    Do you really save anything by ripping all the vowels out of your variable names?

    > /* Select() Setup */
    Good - but it needs to be done just before the select() call. Select modifies the data passed to it, so you need to reset it for each new call.

    > if(select(1, &sockSet, NULL, NULL, &tv) == 0)
    The first parameter is "n is the highest‐numbered descriptor in any of the three sets, plus 1.", not the number of descriptors set.
    So in this case, it would be
    if(select(trgtSock+1, &sockSet, NULL, NULL, &tv) == 0)

  7. #7
    Registered User
    Join Date
    Jan 2005
    Posts
    6
    well thanks for the response and the few bits of information... i'll have to fix the buffer cleanup and i had already found the first argument in the select() problem...
    and i enjoy the way i name my variables thank you very much w/ or without the vowels their still understandable, and this way it saves me a little bit of room so my code doesn't rap around the screen on my laptop.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. server client application - (i really need your help)
    By sarahnetworking in forum C Programming
    Replies: 3
    Last Post: 03-01-2008, 10:54 PM
  2. Server Architecture
    By coder8137 in forum Networking/Device Communication
    Replies: 2
    Last Post: 01-29-2008, 11:21 PM
  3. Where's the EPIPE signal?
    By marc.andrysco in forum Networking/Device Communication
    Replies: 0
    Last Post: 12-23-2006, 08:04 PM
  4. IE 6 status bar
    By DavidP in forum Tech Board
    Replies: 15
    Last Post: 10-23-2002, 05:31 PM
  5. socket question
    By Unregistered in forum C Programming
    Replies: 3
    Last Post: 07-19-2002, 01:54 PM