• R/O
  • SSH
  • HTTPS

ogup: 提交


Commit MetaInfo

修訂6 (tree)
時間2020-12-24 18:49:18
作者mateuszviste

Log Message

imported ver 20190424 to tags

Change Summary

差異

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