Friday, January 13, 2006

Socket Programming Under Cygwin

While I have talked about how great Cygwin is before, one of the strengths that really makes it stand out is that it adds another layer of compatibility for source written for GNU based systems to Windows (now if only things could go the other way as easily), which includes the networking components. If you are familiar with any of the Unix-like operating systems TCP/IP stacks, you should have no problem writing network aware programs under Cygwin. I demonstrated this with the Scenario of the infiltration of a Red Hat 6.2 where I compiled an exploit written for *nix type systems under Windows and had it successfully run. A full tutorial on sockets programming is outside of the scope of this article. However, there are plenty of good resources on the net, a few of which are listed below:

http://www.scit.wlv.ac.uk/~jphb/comms/sockets.html
http://www.cs.cf.ac.uk/Dave/C/node28.html#SECTION002800000000000000000
http://www.cs.rpi.edu/courses/sysprog/sockets/sock.html
http://www.ecst.csuchico.edu/~chafey/prog/sockets/sinfo1.html

There are also several other useful *nix facilities ported over through Cygwin, such as IPC methods like shared memory and semaphores. In this article I will demonstrate writing a simple client/server echo program demonstrating how to do socket programming for Windows under Cygwin. This will show just basics like how to create a socket, connect to a socket, and read/write data to and from a socket. I will follow up with a later article on using shared memory under Cygwin (which provides two methods, one is an external IPC program, the other is a Windows service).

First lets look at the client program. The client program will take three arguments from the command line, the host IP, the port, and a single 1-word message to pass to the server. It will then do the appropriate conversions, find the host, create the sockets, and connect. Then it will send the message to the host and receive the echo. Pretty simple. Below is the code.

//needed for out networking functionality
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

int main(int argc, char *argv[])
{
     //for the socket file descriptor, portnumber, and return status
     int sockfd, portno, n;
     //buffer for the messages
     char buffer[255];

     //server information and structure for host info returned by gethostbyname
     struct sockaddr_in serv_addr;
         struct hostent *server;

     //I hate using a union for this purpose, but I had issues with copying and casting
     union sock
     {
          struct     sockaddr s;
          struct  sockaddr_in i;
     } sock;
     
     //not exactly the best way to validate commandline input, but it will do for demonstration
     if (argc < 4)
     {
          printf("Please provide correct arguments");
          exit(1);
     }
     
     //Get the port number from the command line
     portno = atoi(argv[2]);
     
     //set up the socket file descriptor
         sockfd = socket(AF_INET, SOCK_STREAM, 0);
         if (sockfd < 0)
             exit(1);
     
     //get server from argument passed into the command line
     server = gethostbyname(argv[1]);
     if (server == NULL) {
             fprintf(stderr,"ERROR, no such host\n");
             exit(1);
         }
     
     //Clear out the servers info
     bzero((char *) &serv_addr, sizeof(serv_addr));
     
     //Set to a network socket instead of a Unix Domain
     serv_addr.sin_family = AF_INET;

     //byte copy the address retrieved from server  into the
     //server addr structure
     bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length);
     serv_addr.sin_port = htons(portno);

     //Copy the serv_addr into the union
     bcopy(&serv_addr, &sock.i, sizeof(struct sockaddr_in));

     //Connect to the server
     if ( connect(sockfd, &sock.s, sizeof(struct sockaddr)) < 0)
     {
          printf("Error with connection");
             exit(1);
     }
     
     //Show the user what they put intot he command line
     printf("Message typed in is: %s \n", argv[3]);
     
     //write to the server
     write(sockfd, argv[3], sizeof(buffer));

     //Read data back from server
     read(sockfd, buffer, sizeof(buffer));
     
     //Show the user what was recieved from the server
     printf("Recieved from server: %s\n", buffer);

     //Close the socket
     close(sockfd);

     return 0;
}


Few things to note about the above code. First is the Union. I hate unions. But in this case I couldn’t avoid it. I had issues copying the structures for some reason, and a union seemed to work well in this situation. In normal circumstances I try to avoid unions since they make reading code difficult. Also, I used bcopy instead of memcpy.

