Introduction
Introduction Statistics Contact Development Disclaimer Help
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 }
You are viewing proxied material from suckless.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.