Distributed Systems Lab/06


Lab 2 / Multi-Threaded Sockets File Transfer Server

Introduction

A threaded socket-based server is probably the easiest server type to understand. A server program almost always needs to handle more than one connection at a time. The reason is, since each connection gets its own thread, each thread can use simple blocking I/O on the socket. All other multi-connection server types use non-synchronous I/O of varying complexities in order to avoid thread overhead.

A multi-threaded server isn't that different from the basic single-connection server. The architectures are fairly different, but some of the code is identical between the two server programs.

Detailed Description

In this assignment, you provide a server-side implementation of the DSGSFTP protocol that you used in Lab 1. Your objective is to develop a simple sockets-based, multi-threaded text file transfer server in Java (version 1.5). This server (that you actually used in Lab 1 ;-)) allows clients to connect to itself concurrently so that they can look for ASCII TEXT files and download them (i.e., you do not need to worry about transferring binary files such as images and programs).

The functionality of your file transfer server should be coded into the class Server, which is stored in the file Server.java. The class should be a stand-alone program that will be directly invoked by java. Therefore, it must contain a static main() function.

Your program is supposed to take exactly three command-line arguments. If more or less than three command-line arguments are passed to your program, or some error happens (e.g., cannot bind socket) you have to exit immediately with exit code 1 (i.e., System.exit(1)). In case of serious errors that prevent your server from running (e.g., cannot bind socket, etc.), catch the exception and terminate your program with exit code 1. In all other cases, (i.e., your server is shutdown gracefully) your program should exit with exit code 0. The synopsis of the program is as follows:

java Server <server port> <shutdown port> <shutdown password>

where

  1. server port is the listening TCP port where clients can connect to (e.g., like 10000 as on our server).
  2. shutdown port is the listening UDP port where shutdown request packets can be sent to (e.g., 10001).
  3. shutdown password is the password with which the server can be remotely shutdown. To avoid shutdown packet problems we suggest to use simple and short passwords. Yes, we are aware of the security risks, but we try to keep the server as simple as possible ;)

When your program is invoked with three arguments, it should bind to the server port (i.e., first argument) and listen for incoming connections from clients. At the same time, it should also bind to the UDP shutdown port (i.e., second argument) and listen for a valid shutdown UDP packet. A valid shutdown UDP packet is a packet that contains the shutdown password (i.e., third argument). If such a packet is received, the server should gracefully shut itself down after the last connected client quits (i.e., disconnects).

Note that your server should use the UNIX file separator "/" in file and directory names (e.g., "/funny-stuff").

Your file transfer server uses a simple protocol to communicate with its clients. Here is the protocol description (to give you some "hands-on" experience reading protocol descriptions, we made it look like a typical protocol specification). If you have solved Lab 1, you will already be familiar with this protocol (so it does pay off to write code after all ;-)).

Here is an Tests ZIP file you can use for testing. Unzip it and you will get the following file structure (Note that these files were created on Linux, you might have problems under Windows so copy them to the lab environment and test them there):

  • input.txt
  • output.txt
  • pub/
Start your server, connect with Netcat, send the input Tests and save the Output Test to out.txt. For example, if your server is running on port 10900, you can test it like this:

nc localhost 10900 < input.txt > out.txt

Now compare the out.txt file with the output.txt file we provide (e.g., using diff)!

If your server tries to bind to a TCP port that is already listening for a connection, an exception will be thrown by your server. Because many people will be testing their servers in the lab environment, such binding errors are probable. In order to prevent this from happening, we suggest that you use your dslabXXX number when choosing a port. For example, if your dslab number is dslab900, you should use 10000+900 = 10900 as the server port, and 10000+901=10901 as the listening UDP port. For example, user dslab900 would test his/her program using the following command (supposing that he/she is using the password dslab06):

java Server 10900 10901 dslab06

