Dhoha
Dhoha

Reputation: 369

Coding FTP service over TCP in C code

I'm trying to code a TCP FTP service for a multi-threaded server. I found this tutorial http://www.mario-konrad.ch/wiki/doku.php?id=programming:multithreading:tutorial-04 which is very helpful to understand the multithreading of clients for the TCP protocol. In fact, this code create a server that can accept multiple connections from different clients simultaneously.

However, I'm struggling to find how to apply the ftp service on it. Precisely how to put and get a file to and from the FTP server.

Any help please?

Upvotes: 1

Views: 21323

Answers (2)

Andrew Lambert
Andrew Lambert

Reputation: 1909

FTP uses two TCP connections to transfer files: a control connection and a data connection.

Establish a control connection to the FTP server, and log in.

Next, establish the data connection. If you are connecting from behind a router then send the PASV command to ask the server to listen on a new data socket and send the IP/port back to you. If you're not behind a router then you will need to listen on a new data socket and use the PORT command to send the IP/port to the server.

Once the data connection is established, send the STOR (upload) or RETR (download) command over the control connection to begin the transfer.

Data going over the control socket would look something like this:

(Open Control Connection)
SERVER: 220 System Ready
CLIENT: USER MyUserName
SERVER: 331 Please specify the password.
CLIENT: PASS MyPassword
SERVER: 230 Login successful.
CLIENT: TYPE I
SERVER: 200 Switching to Binary mode.
CLIENT: PASV
SERVER: 227 Entering Passive Mode (173,254,254,254,211,37)
(Client connects data connection to the specified address)
CLIENT: RETR example.html
SERVER: 150 Opening BINARY mode data connection for example.html (10403 bytes).
SERVER: 226 File send OK.
(Both close the data connection)
CLIENT: QUIT
SERVER: 221 Goodbye
(Both close control connection)

Upvotes: 1

Yue Wang
Yue Wang

Reputation: 1750

The code below is an assignment I did for a networking paper last year. Hope it helps.

#include <windows.h>
#include <stdio.h> 
#include <string.h>
#include <stdlib.h>
#include <winsock.h>
#include <winsock2.h>

#define BUFFERSIZE 800
#define WSVERS MAKEWORD(2,0)

