Recently Written · git

sacc

Mirror of git://bitreich.org/sacc, the sacc gopher client from bitreich.org.

git clone https://github.com/equwal/sacc

Log | Files | Refs


commit 5a5611b3293eaac0b26832a54b845c6822736b65
Quentin Rameau <quinq@fifth.space>
2021-04-10 15:56:05 +0200

TLS: use wrapper functions

 Makefile  |   8 +--
 config.mk |  12 +++--
 io.h      |  17 ++++++
 io_clr.c  |  63 +++++++++++++++++++++++
 io_tls.c  | 129 ++++++++++++++++++++++++++++++++++++++++++++++
 sacc.c    | 173 ++++++++++++++++----------------------------------------------
 6 files changed, 264 insertions(+), 138 deletions(-)
diff --git a/Makefile b/Makefile
index 54841cd..fc55c4b 100644
--- a/Makefile
+++ b/Makefile
@@ -6,7 +6,7 @@ include config.mk
 
 BIN = sacc
 MAN = $(BIN).1
-OBJ = $(BIN:=.o) ui_$(UI).o
+OBJ = $(BIN:=.o) ui_$(UI).o io_$(IO).o
 
 all: $(BIN)
 
@@ -14,9 +14,9 @@ config.h:
 	cp config.def.h config.h
 
 $(BIN): $(OBJ)
-	$(CC) $(OBJ) $(LDFLAGS) $(LIBS) $(TLSLIBS) -o $@
+	$(CC) $(OBJ) $(LDFLAGS) $(IOLIBS) $(LIBS) -o $@
 
-$(OBJ): config.h config.mk common.h
+$(OBJ): config.h config.mk common.h io.h
 
 clean:
 	rm -f $(BIN) $(OBJ)
@@ -33,7 +33,7 @@ uninstall:
 
 # Stock FLAGS
 SACCCFLAGS = -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=700 -D_BSD_SOURCE -D_GNU_SOURCE \
-             $(TLSCFLAGS) $(CFLAGS)
+             $(IOCFLAGS) $(CFLAGS)
 
 .c.o:
 	$(CC) $(SACCCFLAGS) -c $<
diff --git a/config.mk b/config.mk
index 41622f9..62c1067 100644
--- a/config.mk
+++ b/config.mk
@@ -9,10 +9,14 @@ MANDIR = $(PREFIX)/share/man/man1
 UI=ti
 LIBS=-lcurses
 
+# IO type
+# clr (clear)
+#IO = clr
+# tls (Transport Layer Security)
+IO = tls
+IOLIBS = -ltls
+IOCFLAGS = -DUSE_TLS
+
 # Define NEED_ASPRINTF and/or NEED_STRCASESTR in your cflags if your system does
 # not provide asprintf() or strcasestr(), respectively.
 #CFLAGS = -DNEED_ASPRINTF -DNEED_STRCASESTR
