diff --git a/README.md b/README.md

index aa9f811a9bfd9cfb7b6654954e33fc62e868f8c4..fa803f99158582d982ef3a01d6b87471fb27f0cb 100644

--- a/README.md

+++ b/README.md

@@ -10,6 +10,7 @@

+- basic Client Certificate support (no autocreation of client certs currently)

Non-Features:

@@ -61,6 +62,5 @@

Dependencies:

-- OpenSSL

+- BearSSL

diff --git a/config.sh b/config.sh

index 424cbda346869c7476859d55a600c414c86bbb46..87888929b98a46887dbcb25378385183e4ef2566 100644

--- a/config.sh

+++ b/config.sh

@@ -117,8 +117,8 @@ echo no

	fi

done

printf "Checking for scdoc... "

if scdoc -v >/dev/null 2>&1

diff --git a/configure b/configure

index 70a19b193d6e33b41b5cc7ef13511bb0f5b4e076..6abccdd18d0b85e4b2d4b527d49727d360dec5c3 100755

--- a/configure

+++ b/configure

@@ -4,6 +4,7 @@ eval ". $srcdir/config.sh"

gmni() {

genrules gmni \

	src/client.c \

	src/escape.c \

	src/gmni.c \

@@ -14,6 +15,7 @@ }

cgmnlm() {

genrules cgmnlm \

	src/client.c \

	src/escape.c \

	src/gmnlm.c \

@@ -25,6 +27,7 @@ }

