• R/O
  • SSH
  • HTTPS

ogup: 提交


Commit MetaInfo

修訂3 (tree)
時間2020-12-24 18:47:45
作者mateuszviste

Log Message

imported ver 20190220 to tags

Change Summary

差異

--- tags/ogup-20190220/cmods/csv.c (nonexistent)
+++ tags/ogup-20190220/cmods/csv.c (revision 3)
@@ -0,0 +1,43 @@
1+/*
2+ * CSV-reading C module
3+ *
4+ * Copyright (C) 2019 Mateusz Viste
5+ *
6+ */
7+
8+/* reads a csv line from file. returns 0 on success, -1 on EOF or error. */
9+static int csvreadline(char *buf, unsigned short maxlinelen, char **ptrs, int maxptrs, FILE *fd) {
10+ int c;
11+ int i;
12+ int t;
13+ int blen = 0;
14+ /* read the line into buf */
15+ for (;;) {
16+ c = fgetc(fd);
17+ if ((c < 0) && (blen == 0)) return(-1);
18+ if ((c < 0) || (c == '\n')) break;
19+ if (c == '\r') continue;
20+ buf[blen++] = c;
21+ if (blen == maxlinelen) return(-1);
22+ }
23+ buf[blen] = 0;
24+
25+ /* set up pointers and terminate fields */
26+ for (i = 0; i < maxptrs; i++) ptrs[i] = buf + blen; /* preinit to empty */
27+ ptrs[0] = buf;
28+ t = 1;
29+ for (i = 0;; i++) {
30+ switch (buf[i]) {
31+ case ',':
32+ buf[i] = 0;
33+ ptrs[t] = buf + i + 1;
34+ if ((t + 1) < maxptrs) t++;
35+ break;
36+ case '\r':
37+ case '\n':
38+ case 0:
39+ buf[i] = 0;
40+ return(0);
41+ }
42+ }
43+}
--- tags/ogup-20190220/frontend/down/gophermap.c (nonexistent)
+++ tags/ogup-20190220/frontend/down/gophermap.c (revision 3)
@@ -0,0 +1,90 @@
1+/*
2+ * OGUP "recently down" servers list gophermap
3+ *
4+ * Copyright (C) 2019 Mateusz Viste
5+ */
6+
7+
8+#include <stdio.h>
9+#include <stdlib.h>
10+#include <sys/stat.h>
11+#include <time.h>
12+#include <unistd.h>
13+
14+
15+#include "../../cmods/csv.c"
16+
17+
18+static char *howold(long s) {
19+ static char buf[64];
20+ if (s < 120) {
21+ sprintf(buf, "%ld seconds", s);
22+ } else if (s < 3600) {
23+ sprintf(buf, "%ld minutes", s / 60);
24+ } else if (s < 7200) {
25+ sprintf(buf, "%ld h and %ld minutes", s / 3600, (s % 3600) / 60);
26+ } else if (s < 172800) {
27+ sprintf(buf, "%ld hours", s / 3600);
28+ } else {
29+ sprintf(buf, "%ld days", s / 86400);
30+ }
31+ return(buf);
32+}
33+
34+
35+static char *printtime(struct tm *t) {
36+ static char buf[64];
37+ sprintf(buf, "%d-%02d-%02d %02d:%02d", t->tm_year + 1900, t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min);
38+ return(buf);
39+}
40+
41+
42+int main(void) {
43+ time_t now, then;
44+ struct tm *t;
45+ char csvbuff[128];
46+ char *ptrs[4];
47+ char serv[32];
48+ long downcount = 0;
49+ FILE *f;
50+
51+ printf("i\n"
52+ "iThis page lists gopherspace nodes that have been observed by the OGUP to\n"
53+ "icollapse recently. All timestamps are given in terran UTC time.\n"
54+ "i\n");
55+
56+ f = fopen("../ogupdb.dat", "rb");
57+ if (f == NULL) {
58+ printf("3ERROR: fopen() failure\n");
59+ return(0);
60+ }
61+
62+ now = time(NULL);
63+ for (;;) {
64+ char *howlongago;
65+ int i = csvreadline(csvbuff, sizeof(csvbuff), ptrs, 4, f);
66+ if (i != 0) break;
67+ then = atol(ptrs[2]);
68+ if (then <= 1) continue;
69+ howlongago = howold(now - then);
70+ t = gmtime(&then);
71+ if (atol(ptrs[1]) != 70) {
72+ snprintf(serv, sizeof(serv), "%s:%s", ptrs[0], ptrs[1]);
73+ } else {
74+ snprintf(serv, sizeof(serv), "%s", ptrs[0]);
75+ }
76+ printf("i%-24s went down %s ago (%s)\n", ptrs[0], howlongago, printtime(t));
77+ downcount++;
78+ }
79+
80+ fclose(f);
81+
82+ if (downcount == 0) {
83+ printf("iNothing to list! All known gopherspace servers seem healthy.\n");
84+ }
85+
86+ printf("i\n"
87+ "i--- [EOF] --------------------------------------------------------------\n");
88+
89+ return(0);
90+}
--- tags/ogup-20190220/frontend/list/gophermap.c (nonexistent)
+++ tags/ogup-20190220/frontend/list/gophermap.c (revision 3)
@@ -0,0 +1,100 @@
1+/*
2+ * OGUP servers list gophermap
3+ *
4+ * Copyright (C) 2019 Mateusz Viste
5+ */
6+
7+
8+#include <stdio.h>
9+#include <stdlib.h>
10+#include <sys/stat.h>
11+#include <time.h>
12+#include <unistd.h>
13+
14+
15+#include "../../cmods/csv.c"
16+
17+
18+unsigned long getcount(const char *fname, time_t *modtime, char *errstr, unsigned long *countactive, unsigned long *countpending, unsigned long *countdown) {
19+ FILE *f;
20+ unsigned long count;
21+ char buff[64];
22+ char *ptrs[4];
23+ struct stat sb;
24+
25+ f = fopen(fname, "rb");
26+ if (f == NULL) {
27+ sprintf(errstr, "failed to open db file");
28+ return(0);
29+ }
30+ if (fstat(fileno(f), &sb) != 0) {
31+ fclose(f);
32+ sprintf(errstr, "fstat() failed on db count file");
33+ return(0);
34+ }
35+ *modtime = sb.st_mtime;
36+ csvreadline(buff, sizeof(buff), ptrs, 4, f);
37+ fclose(f);
38+ count = atol(ptrs[0]);
39+ *countactive = atol(ptrs[1]);
40+ *countpending = atol(ptrs[2]);
41+ *countdown = atol(ptrs[3]);
42+
43+ if (count == 0) sprintf(errstr, "empty db");
44+
45+ return(count);
46+}
47+
48+
49+int main(void) {
50+ unsigned long count, countactive, countpending, countdown;
51+ time_t lastupdate;
52+ struct tm *lastupdatetm;
53+ char errstr[64];
54+ char csvbuff[128];
55+ char *ptrs[4];
56+ FILE *f;
57+ int i;
58+
59+ printf("i\n");
60+
61+ count = getcount("../ogupdb.cnt", &lastupdate, errstr, &countactive, &countpending, &countdown);
62+ lastupdatetm = gmtime(&lastupdate);
63+
64+ if (count == 0) {
65+ printf("3ERROR: %s\n", errstr);
66+ } else {
67+ printf("iThe Observable Gopherspace Universe Project knows about:\n"
68+ "i%5lu servers total, of which:\n"
69+ "i%5lu have been validated as operational\n"
70+ "i%5lu are down\n"
71+ "i%5lu are pending validation\n"
72+ "i\n"
73+ "iLast update: %04d-%02d-%02d %02d:%02d UTC\n", count, countactive, countdown, countpending, lastupdatetm->tm_year + 1900, lastupdatetm->tm_mon + 1, lastupdatetm->tm_mday, lastupdatetm->tm_hour, lastupdatetm->tm_min);
74+ }
75+
76+ printf("i\n");
77+
78+ f = fopen("../ogupdb.dat", "rb");
79+ if (f == NULL) {
80+ printf("3ERROR: fopen() failure\n");
81+ return(0);
82+ }
83+
84+ for (;;) {
85+ i = csvreadline(csvbuff, sizeof(csvbuff), ptrs, 4, f);
86+ if (i != 0) break;
87+ if (atol(ptrs[2]) != 0) continue; /* ignore hosts that were offline last time I checked */
88+ if (atol(ptrs[1]) != 70) {
89+ printf("1%s:%s\t\t%s\t%s\n", ptrs[0], ptrs[1], ptrs[0], ptrs[1]);
90+ } else {
91+ printf("1%s\t\t%s\t%s\n", ptrs[0], ptrs[0], ptrs[1]);
92+ }
93+ }
94+
95+ fclose(f);
96+
97+ printf("i\ni--- [EOF] -----------------------------\n");
98+
99+ return(0);
100+}
--- tags/ogup-20190220/frontend/rnd/gophermap.c (nonexistent)
+++ tags/ogup-20190220/frontend/rnd/gophermap.c (revision 3)
@@ -0,0 +1,92 @@
1+/*
2+ * OGUP servers list gophermap
3+ *
4+ * Copyright (C) 2019 Mateusz Viste
5+ */
6+
7+
8+#include <stdio.h>
9+#include <stdlib.h>
10+#include <time.h>
11+#include <unistd.h>
12+
13+
14+#include "../../cmods/csv.c"
15+
16+
17+#define DEBUG 0
18+
19+
20+/* rewinds file fd to nearest start of line */
21+static void rewindtosol(FILE *fd) {
22+ int c;
23+ int iterations = 0;
24+ for (;;) {
25+ iterations++;
26+ if (ftell(fd) == 0) break;
27+ c = fgetc(fd);
28+ if (c == '\n') break;
29+ fseek(fd, -2, SEEK_CUR);
30+ if (iterations > 400) break; /* you never know */
31+ }
32+}
33+
34+
35+int main(void) {
36+ char csvbuff[128];
37+ char *ptrs[4];
38+ FILE *f;
39+ int i, count, csvres;
40+ long fsize, randpos;
41+
42+ printf("i\n");
43+
44+ f = fopen("../ogupdb.dat", "rb");
45+ if (f == NULL) {
46+ printf("3ERROR: fopen() failure\n");
47+ return(0);
48+ }
49+ fseek(f, 0, SEEK_END);
50+ fsize = ftell(f);
51+
52+ if (fsize < 16) {
53+ printf("3ERROR: fsize too low (%ld)\n", fsize);
54+ fclose(f);
55+ return(0);
56+ }
57+
58+ srand(time(NULL));
59+
60+ count = 0;
61+ for (i = 0; i < 50; i++) {
62+
63+ randpos = rand() % (fsize - 2);
64+ fseek(f, randpos, SEEK_SET);
65+
66+ /* rewind back to nearest \n or start of file */
67+ rewindtosol(f);
68+
69+ /* read entry */
70+ csvres = csvreadline(csvbuff, sizeof(csvbuff), ptrs, 4, f);
71+ if (csvres != 0) {
72+ if (DEBUG) printf("icsvreadline() != 0\n");
73+ continue;
74+ }
75+ /* ignore hosts that were offline last time I checked */
76+ if (atol(ptrs[2]) != 0) {
77+ if (DEBUG) printf("iatol(ptrs[2]) != 0 (%ld, '%s')\n", atol(ptrs[2]), ptrs[0]);
78+ continue;
79+ }
80+ if (atol(ptrs[1]) != 70) {
81+ printf("1%s:%s\t\t%s\t%s\n", ptrs[0], ptrs[1], ptrs[0], ptrs[1]);
82+ } else {
83+ printf("1%s\t\t%s\t%s\n", ptrs[0], ptrs[0], ptrs[1]);
84+ }
85+ count++;
86+ if (count == 5) break;
87+ }
88+
89+ fclose(f);
90+
91+ return(0);
92+}
--- tags/ogup-20190220/frontend/gophermap.c (nonexistent)
+++ tags/ogup-20190220/frontend/gophermap.c (revision 3)
@@ -0,0 +1,77 @@
1+/*
2+ * OGUP main gophermap
3+ *
4+ * Copyright (C) 2019 Mateusz Viste
5+ */
6+
7+
8+#include <stdio.h>
9+#include <stdlib.h>
10+#include <sys/stat.h>
11+#include <time.h>
12+#include <unistd.h>
13+
14+
15+unsigned long getcount(const char *fname, time_t *modtime, char *errstr) {
16+ FILE *f;
17+ unsigned long count;
18+ char buff[32];
19+ int i;
20+ struct stat sb;
21+
22+ f = fopen(fname, "rb");
23+ if (f == NULL) {
24+ sprintf(errstr, "failed to open db file");
25+ return(0);
26+ }
27+ if (fstat(fileno(f), &sb) != 0) {
28+ sprintf(errstr, "fstat() failed on db count file");
29+ return(0);
30+ }
31+ *modtime = sb.st_mtime;
32+ i = fread(buff, 1, sizeof(buff) - 1, f);
33+ fclose(f);
34+ if (i < 1) {
35+ sprintf(errstr, "empty count file");
36+ return(0);
37+ }
38+ buff[i] = 0;
39+ count = atol(buff);
40+
41+ if (count == 0) sprintf(errstr, "empty db");
42+
43+ return(count);
44+}
45+
46+
47+int main(void) {
48+ unsigned long count;
49+ time_t lastupdate;
50+ struct tm *lastupdatetm;
51+ char errstr[64];
52+
53+ printf("i\n"
54+ "i Observable Gopherspace Universe Project\n"
55+ "i\n"
56+ "iWelcome to the gopher site of the Observable Gopherspace Universe\n"
57+ "iProject (OGUP)! The OGUP is about maintaining and publishing an\n"
58+ "iindependent list of servers available in the gopherspace.\n"
59+ "i\n"
60+ "1List of servers\tlist\n"
61+ "1Show 5 random servers\trnd\n"
62+ "1Recently down servers\tdown\n"
63+ "1About the OGUP\tabout\n"
64+ "1Support the project!\tsupport\n"
65+ "i\n");
66+
67+ count = getcount("ogupdb.cnt", &lastupdate, errstr);
68+ lastupdatetm = gmtime(&lastupdate);
69+
70+ if (count == 0) {
71+ printf("3ERROR: %s\n", errstr);
72+ } else {
73+ printf("iAs of %04d-%02d-%02d, the OGUP knows about %lu servers.\n", lastupdatetm->tm_year + 1900, lastupdatetm->tm_mon + 1, lastupdatetm->tm_mday, count);
74+ }
75+
76+ return(0);
77+}
--- tags/ogup-20190220/gopherjoker/Makefile (nonexistent)
+++ tags/ogup-20190220/gopherjoker/Makefile (revision 3)
@@ -0,0 +1,16 @@
1+#
2+# build gopherjoker
3+#
4+# Observable Gopherspace Universe Project
5+# Copyright (C) 2019 Mateusz Viste
6+#
7+
8+CC ?= gcc
9+CFLAGS = -g -O0 -Wall -Wextra -pedantic -std=gnu89
10+
11+all: gopherjoker
12+
13+gopherjoker: gopherjoker.c
14+
15+clean:
16+ rm -f gopherjoker
--- tags/ogup-20190220/gopherjoker/gopherjoker.c (nonexistent)
+++ tags/ogup-20190220/gopherjoker/gopherjoker.c (revision 3)
@@ -0,0 +1,565 @@
1+/*
2+ * gopherjoker, part of the Observable Gopherspace Universe Project
3+ * Copyright (C) 2019 Mateusz Viste
4+ *
5+ * 2019-02-20: fixed null ptr dereference, added a 'down' count to countfile
6+ * 2019-02-19: countfile contains 3 values: total, active and pending servers
7+ * 2019-02-18: first public release
8+ *
9+ * Redistribution and use in source and binary forms, with or without
10+ * modification, are permitted provided that the following conditions are met:
11+ * 1. Redistributions of source code must retain the above copyright notice,
12+ * this list of conditions and the following disclaimer.
13+ *
14+ * 2. Redistributions in binary form must reproduce the above copyright
15+ * notice, this list of conditions and the following disclaimer in the
16+ * documentation and/or other materials provided with the distribution.
17+ *
18+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28+ * POSSIBILITY OF SUCH DAMAGE.
29+ */
30+
31+#include <errno.h>
32+#include <fcntl.h>
33+#include <netdb.h> /* gethostbyname() */
34+#include <stdio.h> /* printf(), fopen(), ... */
35+#include <stdlib.h> /* calloc(), rand() */
36+#include <string.h> /* strcpy() */
37+#include <sys/types.h>
38+#include <sys/socket.h>
39+#include <time.h> /* ctime(), time_t */
40+#include <unistd.h> /* sleep() */
41+
42+#include "../cmods/csv.c"
43+
44+struct gopherlist {
45+ time_t failedsince;
46+ struct gopherlist *next;
47+ struct gopherlist *prev;
48+ unsigned short port;
49+ char *selector;
50+ char fqdn[1];
51+};
52+
53+#define WAITPERIOD 10
54+#define SAVEPERIOD 3600
55+#define NEWHOSTSLIST "/tmp/gjoker_newhosts.csv"
56+#define MAXFAILTIME (3600 * 24 * 60)
57+#define TTLINIT 64
58+
59+
60+/**************** FUNCTIONS ****************/
61+
62+
63+static void glist_free(struct gopherlist *glist) {
64+ struct gopherlist *victim;
65+ while (glist) {
66+ free(glist->selector);
67+ victim = glist;
68+ glist = glist->next;
69+ free(victim);
70+ }
71+}
72+
73+
74+/* returns pointer to dbfile name */
75+static int parseargs(int argc, char **argv, char **dbfile, char **dbcount) {
76+ int i;
77+ *dbfile = NULL;
78+ *dbcount = NULL;
79+ for (i = 1; i < argc; i++) {
80+ if ((argv[i][0] != '-') && ((*dbfile == NULL) || (*dbcount == NULL))) {
81+ if (*dbfile == NULL) {
82+ *dbfile = argv[i];
83+ } else {
84+ *dbcount = argv[i];
85+ }
86+ } else {
87+ return(-1);
88+ }
89+ }
90+ return(0);
91+}
92+
93+
94+static struct gopherlist *glist_findhost(struct gopherlist *glist, const char *host, const unsigned short port) {
95+ struct gopherlist *node;
96+ for (node = glist; node != NULL; node = node->next) {
97+ if ((node->port == port) && (strcasecmp(node->fqdn, host) == 0)) return(node);
98+ }
99+ return(NULL);
100+}
101+
102+
103+/* add new host to glist, unless said host:port pair already exists there.
104+ * returns pointer to the new (or already existing) struct. NULL on error. */
105+static struct gopherlist *glist_addnewhost(struct gopherlist **glist, unsigned long *glistlen, char *newhost, unsigned short newport, time_t failedsince) {
106+ struct gopherlist *node;
107+
108+ /* is entry in list already? */
109+ node = glist_findhost(*glist, newhost, newport);
110+ if (node != NULL) return(node);
111+
112+ /* add it */
113+ node = calloc(1, sizeof(struct gopherlist) + strlen(newhost));
114+ if (node == NULL) return(NULL);
115+ /* */
116+ strcpy(node->fqdn, newhost);
117+ node->port = newport;
118+ node->failedsince = failedsince;
119+ node->prev = NULL;
120+ node->next = *glist;
121+ if (*glist != NULL) (*glist)->prev = node;
122+ *glist = node;
123+ *glistlen += 1;
124+ return(*glist);
125+}
126+
127+
128+static struct gopherlist *loaddb(const char *fname, unsigned long *glistlen) {
129+ FILE *fd;
130+ int i;
131+ char *ptrs[4];
132+ char lbuf[256];
133+ struct gopherlist *res = NULL;
134+ *glistlen = 0;
135+ fd = fopen(fname, "rb");
136+ if (fd == NULL) return(NULL);
137+ for (;;) {
138+ i = csvreadline(lbuf, sizeof(lbuf), ptrs, 4, fd);
139+ if (i != 0) break;
140+ /* TSV line structure
141+ * 0 hostname
142+ * 1 port
143+ * 2 failedsince (time_t) */
144+
145+ if (glist_addnewhost(&res, glistlen, ptrs[0], atoi(ptrs[1]), atol(ptrs[2])) == NULL) {
146+ /* on error, free list and quit */
147+ glist_free(res);
148+ *glistlen = 0;
149+ return(NULL);
150+ }
151+ }
152+ fclose(fd);
153+ return(res);
154+}
155+
156+
157+static unsigned long savedb(const char *fname, const char *fcount, struct gopherlist *glist) {
158+ unsigned long count = 0, countactive = 0, countpending = 0, countdown = 0;
159+ FILE *f;
160+ struct gopherlist *node;
161+ f = fopen(fname, "wb");
162+ if (f == NULL) return(0);
163+ for (node = glist; node != NULL; node = node->next) {
164+ count++;
165+ if (node->failedsince == 0) countactive++;
166+ if (node->failedsince == 1) countpending++;
167+ if (node->failedsince > 1) countdown++;
168+ fprintf(f, "%s,%u,%ld\n", node->fqdn, node->port, node->failedsince);
169+ }
170+ fclose(f);
171+ f = fopen(fcount, "wb");
172+ if (f == NULL) return(0);
173+ fprintf(f, "%lu,%lu,%lu,%lu\n", count, countactive, countpending, countdown);
174+ fclose(f);
175+ return(count);
176+}
177+
178+
179+static struct gopherlist *dropnode(struct gopherlist *glist, struct gopherlist *node, unsigned long *glistlen) {
180+ if (node->prev == NULL) {
181+ glist = node->next;
182+ glist->prev = NULL;
183+ *glistlen -= 1;
184+ return(glist);
185+ }
186+ node->prev->next = node->next;
187+ if (node->next != NULL) {
188+ node->next->prev = node->prev;
189+ }
190+ *glistlen -= 1;
191+ return(glist);
192+}
193+
194+
195+static struct gopherlist *loadextrahosts(struct gopherlist *glist, const char *fname, unsigned long *glistlen) {
196+ FILE *f;
197+ char *ptrs[2];
198+ char buff[64];
199+ f = fopen(fname, "rb");
200+ if (f == NULL) return(glist);
201+
202+ while (csvreadline(buff, sizeof(buff), ptrs, 2, f) == 0) {
203+ int port = atoi(ptrs[1]);
204+ if ((port < 1) || (port > 0xffff)) port = 70;
205+ if (glist_addnewhost(&glist, glistlen, ptrs[0], port, 0) == NULL) break;
206+ }
207+ fclose(f);
208+
209+ return(glist);
210+}
211+
212+
213+static struct gopherlist *pickrandhostfromlist(struct gopherlist *glist, unsigned long glistlen) {
214+ static long choice;
215+ long i;
216+ if (glistlen == 0) return(NULL);
217+ choice += rand();
218+ choice %= glistlen;
219+ for (i = 0; i < choice; i++) {
220+ if (glist == NULL) return(NULL);
221+ glist = glist->next;
222+ }
223+ return(glist);
224+}
225+
226+
227+static int connect_nonblocking(int s, const struct sockaddr *addr, socklen_t addrlen, int timeout) {
228+ int r;
229+ struct timeval t;
230+ fd_set selset;
231+ for (;;) {
232+ r = connect(s, addr, addrlen);
233+ if (r == 0) return(0);
234+ if (errno != EINPROGRESS) return(r);
235+ FD_ZERO(&selset);
236+ FD_SET(s, &selset);
237+ t.tv_sec = timeout;
238+ t.tv_usec = 0;
239+ r = select(s + 1, NULL, &selset, NULL, &t);
240+ if (r < 0) return(-3);
241+ r = connect(s, addr, addrlen);
242+ return(r);
243+ }
244+}
245+
246+
247+static long gopher_fetch(char *buff, unsigned short buffsz, const char *host, unsigned short port, const char *selector) {
248+ int sock;
249+ int len;
250+ int flags;
251+ time_t timeout;
252+ struct addrinfo *addr, *addrptr;
253+
254+ /* resolve host & connect */
255+ if (getaddrinfo(host, NULL, NULL, &addr) != 0) return(-1);
256+ for (addrptr = addr; addrptr != NULL; addrptr = addrptr->ai_next) {
257+ struct sockaddr_in *sin;
258+ sock = socket(addrptr->ai_family, addrptr->ai_socktype, addrptr->ai_protocol);
259+ if (sock < 0) continue;
260+ /* set port */
261+ sin = (void *)(addrptr->ai_addr);
262+ sin->sin_port = htons(port);
263+ /* set socket as non-blocking */
264+ flags = fcntl(sock, F_GETFL);
265+ fcntl(sock, F_SETFL, flags | O_NONBLOCK);
266+ /* try to connect */
267+ if (connect_nonblocking(sock, addrptr->ai_addr, addrptr->ai_addrlen, 10) == 0) {
268+ break;
269+ } else { /* close sock and try next option (if any) */
270+ close(sock);
271+ }
272+ }
273+ freeaddrinfo(addr);
274+ if (addrptr == NULL) return(-2);
275+
276+ /* send selector, terminated by a CR/LF pair */
277+ if (selector != NULL) send(sock, selector, strlen(selector), MSG_MORE);
278+ send(sock, "\r\n", 2, 0);
279+
280+ /* fetch answer until end of transmission or timeout */
281+ len = 0;
282+ timeout = time(NULL) + 10;
283+ for (;;) {
284+ unsigned short spaceleft = buffsz - len;
285+ int rlen;
286+ if (time(NULL) > timeout) {
287+ len = -3;
288+ break;
289+ }
290+ if (spaceleft == 0) break; /* I'm stuffed thank you */
291+ rlen = recv(sock, buff + len, spaceleft, 0);
292+ if (rlen == 0) break; /* orderly shutdown */
293+ if (rlen < 0) { /* sock error */
294+ if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) continue;
295+ len = -3;
296+ break;
297+ }
298+ len += rlen;
299+ }
300+
301+ /* close sock and quit */
302+ close(sock);
303+ return(len);
304+}
305+
306+
307+/* read a gopher line entry and fill host, port and selector accordingly.
308+ * returns 0 on success, non-zero otherwise. */
309+static int readgline(char *host, unsigned short hostsz, unsigned short *port, char *selector, unsigned short selectorsz, const char *menu, unsigned short menulen) {
310+ char portstr[8];
311+ unsigned short i, t;
312+ /* skip first tabs (description) */
313+ for (;;) {
314+ i++;
315+ /* printf("i=%d ['%c']\n", i, menu[i]); */
316+ if (menu[i] == '\t') break;
317+ if (menu[i] == '\r') return(-2);
318+ if (menu[i] == '\n') return(-3);
319+ if (i >= menulen) return(-4);
320+ }
321+ /* read selector */
322+ for (t = 0; ; t++) {
323+ if (t >= selectorsz) return(-5);
324+ i++;
325+ if (i >= menulen) return(-6);
326+ if (menu[i] == '\r') return(-7);
327+ if (menu[i] == '\n') return(-8);
328+ selector[t] = menu[i];
329+ if (selector[t] == '\t') {
330+ selector[t] = 0;
331+ break;
332+ }
333+ }
334+ /* read hostname */
335+ for (t = 0; ; t++) {
336+ if (t >= hostsz) return(-9);
337+ i++;
338+ if (i >= menulen) return(-10);
339+ if (menu[i] == '\r') return(-11);
340+ if (menu[i] == '\n') return(-12);
341+ host[t] = menu[i];
342+ if (host[t] == '\t') {
343+ host[t] = 0;
344+ break;
345+ }
346+ }
347+ /* read port */
348+ for (t = 0; ; t++) {
349+ if (t >= sizeof(portstr)) return(-13);
350+ i++;
351+ if (i >= menulen) return(-14);
352+ portstr[t] = menu[i];
353+ if ((portstr[t] == '\t') || (portstr[t] == '\n')) {
354+ int tport;
355+ portstr[t] = 0;
356+ tport = atoi(portstr);
357+ if ((tport < 1) || (tport > 0xffff)) return(-15);
358+ *port = tport;
359+ break;
360+ }
361+ }
362+ return(0);
363+}
364+
365+
366+static int ishostvalid(const char *host) {
367+ unsigned short i;
368+ for (i = 0; host[i] != 0; i++) {
369+ if ((host[i] >= 'a') && (host[i] <= 'z')) continue;
370+ if ((host[i] >= 'A') && (host[i] <= 'Z')) continue;
371+ if ((host[i] >= '0') && (host[i] <= '9')) continue;
372+ if (host[i] == '-') continue;
373+ if (host[i] == '.') continue;
374+ return(-1);
375+ }
376+ if (i < 3) return(-10); /* host len should be at least 3 chars long */
377+ return(0);
378+}
379+
380+
381+/* parses a gopher menu and remembers all the hosts presents in its links */
382+static struct gopherlist *menu2gopherlist(const char *menu, unsigned short menulen) {
383+ long i;
384+ struct gopherlist *res = NULL;
385+ struct gopherlist *node;
386+
387+ /* DEBUG */
388+ printf("------------------------------------------------\n");
389+ for (i = 0; i < menulen; i++) printf("%c", menu[i]);
390+ printf("------------------------------------------------\n\n\n\n\n");
391+
392+ /* iterate line by line */
393+ for (i = 0; i < menulen; i++) {
394+ if (menu[i] == '1') {
395+ char host[64];
396+ char selector[128];
397+ unsigned short port;
398+
399+ if (readgline(host, sizeof(host), &port, selector, sizeof(selector), menu + i, menulen - i) == 0) {
400+ /* validate host name */
401+ if (ishostvalid(host) != 0) continue;
402+ /* */
403+ node = calloc(1, sizeof(struct gopherlist) + strlen(host));
404+ if (node == NULL) {
405+ glist_free(res);
406+ printf("ERR: OUT OF MEMORY\n");
407+ return(NULL);
408+ }
409+ node->port = port;
410+ strcpy(node->fqdn, host);
411+ node->next = res;
412+ node->selector = strdup(selector);
413+ if (res != NULL) res->prev = node;
414+ res = node;
415+ }
416+ }
417+ /* skip to next line */
418+ for (;;) {
419+ i++;
420+ if (i >= menulen) break;
421+ if (menu[i] == '\n') break;
422+ }
423+ }
424+
425+ return(res);
426+}
427+
428+
429+static void gnode_free(struct gopherlist **node) {
430+ if (*node == NULL) return;
431+ free((*node)->selector);
432+ free(*node);
433+ *node = NULL;
434+}
435+
436+
437+static struct gopherlist *gnodedup(struct gopherlist *node) {
438+ struct gopherlist *res;
439+ if (node == NULL) return(NULL);
440+ res = calloc(1, sizeof(struct gopherlist) + strlen(node->fqdn));
441+ if (res == NULL) return(NULL);
442+
443+ memcpy(res, node, sizeof(struct gopherlist) + strlen(node->fqdn));
444+ if (node->selector != NULL) res->selector = strdup(node->selector);
445+ return(res);
446+}
447+
448+
449+/**************** MAIN ****************/
450+
451+int main(int argc, char **argv) {
452+ time_t nextaction = 0;
453+ time_t nextdbsave = 0;
454+ struct gopherlist *glist, *mlist, *gnode;
455+ struct gopherlist *curhost = NULL;
456+ int ttl = 0;
457+ char buff[0xffff];
458+ long bufflen;
459+ char *dbfile, *dbfilecnt;
460+ unsigned long glistlen;
461+ int i;
462+
463+ if (parseargs(argc, argv, &dbfile, &dbfilecnt) != 0) {
464+ printf("usage: gopherjoker dbfile.csv dbcount.txt\n");
465+ return(1);
466+ }
467+
468+ /* load db file */
469+ glist = loaddb(dbfile, &glistlen);
470+
471+ /* init random engine */
472+ srand(time(NULL));
473+
474+ for (;;) {
475+ /* do not browse too fast */
476+ while (time(NULL) < nextaction) sleep(1);
477+ nextaction = time(NULL) + WAITPERIOD;
478+
479+ /* if extra manual hosts required to be added, do it now */
480+ glist = loadextrahosts(glist, NEWHOSTSLIST, &glistlen);
481+
482+ /* save the db once every hour */
483+ if (time(NULL) > nextdbsave) {
484+ unsigned long savedbcount;
485+ printf("dumping hosts lists to %s\n", dbfile);
486+ savedbcount = savedb(dbfile, dbfilecnt, glist);
487+ if (savedbcount != glistlen) {
488+ printf("ERR: savedbcount != glistlen @ %d (%lu != %lu)\n", __LINE__, savedbcount, glistlen);
489+ }
490+ nextdbsave = time(NULL) + SAVEPERIOD;
491+ }
492+
493+ /* if ttl expired, pick a new host to browse */
494+ if ((--ttl == 0) || (curhost == NULL)) {
495+ printf("TTL expired -> picking new host\n");
496+ curhost = pickrandhostfromlist(glist, glistlen);
497+ curhost = gnodedup(curhost);
498+ ttl = TTLINIT;
499+ }
500+
501+ printf("TTL=%d | glistlen=%ld\n", ttl, glistlen);
502+
503+ /* if no host, continue */
504+ if (curhost == NULL) {
505+ printf("hosts list empty. add some through " NEWHOSTSLIST ".\n");
506+ continue;
507+ } else {
508+ printf("fetching %s:%u/1%s ...\n", curhost->fqdn, curhost->port, curhost->selector);
509+ }
510+
511+ /* fetch selector */
512+ bufflen = gopher_fetch(buff, sizeof(buff), curhost->fqdn, curhost->port, curhost->selector);
513+ if (bufflen < 1) { /* fail */
514+ printf("failed\n");
515+ /* if it was about root selector, see if it's time to drop the server */
516+ if (curhost->selector == NULL) {
517+ gnode = glist_findhost(glist, curhost->fqdn, curhost->port);
518+ if (gnode->failedsince == 0) {
519+ /* server was working at some point in the past, but not anymore */
520+ gnode->failedsince = time(NULL);
521+ printf("server %s:%u went down -> flaged as failed since now (%s)\n", gnode->fqdn, gnode->port, ctime(&(gnode->failedsince)));
522+ } else if (time(NULL) > curhost->failedsince + MAXFAILTIME) {
523+ /* remove server from the list */
524+ printf("server removed due to long-time failure: %s:%u (failed since %s)\n", gnode->fqdn, gnode->port, ctime(&(gnode->failedsince)));
525+ glist = dropnode(glist, gnode, &glistlen);
526+ gnode_free(&gnode);
527+ }
528+ }
529+ gnode_free(&curhost);
530+ continue;
531+ }
532+
533+ printf("ok (%ld bytes)\n", bufflen);
534+ gnode = glist_findhost(glist, curhost->fqdn, curhost->port);
535+ gnode->failedsince = 0;
536+
537+ /* menu to glist */
538+ mlist = menu2gopherlist(buff, bufflen);
539+ if (mlist == NULL) {
540+ printf("ERR: no entries found in menu\n");
541+ gnode_free(&curhost);
542+ continue;
543+ }
544+
545+ /* try adding hosts to global glist (and count 'em) */
546+ i = 0;
547+ for (gnode = mlist; gnode != NULL; gnode = gnode->next) {
548+ i++;
549+ glist_addnewhost(&glist, &glistlen, gnode->fqdn, gnode->port, 1);
550+ }
551+
552+ /* choose a random entry from mlist */
553+ gnode = pickrandhostfromlist(mlist, i);
554+ if (gnode == NULL) {
555+ printf("ERR: pickrandhostfromlist() failed @ %d\n", __LINE__);
556+ gnode_free(&curhost);
557+ continue;
558+ }
559+ curhost = gnodedup(gnode);
560+
561+ glist_free(mlist);
562+ }
563+
564+ return(0);
565+}
--- tags/ogup-20190220/buildall.sh (nonexistent)
+++ tags/ogup-20190220/buildall.sh (revision 3)
@@ -0,0 +1,13 @@
1+#!/bin/sh
2+
3+CC="gcc"
4+CFLAGS="-Wall -Wextra -pedantic -O2 -std=gnu89"
5+
6+$CC $CFLAGS frontend/gophermap.c -o frontend/gophermap.cgi
7+$CC $CFLAGS frontend/down/gophermap.c -o frontend/down/gophermap.cgi
8+$CC $CFLAGS frontend/list/gophermap.c -o frontend/list/gophermap.cgi
9+$CC $CFLAGS frontend/rnd/gophermap.c -o frontend/rnd/gophermap.cgi
10+
11+cd gopherjoker
12+make
13+cd ..
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
--- tags/ogup-20190220/cleanall.sh (nonexistent)
+++ tags/ogup-20190220/cleanall.sh (revision 3)
@@ -0,0 +1,10 @@
1+#!/bin/sh
2+
3+rm -f frontend/gophermap.cgi
4+rm -f frontend/down/gophermap.cgi
5+rm -f frontend/list/gophermap.cgi
6+rm -f frontend/rnd/gophermap.cgi
7+
8+cd gopherjoker
9+make clean
10+cd ..
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Show on old repository browser