Ancient CS 61 Content Warning!!!!!1!!!
This is not the current version of the class.
This site was automatically translated from a wiki. The translation may have introduced mistakes (and the content might have been wrong to begin with).

Announcements

Let's Review

What happened on Tuesday? We started playing with a pair of client and server processes and then I dumped a mean, nasty client on you! The serviceblaster is not a nice program. It is intentionally trying to consume resources on the server. I'm assuming that you all observed that no matter what architecture you tried for your serviceserver, you ended up hitting some resource limit: open files, ability to fork processes, memory for creating threads. Let me step back and make two points:

1. You should not build clients like serviceblaster. You should build more polite clients, but you should be aware that there are people in this world who take great delight in building clients that do bad things do your server. (My apologies for not making this more explicit last time.) A client like this is launching a Denial of Service Attack (DOS). When you have such an attack launched from multiple locations, it's called a DDOS -- Distributed Denial of Service attack.

2. You want to learn to build servers that are immune to attacks (or as immune as possible).

Learning Objectives

Get the Code

We'll start with the serviceblaster from Tuesday and a version of the serviceserver. You'll find this in our last cs61-exercises directory l26.

What Problem do We Need to Solve

If you have not already done so, take a look at serviceblaster and see what makes it evil. (Note that once it establishes a connection, it simply spins forever doing nothing! Thus it holds the connection open, but is not sending any information over the socket, so the fgets on the server never returns; it blocks.)

So, what can we do on our server to ensure that we don't hang forever? We want to read from a file descriptor, but if nothing ever gets written to it, we'd like to timeout after waiting a reasonable amount of time. Do you recall what system call lets us wait on a file descriptor subject to a timeout?

Building a more Robust Server

OK, so let's figure out how to transform our server into one that is robust against the evil serviceblaster.

1. You may recall that when we started creating a lot of threads, we got quite slow. So, we're going to start with an unthreaded server and see if we can make it handle the load! So, let's start with our original serviceserver that looped handling individual requests. This is the version you'll find in today's directory. Note: This means that we need to be careful that we never let our server block, because if we do, we may block for a long time and there are no other threads to run. You'll want to think about that as you architect your server.

If we aren't spawning multiple threads or processes, then we need to avoid having our server block on anything. Read the man page for fcntl. Look for O_NONBLOCK. Figure out how to issue an appropriate fcntl call that will prevent your server from blocking on its listening fd. Make sure your code still builds and runs, but you haven't solved many problems yet, so there is no need to run a full test.

Note: fcntl is, in many ways, a huge hack. Nonetheless, it is an enormously useful hack. You need not memorize every flag and option, but we do recommend that you skim the manual page so you have an idea of the kinds of things it can do. In the future, you might find the time investment useful!

2. Let's start simple: Add a call to select to determine if you should try to do an accept on the listening fd. (Do not specify a timeout value -- two reasons -- first, we would need time to start up our client, second we have non-blocking FDs so we shouldn't need it.)

You'll need to first construct a file descriptor set -- check the select man page if you've forgotten how to do that.

Build that. If you run it, you'll see that it accepts the first connection, but then hangs, why?

3. Oh right! We made the listening socket non-blocking, but what happens when we then accept the connection and go into handle_connection? We block on the fgets, because the client never sends a message. I guess we should make this file descriptor non-blocking as well! You know how to do that, so go for it. Now what happens if you build and run?

If you got this right, you'll see that the server now reports an error on every connection, because the fgets returns having not received a message (which is what we intended). But, rather than reporting an error, what should we do?

4. Well, we have a couple of options. We could close down the connection -- a reasonable response if we know that the client is evil, but what if we don't know it's evil? We might want to keep the connection open for awhile. Let's do this in two parts. Modify handle_connection so that it simply returns (without printing an error message) when the (now non-blocking) fgets call returns with no data.

Now what happens?

5. Yeay! If you got this far then you should now be able to run the serviceblaster to completion! And it runs quickly. But -- what if the client weren't completely evil and every once in awhile it did something productive? Is there a way that we could keep the individual connections open for awhile in case the client sent a valid message? You'll need to do two things: 1) do something to differentiate a connection whose fgets returned because no bytes were read and one where the server sent a valid message and 2) keep track of the open connections so will be able to handle the connections later.

There are various ways to do this, but once you do, you'll have a reasonably robust server. Here are some things to consider:

You can conceivably play with this for a long time -- play and experiment until you're happy that your server is behaving well and that you understand why.

If you finished all that and still have time

You are a master of the course! Congratulations.

Here's a fun little test: see if you can tweak your clients and servers to copy files! That is, rather than specifying a protocol on the serviceclient.c command line, assume that the argument is a file name. The client should send the file name to the server and then send the contents of the file to the server; the server will write the data to a file of the same name (be sure to run these in different directories).

Wrapping Up

No post-class survey for today! Instead, we'll have a short presentation at the end of class.