libgmni_a() {

genrules libgmni.a \

	src/client.c \

	src/escape.c \

	src/tofu.c \

diff --git a/doc/gmni.scd b/doc/gmni.scd

index 1f20672f1fb0c8ff6a4f3a97a3324a25d2a0a01d..866dd418a510ccd141d9c23afb4b321b84a5f9ec 100644

--- a/doc/gmni.scd

+++ b/doc/gmni.scd

@@ -38,11 +38,9 @@ If the server requests user input, path is opened and read, and a

second request is performed with the contents of _path_ as the user

input.

--E path[:password]

+-E path:key

-l

For *text/\** responses, *gmni* normally adds a line feed if stdout is a

diff --git a/include/gmni/certs.h b/include/gmni/certs.h

new file mode 100644

index 0000000000000000000000000000000000000000..53b8aad27fa50649ddfdacbd08724d5d0b033120

--- /dev/null

+++ b/include/gmni/certs.h

@@ -0,0 +1,27 @@

+#ifndef GEMINI_CERTS_H

+#define GEMINI_CERTS_H

+#include <bearssl.h>

+#include <stdio.h>

+struct gmni_options;

+struct gmni_client_certificate {

+};

+struct gmni_private_key {

+};

+// Returns nonzero on failure and sets errno. Closes both files.

+int gmni_ccert_load(struct gmni_client_certificate *cert,

+#endif

diff --git a/include/gmni/gmni.h b/include/gmni/gmni.h

index 7e27b489d71fd3a43ca60292b17d56cab3caa5f8..22295e20fb36d492a979c060de8dcdca14df5a34 100644

--- a/include/gmni/gmni.h

+++ b/include/gmni/gmni.h

@@ -1,7 +1,7 @@

#ifndef GEMINI_CLIENT_H

#define GEMINI_CLIENT_H

+#include <bearssl.h>

#include <netdb.h>

-#include <openssl/ssl.h>

#include <stdbool.h>

#include <sys/socket.h>

@@ -52,20 +52,18 @@ struct gemini_response {

enum gemini_status status;

char *meta;

// Response body may be read from here if appropriate:

// Connection state

int fd;

};

-struct gemini_options {

+struct gmni_client_certificate;

+struct gemini_options {

// If ai_family != AF_UNSPEC (the default value on most systems), the

// client will connect to this address and skip name resolution.

struct addrinfo *addr;

@@ -73,7 +71,13 @@

// If non-NULL, these hints are provided to getaddrinfo. Useful, for

// example, to force IPv4/IPv6.

struct addrinfo *hints;

};

+struct gemini_tofu;

// Requests the specified URL via the gemini protocol. If options is non-NULL,

// it may specify some additional configuration to adjust client behavior.

@@ -84,6 +88,7 @@ // Caller must call gemini_response_finish afterwards to clean up resources

// before exiting or re-using it for another request.

enum gemini_result gemini_request(const char *url,

	struct gemini_options *options,

	struct gemini_response *resp);

// Must be called after gemini_request in order to free up the resources

@@ -137,15 +142,20 @@ };

};

struct gemini_parser {

char *buf;

size_t bufsz;

size_t bufln;

bool preformatted;

};

-// Initializes a text/gemini parser which reads from the specified BIO.

-void gemini_parser_init(struct gemini_parser *p, BIO *f);

+// Initializes a text/gemini parser. The provided "read" function will be called

+// with the provided "state" value in order to obtain more gemtext data. The

+// read function should behave like read(3).

+void gemini_parser_init(struct gemini_parser *p,

// Finishes this text/gemini parser and frees up its resources.

void gemini_parser_finish(struct gemini_parser *p);

diff --git a/include/gmni/tofu.h b/include/gmni/tofu.h

index a88167ba0fb6606b2b170e5005c55131f1861972..51d1d60a3719469a1f8cd295b9d85e21d7affb43 100644

--- a/include/gmni/tofu.h

+++ b/include/gmni/tofu.h

@@ -1,9 +1,7 @@

#ifndef GEMINI_TOFU_H

#define GEMINI_TOFU_H

+#include <bearssl.h>

#include <limits.h>

-#include <openssl/ssl.h>

-#include <openssl/x509.h>

-#include <time.h>

enum tofu_error {

TOFU_VALID,

@@ -24,7 +22,6 @@ };

struct known_host {

char *host, *fingerprint;

int lineno;

struct known_host *next;

};

@@ -34,7 +31,23 @@ // certificate. Return true to trust this certificate.

typedef enum tofu_action (tofu_callback_t)(enum tofu_error error,

const char *fingerprint, struct known_host *host, void *data);

+struct gemini_tofu;

+struct x509_tofu_context {

+};

struct gemini_tofu {

char known_hosts_path[PATH_MAX+1];

struct known_host *known_hosts;

int lineno;

@@ -42,8 +55,7 @@ tofu_callback_t *callback;

void *cb_data;

};

-void gemini_tofu_init(struct gemini_tofu *tofu,

+void gemini_tofu_init(struct gemini_tofu *tofu, tofu_callback_t *cb, void *data);

void gemini_tofu_finish(struct gemini_tofu *tofu);

#endif

diff --git a/include/util.h b/include/util.h

index 6193fdf48c0ac6d313de7f35a1d09ecba62e3283..6c241f41a175c3d27390ecb6ca5b041bd637cb2b 100644

--- a/include/util.h

+++ b/include/util.h

@@ -1,5 +1,7 @@

#ifndef GEMINI_UTIL_H

#define GEMINI_UTIL_H

+#include <stdio.h>

+#include <sys/types.h>

struct pathspec {

const char *var;

@@ -7,6 +9,7 @@ const char *path;

};

char *getpath(const struct pathspec *paths, size_t npaths);

+void posix_dirname(char *path, char *dname);

int mkdirs(char *path, mode_t mode);

int download_resp(FILE *out, struct gemini_response resp, const char *path,

	char *url);

diff --git a/src/certs.c b/src/certs.c

new file mode 100644

index 0000000000000000000000000000000000000000..f40bfa7d27925baaa2fe6dbdf0d6c18ce4e10014

--- /dev/null

+++ b/src/certs.c

@@ -0,0 +1,156 @@

+#include <assert.h>

+#include <bearssl.h>

+#include <errno.h>

+#include <gmni/certs.h>

+#include <gmni/gmni.h>

+#include <stdio.h>

+#include <stdlib.h>

+static void

+crt_append(void *ctx, const void *src, size_t len)

+{

+}

+static void

+key_append(void *ctx, const void *src, size_t len)

+{

+}

+int

+gmni_ccert_load(struct gmni_client_certificate *cert, FILE *certin, FILE *skin)

+{

+error:

+}

diff --git a/src/client.c b/src/client.c

index a8a12f3cfbe1e98fd2045eec257cfaf95c319910..127a56ca59859e645fea6f1e4ac62431d734e4fd 100644

--- a/src/client.c

+++ b/src/client.c

@@ -1,15 +1,16 @@

#include <assert.h>

#include <errno.h>

#include <netdb.h>

-#include <openssl/bio.h>

-#include <openssl/err.h>

-#include <openssl/ssl.h>

+#include <bearssl.h>

#include <stdlib.h>

+#include <stdio.h>

#include <string.h>

#include <sys/socket.h>

#include <sys/types.h>

#include <unistd.h>

+#include <gmni/certs.h>

#include <gmni/gmni.h>

+#include <gmni/tofu.h>

#include <gmni/url.h>

static enum gemini_result

@@ -88,9 +89,41 @@

#define GEMINI_META_MAXLEN 1024

#define GEMINI_STATUS_MAXLEN 2

+static int

+sock_read(void *ctx, unsigned char *buf, size_t len)

+{

+}

+static int

+sock_write(void *ctx, const unsigned char *buf, size_t len)

+{

+}

enum gemini_result

gemini_request(const char *url, struct gemini_options *options,

{

assert(url);

assert(resp);

@@ -128,84 +161,69 @@ free(host);

	goto cleanup;

}

int r;

res = gemini_connect(uri, options, resp, &resp->fd);

if (res != GEMINI_OK) {

	free(host);

	goto cleanup;

}

}

char req[1024 + 3];

r = snprintf(req, sizeof(req), "%s\r\n", url);

assert(r > 0);

char buf[GEMINI_META_MAXLEN

	+ GEMINI_STATUS_MAXLEN

	+ 2 /* CRLF */ + 1 /* NUL */];

}

	res = GEMINI_ERR_PROTOCOL;

	goto cleanup;

}

@@ -217,9 +235,9 @@ fprintf(stderr, "invalid status\n");

	res = GEMINI_ERR_PROTOCOL;

	goto cleanup;

}

