commit - 3309ef975cf570aeb5c1dbf568af323c19bbb3c0
commit + 497977d501eb736c37353ec8fa4c83315e025bc0
blob - b7d52b064c5a8f523cf3ffa962700cd24390f97d
blob + c7d3334c6fe6f05dfcdfa0484b5e093c2ce6b3bc
--- .gitignore
+++ .gitignore
*.pem
TAGS
gmid
-iri_test
+gg
*.o
docs
lex.yy.c
blob - e04c1502119be3c88b3e03c80358647e4a966d3a
blob + b2626f532aead29639a199670ca7ff58c1653f25
--- Makefile
+++ Makefile
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
+.\" Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+.\"
+.\" 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
+/*
+ * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ *
+ * 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 <string.h>
+
+#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;
+}