Search code examples
csocketsunixposix-select

Can we make a non-blocking server with blocking sockets?


I have to make a simple IRC client/server programs for my IT school. The subject asks us to use select(2) for socket polling but forbids us to use O_NONBLOCK sockets.

  • Your server will accept multiple simultaneous connections.
    Attention, the use of fork is prohibited. So you should imperatively use select
  • Your server must not be blocking.
    This has nothing to do with non-blocking sockets, which are prohibited (so do not use fcntl(s, O_NONBLOCK))

I’m wondering if it is even possible to design a non-blocking server (which does not fork) with blocking sockets even using select(2).

Here is a simple example: let say we have a simple text protocol with one command per line. Each client has a buffer. When select(2) tells us a client is ready for read(2), we read until we found a \n in the client buffer, therefor we process the command. With non-blocking sockets, we would read until EAGAIN.

Let imagine now that we are using blocking sockets and a malicious client sends text with no line-break. select(2) tells us data is available, we then read(2) on the client. But we will never read the expected \n. Instead of returning EAGAIN, the syscall will block indefinitely. This is a denial of service attack.

Is it really possible to design a non-blocking server with blocking-sockets and select(2) (no fork(2))?


Solution

  • Yes, you read once from the socket that select tells you is ready. If the read contains the \n, then process that line. Otherwise, store any data that was received, and immediately go back to the select.

    This means of course, that for every open socket, you must maintain state information, and a buffer of data read so far. This allows the code to process each read independently, without the need to finish a full line before going back to the select.