cleanup:

curl_url_cleanup(uri);

@@ -237,26 +255,18 @@ if (!resp) {

	return;

}

}

free(resp->meta);

}

resp->meta = NULL;

}

@@ -277,11 +287,11 @@ return gai_strerror(resp->status);

case GEMINI_ERR_CONNECT:

	return strerror(errno);

case GEMINI_ERR_SSL:

case GEMINI_ERR_SSL_VERIFY:

case GEMINI_ERR_IO:

	return "I/O error";

case GEMINI_ERR_PROTOCOL:

diff --git a/src/gmni.c b/src/gmni.c

index 49abb8524a012c27249006dc45f4688a846e63c3..f3015ac679eba77397fb4aac5068f3a63e1cdbab 100644

--- a/src/gmni.c

+++ b/src/gmni.c

@@ -1,9 +1,8 @@

#include <assert.h>

+#include <bearssl.h>

#include <errno.h>

#include <getopt.h>

#include <netdb.h>

-#include <openssl/bio.h>

-#include <openssl/err.h>

#include <stdbool.h>

#include <stdio.h>

#include <stdlib.h>

@@ -12,6 +11,7 @@ #include <sys/socket.h>

#include <sys/types.h>

#include <termios.h>

#include <unistd.h>

+#include <gmni/certs.h>

#include <gmni/gmni.h>

#include <gmni/tofu.h>

#include <gmni/url.h>

@@ -110,6 +110,45 @@

return action;

}

+static struct gmni_client_certificate *

