CSS 432
FAQ on Final Project: Network Application


Note: Q1 - Q9 refer to the design and implementation of the ftp client. Please ignore Q11 - Q20, which refer to the design and implementation of an ftp server, which is not included in the current final project.

Q1: How many lines should the ftp client expect to read as a server reply?

I have a question about the replies the server sends back. For replies, what can the client assume as far as the message size or line count? I am running into a problem where if I don't know how many lines to read for each reply, I am not getting all of the data. If I just keep looping, I will read all of the data, but as soon as no data is left, I go into a read call that blocks and waits for incoming data. So basically my question is, what can we assume about the server replies, and if we can assume nothing, how can we make sure we read the entire reply without going into a read call that blocks?

I read Section 4.2 in RFC 959 about server replies and how the first line and the last line should contain a common sequence to tell the client that more data exists on additional lines and that no more data is left after the last line. Should we use this implementation for our client?

A: Use poll( &ufds, 1, 1000 ) to check if you have more data to read

While RFC 959 mentions the first and the last line format returned from the server, all you need to use is the poll() system call.
   #include <sys/poll.h>
   struct pollfd ufds;

   ufds.fd = sd;                     // a socket descriptor to exmaine for read
   ufds.events = POLLIN;             // check if this sd is ready to read
   ufds.revents = 0;                 // simply zero-initialized
   int val = poll( &ufds, 1, 1000 ); // poll this socket for 1000msec (=1sec)

   if ( val > 0 ) {                  // the socket is ready to read
      char buf[BUFLEN];
      int nread = read( sd, buf, BUFLEN ); // guaranteed to return from read 
                                           // even if nread < BUFLEN

Q2: How do we read from cin without knowing the # of reads in advance?

Since we have to have the program handle input in a variety of ways, how do we read from the console (cin) without know the # of reads in advance?

A: Use cin.getline( )

Here is a code example. After receiving a cin input in command[], you have to lexically analyze its contents.
   #define 10000                     // allocate a big character array

   char command[CMD_MAX];            // receive a cin input
   cin.getline( command, CMD_MAX );

Q3: Which messages need to have the CRLF delimiter and how do we add it to them?

A: All commands must be terminated with a Telnet string end-of-line CRLF ("\r\n") code. All server responses must have this CRLF code at the end, too.

Q4: Do we need to fork() the client application in the case of receiving data over the passive ftp connection?

For our project you talked about forking the client application in class when receiving data over the passive ftp connection. For example in order:
   Send PASV
   Passively Connect()
   Fork() a child
   Send LIST
What I cannot figure out is why the client needs to fork in order to get the data? Currently my client is complete and, get, put and ls work just like the actual ftp program without using a fork. Is there some specific reason we need to use fork() that I am not realizing?

A: It is always safe to let a child process or a thread read data from the server.

If your client sends an ls (LIST) command and thereafter tries to read data from the server, the server might have already sent data and closed the passive connection before your client actually reads the data, in which case you will receive a "broken pipe" error. To prevent this error, you should spawn a child process (or thread) and have the child read the data, (i.e., call read()) before you actually send an ls command. Although the spawned child process or thread may be initially blocked for a read, once the server receives ls and returns data, the child can immediately read data. That's the reason why you may need to spawn another instance to read data. In summary, the recommended sequence is:
   send a PASV command to the server
   receive a server response
   establish a "data" connection
   fork a child process/thread and let it be blocked on read()
   send a RETR command to the server
   the child reads all data from the server and terminates itself.

Q5: For the client do we have to get the system username who is logged and display it in the prompt like the real ftp client?

A: Yes. Use getlogin()

A code example is:
   #include <unistd.h>
   #include <stdio.h>

   char *serverIp;     // let it point to one of argv[] that includes serverIp.

   string userString( getlogin() );
   cout << "Name (" << serverIp << ":" << userString << "): ";

Q6: What file modes does the client have to specify when writing to its local disk a file received from the server?

A: Use S_IRUSR, S_IWUSR, S_IRGRP, and S_IROTH.

For the get command, the client should set these file modes before writing a fie received from the server:
   #include <sys/types.h>
   #include <sys/stat.h>
   #include <fcntl.h>

   mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
   int file = open( filename, O_WRONLY | OCREAT, mode );

Q7: I received a multiple definition error when compiling my program.

When i tried to put the socket into one class and compile the ftp and ftpd programs, i am getting a multiple definition error for all of the functions. any idea why?

A: Use compiler directives #ifndef, #define and #endif

If you code many classes that may refer to each other, the compiler may read the same header file repeatedly, which causes this multiple definition error. Use #ifndef, #define, and #endif in your header file. Assuming your header file name is Socket.h, your file should be:
   #ifndef _SOCKET_H_
   #define _SOCKET_H_

   your class definition

   #endif
This will prevent the compiler from scanning the same class definition multiple times.

Q8: Although I converted all C++ string into C chars, write() still does not work.

Here is my code:
char sendCmd[100];
cin >> cmd;
strcpy( sendCmd, "PASS " );
strcat( sendCmd, cmd );
strcat( sendCmd, "\r\n" );
int nwrite = write( clientSd, sendCmd, sizeof( sendCmd ) );
What's wrong?

A: Make sure to determine the exact number of bytes you will send

If, for example, cmd is "abc", sendCmd contains "PASS abc\r\n", which is 10 bytes long. However sizeof( sendCmd ) always return 100 in the above case.

Q9: I have am having trouble with getline()

A: Use cin >>.

The following FAQs only apply to the design of an ftp server

They can be ignored for the current version of this project, which focuses only on the ftp client.

Q11: Should our FTP server be able to accept multiple client connections?

For our FTP server do we have to assume we will multiple clients connected at once? Just for clarification on how an FTP servers work: if you have multiple clients connecting at once to ONE ftp server, do you have to spawn an instance of the server process for each client connected? In such a case then, wouldn't each instance of the server process have to use a unique port number for the persistent active connection generally on port 21?

A: Yes

Once the ftp server accepts a new connection, it must spawn a child process that handles this connection. The parent ftp server must always wait for a new connection on a given port or a default port #21. Each child must use a free port when a passive mode is requested by its client.

Q12: Why is the discrepancy between "telnet hostname port" and ftp -p port hostname

In the example on a lecture slide, you create an ftp with the command sequence:
   >telnet hostname port
but in the assignment we do
   >ftp -p port hostname.
why the discrespancy

A: No reason

If I try to find some reasons, the unix ftp can receive only one argument, (i.e., hostname). All the other parameters must be given as -vdingkfxut options. So, the project specification somewhat follows this format.

Q13: Does the client first create a temporary connection of port 21?

To clarify, the client create a temp connection of port 21. at this point it has to send a userID and a password in order to connect to the server.

A: If the client does not receive a -p option, you can do so.

If the client has receive a -p option, it must establish a connection of the port given in -p. This option is necessary to communicate with your own ftpd that must run on a port rather than 21.

Q14: How can the server get its local IP address?

I am following up about my question how to get the local IP address for my server to send back its IP address to the client. I tried using getsockname on the binded socket, but that did not work.

A: Use gethostname, gethostbyname, and inet_ntoa

Use the following code:
#define MAX 200 or what you like
char hostname[MAX];
bzero( hostname, MAX );
gethostname( hostname, MAX );
struct hostent* host = gethostbyname( hostname );
char *ipAddr = inet_ntoa( *( struct in_addr * )*host->h_addr_list );
You'll receive the IP address in ipAddr. For more details of each system call, use man.

Q15: For the FTP server, how should it handle sending files in binary or ascii mode?

According to the RFC for FTP, ASCII mode should send each line with a CRLF character (\r\n) at the end. The problem I am having is when the server open’s a binary file but in ASCII mode as requested by the client, how do we figure out where the end of line is to append CRLF when we are dealing with binary data? Is it sufficient to just open a file in the corresponding mode and read its entire contents and send it, or do we need to do further processing depending on the mode?

A: Simply assume that the FTP server can handle only the binary mode for file transfer.

For the FTP client, you have to always direct the FTP server to transfer data in the binary mode. Use "TYPE I". For the "ls" command, both the client and server should assume the ASCII mode.

Q16: How can we check a valid password on the client when not using anonymous?

A: The server should read the passwd file in its local directory for a validation.

The server must scan all account/password pairs in this file and check if there is such a paper that matches the account and password given by the client through the USER and PASS commands.

Q17: What file modes does the server have to specify when writing to its local disk a file received from the client?

A: Use S_IRUSR, S_IWUSR, S_IRGRP, and S_IROTH.

Q18: How does the server change its working directory?

A: Use the open and fchdir commands.

   #include <sys/types.h>
   #include <sys/stat.h>
   #include <fcntl.h>
   #include <unistd.h>

   int dirfd = open( directoryName, O_DIRECTORY | O_RDONLY );
   fchdir( dirfd );

Q19: How can the server send the contents of its current working directory?

A: Use system( "/bin/ls -l" ) and dup2.

Here is a recommended squence:
   create a pipe.
   fork a child process.
   let the child map the pipe input to descritor 1, (i.e., stdout) using dup2.
   let the child run system("/bin/ls -l") to pass ls outputs to the pipe input.
   let the parent read the ls outputs through the pipe output.

Q20: I got "./ftpd: Permission denied"

When trying to run your server program, i get a permission denied error. Here is the ouput:
   [user@uw1-320-29 put]$ ./ftpd -p 3375
   -bash: ./ftpd: Permission denied

A: Try chmod and a different port.

You might have changed your ftpd's file mode for some reason. Furthermore, use the last five digitas of your student id for the sever port.
   chmod 700 ftpd
   ./ftpd -p 33750