WSADATA wsadata;    
void    wy_fileName_collector(char  *_buffer, char  *_nameBuffer);
int main(int argc, char *argv[]) 
{
    //INITIALIZATION
    struct sockaddr_in localaddr,remoteaddr;
    struct sockaddr_in remoteaddr_act;

    SOCKET s,ns;
    SOCKET s_data_act=0;

    char send_buffer[BUFFERSIZE],receive_buffer[BUFFERSIZE];
    char fileName[40];


    int n,bytes,addrlen;
    memset(&localaddr,0,sizeof(localaddr));//clean up the structure
    memset(&localaddr,0,sizeof(remoteaddr));//clean up the structure

    //WSASTARTUP
    if (WSAStartup(WSVERS, &wsadata) != 0)
    {
        WSACleanup();
        printf("WSAStartup failed\n");
        exit(1);
    }

    //SOCKET
    s = socket(PF_INET, SOCK_STREAM, 0);
    if (s <0)
    {
        printf("socket failed\n");
    }
    localaddr.sin_family = AF_INET;
    if (argc == 2) localaddr.sin_port = htons((u_short)atoi(argv[1]));
    else localaddr.sin_port = htons(1234);//default listening port
    localaddr.sin_addr.s_addr = INADDR_ANY;//server address should be local

    //BIND
    if (bind(s,(struct sockaddr *)(&localaddr),sizeof(localaddr)) != 0)
    {
        printf("Bind failed!\n");
        exit(0);
    }

    //LISTEN
    listen(s,5);

    while (1)
    {
        addrlen = sizeof(remoteaddr);

        //NEW SOCKET newsocket = accept
        ns = accept(s,(struct sockaddr *)(&remoteaddr),&addrlen);
        if (ns <0 ) break;
        printf("accepted a connection from client IP %s port %d \n",inet_ntoa(remoteaddr.sin_addr),ntohs(localaddr.sin_port));

        //Respond with welcome message
        sprintf(send_buffer,"\n==220 Welcome to Alan's FTP site== \r\n\n");
        bytes = send(ns, send_buffer, strlen(send_buffer), 0);

        while (1)
        {
            n = 0;
            while (1)
            {
                //RECEIVE
                bytes = recv(ns, &receive_buffer[n], 1, 0);//receive byte by byte...

                //PROCESS REQUEST
                if ( bytes <= 0 ) break;
                if (receive_buffer[n] == '\n')
                { /*end on a LF*/
                    receive_buffer[n] = '\0';
                    break;
                }
                if (receive_buffer[n] != '\r') n++; /*ignore CRs*/
            }
            if ( bytes <= 0 ) break;

            printf("-->DEBUG: the message from client reads: '%s' \r\n", receive_buffer);

            /**
              * @brief  Exception handling
              */
            if(strncmp(receive_buffer,"USER",4)  && strncmp(receive_buffer,"PASS",4)    &&  strncmp(receive_buffer,"SYST",4)
                    &&strncmp(receive_buffer,"PORT",4)   && strncmp(receive_buffer,"STOR",4)    &&  strncmp(receive_buffer,"RETR",4)
                    &&strncmp(receive_buffer,"LIST",4)  &&  strncmp(receive_buffer,"NLST",4)    &&  strncmp(receive_buffer,"QUIT",4))
            {
                sprintf(send_buffer,"202 Command not implemented, superfluous at this site. \r\n");
                bytes = send(ns, send_buffer, strlen(send_buffer), 0);
            }


            if (strncmp(receive_buffer,"USER",4)==0)
            {
                printf("Logging in \n");
                sprintf(send_buffer,"331 Password required \r\n");
                bytes = send(ns, send_buffer, strlen(send_buffer), 0);
                if (bytes < 0) break;
            }

            if (strncmp(receive_buffer,"PASS",4)==0)
            {
                printf("Typing password (anything will do... \n");
                sprintf(send_buffer,"230 Public login sucessful \r\n");
                bytes = send(ns, send_buffer, strlen(send_buffer), 0);
                if (bytes < 0) break;
            }

            if (strncmp(receive_buffer,"SYST",4)==0)
            {
                system("ver > tmp.txt");

                FILE *fin=fopen("tmp.txt","r");//open tmp.txt file
                sprintf(send_buffer,"150 Transfering... \r\n");
                bytes = send(ns, send_buffer, strlen(send_buffer), 0);
                char temp_buffer[80];
                while (!feof(fin))
                {
                    fgets(temp_buffer,78,fin);
                    sprintf(send_buffer,"%s",temp_buffer);
                    send(s_data_act, send_buffer, strlen(send_buffer), 0);
                }
                fclose(fin);
                sprintf(send_buffer,"226 File transfer completed... \r\n");
                bytes = send(ns, send_buffer, strlen(send_buffer), 0);

                if (bytes < 0) break;
            }

            //PORT
            if(strncmp(receive_buffer,"PORT",4)==0)
            {
                s_data_act = socket(AF_INET, SOCK_STREAM, 0);
                //local variables
                unsigned char act_port[2];
                int act_ip[4], port_dec;
                char ip_decimal[40];
                sscanf(receive_buffer, "PORT %d,%d,%d,%d,%d,%d",&act_ip[0],&act_ip[1],&act_ip[2],&act_ip[3],(int*)&act_port[0],(int*)&act_port[1]);
                remoteaddr_act.sin_family=AF_INET;//local_data_addr_act
                sprintf(ip_decimal, "%d.%d.%d.%d", act_ip[0], act_ip[1], act_ip[2],act_ip[3]);
                printf("IP is %s\n",ip_decimal);
                remoteaddr_act.sin_addr.s_addr=inet_addr(ip_decimal);
                port_dec=act_port[0]*256+act_port[1];
                printf("port %d\n",port_dec);
                remoteaddr_act.sin_port=htons(port_dec);

                if (connect(s_data_act, (struct sockaddr *)&remoteaddr_act, (int) sizeof(struct sockaddr)) != 0)
                {
                    printf("trying connection in %s %d\n",inet_ntoa(remoteaddr_act.sin_addr),ntohs(remoteaddr_act.sin_port));
                    sprintf(send_buffer, "425 Something is wrong, can't start the active connection... \r\n");
                    bytes = send(ns, send_buffer, strlen(send_buffer), 0);

                    closesocket(s_data_act);
                }
                else
                {
                    sprintf(send_buffer, "200 Ok\r\n");
                    bytes = send(ns, send_buffer, strlen(send_buffer), 0);
                    printf("Data connection to client created (active connection) \n");
                }
            }

            /**
              * @brief  STOR
              */
            if((strncmp(receive_buffer,"STOR",4)==0))
            {
                memset(&fileName, 0, strlen(fileName));
                wy_fileName_collector(receive_buffer,   fileName);

                if(fopen(fileName,"w")  ==  NULL)
                {
                    printf("@alan:line1\n");
                    sprintf(send_buffer,"450 Requested file action not taken. \r\n");
                    bytes = send(ns, send_buffer, strlen(send_buffer), 0);
                }
                else
                {
                    FILE *fout=fopen(fileName,"w");

                    sprintf(send_buffer,"150 File status okay; about to open data connection. \r\n");
                    bytes = send(ns, send_buffer, strlen(send_buffer), 0);
                    while(1)
                    {
                        n = 0;
                        while(1)
                        {
                            //RECEIVE
                            bytes = recv(s_data_act, &receive_buffer[n], 1, 0);//receive byte by byte...
                            //PROCESS REQUEST
                            if ( bytes <= 0 ) break;

                            if (receive_buffer[n] == '\n')
                            { /*end on a LF*/
                                receive_buffer[n] = '\0';
                                break;
                            }

                            if (receive_buffer[n] != '\r') n++; /*ignore CRs*/
                        }
                        if ( bytes <= 0 ) break;
                        fprintf(fout,"%s\n",receive_buffer);
                    }

                    sprintf(send_buffer,"226 File transfer completed... \r\n");
                    bytes = send(ns, send_buffer, strlen(send_buffer), 0);

                    fclose(fout);
                    closesocket(s_data_act);
                }
            }

            /**
              * @brief  RETR
              */
            if((strncmp(receive_buffer,"RETR",4)==0))
            {
                memset(&fileName, 0, strlen(fileName));
                wy_fileName_collector(receive_buffer,   fileName);

                if(fopen(fileName,"r")==NULL)
                {
                    sprintf(send_buffer,"450 Requested file action not taken. \r\n");
                    bytes = send(ns, send_buffer, strlen(send_buffer), 0);
                }
                else
                {
                    FILE *fin=fopen(fileName,"r");//open tmp.txt file
                    sprintf(send_buffer,"150 Transfering... \r\n");
                    bytes = send(ns, send_buffer, strlen(send_buffer), 0);
                    char temp_buffer[80];
                    while (!feof(fin))
                    {
                        fgets(temp_buffer,78,fin);
                        sprintf(send_buffer,"%s",temp_buffer);
                        send(s_data_act, send_buffer, strlen(send_buffer), 0);
                    }
                    fclose(fin);
                    sprintf(send_buffer,"226 File transfer completed... \r\n");
                    bytes = send(ns, send_buffer, strlen(send_buffer), 0);
                }
                closesocket(s_data_act);
            }



            //LIST or NLST
            if ( (strncmp(receive_buffer,"LIST",4)==0) || (strncmp(receive_buffer,"NLST",4)==0))
            {
                system("dir > tmp.txt");

                FILE *fin=fopen("tmp.txt","r");//open tmp.txt file
                sprintf(send_buffer,"150 Transfering... \r\n");
                bytes = send(ns, send_buffer, strlen(send_buffer), 0);
                char temp_buffer[80];
                while (!feof(fin))
                {
                    fgets(temp_buffer,78,fin);
                    sprintf(send_buffer,"%s",temp_buffer);
                    send(s_data_act, send_buffer, strlen(send_buffer), 0);
                }
                fclose(fin);
                sprintf(send_buffer,"226 File transfer completed... \r\n");
                bytes = send(ns, send_buffer, strlen(send_buffer), 0);

                closesocket(s_data_act);
            }


            //QUIT
            if (strncmp(receive_buffer,"QUIT",4)==0)
            {
                printf("Quit \n");
                sprintf(send_buffer,"221 Connection closed by the FTP client\r\n");
                bytes = send(ns, send_buffer, strlen(send_buffer), 0);
                if (bytes < 0) break;
                closesocket(ns);
            }
        }
        //CLOSE SOCKET
        closesocket(ns);
        printf("disconnected from %s\n",inet_ntoa(remoteaddr.sin_addr));
    }
    closesocket(s);//it actually never gets to this point....use CTRL_C
}

/**
  * @brief  Return file name from the string passed in.
  */
void wy_fileName_collector(char *_buffer, char  *_nameBuffer)
{
    char bf[BUFFERSIZE];
    char sep[2] = " "; //separation is space
    char *word;
    int  wcount=0;

    strcpy(bf, _buffer);
    for (word = strtok(bf, sep);word;word = strtok(NULL, sep))
    {
        wcount++;
        if(wcount == 2) // jump the first space"_".
        {
            strcpy(_nameBuffer, word);
        }
    }
}

Upvotes: 4

Related Questions