+load_client_cert(char *argv_0, char *path)

+{

+}

int

main(int argc, char *argv[])

{

@@ -166,7 +205,7 @@ }

		}

		break;

	case 'E':

		break;

	case 'h':

		usage(argv[0]);

@@ -222,15 +261,12 @@ usage(argv[0]);

	return 1;

}

bool exit = false;

struct Curl_URL *url = curl_url();

	// TODO: Better error

	fprintf(stderr, "Error: invalid URL\n");

	return 1;

@@ -242,7 +278,8 @@ char *buf;

	curl_url_get(url, CURLUPART_URL, &buf, 0);

	struct gemini_response resp;

	free(buf);

@@ -340,11 +377,8 @@

		char last = 0;

		char buf[BUFSIZ];

		for (int n = 1; n > 0;) {

				last = buf[n - 1];

			}

			ssize_t w = 0;

@@ -370,7 +404,6 @@ next:

	gemini_response_finish(&resp);

}

curl_url_cleanup(url);

gemini_tofu_finish(&cfg.tofu);

return ret;

diff --git a/src/gmnlm.c b/src/gmnlm.c

index e28bf70afc5d6fd4c5c6eff85d85198d62a127e8..ef4c97950495e369e4fd3b1256c185fdb01f8cf1 100644

--- a/src/gmnlm.c

+++ b/src/gmnlm.c

@@ -1,22 +1,26 @@

#include <assert.h>

+#include <bearssl.h>

#include <ctype.h>

+#include <errno.h>

+#include <fcntl.h>

#include <getopt.h>

+#include <gmni/certs.h>

+#include <gmni/gmni.h>

+#include <gmni/tofu.h>

+#include <gmni/url.h>

#include <libgen.h>

#include <limits.h>

-#include <openssl/bio.h>

-#include <openssl/err.h>

#include <regex.h>

#include <stdbool.h>

#include <stdio.h>

+#include <stdlib.h>

#include <string.h>

#include <sys/ioctl.h>

#include <sys/stat.h>

+#include <sys/types.h>

#include <sys/wait.h>

#include <termios.h>

#include <unistd.h>

-#include <gmni/gmni.h>

-#include <gmni/tofu.h>

-#include <gmni/url.h>

#include "util.h"

#define ANSI_COLOR_RED "\x1b[91m"

@@ -400,10 +404,13 @@ close(pfd[0]);

FILE *f = fdopen(pfd[1], "w");

// XXX: may affect history, do we care?

for (int n = 1; n > 0;) {

	}

	ssize_t w = 0;

	while (w < (ssize_t)n) {

@@ -430,13 +437,50 @@ {

int nredir = 0;

bool requesting = true;

enum gemini_result res;

	CURLUcode uc = curl_url_get(browser->url,

	if (strcmp(scheme, "file") == 0) {

		requesting = false;

		char *path;

@@ -447,23 +491,19 @@ resp->status = GEMINI_STATUS_BAD_REQUEST;

			break;

		}

			resp->status = GEMINI_STATUS_NOT_FOUND;

			resp->meta = NULL;

			resp->fd = -1;

			free(path);

			break;

		}

		if (has_suffix(path, ".gmi") || has_suffix(path, ".gemini")) {

			resp->meta = strdup("text/gemini");

		} else if (has_suffix(path, ".txt")) {

@@ -473,14 +513,14 @@ resp->meta = strdup("application/x-octet-stream");

		}

		free(path);

		resp->status = GEMINI_STATUS_SUCCESS;

	}

	if (res != GEMINI_OK) {

		fprintf(stderr, "Error: %s\n", gemini_strerr(res, resp));

		requesting = false;

@@ -519,7 +559,26 @@ }

		set_url(browser, resp->meta, NULL);

		break;

	case GEMINI_STATUS_CLASS_CLIENT_CERTIFICATE_REQUIRED:

	case GEMINI_STATUS_CLASS_TEMPORARY_FAILURE:

	case GEMINI_STATUS_CLASS_PERMANENT_FAILURE:

		requesting = false;

@@ -529,7 +588,7 @@ "TEMPORARY FAILURE" : "PERMANENT FAILURE",

			resp->status, resp->meta);

		break;

	case GEMINI_STATUS_CLASS_SUCCESS:

	}

	if (requesting) {

@@ -537,6 +596,11 @@ gemini_response_finish(resp);

	}

}

