| connection.c - quark - quark web server | |
| git clone git://git.suckless.org/quark | |
| Log | |
| Files | |
| Refs | |
| 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 fail… | |
| 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-stri… | |
| 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->bu… | |
| 109 /* couldn't generate the header, we fail… | |
| 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, … | |
| 134 &c->progr… | |
| 135 /* too late to do any real error… | |
| 136 c->res.status = s; | |
| 137 goto err; | |
| 138 } | |
| 139 | |
| 140 /* if the buffer remains empty, we are d… | |
| 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… | |
| 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 nslo… | |
| 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-ad… | |
| 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) … | |
| 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 inn… | |
| 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.typ… | |
| 203 /* | |
| 204 * mixed response types; progress | |
| 205 * is not comparable | |
| 206 * | |
| 207 * the res-type-enum is ordered … | |
| 208 * DIRLISTING, ERROR, FILE, i.e. | |
| 209 * in rising priority, because a | |
| 210 * file transfer is most importa… | |
| 211 * followed by error-messages. | |
| 212 * Dirlistings as an "interactiv… | |
| 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 res… | |
| 225 * type, C_RECV_HEADER and C_SEN… | |
| 226 * it is sufficient to compare t… | |
| 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 nslo… | |
| 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 } |