-
-# gophers (gopher over TLS) support
-TLSCFLAGS = -DUSE_TLS
-TLSLIBS = -ltls
diff --git a/io.h b/io.h
new file mode 100644
index 0000000..d18e9ab
--- /dev/null
+++ b/io.h
@@ -0,0 +1,17 @@
+#include <netdb.h>
+
+struct cnx {
+#ifdef USE_TLS
+	struct tls *tls;
+#endif
+	int sock;
+};
+
+extern int tls;
+
+extern int (*ioclose)(struct cnx *);
+extern int (*ioconnect)(struct cnx *, struct addrinfo *, const char *);
+extern void (*ioconnerr)(struct cnx *, const char *, const char *, int);
+extern char *(*ioparseurl)(char *);
+extern ssize_t (*ioread)(struct cnx *, void *, size_t);
+extern ssize_t (*iowrite)(struct cnx *, void *, size_t);
diff --git a/io_clr.c b/io_clr.c
new file mode 100644
index 0000000..142f6a5
--- /dev/null
+++ b/io_clr.c
@@ -0,0 +1,63 @@
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+
+#include <sys/socket.h>
+
+#include "common.h"
+#include "io.h"
+
+static int
+close_clr(struct cnx *c)
+{
+	return close(c->sock);
+}
+
+static int
+connect_clr(struct cnx *c, struct addrinfo *ai, const char *host)
+{
+	return connect(c->sock, ai->ai_addr, ai->ai_addrlen);
+}
+
+static void
+connerr_clr(struct cnx *c, const char *host, const char *port, int err)
+{
+	if (c->sock == -1)
+		diag("Can't open socket: %s", strerror(err));
+	else
+		diag("Can't connect to: %s:%s: %s", host, port, strerror(err));
+}
+
+static char *
+parseurl_clr(char *url)
+{
+	char *p;
+
+	if (p = strstr(url, "://")) {
+		if (strncmp(url, "gopher", p - url))
+			die("Protocol not supported: %.*s", p - url, url);
+
+		url = p + 3;
+	}
+
+	return url;
+}
+
+static ssize_t
+read_clr(struct cnx *c, void *buf, size_t bs)
+{
+	return read(c->sock, buf, bs);
+}
+
+static ssize_t
+write_clr(struct cnx *c, void *buf, size_t bs)
+{
+	return write(c->sock, buf, bs);
+}
+
+int (*ioclose)(struct cnx *) = close_clr;
+int (*ioconnect)(struct cnx *, struct addrinfo *, const char *) = connect_clr;
+void (*ioconnerr)(struct cnx *, const char *, const char *, int) = connerr_clr;
+char *(*ioparseurl)(char *) = parseurl_clr;
+ssize_t (*ioread)(struct cnx *, void *, size_t) = read_clr;
+ssize_t (*iowrite)(struct cnx *, void *, size_t) = write_clr;
diff --git a/io_tls.c b/io_tls.c
new file mode 100644
index 0000000..a50aebd
--- /dev/null
+++ b/io_tls.c
@@ -0,0 +1,129 @@
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+
+#include <sys/socket.h>
+
+#include <tls.h>
+
+#include "common.h"
+#include "io.h"
+
+int tls;
+
+static int
+close_tls(struct cnx *c)
+{
+	int r;
+
+	if (tls) {
+		if (c->tls) {
+			do {
+				r = tls_close(c->tls);
+			} while (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT);
+		}
+		tls_free(c->tls);
+	}
+
+	return close(c->sock);
+}
+
+static int
+connect_tls(struct cnx *c, struct addrinfo *ai, const char *host)
+{
+	struct tls *t;
+	int s;
+
+	c->tls = NULL;
+	s = c->sock;
+
+	if (connect(s, ai->ai_addr, ai->ai_addrlen) == -1)
+		return -1;
+
+	if (tls) {
+		if ((t = tls_client()) == NULL)
+			return -1;
+		if (tls_connect_socket(t, s, host) == -1)
+			return -1;
+
+		c->tls = t;
+	}
+
+	return 0;
+}
+
+static void
+connerr_tls(struct cnx *c, const char *host, const char *port, int err)
+{
+	if (c->sock == -1) {
+		diag("Can't open socket: %s", strerror(err));
+	} else {
+		if (tls && c->tls) {
+			diag("Can't establish TLS with \"%s\": %s",
+			     host, tls_error(c->tls));
+		} else {
+			diag("Can't connect to: %s:%s: %s", host, port,
+			     strerror(err));
+		}
+	}
+}
+
+static char *
+parseurl_tls(char *url)
+{
+	char *p;
+
+	if (p = strstr(url, "://")) {
+		if (!strncmp(url, "gopher", p - url)) {
+			if (tls)
+				diag("Switching from gophers to gopher");
+			tls = 0;
+		} else if (!strncmp(url, "gophers", p - url)) {
+			tls = 1;
+		} else {
+			die("Protocol not supported: %.*s", p - url, url);
+		}
+		url = p + 3;
+	}
+
+	return url;
+}
+
+static ssize_t
+read_tls(struct cnx *c, void *buf, size_t bs)
+{
+	ssize_t n;
+
+	if (tls && c->tls) {
+		do {
+			n = tls_read(c->tls, buf, bs);
+		} while (n == TLS_WANT_POLLIN || n == TLS_WANT_POLLOUT);
+	} else {
+		n = read(c->sock, buf, bs);
+	}
+
+	return n;
+}
+
+static ssize_t
+write_tls(struct cnx *c, void *buf, size_t bs)
+{
+	ssize_t n;
+
+	if (tls) {
+		do {
+			n = tls_write(c->tls, buf, bs);
+		} while (n == TLS_WANT_POLLIN || n == TLS_WANT_POLLOUT);
+	} else {
+		n = write(c->sock, buf, bs);
+	}
+
+	return n;
+}
+
+int (*ioclose)(struct cnx *) = close_tls;
+int (*ioconnect)(struct cnx *, struct addrinfo *, const char *) = connect_tls;
+void (*ioconnerr)(struct cnx *, const char *, const char *, int) = connerr_tls;
+char *(*ioparseurl)(char *) = parseurl_tls;
+ssize_t (*ioread)(struct cnx *, void *, size_t) = read_tls;
+ssize_t (*iowrite)(struct cnx *, void *, size_t) = write_tls;
diff --git a/sacc.c b/sacc.c
index 2a5a4be..9569eed 100644
--- a/sacc.c
+++ b/sacc.c
@@ -18,26 +18,15 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 
-#ifdef USE_TLS
-#include <tls.h>
-#endif
-
 #include "common.h"