+out:

return res;

}

@@ -842,12 +906,23 @@ }

return fprintf(f, "%s\n", s) - 1;

}

+static int

+resp_read(void *state, void *buf, size_t nbyte)

+{

+}

static bool

display_gemini(struct browser *browser, struct gemini_response *resp)

{

int nlinks = 0;

struct gemini_parser p;

free(browser->page_title);

browser->page_title = NULL;

@@ -1036,10 +1111,13 @@ ioctl(fileno(browser->tty), TIOCGWINSZ, &ws);

char buf[BUFSIZ];

for (int n = 1; n > 0;) {

	}

	ssize_t w = 0;

	while (w < (ssize_t)n) {

@@ -1214,11 +1292,7 @@ } else {

	open_bookmarks(&browser);

}

struct gemini_response resp;

browser.running = true;

@@ -1280,7 +1354,6 @@ while (hist && hist->prev) {

	hist = hist->prev;

}

history_free(hist);

curl_url_cleanup(browser.url);

free(browser.page_title);

free(browser.plain_url);

diff --git a/src/parser.c b/src/parser.c

index ad2c0e6306872f8dde450063ba8815e2ee7e314a..6f12456ddbe5248b59ea146464a1d76365505c56 100644

--- a/src/parser.c

+++ b/src/parser.c

@@ -1,21 +1,23 @@

#include <assert.h>

#include <ctype.h>

-#include <openssl/bio.h>

#include <stdbool.h>

#include <stddef.h>

+#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <gmni/gmni.h>

void

-gemini_parser_init(struct gemini_parser *p, BIO *f)

+gemini_parser_init(struct gemini_parser *p,

{

p->bufln = 0;

p->bufsz = BUFSIZ;

p->buf = malloc(p->bufsz + 1);

p->buf[0] = 0;

p->preformatted = false;

}

@@ -25,7 +27,6 @@ {

if (!p) {

	return;

}

free(p->buf);

}

@@ -42,7 +43,7 @@ p->buf = realloc(p->buf, p->bufsz);

		assert(p->buf);

	}

	if (n == -1) {

		return -1;

	} else if (n == 0) {

diff --git a/src/tofu.c b/src/tofu.c

index 48395c08cdbf68b31c5defd15b360f1eb2897a3f..54183a79278de45bfbe5a1f8ddbcbf10be1c01c4 100644

--- a/src/tofu.c

+++ b/src/tofu.c

@@ -1,95 +1,92 @@

#include <assert.h>

+#include <bearssl.h>

#include <errno.h>

+#include <gmni/gmni.h>

+#include <gmni/tofu.h>

#include <libgen.h>

#include <limits.h>

-#include <openssl/asn1.h>

-#include <openssl/evp.h>

-#include <openssl/ssl.h>

-#include <openssl/x509.h>

-#include <openssl/x509v3.h>

+#include <stdint.h>

#include <stdio.h>

+#include <stdlib.h>

#include <string.h>

-#include <time.h>

-#include <gmni/gmni.h>

-#include <gmni/tofu.h>

#include "util.h"

-static int

-verify_callback(X509_STORE_CTX *ctx, void *data)

+static void

+xt_start_chain(const br_x509_class **ctx, const char *server_name)

{

+}

+static void

+xt_start_cert(const br_x509_class **ctx, uint32_t length)

+{

}

}

+}

+static void

+xt_append(const br_x509_class **ctx, const unsigned char *buf, size_t len)

+{

}

}

