commit f48e3b85a97ab6dbf808559c2231d0ab6344ba66 from: Omar Polo date: Mon May 24 13:48:55 2021 UTC move gg to regress commit - 0d047efcb40ec311da407e0b705a92f764c96338 commit + f48e3b85a97ab6dbf808559c2231d0ab6344ba66 blob - 4d7c6250a5962aa691682a24e88a796da0278bd1 blob + df68f5fe15728c02a7547eefaa8923a40092d37f --- .gitignore +++ .gitignore @@ -25,3 +25,4 @@ regress/fill-file regress/iri_test regress/puny-test regress/*.o +regress/gg blob - 521c5b2b4b0603cebeffbf3e66f11a72e61a93ce blob + 5f91bd7d1cc61800f8d3f469a0d004975abecb3e --- Makefile +++ Makefile @@ -20,9 +20,6 @@ OBJS = ${SRCS:.c=.o} lex.yy.o y.tab.o ${COMPAT} gmid: ${OBJS} ${CC} ${OBJS} -o gmid ${LDFLAGS} -gg: gg.o iri.o utf8.o ${COMPAT} - ${CC} gg.o iri.o utf8.o ${COMPAT} -o $@ ${LDFLAGS} - static: ${OBJS} ${CC} ${OBJS} -o gmid ${LDFLAGS} ${STATIC} @@ -30,11 +27,11 @@ TAGS: ${SRCS} @(etags ${SRCS} || true) 2>/dev/null clean: - rm -f *.o compat/*.o lex.yy.c y.tab.c y.tab.h y.output gmid gg + rm -f *.o compat/*.o lex.yy.c y.tab.c y.tab.h y.output gmid rm -f compile_flags.txt make -C regress clean -regress: gmid gg +regress: gmid make -C regress all install: gmid blob - ad47822a9b871beab8e34e4ad26466b12203fa71 (mode 644) blob + /dev/null --- gg.1 +++ /dev/null @@ -1,70 +0,0 @@ -.\" Copyright (c) 2021 Omar Polo -.\" -.\" 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. -.Dd $Mdocdate: January 23 2021$ -.Dt GG 1 -.Os -.Sh NAME -.Nm gg -.Nd simple Gemini client -.Sh SYNOPSIS -.Nm -.Bk -words -.Op Fl 23bchNVv -.Op Fl C Pa cert.pem Fl K Pa key.pem -.Op Fl H Ar hostname -.Ar IRI -.Ek -.Sh DESCRIPTION -.Nm -is a simple Gemini client. -It fetches the Gemini page given and prints the server response to -standard output. -The option are as follows: -.Bl -tag -width 6m -.It Fl 2 -Use only TLSv1.2. -.It Fl 3 -Use only TLSv1.3. -.It Fl b -Print only the body of the response. -.It Fl C Pa cert.pem -Load the client certificate, must be in PEM format. -.It Fl c -Print only the response code. -.It Fl H Ar hostname -Use the given -.Ar hostname -for SNI, instead of the one extracted from the IRI. -The IRI hostname will still be used for the DNS resolution. -.It Fl h -Print only the response header. -.It Fl K Pa key.pem -Load the client certificate key, must be in PEM format. -.It Fl N -Don't check whether the peer certificate name matches the requested -hostname. -.It Fl V -Only validate the IRI, don't do the Gemini transaction. -.It Fl v -Print also the request. -.El -.Pp -Note that -.Nm -won't try to do TOFU (Trust On First Use) or any X.509 certificate -validation: it will happily accept any certificate it is given. -.Pp -By default -.Nm -will accept both TLSv1.2 and TLSv1.3 and will always do SNI. blob - 8a1e7eb445d39a1bc140c48d929f6d0415e3f0c2 (mode 644) blob + /dev/null --- gg.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (c) 2021 Omar Polo - * - * 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 "gmid.h" - -#include - -int flag2, flag3, bflag, cflag, hflag, Nflag, Vflag, vflag; -const char *cert, *key; - -int -main(int argc, char **argv) -{ - struct iri iri; - struct tls_config *conf; - struct tls *ctx; - char iribuf[GEMINI_URL_LEN], buf[GEMINI_URL_LEN]; - const char *parse_err = "unknown error", *port = "1965"; - const char *hostname; - char *t; - int ch; - int handshake; - ssize_t len; - - hostname = NULL; - while ((ch = getopt(argc, argv, "23C:cbH:hK:NVv")) != -1) { - switch (ch) { - case '2': - flag2 = 1; - break; - case '3': - flag3 = 1; - break; - case 'b': - bflag = 1; - break; - case 'C': - cert = optarg; - break; - case 'c': - cflag = 1; - break; - case 'H': - hostname = optarg; - break; - case 'h': - hflag = 1; - break; - case 'K': - key = optarg; - break; - case 'N': - Nflag = 1; - break; - case 'V': - Vflag = 1; - break; - case 'v': - vflag = 1; - break; - default: - fprintf(stderr, "USAGE: %s [-23cbhNVv] [-H hostname]\n", - *argv); - return 1; - } - } - argc -= optind; - argv += optind; - - if ((bflag + cflag + hflag + Vflag) > 1) - errx(1, "only one of bchr flags can be used."); - - if (flag2 + flag3 > 1) - errx(1, "only -2 or -3 can be specified at the same time."); - - if ((cert != NULL && key == NULL) || (cert == NULL && key != NULL)) - errx(1, "missing certificate or key"); - - if (argc != 1) - errx(1, "missing IRI"); - - if (strlcpy(iribuf, argv[0], sizeof(iribuf)) >= sizeof(iribuf)) - errx(1, "request too long: %s", argv[0]); - if (strlcpy(buf, argv[0], sizeof(buf)) >= sizeof(iribuf)) - errx(1, "request too long: %s", argv[0]); - if (strlcat(buf, "\r\n", sizeof(buf)) >= sizeof(buf)) - errx(1, "request too long: %s", argv[0]); - - if (!parse_iri(iribuf, &iri, &parse_err)) - errx(1, "invalid IRI: %s", parse_err); - - if (Vflag) - errx(0, "IRI: OK"); - - if ((conf = tls_config_new()) == NULL) - errx(1, "tls_config_new"); - - tls_config_insecure_noverifycert(conf); - if (Nflag) - tls_config_insecure_noverifyname(conf); - - if (flag2 && tls_config_set_protocols(conf, TLS_PROTOCOL_TLSv1_2) == -1) - errx(1, "cannot set TLSv1.2"); - if (flag3 && tls_config_set_protocols(conf, TLS_PROTOCOL_TLSv1_3) == -1) - errx(1, "cannot set TLSv1.3"); - - if (cert != NULL && tls_config_set_keypair_file(conf, cert, key)) - errx(1, "couldn't load cert: %s", cert); - - if ((ctx = tls_client()) == NULL) - errx(1, "tls_client creation failed"); - - if (tls_configure(ctx, conf) == -1) - errx(1, "tls_configure: %s", tls_error(ctx)); - - if (*iri.port != '\0') - port = iri.port; - - if (hostname == NULL) - hostname = iri.host; - - if (tls_connect_servername(ctx, iri.host, port, hostname) == -1) - errx(1, "tls_connect: %s", tls_error(ctx)); - - for (handshake = 0; !handshake;) { - switch (tls_handshake(ctx)) { - case 0: - case -1: - handshake = 1; - break; - } - } - - if (vflag) - printf("%s", buf); - if (tls_write(ctx, buf, strlen(buf)) == -1) - errx(1, "tls_write: %s", tls_error(ctx)); - - for (;;) { - switch (len = tls_read(ctx, buf, sizeof(buf))) { - case 0: - case -1: - goto end; - case TLS_WANT_POLLIN: - case TLS_WANT_POLLOUT: - continue; - } - - if (bflag) { - bflag = 0; - if ((t = strchr(buf, '\r')) != NULL) - t += 2; - else if ((t = strchr(buf, '\n')) != NULL) - t += 1; - else - continue; - len -= t - buf; - write(1, t, len); - continue; - } - - if (cflag) { - write(1, buf, 2); - write(1, "\n", 1); - break; - } - - if (hflag) { - t = strchr(buf, '\r'); - if (t == NULL) - t = strchr(buf, '\n'); - if (t == NULL) - t = &buf[len]; - write(1, buf, t - buf); - write(1, "\n", 1); - break; - } - - write(1, buf, len); - } -end: - - tls_close(ctx); - tls_free(ctx); - - return 0; -} blob - b485d6498603cf02baede84cf3178ae0c7b112af blob + 4f416d36626c56cd952559923fe8d658465648ce --- regress/Makefile +++ regress/Makefile @@ -2,11 +2,14 @@ include ../Makefile.local .PHONY: all clean runtime -all: puny-test testdata iri_test cert.pem testca.pem valid.crt invalid.cert.pem +all: gg puny-test testdata iri_test cert.pem testca.pem valid.crt invalid.cert.pem ./puny-test ./runtime ./iri_test +gg: gg.o ../iri.o ../utf8.o ${COMPAT} + ${CC} gg.o ../iri.o ../utf8.o ${COMPAT} -o $@ ${LDFLAGS} + puny-test: puny-test.o ../puny.o ../utf8.o ../utils.o ../log.o ${COMPAT} ${CC} puny-test.o ../puny.o ../utf8.o ../utils.o ../log.o ${COMPAT} \ -o puny-test ${LDFLAGS} @@ -59,7 +62,7 @@ invalid.cert.pem: cert.pem clean: rm -f *.o iri_test cert.pem key.pem rm -f testca.* valid.csr valid.key invalid.*pem - rm -rf testdata fill-file puny-test + rm -rf testdata fill-file puny-test gg testdata: fill-file mkdir testdata blob - /dev/null blob + ad47822a9b871beab8e34e4ad26466b12203fa71 (mode 644) --- /dev/null +++ regress/gg.1 @@ -0,0 +1,70 @@ +.\" Copyright (c) 2021 Omar Polo +.\" +.\" 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. +.Dd $Mdocdate: January 23 2021$ +.Dt GG 1 +.Os +.Sh NAME +.Nm gg +.Nd simple Gemini client +.Sh SYNOPSIS +.Nm +.Bk -words +.Op Fl 23bchNVv +.Op Fl C Pa cert.pem Fl K Pa key.pem +.Op Fl H Ar hostname +.Ar IRI +.Ek +.Sh DESCRIPTION +.Nm +is a simple Gemini client. +It fetches the Gemini page given and prints the server response to +standard output. +The option are as follows: +.Bl -tag -width 6m +.It Fl 2 +Use only TLSv1.2. +.It Fl 3 +Use only TLSv1.3. +.It Fl b +Print only the body of the response. +.It Fl C Pa cert.pem +Load the client certificate, must be in PEM format. +.It Fl c +Print only the response code. +.It Fl H Ar hostname +Use the given +.Ar hostname +for SNI, instead of the one extracted from the IRI. +The IRI hostname will still be used for the DNS resolution. +.It Fl h +Print only the response header. +.It Fl K Pa key.pem +Load the client certificate key, must be in PEM format. +.It Fl N +Don't check whether the peer certificate name matches the requested +hostname. +.It Fl V +Only validate the IRI, don't do the Gemini transaction. +.It Fl v +Print also the request. +.El +.Pp +Note that +.Nm +won't try to do TOFU (Trust On First Use) or any X.509 certificate +validation: it will happily accept any certificate it is given. +.Pp +By default +.Nm +will accept both TLSv1.2 and TLSv1.3 and will always do SNI. blob - /dev/null blob + 7eb698f5700f3ad09a243917ce979f3da2e3dc82 (mode 644) --- /dev/null +++ regress/gg.c @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2021 Omar Polo + * + * 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 "../gmid.h" + +#include + +int flag2, flag3, bflag, cflag, hflag, Nflag, Vflag, vflag; +const char *cert, *key; + +int +main(int argc, char **argv) +{ + struct iri iri; + struct tls_config *conf; + struct tls *ctx; + char iribuf[GEMINI_URL_LEN], buf[GEMINI_URL_LEN]; + const char *parse_err = "unknown error", *port = "1965"; + const char *hostname; + char *t; + int ch; + int handshake; + ssize_t len; + + hostname = NULL; + while ((ch = getopt(argc, argv, "23C:cbH:hK:NVv")) != -1) { + switch (ch) { + case '2': + flag2 = 1; + break; + case '3': + flag3 = 1; + break; + case 'b': + bflag = 1; + break; + case 'C': + cert = optarg; + break; + case 'c': + cflag = 1; + break; + case 'H': + hostname = optarg; + break; + case 'h': + hflag = 1; + break; + case 'K': + key = optarg; + break; + case 'N': + Nflag = 1; + break; + case 'V': + Vflag = 1; + break; + case 'v': + vflag = 1; + break; + default: + fprintf(stderr, "USAGE: %s [-23cbhNVv] [-H hostname]\n", + *argv); + return 1; + } + } + argc -= optind; + argv += optind; + + if ((bflag + cflag + hflag + Vflag) > 1) + errx(1, "only one of bchr flags can be used."); + + if (flag2 + flag3 > 1) + errx(1, "only -2 or -3 can be specified at the same time."); + + if ((cert != NULL && key == NULL) || (cert == NULL && key != NULL)) + errx(1, "missing certificate or key"); + + if (argc != 1) + errx(1, "missing IRI"); + + if (strlcpy(iribuf, argv[0], sizeof(iribuf)) >= sizeof(iribuf)) + errx(1, "request too long: %s", argv[0]); + if (strlcpy(buf, argv[0], sizeof(buf)) >= sizeof(iribuf)) + errx(1, "request too long: %s", argv[0]); + if (strlcat(buf, "\r\n", sizeof(buf)) >= sizeof(buf)) + errx(1, "request too long: %s", argv[0]); + + if (!parse_iri(iribuf, &iri, &parse_err)) + errx(1, "invalid IRI: %s", parse_err); + + if (Vflag) + errx(0, "IRI: OK"); + + if ((conf = tls_config_new()) == NULL) + errx(1, "tls_config_new"); + + tls_config_insecure_noverifycert(conf); + if (Nflag) + tls_config_insecure_noverifyname(conf); + + if (flag2 && tls_config_set_protocols(conf, TLS_PROTOCOL_TLSv1_2) == -1) + errx(1, "cannot set TLSv1.2"); + if (flag3 && tls_config_set_protocols(conf, TLS_PROTOCOL_TLSv1_3) == -1) + errx(1, "cannot set TLSv1.3"); + + if (cert != NULL && tls_config_set_keypair_file(conf, cert, key)) + errx(1, "couldn't load cert: %s", cert); + + if ((ctx = tls_client()) == NULL) + errx(1, "tls_client creation failed"); + + if (tls_configure(ctx, conf) == -1) + errx(1, "tls_configure: %s", tls_error(ctx)); + + if (*iri.port != '\0') + port = iri.port; + + if (hostname == NULL) + hostname = iri.host; + + if (tls_connect_servername(ctx, iri.host, port, hostname) == -1) + errx(1, "tls_connect: %s", tls_error(ctx)); + + for (handshake = 0; !handshake;) { + switch (tls_handshake(ctx)) { + case 0: + case -1: + handshake = 1; + break; + } + } + + if (vflag) + printf("%s", buf); + if (tls_write(ctx, buf, strlen(buf)) == -1) + errx(1, "tls_write: %s", tls_error(ctx)); + + for (;;) { + switch (len = tls_read(ctx, buf, sizeof(buf))) { + case 0: + case -1: + goto end; + case TLS_WANT_POLLIN: + case TLS_WANT_POLLOUT: + continue; + } + + if (bflag) { + bflag = 0; + if ((t = strchr(buf, '\r')) != NULL) + t += 2; + else if ((t = strchr(buf, '\n')) != NULL) + t += 1; + else + continue; + len -= t - buf; + write(1, t, len); + continue; + } + + if (cflag) { + write(1, buf, 2); + write(1, "\n", 1); + break; + } + + if (hflag) { + t = strchr(buf, '\r'); + if (t == NULL) + t = strchr(buf, '\n'); + if (t == NULL) + t = &buf[len]; + write(1, buf, t - buf); + write(1, "\n", 1); + break; + } + + write(1, buf, len); + } +end: + + tls_close(ctx); + tls_free(ctx); + + return 0; +} blob - 02cd09b8190a6bee289f07716da2a20e7c8a1476 blob + 098db1a4668faecb1226eae970a097a09e7c26e7 --- regress/runtime +++ regress/runtime @@ -27,19 +27,19 @@ checkconf() { # usage: get # return the body of the request on stdout get() { - ./../gg -b $ggflags "gemini://localhost:10965/$1" + ./gg -b $ggflags "gemini://localhost:10965/$1" } # usage: head # return the meta response line on stdout head() { - ./../gg -h $ggflags "gemini://localhost:10965/$1" + ./gg -h $ggflags "gemini://localhost:10965/$1" } # usage: raw # return both header and body raw() { - ./../gg $ggflags "gemini://localhost:10965/$1" + ./gg $ggflags "gemini://localhost:10965/$1" } run() {