~ft/gemnine

3a91d0da6accefe72f46e3c7d149ef3d5cfd759e — Sigrid Haflínudóttir 2 years ago 54f4cf6
fix queries, split request/response code
5 files changed, 141 insertions(+), 135 deletions(-)

M gemnine.h
M main.c
M mkfile
A req.c
M url.c
M gemnine.h => gemnine.h +24 -2
@@ 1,7 1,15 @@
typedef struct Response Response;
typedef struct Url Url;

struct Url
{
struct Response {
	Url *url;
	char *mime;
	char *prompt;
	int status;
	int fd;
};

struct Url {
	char *full;
	char *scheme;
	char *user;


@@ 13,8 21,21 @@ struct Url
	char *fragment;
};

typedef struct {
	char *s1;
	char *s2;
}Str2;

#pragma varargck type "U" Url*
#pragma varargck type "E" Str2
#pragma varargck type "N" char*
#pragma varargck type "]" char*

/* req.c */
Response *request(Url *url);
void freeresponse(Response *r);

/* url.c */
int	Efmt(Fmt*);
int	Nfmt(Fmt*);
int	Mfmt(Fmt*);


@@ 24,5 45,6 @@ int matchurl(Url *u, Url *s);
void freeurl(Url *u);
char *Upath(Url *u);

/* util.c */
void *emalloc(int n);
char *estrdup(char *s);

M main.c => main.c +2 -124
@@ 1,132 1,10 @@
#include <u.h>
#include <libc.h>
#include <libsec.h>
#include <bio.h>
#include <ctype.h>
#include <plumb.h>
#include <ctype.h>
#include "gemnine.h"

typedef struct Response Response;

struct Response {
	Url *url;
	char *mime;
	char *prompt;
	int status;
	int fd;
};

#pragma varargck type "E" char*

void
freeresponse(Response *r)
{
	if(r != nil){
		close(r->fd);
		freeurl(r->url);
		free(r->mime);
		free(r->prompt);
		free(r);
	}
}

Response *
request(Url *url)
{
	Thumbprint *th;
	Response *r;
	char *s, buf[1024], *port;
	TLSconn conn;
	int i, ok, len, oldfd;
	Url *u;

	r = calloc(1, sizeof(*r));
	r->fd = -1;
	r->url = url;

	if((port = url->port) == nil)
		port = "1965";
	if((r->fd = dial(netmkaddr(url->host, "tcp", port), nil, nil, nil)) < 0){
		werrstr("dial: %r");
		goto err;
	}
	th = initThumbprints("/sys/lib/ssl/gemini", nil, "x509");
	memset(&conn, 0, sizeof(conn));
	conn.serverName = r->url->host;
	oldfd = r->fd;
	r->fd = tlsClient(oldfd, &conn);
	close(oldfd);
	if(r->fd < 0){
		werrstr("tls: %r");
		goto err;
	}

	/* FIXME find a way to trust on the first run */
	if(th != nil){
		ok = okCertificate(conn.cert, conn.certlen, th);
		freeThumbprints(th);
		if(!ok){
			//fprint(2, "echo 'x509 %r server=%s' >>/sys/lib/ssl/gemini\n", r->url->server);
			//werrstr("untrusted cert");
			//goto err;
		}
	}

	fprint(r->fd, "%s\r\n", r->url->full);
	for(len = 0; len < sizeof(buf)-1; len++){
		if((i = read(r->fd, buf+len, 1)) < 0){
			werrstr("read: %r");
			goto err;
		}
		if(i == 0 || buf[len] == '\n')
			break;
	}

	s = buf;
	s[len] = 0;
	for(len--; len >= 0 && (s[len] == '\r' || s[len] == '\n'); len--)
		s[len] = 0;
	if(s[0] < '0' || s[0] > '9' || s[1] < '0' || s[1] > '9'){
		werrstr("invalid status");
		goto err;
	}
	r->status = 10*(int)(s[0]-'0') + s[1] - '0';
	s += 2;
	while(isspace(*s))
		s++;

	if(r->status >= 10 && r->status < 20){ /* input */
		r->prompt = estrdup(s);
	}else if(r->status >= 20 && r->status < 30){ /* success */
		r->mime = estrdup(s[0] ? s : "text/gemini");
	}else if(r->status >= 30 && r->status < 40){ /* redirect */
		if((u = urlparse(r->url, s)) == nil){
			werrstr("invalid redirect url");
			goto err;
		}
		freeresponse(r);
		if((r = request(u)) == nil)
			freeurl(u);
	}else if(r->status >= 40 && r->status < 50){
		werrstr("temporary failure: %s", s);
		goto err;
	}else if(r->status >= 50 && r->status < 60){
		werrstr("permanent failure: %s", s);
		goto err;
	}else if(r->status >= 60 && r->status < 70){
		werrstr("client cert required: %s", s);
		goto err;
	}

	return r;

err:
	if(r != nil && r->url != nil)
		werrstr("%U: %r", r->url);
	freeresponse(r);
	return nil;
}

char *
readall(int fd)
{


@@ 249,7 127,7 @@ nextreq:
				print("%s\n", r->prompt);
				s = readall(0);
				free(url);
				t = smprint("%s?%E", r->url->full, s);
				t = smprint("%s?%E", r->url->full, (Str2){s, "/:@ \n"});
				free(s);
				url = urlparse(nil, t);
				free(t);

M mkfile => mkfile +1 -0
@@ 9,6 9,7 @@ HFILES=\

OFILES=\
	main.$O\
	req.$O\
	url.$O\
	util.$O\


A req.c => req.c +114 -0
@@ 0,0 1,114 @@
#include <u.h>
#include <libc.h>
#include <libsec.h>
#include <ctype.h>
#include "gemnine.h"

Response *
request(Url *url)
{
	Thumbprint *th;
	Response *r;
	char *s, buf[1024], *port;
	TLSconn conn;
	int i, ok, len, oldfd;
	Url *u;

	r = calloc(1, sizeof(*r));
	r->fd = -1;
	r->url = url;

	if((port = url->port) == nil)
		port = "1965";
	if((r->fd = dial(netmkaddr(url->host, "tcp", port), nil, nil, nil)) < 0){
		werrstr("dial: %r");
		goto err;
	}
	th = initThumbprints("/sys/lib/ssl/gemini", nil, "x509");
	memset(&conn, 0, sizeof(conn));
	conn.serverName = r->url->host;
	oldfd = r->fd;
	r->fd = tlsClient(oldfd, &conn);
	close(oldfd);
	if(r->fd < 0){
		werrstr("tls: %r");
		goto err;
	}

	/* FIXME find a way to trust on the first run */
	if(th != nil){
		ok = okCertificate(conn.cert, conn.certlen, th);
		freeThumbprints(th);
		if(!ok){
			//fprint(2, "echo 'x509 %r server=%s' >>/sys/lib/ssl/gemini\n", r->url->server);
			//werrstr("untrusted cert");
			//goto err;
		}
	}

	fprint(r->fd, "%s\r\n", r->url->full);
	for(len = 0; len < sizeof(buf)-1; len++){
		if((i = read(r->fd, buf+len, 1)) < 0){
			werrstr("read: %r");
			goto err;
		}
		if(i == 0 || buf[len] == '\n')
			break;
	}

	s = buf;
	s[len] = 0;
	for(len--; len >= 0 && (s[len] == '\r' || s[len] == '\n'); len--)
		s[len] = 0;
	if(s[0] < '0' || s[0] > '9' || s[1] < '0' || s[1] > '9'){
		werrstr("invalid status");
		goto err;
	}
	r->status = 10*(int)(s[0]-'0') + s[1] - '0';
	s += 2;
	while(isspace(*s))
		s++;

	if(r->status >= 10 && r->status < 20){ /* input */
		r->prompt = estrdup(s);
	}else if(r->status >= 20 && r->status < 30){ /* success */
		r->mime = estrdup(s[0] ? s : "text/gemini");
	}else if(r->status >= 30 && r->status < 40){ /* redirect */
		if((u = urlparse(r->url, s)) == nil){
			werrstr("invalid redirect url");
			goto err;
		}
		freeresponse(r);
		if((r = request(u)) == nil)
			freeurl(u);
	}else if(r->status >= 40 && r->status < 50){
		werrstr("temporary failure: %s", s);
		goto err;
	}else if(r->status >= 50 && r->status < 60){
		werrstr("permanent failure: %s", s);
		goto err;
	}else if(r->status >= 60 && r->status < 70){
		werrstr("client cert required: %s", s);
		goto err;
	}

	return r;

err:
	if(r != nil && r->url != nil)
		werrstr("%U: %r", r->url);
	freeresponse(r);
	return nil;
}

void
freeresponse(Response *r)
{
	if(r != nil){
		close(r->fd);
		freeurl(r->url);
		free(r->mime);
		free(r->prompt);
		free(r);
	}
}

M url.c => url.c +0 -9
@@ 8,15 8,6 @@ enum {
	Domlen = 256,
};

typedef struct {
	char *s1;
	char *s2;
}Str2;

#pragma varargck type "E" Str2
#pragma varargck type "N" char*
#pragma varargck type "]" char*

static char reserved[] = "%:/?#[]@!$&'()*+,;=";

static int