Introduction
Introduction Statistics Contact Development Disclaimer Help
ind.c - geomyidae - a small C-based gopherd (mirror)
git clone git://git.codemadness.org/geomyidae
Log
Files
Refs
README
LICENSE
---
ind.c (13717B)
---
1 /*
2 * Copy me if you can.
3 * by 20h
4 */
5
6 #ifdef __linux__
7 #define _GNU_SOURCE
8 #endif
9
10 #include <libgen.h>
11 #include <unistd.h>
12 #include <stdarg.h>
13 #include <string.h>
14 #include <memory.h>
15 #include <fcntl.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <stdint.h>
19 #include <time.h>
20 #include <netdb.h>
21 #include <sys/socket.h>
22 #include <sys/stat.h>
23 #include <netinet/in.h>
24 #include <netinet/tcp.h>
25 #include <arpa/inet.h>
26 #include <sys/ioctl.h>
27 #include <limits.h>
28 #include <errno.h>
29
30 #define PAGE_SHIFT 12
31 #define PAGE_SIZE (1UL << PAGE_SHIFT)
32 #define BLOCK_SIZE ((PAGE_SIZE * 16) - 1)
33
34 #include "arg.h"
35 #include "ind.h"
36 #include "handlr.h"
37
38 /*
39 * Be careful, to look at handlerequest(), in case you add any executing
40 * handler, so nocgi will be valuable.
41 *
42 * All files are handled as binary, without a following ".\r\n". Proper
43 * encoding lines with beginning "." would be a really slow function, not
44 * adding any feature to gopher. Clients can check for the types
45 * requested and assume ".\r\n" or leave it out.
46 *
47 * Geomyidae only adds ".\r\n" in all kind of menus, like dir listings
48 * or dcgi files. There the case of some maybe future "." item type needs
49 * to be handled, if really used.
50 */
51
52 #include "filetypes.h"
53
54 int
55 pendingbytes(int sock)
56 {
57 int pending, rval;
58
59 pending = 0;
60 rval = 0;
61 #if defined(TIOCOUTQ) && !defined(__OpenBSD__)
62 rval = ioctl(sock, TIOCOUTQ, &pending);
63 #else
64 #ifdef SIOCOUTQ
65 rval = ioctl(sock, SIOCOUTQ, &pending);
66 #endif
67 #endif
68
69 if (rval != 0)
70 return 0;
71
72 return pending;
73 }
74
75 void
76 waitforpendingbytes(int sock)
77 {
78 int npending = 0, opending = 0;
79 useconds_t trytime = 10;
80
81 /*
82 * Wait until there is nothing pending or the connection stalled
83 * (nothing was sent) for around 40 seconds. Beware, trytime is
84 * an exponential wait.
85 */
86 while ((npending = pendingbytes(sock)) > 0 && trytime < 20000000…
87 if (opending != 0) {
88 if (opending != npending) {
89 trytime = 10;
90 } else {
91 /*
92 * Exponentially increase the usleep
93 * waiting time to not waste CPU
94 * resources.
95 */
96 trytime += trytime;
97 }
98 }
99 opending = npending;
100
101 usleep(trytime);
102 }
103 }
104
105 #ifdef __linux__
106 int
107 xsplice(int fd, int sock)
108 {
109 int pipefd[2], ret = 0;
110 ssize_t nread, nwritten;
111 off_t in_offset = 0;
112
113 if (pipe(pipefd) < 0)
114 return -1;
115
116 do {
117 nread = splice(fd, &in_offset, pipefd[1], NULL,
118 BLOCK_SIZE, SPLICE_F_MOVE | SPLICE_F_MORE);
119
120 if (nread <= 0) {
121 ret = nread < 0 ? -1 : 0;
122 goto out;
123 }
124
125 nwritten = splice(pipefd[0], NULL, sock, NULL, BLOCK_SI…
126 SPLICE_F_MOVE | SPLICE_F_MORE);
127 if (nwritten < 0) {
128 ret = -1;
129 goto out;
130 }
131 } while (nwritten > 0);
132
133 out:
134 close(pipefd[0]);
135 close(pipefd[1]);
136
137 return ret;
138 }
139 #endif
140
141 int
142 xsendfile(int fd, int sock)
143 {
144 struct stat st;
145 char *sendb, *sendi;
146 size_t bufsiz = BUFSIZ;
147 int len, sent, optval;
148
149 #ifdef splice
150 return xsplice(fd, sock);
151 #endif
152
153 USED(optval);
154
155 /*
156 * The story of xsendfile.
157 *
158 * Once upon a time, here you saw a big #ifdef switch source of
159 * many ways how to send files with special functions on
160 * different operating systems. All of this was removed, because
161 * operating systems and kernels got better over time,
162 * simplifying what you need and reducing corner cases.
163 *
164 * For example Linux sendfile(2) sounds nice and faster, but
165 * the function is different on every OS and slower to the now
166 * used approach of read(2) and write(2).
167 *
168 * If you ever consider changing this to some "faster" approach,
169 * consider benchmarks on all platforms.
170 */
171
172 if (fstat(fd, &st) >= 0) {
173 if ((bufsiz = st.st_blksize) < BUFSIZ)
174 bufsiz = BUFSIZ;
175 }
176
177 sendb = xmalloc(bufsiz);
178 while ((len = read(fd, sendb, bufsiz)) > 0) {
179 sendi = sendb;
180 while (len > 0) {
181 if ((sent = write(sock, sendi, len)) < 0) {
182 free(sendb);
183 return -1;
184 }
185 len -= sent;
186 sendi += sent;
187 }
188 }
189 free(sendb);
190
191 return 0;
192 }
193
194 void *
195 xcalloc(size_t nmemb, size_t size)
196 {
197 void *p;
198
199 if (!(p = calloc(nmemb, size))) {
200 perror("calloc");
201 exit(1);
202 }
203
204 return p;
205 }
206
207 void *
208 xmalloc(size_t size)
209 {
210 void *p;
211
212 if (!(p = malloc(size))) {
213 perror("malloc");
214 exit(1);
215 }
216
217 return p;
218 }
219
220 void *
221 xrealloc(void *ptr, size_t size)
222 {
223 if (!(ptr = realloc(ptr, size))) {
224 perror("realloc");
225 exit(1);
226 }
227
228 return ptr;
229 }
230
231 char *
232 xstrdup(const char *str)
233 {
234 char *ret;
235
236 if (!(ret = strdup(str))) {
237 perror("strdup");
238 exit(1);
239 }
240
241 return ret;
242 }
243
244 filetype *
245 gettype(char *filename)
246 {
247 char *end;
248 int i;
249
250 end = strrchr(filename, '.');
251 if (end == NULL)
252 return &type[0];
253 end++;
254
255 for (i = 0; type[i].end != NULL; i++)
256 if (!strcasecmp(end, type[i].end))
257 return &type[i];
258
259 return &type[0];
260 }
261
262 void
263 gph_freeelem(gphelem *e)
264 {
265 if (e != NULL) {
266 if (e->e != NULL) {
267 for (;e->num > 0; e->num--)
268 if (e->e[e->num - 1] != NULL)
269 free(e->e[e->num - 1]);
270 free(e->e);
271 }
272 free(e);
273 }
274 return;
275 }
276
277 void
278 gph_freeindex(gphindex *i)
279 {
280 if (i != NULL) {
281 if (i->n != NULL) {
282 for (;i->num > 0; i->num--)
283 gph_freeelem(i->n[i->num - 1]);
284 free(i->n);
285 }
286 free(i);
287 }
288
289 return;
290 }
291
292 void
293 gph_addelem(gphelem *e, char *s)
294 {
295 e->num++;
296 e->e = xrealloc(e->e, sizeof(char *) * e->num);
297 e->e[e->num - 1] = xstrdup(s);
298
299 return;
300 }
301
302 gphelem *
303 gph_getadv(char *str)
304 {
305 char *b, *e, *o, *bo;
306 gphelem *ret;
307
308 ret = xcalloc(1, sizeof(gphelem));
309
310 if (strchr(str, '\t')) {
311 gph_addelem(ret, "i");
312 gph_addelem(ret, "Happy helping ☃ here: You tried to "
313 "output a spurious TAB character. This will "
314 "break gopher. Please review your scripts. "
315 "Have a nice day!");
316 gph_addelem(ret, "Err");
317 gph_addelem(ret, "server");
318 gph_addelem(ret, "port");
319
320 return ret;
321 }
322
323 /* Check for escape sequence. */
324 if (str[0] == '[' && str[1] != '|') {
325 o = xstrdup(str);
326 b = o + 1;
327 bo = b;
328 while ((e = strchr(bo, '|')) != NULL) {
329 if (e != bo && e[-1] == '\\') {
330 memmove(&e[-1], e, strlen(e));
331 bo = e;
332 continue;
333 }
334 *e = '\0';
335 e++;
336 gph_addelem(ret, b);
337 b = e;
338 bo = b;
339 }
340
341 e = strchr(b, ']');
342 if (e != NULL) {
343 *e = '\0';
344 gph_addelem(ret, b);
345 }
346 free(o);
347
348 if (ret->e != NULL && ret->e[0] != NULL &&
349 ret->e[0][0] != '\0' && ret->num == 5) {
350 return ret;
351 }
352
353 /* Invalid entry: Give back the whole line. */
354 gph_freeelem(ret);
355 ret = xcalloc(1, sizeof(gphelem));
356 }
357
358 gph_addelem(ret, "i");
359 /* Jump over escape sequence. */
360 if (str[0] == '[' && str[1] == '|')
361 str += 2;
362 gph_addelem(ret, str);
363 gph_addelem(ret, "Err");
364 gph_addelem(ret, "server");
365 gph_addelem(ret, "port");
366
367 return ret;
368 }
369
370 void
371 gph_addindex(gphindex *idx, gphelem *el)
372 {
373 idx->num++;
374 idx->n = xrealloc(idx->n, sizeof(gphelem *) * idx->num);
375 idx->n[idx->num - 1] = el;
376
377 return;
378 }
379
380 gphindex *
381 gph_scanfile(char *fname)
382 {
383 char *ln = NULL;
384 size_t linesiz = 0;
385 ssize_t n;
386 FILE *fp;
387 gphindex *ret;
388 gphelem *el;
389
390 if (!(fp = fopen(fname, "r")))
391 return NULL;
392
393 ret = xcalloc(1, sizeof(gphindex));
394
395 while ((n = getline(&ln, &linesiz, fp)) > 0) {
396 if (ln[n - 1] == '\n')
397 ln[--n] = '\0';
398 el = gph_getadv(ln);
399 if (el == NULL)
400 continue;
401
402 gph_addindex(ret, el);
403 }
404 if (ferror(fp))
405 perror("getline");
406 free(ln);
407 fclose(fp);
408
409 if (ret->n == NULL) {
410 free(ret);
411 return NULL;
412 }
413
414 return ret;
415 }
416
417 int
418 gph_printelem(int fd, gphelem *el, char *file, char *base, char *addr, c…
419 {
420 char *path, *p, *argbase, buf[PATH_MAX+1], *argp, *realbase, *rp…
421 int len, blen;
422
423 if (!strcmp(el->e[3], "server")) {
424 free(el->e[3]);
425 el->e[3] = xstrdup(addr);
426 }
427 if (!strcmp(el->e[4], "port")) {
428 free(el->e[4]);
429 el->e[4] = xstrdup(port);
430 }
431
432 /*
433 * Ignore if the path is from base, if it might be some h type w…
434 * some URL and ignore various types that have different semanti…
435 * do not point to some file or directory.
436 */
437 if ((el->e[2][0] != '\0'
438 && el->e[2][0] != '/' /* Absolute Request. */
439 && el->e[0][0] != 'i' /* Informational item. */
440 && el->e[0][0] != '2' /* CSO server */
441 && el->e[0][0] != '3' /* Error */
442 && el->e[0][0] != '8' /* Telnet */
443 && el->e[0][0] != 'w' /* Selector is direct URI. */
444 && el->e[0][0] != 'T') && /* tn3270 */
445 !(el->e[0][0] == 'h' && !strncmp(el->e[2], "URL:", 4))) {
446 path = file + strlen(base);
447
448 /* Strip off original gph file name. */
449 if ((p = strrchr(path, '/'))) {
450 len = strlen(path) - strlen(basename(path));
451 } else {
452 len = strlen(path);
453 }
454
455 /* Strip off arguments for realpath. */
456 argbase = strchr(el->e[2], '?');
457 if (argbase != NULL) {
458 blen = argbase - el->e[2];
459 } else {
460 blen = strlen(el->e[2]);
461 }
462
463 /*
464 * Print everything together. Realpath will resolve it.
465 */
466 snprintf(buf, sizeof(buf), "%s%.*s%.*s", base, len,
467 path, blen, el->e[2]);
468
469 if ((rpath = realpath(buf, NULL)) &&
470 (realbase = realpath(*base? base : "/", …
471 !strncmp(realbase, rpath, strlen(realbas…
472 p = rpath + (*base? strlen(realbase) : 0);
473
474 /*
475 * Do not forget to re-add arguments which were
476 * stripped off.
477 */
478 argp = smprintf("%s%s", *p? p : "/", argbase? ar…
479
480 free(el->e[2]);
481 el->e[2] = argp;
482 free(realbase);
483 }
484 if (rpath != NULL)
485 free(rpath);
486 }
487
488 if (dprintf(fd, "%.1s%s\t%s\t%s\t%s\r\n", el->e[0], el->e[1], el…
489 el->e[3], el->e[4]) < 0) {
490 perror("printgphelem: dprintf");
491 return -1;
492 }
493 return 0;
494 }
495
496 char *
497 smprintf(char *fmt, ...)
498 {
499 va_list fmtargs;
500 char *ret;
501 int size;
502
503 va_start(fmtargs, fmt);
504 size = vsnprintf(NULL, 0, fmt, fmtargs);
505 va_end(fmtargs);
506
507 ret = xcalloc(1, ++size);
508 va_start(fmtargs, fmt);
509 vsnprintf(ret, size, fmt, fmtargs);
510 va_end(fmtargs);
511
512 return ret;
513 }
514
515 char *
516 reverselookup(char *host)
517 {
518 struct in_addr hoststr;
519 struct hostent *client;
520 char *rethost;
521
522 rethost = NULL;
523
524 if (inet_pton(AF_INET, host, &hoststr)) {
525 client = gethostbyaddr((const void *)&hoststr,
526 sizeof(hoststr), AF_INET);
527 if (client != NULL)
528 rethost = xstrdup(client->h_name);
529 }
530
531 if (rethost == NULL)
532 rethost = xstrdup(host);
533
534 return rethost;
535 }
536
537 void
538 setcgienviron(char *file, char *path, char *port, char *base, char *args,
539 char *sear, char *ohost, char *chost, char *bhost, int i…
540 char *sel, char *traverse)
541 {
542 /*
543 * <@__20h__> satan, why did you add so much uncertainties
544 * into RFC3875?
545 * <annna> Foolish mortal, I added uncertainties to RFC3875 to
546 * ensure that humans would forever be plagued by the complexi…
547 * of web application management.
548 */
549
550 /* RFC3875 4.1.1 */
551 setenv("AUTH_TYPE", "", 1);
552 /* RFC3875 4.1.2 */
553 unsetenv("CONTENT_LENGTH");
554 /* RFC3875 4.1.3 */
555 unsetenv("CONTENT_TYPE");
556 /* RFC3875 4.1.4 */
557 setenv("GATEWAY_INTERFACE", "CGI/1.1", 1);
558 /* RFC3875 4.1.5 */
559 setenv("PATH_INFO", traverse, 1);
560 /* RFC3875 4.1.6 */
561 setenv("PATH_TRANSLATED", path, 1);
562 /* RFC3875 4.1.7 */
563 setenv("QUERY_STRING", args, 1);
564 /* RFC3875 4.1.8 */
565 setenv("REMOTE_ADDR", chost, 1);
566 /* RFC3875 4.1.9 */
567 setenv("REMOTE_HOST", chost, 1);
568 /* RFC3875 4.1.10 */
569 unsetenv("REMOTE_IDENT");
570 /* RFC3875 4.1.11 */
571 unsetenv("REMOTE_USER");
572 /* RFC3875 4.1.12 */
573 setenv("REQUEST_METHOD", "GET", 1);
574 /* RFC3875 4.1.13 */
575 setenv("SCRIPT_NAME", path+strlen(base), 1);
576 /* RFC3875 4.1.14 */
577 setenv("SERVER_NAME", ohost, 1);
578 /* RFC3875 4.1.15 */
579 setenv("SERVER_PORT", port, 1);
580 /* RFC3875 4.1.16 */
581 setenv("SERVER_PROTOCOL", "gopher/1.0", 1);
582 /* RFC3875 4.1.17 */
583 setenv("SERVER_SOFTWARE", "geomyidae", 1);
584
585 /* GOPHER-specific variables */
586 /* This is allowed by RFC3875 */
587 setenv("GOPHER_SELECTOR", sel, 1);
588 setenv("GOPHER_REQUEST", sel, 1);
589 setenv("GOPHER_SEARCH", sear, 1);
590 setenv("GOPHER_SCRIPT_FILENAME", path, 1);
591 setenv("GOPHER_DOCUMENT_ROOT", base, 1);
592
593 /* legacy compatibility */
594 /* Gopher stuff should begin with GOPHER_ by RFC3875 */
595 /* See: https://boston.conman.org/2020/01/06.1 */
596 setenv("SCRIPT_FILENAME", path, 1);
597 /* Bucktooth compatibility */
598 setenv("SELECTOR", sel, 1);
599 /* Motsognir compatibility */
600 setenv("QUERY_STRING_SEARCH", sear, 1);
601 setenv("QUERY_STRING_URL", sear, 1);
602 /* Gophernicus */
603 setenv("SEARCHREQUEST", sear, 1);
604 /*
605 * TODO Do we need those? Does anyone use those?
606 * COLUMNS, DOCUMENT_ROOT, GOPHER_CHARSET, GOPHER_FILETYPE,
607 * GOPHER_REFERER, HTTP_ACCEPT_CHARSET, HTTP_REFERER, LOCAL_ADDR,
608 * PATH, REQUEST, SERVER_ARCH, SERVER_CODENAME,
609 * SERVER_DESCRIPTION, SERVER_TLS_PORT, SERVER_VERSION,
610 * SESSION_ID, TLS
611 */
612
613 /* Make PHP happy. */
614 setenv("REDIRECT_STATUS", "", 1);
615 setenv("SERVER_LISTEN_NAME", bhost, 1);
616 if (istls) {
617 setenv("GOPHERS", "on", 1);
618 setenv("HTTPS", "on", 1);
619 } else {
620 unsetenv("GOPHERS");
621 unsetenv("HTTPS");
622 }
623 }
624
625 char *
626 humansize(off_t n)
627 {
628 static char buf[16];
629 const char postfixes[] = "BKMGTPE";
630 double size;
631 int i = 0;
632
633 for (size = n; size >= 1024 && i < strlen(postfixes); i++)
634 size /= 1024;
635
636 if (!i) {
637 snprintf(buf, sizeof(buf), "%ju%c", (uintmax_t)n,
638 postfixes[i]);
639 } else {
640 snprintf(buf, sizeof(buf), "%.1f%c", size, postfixes[i]);
641 }
642
643 return buf;
644 }
645
646 char *
647 humantime(const time_t *clock)
648 {
649 static char buf[32];
650 struct tm *tm;
651
652 tm = localtime(clock);
653 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M %Z", tm);
654
655 return buf;
656 }
657
658 void
659 lingersock(int sock)
660 {
661 struct linger lingerie;
662 int j;
663
664 /*
665 * On close only wait for at maximum 60 seconds for all data to …
666 * transmitted before forcefully closing the connection.
667 */
668 lingerie.l_onoff = 1;
669 lingerie.l_linger = 60;
670 setsockopt(sock, SOL_SOCKET, SO_LINGER,
671 &lingerie, sizeof(lingerie));
672
673 /*
674 * Force explicit flush of buffers using TCP_NODELAY.
675 */
676 j = 1;
677 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &j, sizeof(int));
678 waitforpendingbytes(sock);
679 j = 0;
680 setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &j, sizeof(int));
681
682 return;
683 }
684
You are viewing proxied material from codemadness.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.