commit 497977d501eb736c37353ec8fa4c83315e025bc0 from: Omar Polo date: Sat Jan 23 16:56:24 2021 UTC add gg, a barebones Gemini client commit - 3309ef975cf570aeb5c1dbf568af323c19bbb3c0 commit + 497977d501eb736c37353ec8fa4c83315e025bc0 blob - b7d52b064c5a8f523cf3ffa962700cd24390f97d blob + c7d3334c6fe6f05dfcdfa0484b5e093c2ce6b3bc --- .gitignore +++ .gitignore @@ -1,7 +1,7 @@ *.pem TAGS gmid -iri_test +gg *.o docs lex.yy.c blob - e04c1502119be3c88b3e03c80358647e4a966d3a blob + b2626f532aead29639a199670ca7ff58c1653f25 --- Makefile +++ Makefile @@ -19,6 +19,9 @@ 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 + ${CC} gg.o iri.o utf8.o -o $@ ${LDFLAGS} + static: ${OBJS} ${CC} -static ${OBJS} \ ${LIBDIR}/libcrypto.a ${LIBDIR}/libtls.a ${LIBDIR}/libssl.a \ blob - /dev/null blob + 4c2344bfbe79b5859348bdf0436eadada4d6b4d3 (mode 644) --- /dev/null +++ gg.1 @@ -0,0 +1,57 @@ +.\" 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 23bchNV +.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 +Print only the response code. +.It Fl h +Print only the response header. +.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. +.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 + 376bc495c4e7af050c5bac2301dc3037d34b6a48 (mode 644) --- /dev/null +++ gg.c @@ -0,0 +1,156 @@ +/* + * 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 + +#include "gmid.h" + +int +main(int argc, char **argv) +{ + struct iri iri; + struct tls_config *conf; + struct tls *ctx; + char iribuf[GEMINI_URL_LEN], req[GEMINI_URL_LEN], buf[1024]; + const char *parse_err = "unknown error", *port = "1965"; + char *t; + int ch, flag2, flag3, bflag, cflag, hflag, Nflag, Vflag; + ssize_t len; + + flag2 = flag3 = bflag = cflag = hflag = Nflag = Vflag = 0; + while ((ch = getopt(argc, argv, "23cbhNV")) != -1) { + switch (ch) { + case '2': + flag2 = 1; + break; + case '3': + flag3 = 1; + break; + case 'b': + bflag = 1; + break; + case 'c': + cflag = 1; + break; + case 'h': + hflag = 1; + break; + case 'N': + Nflag = 1; + break; + case 'V': + Vflag = 1; + break; + default: + fprintf(stderr, "USAGE: %s [-23cbhNV]", *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 (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(req, argv[0], sizeof(req)) >= sizeof(iribuf)) + errx(1, "request too long: %s", argv[0]); + if (strlcat(req, "\r\n", sizeof(req)) >= sizeof(req)) + 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 ((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') + iri.port = (char*)port; + if (tls_connect(ctx, iri.host, iri.port) == -1) + errx(1, "tls_connect: %s", tls_error(ctx)); + + tls_write(ctx, req, strlen(req)); + /* if (tls_write(ctx, req, strlen(req)) != -1) */ + /* errx(1, "tls_write: %s", tls_error(ctx)); */ + + for (;;) { + len = tls_read(ctx, buf, sizeof(buf)); + if (len == 0 || len == -1) + break; + + 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); + } + + tls_close(ctx); + tls_free(ctx); + + return 0; +}