commit 9d52b46d0cad591dd547fcc2f4a17372518a97f0
Author: hhvn <dev@hhvn.uk>
Date: Sun, 16 Jan 2022 01:29:59 +0000
Init (gets raw text for one URI)
Diffstat:
A | Makefile | | | 34 | ++++++++++++++++++++++++++++++++++ |
A | config.mk | | | 12 | ++++++++++++ |
A | debug.c | | | 36 | ++++++++++++++++++++++++++++++++++++ |
A | plain.c | | | 47 | +++++++++++++++++++++++++++++++++++++++++++++++ |
A | tls.c | | | 121 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | zygo.c | | | 282 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | zygo.h | | | 70 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
7 files changed, 602 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -0,0 +1,34 @@
+# zygo/Makefile
+#
+# Copyright (c) 2022 hhvn <dev@hhvn.uk>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+BIN = zygo
+SRC += zygo.c
+OBJ = $(SRC:.c=.o)
+
+include config.mk
+
+$(BIN): $(OBJ)
+ $(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(OBJ)
+
+$(OBJ): Makefile zygo.h
+
+.c.o:
+ $(CC) $(CFLAGS) -c $< -o $@
+
+clean:
+ -rm -f $(OBJ) $(BIN)
+
+.PHONY: clean
diff --git a/config.mk b/config.mk
@@ -0,0 +1,12 @@
+# Public domain
+
+# debug
+# CFLAGS += -g3 -O0 -DDEBUG
+# SRC += debug.c
+
+# tls
+LDFLAGS += -ltls
+SRC += tls.c
+
+# plain
+# SRC += plain.c
diff --git a/debug.c b/debug.c
@@ -0,0 +1,36 @@
+/*
+ * zygo/debug.c
+ *
+ * Copyright (c) 2022 hhvn <dev@hhvn.uk>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include <stdio.h>
+#include "zygo.h"
+
+#define debug_start() printf("--- %s: start ---\n", __func__)
+#define debug_end() printf("--- %s: end ---\n", __func__)
+
+void *
+elem_put(Elem *e) {
+ debug_start();
+ fprintf(stderr, "TLS: %d\n", e->tls);
+ fprintf(stderr, "Type: %c\n", e->type);
+ fprintf(stderr, "Desc: %s\n", e->desc);
+ fprintf(stderr, "Selector: %s\n", e->selector);
+ fprintf(stderr, "Server: %s\n", e->server);
+ fprintf(stderr, "Port: %s\n", e->port);
+ debug_end();
+}
diff --git a/plain.c b/plain.c
@@ -0,0 +1,47 @@
+#include <unistd.h>
+#include <netdb.h>
+#include "zygo.h"
+
+static int fd;
+
+#define net_free() \
+ if (ai) freeaddrinfo(ai)
+
+int
+net_connect(Elem *e) {
+ struct addrinfo *ai = NULL;
+ int ret;
+
+ if ((ret = getaddrinfo(e->server, e->port, NULL, &ai)) != 0 || ai == NULL) {
+ error("could not lookup %s:%s", e->server, e->port);
+ goto fail;
+ }
+
+ if ((fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1 ||
+ connect(fd, ai->ai_addr, ai->ai_addrlen) == -1) {
+ error("could not connect to %s:%s", e->server, e->port);
+ goto fail;
+ }
+
+ net_free();
+ return 0;
+
+fail:
+ net_free();
+ return -1;
+}
+
+int
+net_read(void *buf, size_t count) {
+ return read(fd, buf, count);
+}
+
+int
+net_write(void *buf, size_t count) {
+ return write(fd, buf, count);
+}
+
+int
+net_close(void) {
+ return close(fd);
+}
diff --git a/tls.c b/tls.c
@@ -0,0 +1,121 @@
+#include <unistd.h>
+#include <netdb.h>
+#include <tls.h>
+#include "zygo.h"
+
+static struct tls *ctx = NULL;
+static struct tls_config *conf = NULL;
+static int fd;
+static int tls;
+
+#define net_free() \
+ if (ai) freeaddrinfo(ai); \
+ if (ctx) tls_free(ctx); \
+ if (conf) tls_config_free(conf)
+
+int
+net_connect(Elem *e) {
+ struct addrinfo *ai = NULL;
+ int ret;
+
+ tls = e->tls;
+
+ if (tls) {
+ if (conf)
+ tls_config_free(conf);
+
+ if ((conf = tls_config_new()) == NULL) {
+ error("tls_config_new(): %s", tls_config_error(conf));
+ goto fail;
+ }
+
+ if (!config[CONF_TLS_VERIFY]) {
+ tls_config_insecure_noverifycert(conf);
+ tls_config_insecure_noverifyname(conf);
+ }
+
+ if ((ctx = tls_client()) == NULL) {
+ error("tls_client(): %s", tls_error(ctx));
+ goto fail;
+ }
+
+ if (tls_configure(ctx, conf) == -1) {
+ error("tls_configure(): %s", tls_error(ctx));
+ goto fail;
+ }
+ }
+
+ if ((ret = getaddrinfo(e->server, e->port, NULL, &ai)) != 0 || ai == NULL) {
+ error("could not lookup %s:%s", e->server, e->port);
+ goto fail;
+ }
+
+ if ((fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1 ||
+ connect(fd, ai->ai_addr, ai->ai_addrlen) == -1) {
+ error("could not connect to %s:%s", e->server, e->port);
+ goto fail;
+ }
+
+ if (tls) {
+ if (tls_connect_socket(ctx, fd, e->server) == -1) {
+ error("could not tls-ify connection to %s:%s", e->server, e->port);
+ goto fail;
+ }
+ }
+
+ freeaddrinfo(ai);
+ return 0;
+
+fail:
+ net_free();
+ return -1;
+}
+
+int
+net_read(void *buf, size_t count) {
+ int ret;
+
+ if (tls) {
+ do {
+ ret = tls_read(ctx, buf, count);
+ } while (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT);
+ if (ret == -1)
+ error("tls_read(): %s", tls_error(ctx));
+ } else {
+ ret = read(fd, buf, count);
+ }
+
+ return ret;
+}
+
+int
+net_write(void *buf, size_t count) {
+ int ret;
+
+ if (tls) {
+ do {
+ ret = tls_write(ctx, buf, count);
+ } while (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT);
+ if (ret == -1)
+ error("tls_write(): %s", tls_error(ctx));
+ } else {
+ ret = write(fd, buf, count);
+ }
+
+ return ret;
+}
+
+int
+net_close(void) {
+ int ret;
+
+ if (tls) {
+ do {
+ ret = tls_close(ctx);
+ } while (ret == TLS_WANT_POLLIN || TLS_WANT_POLLOUT);
+ tls_free(ctx);
+ tls_config_free(conf);
+ }
+
+ return close(fd);
+}
diff --git a/zygo.c b/zygo.c
@@ -0,0 +1,282 @@
+/*
+ * zygo/zygo.c
+ *
+ * Copyright (c) 2022 hhvn <dev@hhvn.uk>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <stdio.h>
+#include "zygo.h"
+
+List *history = NULL;
+List *page = NULL;
+Elem *current = NULL;
+
+int config[] = {
+ [CONF_TLS_VERIFY] = 0,
+};
+
+void
+error(char *format, ...) {
+ va_list ap;
+
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+}
+
+void *
+emalloc(size_t size) {
+ void *mem;
+
+ if ((mem = malloc(size)) == NULL) {
+ perror("malloc()");
+ exit(EXIT_FAILURE);
+ }
+
+ return mem;
+}
+
+void *
+erealloc(void *ptr, size_t size) {
+ void *mem;
+
+ if ((mem = realloc(ptr, size)) == NULL) {
+ perror("realloc()");
+ exit(EXIT_FAILURE);
+ }
+
+ return mem;
+}
+
+char *
+estrdup(const char *str) {
+ char *ret;
+
+ if ((ret = strdup(str)) == NULL) {
+ perror("strdup()");
+ exit(EXIT_FAILURE);
+ }
+
+ return ret;
+}
+
+void
+estrappend(char **s1, const char *s2) {
+ size_t len;
+
+ len = strlen(*s1) + strlen(s2) + 1;
+ *s1 = erealloc(*s1, len);
+ snprintf(*s1 + strlen(*s1), len - strlen(s2), "%s", s2);
+}
+
+void
+elem_free(Elem *e) {
+ if (e) {
+ free(e->desc);
+ free(e->selector);
+ free(e->server);
+ free(e->port);
+ free(e);
+ }
+}
+
+Elem *
+elem_create(char type, char *desc, char *selector, char *server, char *port) {
+ Elem *ret;
+
+#define DUP(str) str ? NULL : estrdup(str)
+ ret = emalloc(sizeof(Elem));
+ ret->type = type;
+ ret->desc = DUP(desc);
+ ret->selector = DUP(selector);
+ ret->server = DUP(server);
+ ret->port = DUP(port);
+#undef DUP
+
+ return ret;
+}
+
+Elem *
+elem_dup(Elem *e) {
+ if (e)
+ return elem_create(e->type, e->desc, e->selector, e->server, e->port);
+ else
+ return NULL;
+}
+
+char *
+elemtouri(Elem *e) {
+ static char *ret = NULL;
+ char type[2] = {0, 0};
+
+ free(ret);
+ ret = NULL;
+
+ switch (e->type) {
+ case 'T':
+ case '8':
+ ret = estrdup("teln://");
+ break;
+ case 'h':
+ if (strncmp(e->selector, "URL:", strlen("URL:")) == 0)
+ return (ret = estrdup(e->selector + strlen("URL:")));
+ else if (strncmp(e->selector, "/URL:", strlen("/URL:")) == 0)
+ return (ret = estrdup(e->selector + strlen("/URL:")));
+ /* fallthrough */
+ default:
+ ret = estrdup(e->tls ? "gophers://" : "gopher://");
+ break;
+ }
+
+ assert(e->server);
+ assert(e->port);
+
+ estrappend(&ret, e->server);
+ if (strcmp(e->port, "70") != 0) {
+ estrappend(&ret, ":");
+ estrappend(&ret, e->port);
+ }
+
+ type[0] = e->type;
+ estrappend(&ret, "/");
+ estrappend(&ret, type);
+
+ if (e->selector && *e->selector && strcmp(e->selector, "/") != 0)
+ estrappend(&ret, e->selector);
+
+ return ret;
+}
+
+Elem *
+uritoelem(char *uri) {
+ Elem *ret;
+ char *dup = strdup(uri);
+ char *tmp = dup;
+ char *p;
+ enum {SEGSERVER, SEGPORT, SEGTYPE, SEGSELECTOR};
+ int seg;
+
+ ret = emalloc(sizeof(Elem));
+ ret->tls = ret->type = 0;
+ ret->desc = ret->selector = ret->server = ret->port;
+
+ if (strncmp(tmp, "gopher://", strlen("gopher://")) == 0) {
+ tmp += strlen("gopher://");
+ } else if (strncmp(tmp, "gophers://", strlen("gophers://")) == 0) {
+ ret->tls = 1;
+ tmp += strlen("gophers://");
+ } else if (strstr(tmp, "://")) {
+ error("non-gopher protocol entered");
+ free(ret);
+ ret = NULL;
+ goto end;
+ }
+
+ for (p = tmp, seg = SEGSERVER; *p; p++) {
+ if (seg == SEGSELECTOR || *p == '\t') {
+ ret->selector = strdup(p);
+ switch (seg) {
+ case SEGSERVER:
+ *p = '\0';
+ ret->server = strdup(tmp);
+ break;
+ case SEGPORT:
+ *p = '\0';
+ ret->port = strdup(tmp);
+ break;
+ }
+ break;
+ } else if (seg == SEGSERVER && *p == ':') {
+ *p = '\0';
+ ret->server = strdup(tmp);
+ tmp = p + 1;
+ seg = SEGPORT;
+ } else if (seg == SEGSERVER && *p == '/') {
+ *p = '\0';
+ ret->server = strdup(tmp);
+ tmp = p + 1;
+ seg = SEGTYPE;
+ } else if (seg == SEGPORT && *p == '/') {
+ *p = '\0';
+ ret->port = strdup(tmp);
+ tmp = p + 1;
+ seg = SEGTYPE;
+ } else if (seg == SEGTYPE) {
+ ret->type = *p;
+ tmp = p + 1;
+ break;
+ }
+ }
+
+ ret->type = ret->type ? ret->type : '1';
+ ret->server = ret->server ? ret->server : strdup(tmp);
+ ret->port = ret->port ? ret->port : strdup("70");
+ ret->selector = ret->selector ? ret->selector : strdup(tmp);
+
+#ifdef DEBUG
+ elem_put(ret);
+#endif /* DEBUG */
+
+end:
+ free(dup);
+ return ret;
+}
+
+int
+readline(char *buf, size_t count) {
+ size_t i = 0;
+ char c = 0;
+
+ do {
+ if (net_read(&c, sizeof(char)) != sizeof(char))
+ return 0;
+ if (c != '\r')
+ buf[i++] = c;
+ } while (c != '\n' && i < count);
+
+ buf[i - 1] = 0;
+ return 1;
+}
+
+int
+go(Elem *e) {
+ char line[BUFLEN];
+
+ if (e->type != '1' && e->type != '7' && e->type != '+') {
+ /* TODO: call plumber */
+ return -1;
+ }
+
+ if (net_connect(e) == -1)
+ return -1;
+
+ net_write(e->selector, strlen(e->selector));
+ net_write("\r\n", 2);
+
+ while (readline(line, sizeof(line))) {
+ fprintf(stderr, "%s\n", line);
+ }
+}
+
+int
+main(void) {
+ Elem *s = uritoelem("gophers://hhvn.uk/1/");
+ go(s);
+}
diff --git a/zygo.h b/zygo.h
@@ -0,0 +1,70 @@
+/*
+ * zygo/zygo.h
+ *
+ * Copyright (c) 2022 hhvn <dev@hhvn.uk>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#define BUFLEN 2048
+
+typedef struct Elem Elem;
+struct Elem {
+ int tls;
+ char type;
+ char *desc;
+ char *selector;
+ char *server;
+ char *port;
+};
+
+typedef struct List List;
+struct List {
+ struct Elem **elems;
+ size_t len;
+};
+
+enum {
+ CONF_TLS_VERIFY = 'k',
+};
+
+extern List *history;
+extern List *page;
+extern Elem *current;
+extern int config[];
+
+void error(char *format, ...);
+void *emalloc(size_t size);
+void *erealloc(void *ptr, size_t size);
+char *estrdup(const char *str);
+void estrappend(char **s1, const char *s2);
+
+void elem_free(Elem *e);
+Elem *elem_create(char type, char *desc, char *selector, char *server, char *port);
+Elem *elem_dup(Elem *e);
+Elem *uritoelem(char *uri);
+char *elemtouri(Elem *e);
+int readline(char *buf, size_t count);
+int go(Elem *e);
+
+/* only works with one fd/ctx at
+ * a time, reset at net_connect */
+int net_connect(Elem *e);
+int net_read(void *buf, size_t count);
+int net_write(void *buf, size_t count);
+int net_close(void);
+
+#ifdef DEBUG
+void *elem_put(Elem *e); /* debug */
+#endif /* DEBUG */