- A6 due December 8 (9th for Extension).
- During reading period, you may submit up to one assignment for regrading for up to 90% of the total points on the assignment. Should you wish to do this, send mail to firstname.lastname@example.org when you have submitted the version you wish to be graded. Also, please make sure the commit says, "RESUBMIT GRADE ME."
- All work (resubmissions and A6) must be submitted by the end of day (5:00 PM) on December 10.
- The final exam is on Saturday, December 12 from 9:00 AM to Noon. We are in Lowell Lecture Hall. There are a few outlets, but not many, so please bring your laptop charged. We will try to make the outlets available, so you can recharge for a brief period and then return to your seat so others can do the same.
- The exam will be completed online as the midterm was. I have created the final directory in the
cs61-psetsrepository. Please follow the standard procedure (as outlined in each problem set and the midterm examination) to pull the repository and have your final exam entry on the grading server reference your repository (not your partner's). PLEASE DO THIS BEFORE THE EXAM, so that we don't end up with any last minute panic the morning of the exam. I will undoubtedly edit the exam.txt until about Wednesday, 12/10, so you may want to pull now to make sure you're all set up and then re-pull again towards the end of next week to get the most up to date version of the exam.txt file.
- Please fill out the Q Guide surveys (and equivalent for the extension school). We take feedback quite seriously and appreciate any feedback you can offer. The surveys are anonymous, and we do not get results until after final grades have been submitted.
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).
- Apply defensive coding practices to make your server immune to a DOS attack like the one launched by
Get the Code
We'll start with the
serviceblaster from Tuesday and a version of the
serviceserver. You'll find this in our last
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
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.
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
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:
- How long should you wait before closing a connection?
- When should you close connections?
- How can you manage the set of file descriptors on which you want to select?
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).
No post-class survey for today! Instead, we'll have a short presentation at the end of class.