+#include "io.h"
 #include "config.h"
 
-struct cnx {
-#ifdef USE_TLS
-	struct tls *tls;
-#endif
-	int sock;
-};
-
 static char *mainurl;
 static Item *mainentry;
 static int devnullfd;
 static int parent = 1;
 static int interactive;
-static int tls;
 
 static void (*diag)(char *fmt, ...);
 
@@ -429,7 +418,7 @@ molddiritem(char *raw)
 }
 
 static char *
-getrawitem(struct cnx *cnx)
+getrawitem(struct cnx *c)
 {
 	char *raw, *buf;
 	size_t bn, bs;
@@ -458,17 +447,7 @@ getrawitem(struct cnx *cnx)
 			bs = BUFSIZ;
 		}
 
-#ifdef USE_TLS
-		if (tls) {
-			do {
-				n = tls_read(cnx->tls, buf, bs);
-			} while (n == TLS_WANT_POLLIN || n == TLS_WANT_POLLOUT);
-		} else
-#endif
-		{
-			n = read(cnx->sock, buf, bs);
-		}
-	} while (n > 0);
+	} while ((n = ioread(c, buf, bs)) > 0);
 
 	*buf = '\0';
 
@@ -481,7 +460,7 @@ getrawitem(struct cnx *cnx)
 }
 
 static int
-sendselector(struct cnx *cnx, const char *selector)
+sendselector(struct cnx *c, const char *selector)
 {
 	char *msg, *p;
 	size_t ln;
@@ -491,21 +470,10 @@ sendselector(struct cnx *cnx, const char *selector)
 	msg = p = xmalloc(ln);
 	snprintf(msg, ln--, "%s\r\n", selector);
 
-	do {
-#ifdef USE_TLS
-		if (tls) {
-			do {
-				n = tls_write(cnx->tls, p, ln);
-			} while (n == TLS_WANT_POLLIN || n == TLS_WANT_POLLOUT);
-		} else
-#endif
-		{
-			n = write(cnx->sock, p, ln);
-		}
-
+	while ((n = iowrite(c, p, ln)) > 0) {
 		ln -= n;
 		p += n;
-	} while (n > 0);
+	};
 
 	free(msg);
 	if (n == -1)
@@ -514,20 +482,8 @@ sendselector(struct cnx *cnx, const char *selector)
 	return n;
 }
 
-static void
-closecnx(struct cnx *cnx)
-{
-#ifdef USE_TLS
-	if (tls) {
-		tls_close(cnx->tls);
-		tls_free(cnx->tls);
-	}
-#endif
-	close(cnx->sock);
-}
-
 static int
-connectto(const char *host, const char *port, struct cnx *cnx)
+connectto(const char *host, const char *port, struct cnx *c)
 {
 	sigset_t set, oset;
 	static const struct addrinfo hints = {
@@ -536,7 +492,7 @@ connectto(const char *host, const char *port, struct cnx *cnx)
 	    .ai_protocol = IPPROTO_TCP,
 	};
 	struct addrinfo *addrs, *addr;
-	int r, sock = -1;
+	int r, err;
 
 	sigemptyset(&set);
 	sigaddset(&set, SIGWINCH);
@@ -548,86 +504,54 @@ connectto(const char *host, const char *port, struct cnx *cnx)
 		goto err;
 	}
 
