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 }