We will ignore any debugging messages you may print to standard output or standard error (i.e., System.out, System.err in Java) so feel free to print anything you want (as long as you are not obscene and you don't insult our grading robot or us *grin*).

When your server receives GET or LS requests from clients, it should read a local directory called pub and deliver its contents to clients. All files that clients should be able to see and download can be placed in this local directory.

Suppose your pub directory is located at /home/uebungen/dslab/dslab900/pub. When a client issues a LS\n/\n request (i.e., list all the files in the root directory /), this request would be equivalent to getting a list of all files and directories in /home/uebungen/dslab/dslab900/pub/

Of course, it should not be possible for a client to see directories or files outside of the pub directory. The simplest trick somebody could use to by-pass your server could be a so-called "directory traversal attack". That is, the client could issue a request such as LS\n../\n and get a file listing of

/home/uebungen/dslab/dslab900/pub/../

which is equivalent to

/home/uebungen/dslab/dslab900/

If you are concatenating strings, do not forget to filter ".." from the input you receive. We will surely test if your server can withstand this attack ;-) Your server should react like our server does (connect to www.dslab.tuwien.ac.at, port 10000, and find out ;)).

Remarks

Note that, in real life, passing a password as a command line argument is actually a very bad (and dumb) thing to do. A local attacker could issue a shell command such as ps auxww | grep Server to simply retrieve your password (!). However, we are doing this in DSLab just for the sake of simplicity. If you are interested in security issues and secure programming, then we suggest you take our introductory security course.

Hints for Solving the Lab

  1. If you are new to network programming, you might like to read the sockets tutorial.
  2. In order to test your server implementation, you can use our file transfer server and compare the results it delivers. The DSLab file transfer server is running at www.dslab.tuwien.ac.at at port 10000 as you will remember from Lab1.
  3. telnet is a useful tool in network programming and debugging. Yes, we keep repeating this because it is something you will often use when dealing with and debugging network protocols. Try connecting to your file transfer server using telnet and giving commands manually. For example, for user dslab900, like this:

    ek@pizza:~> telnet pizza.dslab.tuwien.ac.at 10900
    Trying 128.131.172.139...
    Connected to localhost.
    Escape character is '^]'.
    100 Welcome to the DSLab File Server
    QUIT
    107 Sayonara, take good care now
    Connection closed by foreign host.
    ek@pizza:~>

    Using telnet will allow you to see the messages being exchanged between the server and the client... Of course, you can also simply use the client you implemented in the last assignment ;-)
  4. Another very useful network tool is netcat. On the servers, you can start netcat using the command nc. netcat allows you to send and listen to TCP and UDP packets and is a great tool for debugging. For example, you can use netcat to connect to your server and send a shutdown UDP request like this:

    nc -u pizza.dslab.tuwien.ac.at 10901
    dslab06

  5. Note that if no parameters are given to the server with the LS and GET commands, it assumes that the / directory (i.e., root directory) is meant.

Deliverables

To submit your solution to us, you need to follow these steps:

  1. Develop your solution in Java (version 1.5), writing the code into the Server class in a file called Server.java (e.g., using vi, emacs, whatever). Make sure that your Java program compiles and runs in the lab environment.
  2. If you have created this file on some other machine, copy it over using scp (secure copy).
  3. In the directory where your Server.java file is located, on pizza.dslab.tuwien.ac.at or pasta.dslab.tuwien.ac.at, call /usr/local/bin/submit2
  4. Read any error or success messages
  5. It might take up to an hour for our grading robot to check your program. Don't forget to read your e-mail to check the results of the automatic grading robot. If you have errors, correct them and try again!

Deadline

You need to submit your solution until November the 16th, 17:59.

Closing words

Server is our success ;-)


Last Modified: Fre Nov 10 15:32:01 CET 2006


Distributed Systems Group, Technical University of Vienna, Argentinierstrasse 8 / 184-1, 1040 Vienna, Austria, www.infosys.tuwien.ac.at