Introduction
Introduction Statistics Contact Development Disclaimer Help
quark-basecgi-20190317-4677877.diff - sites - public wiki contents of suckless.…
git clone git://git.suckless.org/sites
Log
Files
Refs
---
quark-basecgi-20190317-4677877.diff (10208B)
---
1 From 4677877693196823e8d806b0a0f520a35dd08533 Mon Sep 17 00:00:00 2001
2 From: Platon Ryzhikov <[email protected]>
3 Date: Sun, 17 Mar 2019 11:44:36 +0300
4 Subject: [PATCH] Add basic cgi support
5
6 ---
7 http.c | 67 ++++++++++++++++++++++++++++++++++++++++----------
8 http.h | 3 +++
9 main.c | 25 +++++++++++++++++--
10 quark.1 | 20 ++++++++++++++-
11 resp.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
12 resp.h | 1 +
13 util.h | 8 ++++++
14 7 files changed, 184 insertions(+), 16 deletions(-)
15
16 diff --git a/http.c b/http.c
17 index efc4136..d3af686 100644
18 --- a/http.c
19 +++ b/http.c
20 @@ -8,6 +8,7 @@
21 #include <stddef.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 +#include <stdlib.h>
25 #include <string.h>
26 #include <strings.h>
27 #include <sys/socket.h>
28 @@ -30,10 +31,12 @@ const char *req_field_str[] = {
29 const char *req_method_str[] = {
30 [M_GET] = "GET",
31 [M_HEAD] = "HEAD",
32 + [M_POST] = "POST",
33 };
34
35 const char *status_str[] = {
36 [S_OK] = "OK",
37 + [S_NO_CONTENT] = "No content",
38 [S_PARTIAL_CONTENT] = "Partial Content",
39 [S_MOVED_PERMANENTLY] = "Moved Permanently",
40 [S_NOT_MODIFIED] = "Not Modified",
41 @@ -97,6 +100,7 @@ http_get_request(int fd, struct request *r)
42 size_t hlen, i, mlen;
43 ssize_t off;
44 char h[HEADER_MAX], *p, *q;
45 + size_t clen;
46
47 /* empty all fields */
48 memset(r, 0, sizeof(*r));
49 @@ -111,23 +115,23 @@ http_get_request(int fd, struct request *r)
50 break;
51 }
52 hlen += off;
53 - if (hlen >= 4 && !memcmp(h + hlen - 4, "\r\n\r\n", 4)) {
54 - break;
55 + if (hlen >= 4 && strstr(h, "\r\n\r\n")) {
56 + if (strstr(h, "Content-Length:")) {
57 + /* Make sure that all data is read */
58 + sscanf(strstr(h, "Content-Length:"), "C…
59 + if (strlen(strstr(h, "\r\n\r\n")) == 4 …
60 + break;
61 + }
62 + }
63 + else {
64 + break;
65 + }
66 }
67 if (hlen == sizeof(h)) {
68 return http_send_status(fd, S_REQUEST_TOO_LARGE…
69 }
70 }
71
72 - /* remove terminating empty line */
73 - if (hlen < 2) {
74 - return http_send_status(fd, S_BAD_REQUEST);
75 - }
76 - hlen -= 2;
77 -
78 - /* null-terminate the header */
79 - h[hlen] = '\0';
80 -
81 /*
82 * parse request line
83 */
84 @@ -137,6 +141,7 @@ http_get_request(int fd, struct request *r)
85 mlen = strlen(req_method_str[i]);
86 if (!strncmp(req_method_str[i], h, mlen)) {
87 r->method = i;
88 + setenv("REQUEST_METHOD", req_method_str[i], 1);
89 break;
90 }
91 }
92 @@ -161,7 +166,6 @@ http_get_request(int fd, struct request *r)
93 return http_send_status(fd, S_REQUEST_TOO_LARGE);
94 }
95 memcpy(r->target, p, q - p + 1);
96 - decode(r->target, r->target);
97
98 /* basis for next step */
99 p = q + 1;
100 @@ -200,7 +204,11 @@ http_get_request(int fd, struct request *r)
101 if (i == NUM_REQ_FIELDS) {
102 /* unmatched field, skip this line */
103 if (!(q = strstr(p, "\r\n"))) {
104 - return http_send_status(fd, S_BAD_REQUE…
105 + if (r->method == M_POST) {
106 + break;
107 + } else {
108 + return http_send_status(fd, S_B…
109 + }
110 }
111 p = q + (sizeof("\r\n") - 1);
112 continue;
113 @@ -230,6 +238,9 @@ http_get_request(int fd, struct request *r)
114 /* go to next line */
115 p = q + (sizeof("\r\n") - 1);
116 }
117 +
118 + /* all other data will be later passed to script */
119 + sprintf(r->cgicont, "%s", p);
120
121 /*
122 * clean up host
123 @@ -361,6 +372,36 @@ http_send_response(int fd, struct request *r)
124 /* make a working copy of the target */
125 memcpy(realtarget, r->target, sizeof(realtarget));
126
127 + /* check if there is some query string */
128 + if (strrchr(realtarget, '?')) {
129 + snprintf(tmptarget, sizeof(realtarget), "%s", strtok(re…
130 + setenv("QUERY_STRING", strtok(NULL, "?"), 1);
131 + memcpy(realtarget, tmptarget, sizeof(tmptarget));
132 + }
133 + decode(realtarget, tmptarget);
134 +
135 + /* match cgi */
136 + if (s.cgi) {
137 + for (i = 0; i < s.cgi_len; i++) {
138 + if (!regexec(&s.cgi[i].re, realtarget, 0,
139 + NULL, 0)) {
140 + snprintf(realtarget, sizeof(tmptarget) …
141 + if (stat(RELPATH(realtarget), &st) < 0)…
142 + return http_send_status(fd, (er…
143 + S_FORBI…
144 + }
145 + setenv("SERVER_NAME", r->field[REQ_HOST…
146 + if (s.port) {
147 + setenv("SERVER_PORT", s.port, 1…
148 + }
149 + setenv("SCRIPT_NAME", realtarget, 1);
150 + return resp_cgi(fd, RELPATH(realtarget)…
151 + }
152 + }
153 + }
154 +
155 + memcpy(realtarget, tmptarget, sizeof(tmptarget));
156 +
157 /* match vhost */
158 vhostmatch = NULL;
159 if (s.vhost) {
160 diff --git a/http.h b/http.h
161 index cd1ba22..b438759 100644
162 --- a/http.h
163 +++ b/http.h
164 @@ -19,6 +19,7 @@ extern const char *req_field_str[];
165 enum req_method {
166 M_GET,
167 M_HEAD,
168 + M_POST,
169 NUM_REQ_METHODS,
170 };
171
172 @@ -28,10 +29,12 @@ struct request {
173 enum req_method method;
174 char target[PATH_MAX];
175 char field[NUM_REQ_FIELDS][FIELD_MAX];
176 + char cgicont[PATH_MAX];
177 };
178
179 enum status {
180 S_OK = 200,
181 + S_NO_CONTENT = 204,
182 S_PARTIAL_CONTENT = 206,
183 S_MOVED_PERMANENTLY = 301,
184 S_NOT_MODIFIED = 304,
185 diff --git a/main.c b/main.c
186 index 9e7788f..471a3a7 100644
187 --- a/main.c
188 +++ b/main.c
189 @@ -165,7 +165,7 @@ static void
190 usage(void)
191 {
192 const char *opts = "[-u user] [-g group] [-n num] [-d dir] [-l]…
193 - "[-i file] [-v vhost] ... [-m map] ...";
194 + "[-i file] [-v vhost] ... [-m map] ... [-c c…
195
196 die("usage: %s -h host -p port %s\n"
197 " %s -U file [-p port] %s", argv0,
198 @@ -195,11 +195,23 @@ main(int argc, char *argv[])
199 s.host = s.port = NULL;
200 s.vhost = NULL;
201 s.map = NULL;
202 - s.vhost_len = s.map_len = 0;
203 + s.cgi = NULL;
204 + s.vhost_len = s.map_len = s.cgi_len = 0;
205 s.docindex = "index.html";
206 s.listdirs = 0;
207
208 ARGBEGIN {
209 + case 'c':
210 + if (spacetok(EARGF(usage()), tok, 2) || !tok[0] || !tok…
211 + usage();
212 + }
213 + if (!(s.cgi = reallocarray(s.cgi, ++s.cgi_len,
214 + sizeof(struct cgi)))) {
215 + die("reallocarray:");
216 + }
217 + s.cgi[s.cgi_len - 1].regex = tok[0];
218 + s.cgi[s.cgi_len - 1].dir = tok[1];
219 + break;
220 case 'd':
221 servedir = EARGF(usage());
222 break;
223 @@ -286,6 +298,15 @@ main(int argc, char *argv[])
224 }
225 }
226
227 + /* compile and check the supplied cgi regexes */
228 + for (i = 0; i < s.cgi_len; i++) {
229 + if (regcomp(&s.cgi[i].re, s.cgi[i].regex,
230 + REG_EXTENDED | REG_ICASE | REG_NOSUB)) {
231 + die("regcomp '%s': invalid regex",
232 + s.cgi[i].regex);
233 + }
234 + }
235 +
236 /* raise the process limit */
237 rlim.rlim_cur = rlim.rlim_max = maxnprocs;
238 if (setrlimit(RLIMIT_NPROC, &rlim) < 0) {
239 diff --git a/quark.1 b/quark.1
240 index ce315b5..cbbcff3 100644
241 --- a/quark.1
242 +++ b/quark.1
243 @@ -16,6 +16,7 @@
244 .Op Fl i Ar file
245 .Oo Fl v Ar vhost Oc ...
246 .Oo Fl m Ar map Oc ...
247 +.Oo Fl c Ar cgi Oc ...
248 .Nm
249 .Fl U Ar file
250 .Op Fl p Ar port
251 @@ -27,11 +28,28 @@
252 .Op Fl i Ar file
253 .Oo Fl v Ar vhost Oc ...
254 .Oo Fl m Ar map Oc ...
255 +.Oo Fl c Ar cgi Oc ...
256 .Sh DESCRIPTION
257 .Nm
258 -is a simple HTTP GET/HEAD-only web server for static content.
259 +is a simple HTTP web server.
260 .Sh OPTIONS
261 .Bl -tag -width Ds
262 +.It Fl c Ar cgi
263 +Add the target prefix mapping rule for dynamic content specified by
264 +.Ar cgi ,
265 +which has the form
266 +.Qq Pa regex dir ,
267 +where each element is separated with spaces (0x20) that can be
268 +escaped with '\\'.
269 +.Pp
270 +A request matching cgi regular expression
271 +.Pa regex
272 +(see
273 +.Xr regex 3 )
274 +executes script located in
275 +.Pa dir
276 +passing data to it via QUERY_STRING environment variable
277 +or via stdout and then sends its stdout.
278 .It Fl d Ar dir
279 Serve
280 .Ar dir
281 diff --git a/resp.c b/resp.c
282 index 3075c28..dccdc3f 100644
283 --- a/resp.c
284 +++ b/resp.c
285 @@ -38,6 +38,82 @@ suffix(int t)
286 return "";
287 }
288
289 +enum status
290 +resp_cgi(int fd, char *name, struct request *r, struct stat *st)
291 +{
292 + enum status sta;
293 + int tocgi[2], fromcgi[2];
294 + pid_t script;
295 + ssize_t bread, bwritten;
296 + static char buf[BUFSIZ], t[TIMESTAMP_LEN];
297 +
298 + /* check if script is executable */
299 + if (!(st->st_mode & S_IXOTH)) {
300 + return http_send_status(fd, S_FORBIDDEN);
301 + }
302 +
303 + /* open two pipes in case for POST method; this doesn't break o…
304 + if (pipe(fromcgi) < 0) {
305 + return http_send_status(fd, S_INTERNAL_SERVER_ERROR);
306 + }
307 +
308 + if (pipe(tocgi) < 0) {
309 + return http_send_status(fd, S_INTERNAL_SERVER_E…
310 + }
311 +
312 + /* start script */
313 + if (!(script = fork())) {
314 + close(0);
315 + close(1);
316 + close(fromcgi[0]);
317 + close(tocgi[1]);
318 + dup2(fromcgi[1], 1);
319 + dup2(tocgi[0], 0);
320 + execlp(name, name, (char*) NULL);
321 + }
322 +
323 + if (script < 0) {
324 + return http_send_status(fd, S_INTERNAL_SERVER_ERROR);
325 + }
326 + close(fromcgi[1]);
327 + close(tocgi[0]);
328 +
329 + /* POST method should obtain its data */
330 + if (dprintf(tocgi[1], "%s\n", r->cgicont) < 0) {
331 + return http_send_status(fd, S_INTERNAL_SERVER_ERROR);
332 + }
333 + close(tocgi[1]);
334 +
335 + /* send header as late as possible */
336 + if (dprintf(fd,
337 + "HTTP/1.1 %d %s\r\n"
338 + "Date: %s\r\n"
339 + "Connection: close\r\n",
340 + S_OK, status_str[S_OK], timestamp(time(NULL), t)) <…
341 + sta = S_REQUEST_TIMEOUT;
342 + goto cleanup;
343 + }
344 +
345 + while ((bread = read(fromcgi[0], buf, BUFSIZ)) > 0) {
346 + if (bread < 0) {
347 + return S_INTERNAL_SERVER_ERROR;
348 + }
349 +
350 + bwritten = write(fd, buf, bread);
351 +
352 + if (bwritten < 0) {
353 + return S_REQUEST_TIMEOUT;
354 + }
355 + }
356 + sta = S_OK;
357 +cleanup:
358 + if (fromcgi[0]) {
359 + close(fromcgi[0]);
360 + }
361 +
362 + return sta;
363 +}
364 +
365 enum status
366 resp_dir(int fd, char *name, struct request *r)
367 {
368 diff --git a/resp.h b/resp.h
369 index d5928ef..2705364 100644
370 --- a/resp.h
371 +++ b/resp.h
372 @@ -7,6 +7,7 @@
373
374 #include "http.h"
375
376 +enum status resp_cgi(int, char *, struct request *, struct stat *);
377 enum status resp_dir(int, char *, struct request *);
378 enum status resp_file(int, char *, struct request *, struct stat *, cha…
379 off_t, off_t);
380 diff --git a/util.h b/util.h
381 index 12b7bd8..ef1a8b3 100644
382 --- a/util.h
383 +++ b/util.h
384 @@ -23,6 +23,12 @@ struct map {
385 char *to;
386 };
387
388 +struct cgi {
389 + char *regex;
390 + char *dir;
391 + regex_t re;
392 +};
393 +
394 extern struct server {
395 char *host;
396 char *port;
397 @@ -32,6 +38,8 @@ extern struct server {
398 size_t vhost_len;
399 struct map *map;
400 size_t map_len;
401 + struct cgi *cgi;
402 + size_t cgi_len;
403 } s;
404
405 #undef MIN
406 --
407 2.21.0
408
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.