+}

+static void

+xt_end_cert(const br_x509_class **ctx)

+{

}

+}

+static unsigned

+xt_end_chain(const br_x509_class **ctx)

+{

}

}

enum tofu_error error = TOFU_UNTRUSTED_CERT;

while (host) {

		goto next;

	}

	if (strcmp(host->fingerprint, fingerprint) == 0) {

@@ -102,66 +99,84 @@ next:

	host = host->next;

}

-callback:

case TOFU_ASK:

	assert(0); // Invariant

case TOFU_FAIL:

case TOFU_TRUST_ONCE:

	// No further action necessary

	return 0;

case TOFU_TRUST_ALWAYS:;

	if (!f) {

		fprintf(stderr, "Error opening %s for writing: %s\n",

		break;

	};

	fclose(f);

	host = calloc(1, sizeof(struct known_host));

	host->fingerprint = strdup(fingerprint);

	return 0;

}

+}

-invalid_cert:

+static const br_x509_pkey *

+xt_get_pkey(const br_x509_class *const *ctx, unsigned *usages)

+{

}

+const br_x509_class xt_vtable = {

+};

+static void

+x509_init_tofu(struct x509_tofu_context *ctx, struct gemini_tofu *store)

+{

+}

void

-gemini_tofu_init(struct gemini_tofu *tofu,

+gemini_tofu_init(struct gemini_tofu *tofu, tofu_callback_t *cb, void *cb_data)

{

const struct pathspec paths[] = {

	{.var = "GMNIDATA", .path = "/%s"},

};

char *path_fmt = getpath(paths, sizeof(paths) / sizeof(paths[0]));

char dname[PATH_MAX+1];

size_t n = 0;

assert(n < sizeof(tofu->known_hosts_path));

strncpy(dname, dirname(tofu->known_hosts_path), sizeof(dname)-1);

@@ -179,10 +194,17 @@ free(path_fmt);

tofu->callback = cb;

tofu->cb_data = cb_data;

tofu->known_hosts = NULL;

FILE *f = fopen(tofu->known_hosts_path, "r");

if (!f) {

	return;

@@ -191,6 +213,11 @@ n = 0;

int lineno = 1;

char *line = NULL;

while (getline(&line, &n, f) != -1) {

	struct known_host *host = calloc(1, sizeof(struct known_host));

	char *tok = strtok(line, " ");

	assert(tok);

@@ -207,10 +234,6 @@

	tok = strtok(NULL, " ");

	assert(tok);

	host->fingerprint = strdup(tok);

	host->lineno = lineno++;

diff --git a/src/util.c b/src/util.c

index 2f62c29ce40ac012993e1d208d65491b56d204aa..8441b584ff199ffd81472539a59acce5d0a18f42 100644

--- a/src/util.c

+++ b/src/util.c

@@ -1,5 +1,7 @@

#include <assert.h>

+#include <bearssl.h>

#include <errno.h>

+#include <gmni/gmni.h>

#include <libgen.h>

#include <limits.h>

#include <stdint.h>

@@ -7,10 +9,9 @@ #include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <sys/stat.h>

-#include <gmni/gmni.h>

#include "util.h"

-static void

+void

posix_dirname(char *path, char *dname)

{

char p[PATH_MAX+1];

@@ -82,7 +83,7 @@ }

fprintf(out, "Downloading %s to %s\n", url, path);

char buf[BUFSIZ];

for (int n = 1; n > 0;) {

	if (n == -1) {

		fprintf(stderr, "Error: read\n");

		return 1;

Proxy Information
Original URL
gemini://gmn.clttr.info:1965/sources/cgmnlm.git/commits/dbc726616e6675ae82d9bb55be5693371255ed2f.patch
Status Code
Success (20)
Meta
text/gemini
Capsule Response Time
261.743743 milliseconds
Gemini-to-HTML Time
16.0989 milliseconds

This content has been proxied by September (ba2dc).