+	r = -1;
 	for (addr = addrs; addr; addr = addr->ai_next) {
-		if ((sock = socket(addr->ai_family, addr->ai_socktype,
-		                   addr->ai_protocol)) == -1)
-			continue;
-
-		r = connect(sock, addr->ai_addr, addr->ai_addrlen);
-		if (r == -1) {
-			close(sock);
+		if ((c->sock = socket(addr->ai_family, addr->ai_socktype,
+		                      addr->ai_protocol)) == -1) {
+			err = errno;
 			continue;
 		}
-#ifdef USE_TLS
-		if (tls) {
-			if ((cnx->tls = tls_client()) == NULL) {
-				diag("Can't establish TLS with \"%s\": %s",
-				     host, tls_error(cnx->tls));
-				close(sock);
-				continue;
-			}
-			r = tls_connect_socket(cnx->tls, sock, host);
-		}
-#endif
-		break;
-	}
 
-	freeaddrinfo(addrs);
+		if ((r = ioconnect(c, addr, host)) == 0)
+			break;
 
-	if (sock == -1) {
-		diag("Can't open socket: %s", strerror(errno));
-		goto err;
-	}
-	if (r == -1) {
-		diag("Can't connect to: %s:%s: %s",
-		     host, port, strerror(errno));
-		goto err;
+		err = errno;
+		close(c->sock);
 	}
 
-	sigprocmask(SIG_SETMASK, &oset, NULL);
-
-	cnx->sock = sock;
+	freeaddrinfo(addrs);
 
-	return 0;
+	if (r == -1)
+		ioconnerr(c, host, port, err);
 err:
 	sigprocmask(SIG_SETMASK, &oset, NULL);
-	return -1;
+
+	return r;
 }
 
 static int
 download(Item *item, int dest)
 {
 	char buf[BUFSIZ];
-	struct cnx cnx;
+	struct cnx c;
 	ssize_t r, w;
-	int src;
 
-	if (!item->tag) {
-		if (connectto(item->host, item->port, &cnx) == -1 ||
-		    sendselector(&cnx, item->selector) == -1)
+	if (item->tag == NULL) {
+		if (connectto(item->host, item->port, &c) == -1 ||
+		    sendselector(&c, item->selector) == -1)
+			return 0;
+	} else {
+		if ((c.sock = open(item->tag, O_RDONLY)) == -1) {
+			printf("Can't open source file %s: %s",
+			       item->tag, strerror(errno));
+			errno = 0;
 			return 0;
-		src = cnx.sock;
-	} else if ((src = open(item->tag, O_RDONLY)) == -1) {
-		printf("Can't open source file %s: %s",
-		       item->tag, strerror(errno));
-		errno = 0;
-		return 0;
-	}
-
-	for (w = 0; w != -1;) {
-#ifdef USE_TLS
-		if (tls) {
-			do {
-				r = tls_read(cnx.tls, buf, sizeof(buf));
-			} while (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT);
-		} else
-#endif
-		{
-			r = read(src, buf, sizeof(buf));
 		}
-		if (r <= 0)
-			break;
+		c.tls = NULL;
+	}
 
+	w = 0;
+	while ((r = ioread(&c, buf, BUFSIZ)) > 0) {
 		while ((w = write(dest, buf, r)) > 0)
 			r -= w;
 	}
@@ -638,7 +562,8 @@ download(Item *item, int dest)
 		errno = 0;
 	}
 
-	closecnx(&cnx);
+	close(dest);
+	ioclose(&c);
 
 	return (r == 0 && w == 0);
 }
@@ -693,15 +618,15 @@ cleanup:
 static int
 fetchitem(Item *item)
 {
-	struct cnx cnx;
+	struct cnx c;
 	char *raw;
 
-	if (connectto(item->host, item->port, &cnx) == -1 ||
-	    sendselector(&cnx, item->selector) == -1)
+	if (connectto(item->host, item->port, &c) == -1 ||
+	    sendselector(&c, item->selector) == -1)
 		return 0;
 
-	raw = getrawitem(&cnx);
-	closecnx(&cnx);
+	raw = getrawitem(&c);
+	ioclose(&c);
 
 	if (raw == NULL || !*raw) {
 		diag("Empty response from server");
@@ -985,19 +910,7 @@ moldentry(char *url)
 	char *p, *host = url, *port = "70", *gopherpath = "1";
 	int parsed, ipv6;
 
-	if (p = strstr(url, "://")) {
-		if (strncmp(url, "gopher", p - url) == 0) {
-			if (tls) {
-				diag("Switching from gophers to gopher");
-				tls = 0;
-			}
-		} else if (strncmp(url, "gophers", p - url) == 0) {
-			tls = 1;
-		} else {
-			die("Protocol not supported: %.*s", p - url, url);
-		}
-		host = p + 3;
-	}
+	host = ioparseurl(url);
 
 	if (*host == '[') {
 		ipv6 = 1;