The server code is not much different. It simple sets up its listening sockets, receives the message, and returns a greeting with the IP address of the connection and the message sent. Below is the code for the server.

//needed for out networking functionality
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>

const int LISTEN_PORT_NUMER = 7000;
const int MAX_STRING_SIZE = 255;

int main()
{
     //sockets for the created socket and connections
     int sockfd, newsockfd;
     //length of the client addr
     socklen_t clilen;
     //structures for various information about the client and server
          struct sockaddr_in serv_addr, cli_addr;
     //the message storing strings
     char incoming_message[MAX_STRING_SIZE], temp_string[MAX_STRING_SIZE];

     //Create Socket, if fail, exit from program
     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     if (sockfd < 0)
     {
          printf("Error building Socket FD");
          exit(1);
     }
     
     //Set up our server information
          serv_addr.sin_family = AF_INET;
          serv_addr.sin_addr.s_addr = INADDR_ANY;
    
          //Assign the port number from assigned port above
          serv_addr.sin_port = htons(LISTEN_PORT_NUMER);
     
     //bind socket
     if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
     {
          printf("Error in Binding");
                   exit(1);
     }
     
     //Set up the socket to listen
        if (listen(sockfd, 2) < 0)
     {
          printf("Error listening");
          exit (1);
     };
     
     //infinite loop
     while(1)
     {
          //set the clilen variable prior to passing to accept.
          //per the accept man page (explining why I am doing this)
          //it should initially contain the size of the structure pointed
          //to by addr; on return it will contain the actual length (in bytes)
          //of the address returned. This would fail if clilen was null
          clilen = sizeof(cli_addr);
          printf("Ready to accept connections\n");
          newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen );

          if (newsockfd < 0)
          {
               printf("Error accepting connection");
                       exit(1);
          }

          //read incoming message
          read(newsockfd, incoming_message, sizeof(incoming_message));

          //create the message to return
          sprintf(temp_string, "Hello %s, you said %s", inet_ntoa(cli_addr.sin_addr), incoming_message);
                    
          //write the return message
          if (write(newsockfd, temp_string, sizeof(incoming_message)) < 0)
          {
               printf("Error writing out");
               exit(1);
          }
          
          //close the connection
          close (newsockfd);
     }

     //This never executes
     close(sockfd);
     
     return 0;
}

One pitfall I ran into using the accept function was that the clilen variable needed to be have the size of cli_addr prior to calling the function. Without that, the accept connect would fail, which I was clued in to this by the accept MAN pages. While the above is not as robust as it could be, it demonstrates how to make Below is a transcript of the client connecting to the server through the DOS prompt:

C:\ >client 127.0.0.1 7000 testing
Message typed in is: testing
Recieved from server: Hello 127.0.0.1, you said testing

Update: Apparently I forgot to include the command line switches for compiling. There is really nothing special about these programs. In my experience, the only time I have had to include special switches is when I’ve used IPC under Cygwin, which I do intend to cover in a future article. Anyway, here are the commands I used to compile:

gcc client.c -o client
gcc server.c –o server

9 comments:

Anonymous said...

Would you mind giving the command line options you used to compile you programs please? Thanks.

John Ward said...

Done. I added it to the post itself. Sorry about that.

Anonymous said...

why did u use read(),write() functions here? send() and receive() functions will work here or not?

John Ward said...

Yes,

send/recv should work as well. From a high levevl, the difference being the number of arguments required, and read/write requires fewer arguments. I believe at a lower level read/write calls send/recv anyhow, but I'd have to read through the source to confirm. I also believe that some non-Unix TCP stacks do not support read/write calls. The main reason I used read/write is because thats what I'm used to and it has fewer parameters, but to each his own.

prashant prahalad said...

U mentioned that u had issues with casting and copying while using unions. Even i am facing the same thing . So could u please share ur thoughts on that!!

John Ward said...

Unfortunately, I dont remember what the issues were. It's been a few years since I have revisited this example...

gift said...

thanks a lot for code...

that's great!....

mukesh said...

bzero bcopy and exit function errors

mukesh said...

bzero bcopy and exit function errors under cygwin