connection.c - quark - quark web server
 (HTM) git clone git://git.suckless.org/quark
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
       connection.c (8277B)
       ---
            1 /* See LICENSE file for copyright and license details. */
            2 #include <errno.h>
            3 #include <netinet/in.h>
            4 #include <stdio.h>
            5 #include <string.h>
            6 #include <sys/socket.h>
            7 #include <sys/types.h>
            8 #include <time.h>
            9 #include <unistd.h>
           10 
           11 #include "connection.h"
           12 #include "data.h"
           13 #include "http.h"
           14 #include "server.h"
           15 #include "sock.h"
           16 #include "util.h"
           17 
           18 struct worker_data {
           19         int insock;
           20         size_t nslots;
           21         const struct server *srv;
           22 };
           23 
           24 void
           25 connection_log(const struct connection *c)
           26 {
           27         char inaddr_str[INET6_ADDRSTRLEN /* > INET_ADDRSTRLEN */];
           28         char tstmp[21];
           29 
           30         /* create timestamp */
           31         if (!strftime(tstmp, sizeof(tstmp), "%Y-%m-%dT%H:%M:%SZ",
           32                       gmtime(&(time_t){time(NULL)}))) {
           33                 warn("strftime: Exceeded buffer capacity");
           34                 tstmp[0] = '\0'; /* tstmp contents are undefined on failure */
           35                 /* continue anyway */
           36         }
           37 
           38         /* generate address-string */
           39         if (sock_get_inaddr_str(&c->ia, inaddr_str, LEN(inaddr_str))) {
           40                 warn("sock_get_inaddr_str: Couldn't generate adress-string");
           41                 inaddr_str[0] = '\0';
           42         }
           43 
           44         printf("%s\t%s\t%s%.*d\t%s\t%s%s%s%s%s\n",
           45                tstmp,
           46                inaddr_str,
           47                (c->res.status == 0) ? "dropped" : "",
           48                (c->res.status == 0) ? 0 : 3,
           49                c->res.status,
           50                c->req.field[REQ_HOST][0] ? c->req.field[REQ_HOST] : "-",
           51                c->req.path[0] ? c->req.path : "-",
           52                c->req.query[0] ? "?" : "",
           53                c->req.query,
           54                c->req.fragment[0] ? "#" : "",
           55                c->req.fragment);
           56 }
           57 
           58 void
           59 connection_reset(struct connection *c)
           60 {
           61         if (c != NULL) {
           62                 shutdown(c->fd, SHUT_RDWR);
           63                 close(c->fd);
           64                 memset(c, 0, sizeof(*c));
           65         }
           66 }
           67 
           68 void
           69 connection_serve(struct connection *c, const struct server *srv)
           70 {
           71         enum status s;
           72         int done;
           73 
           74         switch (c->state) {
           75         case C_VACANT:
           76                 /*
           77                  * we were passed a "fresh" connection which should now
           78                  * try to receive the header, reset buf beforehand
           79                  */
           80                 memset(&c->buf, 0, sizeof(c->buf));
           81 
           82                 c->state = C_RECV_HEADER;
           83                 /* fallthrough */
           84         case C_RECV_HEADER:
           85                 /* receive header */
           86                 done = 0;
           87                 if ((s = http_recv_header(c->fd, &c->buf, &done))) {
           88                         http_prepare_error_response(&c->req, &c->res, s);
           89                         goto response;
           90                 }
           91                 if (!done) {
           92                         /* not done yet */
           93                         return;
           94                 }
           95 
           96                 /* parse header */
           97                 if ((s = http_parse_header(c->buf.data, &c->req))) {
           98                         http_prepare_error_response(&c->req, &c->res, s);
           99                         goto response;
          100                 }
          101 
          102                 /* prepare response struct */
          103                 http_prepare_response(&c->req, &c->res, srv);
          104 response:
          105                 /* generate response header */
          106                 if ((s = http_prepare_header_buf(&c->res, &c->buf))) {
          107                         http_prepare_error_response(&c->req, &c->res, s);
          108                         if ((s = http_prepare_header_buf(&c->res, &c->buf))) {
          109                                 /* couldn't generate the header, we failed for good */
          110                                 c->res.status = s;
          111                                 goto err;
          112                         }
          113                 }
          114 
          115                 c->state = C_SEND_HEADER;
          116                 /* fallthrough */
          117         case C_SEND_HEADER:
          118                 if ((s = http_send_buf(c->fd, &c->buf))) {
          119                         c->res.status = s;
          120                         goto err;
          121                 }
          122                 if (c->buf.len > 0) {
          123                         /* not done yet */
          124                         return;
          125                 }
          126 
          127                 c->state = C_SEND_BODY;
          128                 /* fallthrough */
          129         case C_SEND_BODY:
          130                 if (c->req.method == M_GET) {
          131                         if (c->buf.len == 0) {
          132                                 /* fill buffer with body data */
          133                                 if ((s = data_fct[c->res.type](&c->res, &c->buf,
          134                                                                &c->progress))) {
          135                                         /* too late to do any real error handling */
          136                                         c->res.status = s;
          137                                         goto err;
          138                                 }
          139 
          140                                 /* if the buffer remains empty, we are done */
          141                                 if (c->buf.len == 0) {
          142                                         break;
          143                                 }
          144                         } else {
          145                                 /* send buffer */
          146                                 if ((s = http_send_buf(c->fd, &c->buf))) {
          147                                         /* too late to do any real error handling */
          148                                         c->res.status = s;
          149                                         goto err;
          150                                 }
          151                         }
          152                         return;
          153                 }
          154                 break;
          155         default:
          156                 warn("serve: invalid connection state");
          157                 return;
          158         }
          159 err:
          160         connection_log(c);
          161         connection_reset(c);
          162 }
          163 
          164 static struct connection *
          165 connection_get_drop_candidate(struct connection *connection, size_t nslots)
          166 {
          167         struct connection *c, *minc;
          168         size_t i, j, maxcnt, cnt;
          169 
          170         /*
          171          * determine the most-unimportant connection 'minc' of the in-address
          172          * with most connections; this algorithm has a complexity of O(n²)
          173          * in time but is O(1) in space; there are algorithms with O(n) in
          174          * time and space, but this would require memory allocation,
          175          * which we avoid. Given the simplicity of the inner loop and
          176          * relatively small number of slots per thread, this is fine.
          177          */
          178         for (i = 0, minc = NULL, maxcnt = 0; i < nslots; i++) {
          179                 /*
          180                  * we determine how many connections have the same
          181                  * in-address as connection[i], but also minimize over
          182                  * that set with other criteria, yielding a general
          183                  * minimizer c. We first set it to connection[i] and
          184                  * update it, if a better candidate shows up, in the inner
          185                  * loop
          186                  */
          187                 c = &connection[i];
          188 
          189                 for (j = 0, cnt = 0; j < nslots; j++) {
          190                         if (!sock_same_addr(&connection[i].ia,
          191                                             &connection[j].ia)) {
          192                                 continue;
          193                         }
          194                         cnt++;
          195 
          196                         /* minimize over state */
          197                         if (connection[j].state < c->state) {
          198                                 c = &connection[j];
          199                         } else if (connection[j].state == c->state) {
          200                                 /* minimize over progress */
          201                                 if (c->state == C_SEND_BODY &&
          202                                     connection[i].res.type != c->res.type) {
          203                                         /*
          204                                          * mixed response types; progress
          205                                          * is not comparable
          206                                          *
          207                                          * the res-type-enum is ordered as
          208                                          * DIRLISTING, ERROR, FILE, i.e.
          209                                          * in rising priority, because a
          210                                          * file transfer is most important,
          211                                          * followed by error-messages.
          212                                          * Dirlistings as an "interactive"
          213                                          * feature (that take up lots of
          214                                          * resources) have the lowest
          215                                          * priority
          216                                          */
          217                                         if (connection[i].res.type <
          218                                             c->res.type) {
          219                                                 c = &connection[j];
          220                                         }
          221                                 } else if (connection[j].progress <
          222                                            c->progress) {
          223                                         /*
          224                                          * for C_SEND_BODY with same response
          225                                          * type, C_RECV_HEADER and C_SEND_BODY
          226                                          * it is sufficient to compare the
          227                                          * raw progress
          228                                          */
          229                                         c = &connection[j];
          230                                 }
          231                         }
          232                 }
          233 
          234                 if (cnt > maxcnt) {
          235                         /* this run yielded an even greedier in-address */
          236                         minc = c;
          237                         maxcnt = cnt;
          238                 }
          239         }
          240 
          241         return minc;
          242 }
          243 
          244 struct connection *
          245 connection_accept(int insock, struct connection *connection, size_t nslots)
          246 {
          247         struct connection *c = NULL;
          248         size_t i;
          249 
          250         /* find vacant connection (i.e. one with no fd assigned to it) */
          251         for (i = 0; i < nslots; i++) {
          252                 if (connection[i].fd == 0) {
          253                         c = &connection[i];
          254                         break;
          255                 }
          256         }
          257         if (i == nslots) {
          258                 /*
          259                  * all our connection-slots are occupied and the only
          260                  * way out is to drop another connection, because not
          261                  * accepting this connection just kicks this can further
          262                  * down the road (to the next queue_wait()) without
          263                  * solving anything.
          264                  *
          265                  * This may sound bad, but this case can only be hit
          266                  * either when there's a (D)DoS-attack or a massive
          267                  * influx of requests. The latter is impossible to solve
          268                  * at this moment without expanding resources, but the
          269                  * former has certain characteristics allowing us to
          270                  * handle this gracefully.
          271                  *
          272                  * During an attack (e.g. Slowloris, R-U-Dead-Yet, Slow
          273                  * Read or just plain flooding) we can not see who is
          274                  * waiting to be accept()ed.
          275                  * However, an attacker usually already has many
          276                  * connections open (while well-behaved clients could
          277                  * do everything with just one connection using
          278                  * keep-alive). Inferring a likely attacker-connection
          279                  * is an educated guess based on which in-address is
          280                  * occupying the most connection slots. Among those,
          281                  * connections in early stages (receiving or sending
          282                  * headers) are preferred over connections in late
          283                  * stages (sending body).
          284                  *
          285                  * This quantitative approach effectively drops malicious
          286                  * connections while preserving even long-running
          287                  * benevolent connections like downloads.
          288                  */
          289                 c = connection_get_drop_candidate(connection, nslots);
          290                 c->res.status = 0;
          291                 connection_log(c);
          292                 connection_reset(c);
          293         }
          294 
          295         /* accept connection */
          296         if ((c->fd = accept(insock, (struct sockaddr *)&c->ia,
          297                             &(socklen_t){sizeof(c->ia)})) < 0) {
          298                 if (errno != EAGAIN && errno != EWOULDBLOCK) {
          299                         /*
          300                          * this should not happen, as we received the
          301                          * event that there are pending connections here
          302                          */
          303                         warn("accept:");
          304                 }
          305                 return NULL;
          306         }
          307 
          308         /* set socket to non-blocking mode */
          309         if (sock_set_nonblocking(c->fd)) {
          310                 /* we can't allow blocking sockets */
          311                 return NULL;
          312         }
          313 
          314         return c;
          315 }