2 5c7abf01 2021-12-29 op * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
4 5c7abf01 2021-12-29 op * Permission to use, copy, modify, and distribute this software for any
5 5c7abf01 2021-12-29 op * purpose with or without fee is hereby granted, provided that the above
6 5c7abf01 2021-12-29 op * copyright notice and this permission notice appear in all copies.
8 5c7abf01 2021-12-29 op * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 5c7abf01 2021-12-29 op * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 5c7abf01 2021-12-29 op * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 5c7abf01 2021-12-29 op * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 5c7abf01 2021-12-29 op * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 5c7abf01 2021-12-29 op * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 5c7abf01 2021-12-29 op * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 5c7abf01 2021-12-29 op #include "gmid.h"
19 5c7abf01 2021-12-29 op #include <sys/socket.h>
21 5c7abf01 2021-12-29 op #include <assert.h>
22 5c7abf01 2021-12-29 op #include <ctype.h>
23 5c7abf01 2021-12-29 op #include <errno.h>
24 5c7abf01 2021-12-29 op #include <string.h>
36 5c7abf01 2021-12-29 op int dont_verify_name;
40 5c7abf01 2021-12-29 op int redirects = 5;
42 5c7abf01 2021-12-29 op const char *cert;
43 5c7abf01 2021-12-29 op const char *key;
44 5c7abf01 2021-12-29 op const char *proxy_host;
45 5c7abf01 2021-12-29 op const char *proxy_port;
46 5c7abf01 2021-12-29 op const char *sni;
49 5c7abf01 2021-12-29 op struct tls_config *tls_conf;
52 5c7abf01 2021-12-29 op timeout(int signo)
54 5c7abf01 2021-12-29 op dprintf(2, "%s: timer expired\n", getprogname());
59 5c7abf01 2021-12-29 op load_tls_conf(void)
61 5c7abf01 2021-12-29 op if ((tls_conf = tls_config_new()) == NULL)
62 5c7abf01 2021-12-29 op err(1, "tls_config_new");
64 5c7abf01 2021-12-29 op tls_config_insecure_noverifycert(tls_conf);
65 5c7abf01 2021-12-29 op if (dont_verify_name)
66 5c7abf01 2021-12-29 op tls_config_insecure_noverifyname(tls_conf);
69 5c7abf01 2021-12-29 op tls_config_set_protocols(tls_conf, TLS_PROTOCOL_TLSv1_2) == -1)
70 5c7abf01 2021-12-29 op errx(1, "can't set TLSv1.2");
72 5c7abf01 2021-12-29 op tls_config_set_protocols(tls_conf, TLS_PROTOCOL_TLSv1_3) == -1)
73 5c7abf01 2021-12-29 op errx(1, "can't set TLSv1.3");
75 5c7abf01 2021-12-29 op if (cert != NULL &&
76 5c7abf01 2021-12-29 op tls_config_set_keypair_file(tls_conf, cert, key) == -1)
77 5c7abf01 2021-12-29 op errx(1, "can't load client certificate %s", cert);
81 5c7abf01 2021-12-29 op connectto(struct tls *ctx, const char *host, const char *port)
83 5c7abf01 2021-12-29 op struct addrinfo hints, *res, *res0;
85 5c7abf01 2021-12-29 op int saved_errno;
87 5c7abf01 2021-12-29 op const char *cause = NULL;
88 5c7abf01 2021-12-29 op const char *sname;
90 5c7abf01 2021-12-29 op if (proxy_host != NULL) {
91 5c7abf01 2021-12-29 op host = proxy_host;
92 5c7abf01 2021-12-29 op port = proxy_port;
95 5c7abf01 2021-12-29 op if ((sname = sni) == NULL)
98 5c7abf01 2021-12-29 op memset(&hints, 0, sizeof(hints));
99 5c7abf01 2021-12-29 op hints.ai_family = AF_UNSPEC;
100 5c7abf01 2021-12-29 op hints.ai_socktype = SOCK_STREAM;
101 5c7abf01 2021-12-29 op error = getaddrinfo(host, port, &hints, &res0);
103 5c7abf01 2021-12-29 op errx(1, "%s", gai_strerror(error));
106 5c7abf01 2021-12-29 op for (res = res0; res != NULL; res = res->ai_next) {
107 5c7abf01 2021-12-29 op s = socket(res->ai_family, res->ai_socktype,
108 5c7abf01 2021-12-29 op res->ai_protocol);
109 5c7abf01 2021-12-29 op if (s == -1) {
110 5c7abf01 2021-12-29 op cause = "socket";
114 5c7abf01 2021-12-29 op if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
115 5c7abf01 2021-12-29 op cause = "connect";
116 5c7abf01 2021-12-29 op saved_errno = errno;
118 5c7abf01 2021-12-29 op errno = saved_errno;
127 5c7abf01 2021-12-29 op err(1, "%s: can't connect to %s:%s", cause,
130 5c7abf01 2021-12-29 op freeaddrinfo(res0);
132 5c7abf01 2021-12-29 op if (tls_connect_socket(ctx, s, sname) == -1)
133 5c7abf01 2021-12-29 op errx(1, "tls_connect_socket: %s", tls_error(ctx));
137 5c7abf01 2021-12-29 op doreq(struct tls *ctx, const char *buf)
142 5c7abf01 2021-12-29 op s = strlen(buf);
143 5c7abf01 2021-12-29 op while (s != 0) {
144 5c7abf01 2021-12-29 op switch (w = tls_write(ctx, buf, s)) {
147 5c7abf01 2021-12-29 op errx(1, "tls_write: %s", tls_error(ctx));
148 5c7abf01 2021-12-29 op case TLS_WANT_POLLIN:
149 5c7abf01 2021-12-29 op case TLS_WANT_POLLOUT:
158 5c7abf01 2021-12-29 op static size_t
159 5c7abf01 2021-12-29 op dorep(struct tls *ctx, void *buf, size_t len)
162 5c7abf01 2021-12-29 op size_t tot = 0;
164 5c7abf01 2021-12-29 op while (len != 0) {
165 5c7abf01 2021-12-29 op switch (w = tls_read(ctx, buf, len)) {
169 5c7abf01 2021-12-29 op errx(1, "tls_write: %s", tls_error(ctx));
170 5c7abf01 2021-12-29 op case TLS_WANT_POLLIN:
171 5c7abf01 2021-12-29 op case TLS_WANT_POLLOUT:
184 5c7abf01 2021-12-29 op get(const char *r)
186 5c7abf01 2021-12-29 op struct tls *ctx;
187 5c7abf01 2021-12-29 op struct iri iri;
188 5c7abf01 2021-12-29 op int foundhdr = 0, code = -1, od;
189 5c7abf01 2021-12-29 op char iribuf[GEMINI_URL_LEN];
190 5c7abf01 2021-12-29 op char req[GEMINI_URL_LEN];
191 5c7abf01 2021-12-29 op char buf[2048];
192 5c7abf01 2021-12-29 op const char *parse_err, *host, *port;
194 5c7abf01 2021-12-29 op if (strlcpy(iribuf, r, sizeof(iribuf)) >= sizeof(iribuf))
195 5c7abf01 2021-12-29 op errx(1, "iri too long: %s", r);
197 5c7abf01 2021-12-29 op if (strlcpy(req, r, sizeof(req)) >= sizeof(req))
198 5c7abf01 2021-12-29 op errx(1, "iri too long: %s", r);
200 5c7abf01 2021-12-29 op if (strlcat(req, "\r\n", sizeof(req)) >= sizeof(req))
201 5c7abf01 2021-12-29 op errx(1, "iri too long: %s", r);
203 5c7abf01 2021-12-29 op if (!parse_iri(iribuf, &iri, &parse_err))
204 5c7abf01 2021-12-29 op errx(1, "invalid IRI: %s", parse_err);
207 5c7abf01 2021-12-29 op errx(0, "IRI OK");
209 5c7abf01 2021-12-29 op if ((ctx = tls_client()) == NULL)
210 5c7abf01 2021-12-29 op errx(1, "can't create tls context");
212 5c7abf01 2021-12-29 op if (tls_configure(ctx, tls_conf) == -1)
213 5c7abf01 2021-12-29 op errx(1, "tls_configure: %s", tls_error(ctx));
215 5c7abf01 2021-12-29 op host = iri.host;
216 5c7abf01 2021-12-29 op port = "1965";
217 5c7abf01 2021-12-29 op if (*iri.port != '\0')
218 5c7abf01 2021-12-29 op port = iri.port;
220 5c7abf01 2021-12-29 op connectto(ctx, host, port);
223 5c7abf01 2021-12-29 op while (!od) {
224 5c7abf01 2021-12-29 op switch (tls_handshake(ctx)) {
229 5c7abf01 2021-12-29 op errx(1, "handshake: %s", tls_error(ctx));
233 5c7abf01 2021-12-29 op doreq(ctx, req);
239 5c7abf01 2021-12-29 op len = dorep(ctx, buf, sizeof(buf));
240 5c7abf01 2021-12-29 op if (len == 0)
243 5c7abf01 2021-12-29 op if (foundhdr) {
244 5c7abf01 2021-12-29 op write(1, buf, len);
247 5c7abf01 2021-12-29 op foundhdr = 1;
249 5c7abf01 2021-12-29 op if (memmem(buf, len, "\r\n", 2) == NULL)
250 5c7abf01 2021-12-29 op errx(1, "invalid reply: no \\r\\n");
251 5c7abf01 2021-12-29 op if (!isdigit(buf[0]) || !isdigit(buf[1]) || buf[2] != ' ')
252 5c7abf01 2021-12-29 op errx(1, "invalid reply: invalid response format");
254 5c7abf01 2021-12-29 op code = (buf[0] - '0') * 10 + buf[1] - '0';
256 5c7abf01 2021-12-29 op if (debug == DEBUG_CODE) {
257 5c7abf01 2021-12-29 op printf("%d\n", code);
261 5c7abf01 2021-12-29 op if (debug == DEBUG_HEADER) {
262 5c7abf01 2021-12-29 op t = memmem(buf, len, "\r\n", 2);
263 5c7abf01 2021-12-29 op assert(t != NULL);
265 5c7abf01 2021-12-29 op printf("%s\n", buf);
269 5c7abf01 2021-12-29 op if (debug == DEBUG_META) {
270 5c7abf01 2021-12-29 op t = memmem(buf, len, "\r\n", 2);
271 5c7abf01 2021-12-29 op assert(t != NULL);
273 5c7abf01 2021-12-29 op printf("%s\n", buf+3);
277 e89f4739 2022-01-27 op if (debug == DEBUG_ALL) {
278 5c7abf01 2021-12-29 op write(1, buf, len);
282 5c7abf01 2021-12-29 op /* skip the header */
283 5c7abf01 2021-12-29 op t = memmem(buf, len, "\r\n", 2);
284 5c7abf01 2021-12-29 op assert(t != NULL);
285 5c7abf01 2021-12-29 op t += 2; /* skip \r\n */
286 5c7abf01 2021-12-29 op len -= t - buf;
287 5c7abf01 2021-12-29 op write(1, t, len);
291 5c7abf01 2021-12-29 op od = tls_close(ctx);
292 5c7abf01 2021-12-29 op if (od == TLS_WANT_POLLIN || od == TLS_WANT_POLLOUT)
295 5c7abf01 2021-12-29 op tls_close(ctx);
296 5c7abf01 2021-12-29 op tls_free(ctx);
300 5c7abf01 2021-12-29 op static void __attribute__((noreturn))
303 c5b4db93 2022-09-10 op fprintf(stderr, "version: " GG_STRING "\n");
304 21cf735f 2022-10-30 op fprintf(stderr, "usage: %s [-23Nn] [-C cert] [-d mode] [-H sni] "
305 febfcde8 2022-01-13 op "[-K key] [-P host[:port]]\n",
306 5c7abf01 2021-12-29 op getprogname());
307 5c7abf01 2021-12-29 op fprintf(stderr, " [-T seconds] gemini://...\n");
312 5c7abf01 2021-12-29 op parse_debug(const char *arg)
314 5c7abf01 2021-12-29 op if (!strcmp(arg, "none"))
315 5c7abf01 2021-12-29 op return DEBUG_NONE;
316 5c7abf01 2021-12-29 op if (!strcmp(arg, "code"))
317 5c7abf01 2021-12-29 op return DEBUG_CODE;
318 5c7abf01 2021-12-29 op if (!strcmp(arg, "header"))
319 5c7abf01 2021-12-29 op return DEBUG_HEADER;
320 5c7abf01 2021-12-29 op if (!strcmp(arg, "meta"))
321 5c7abf01 2021-12-29 op return DEBUG_META;
322 e89f4739 2022-01-27 op if (!strcmp(arg, "all"))
323 e89f4739 2022-01-27 op return DEBUG_ALL;
328 5c7abf01 2021-12-29 op parse_proxy(const char *arg)
332 5c7abf01 2021-12-29 op if ((proxy_host = strdup(arg)) == NULL)
333 5c7abf01 2021-12-29 op err(1, "strdup");
335 5c7abf01 2021-12-29 op proxy_port = "1965";
337 5c7abf01 2021-12-29 op if ((at = strchr(proxy_host, ':')) == NULL)
340 5c7abf01 2021-12-29 op proxy_port = ++at;
342 5c7abf01 2021-12-29 op if (strchr(proxy_port, ':') != NULL)
343 5c7abf01 2021-12-29 op errx(1, "invalid port %s", proxy_port);
347 5c7abf01 2021-12-29 op main(int argc, char **argv)
349 5c7abf01 2021-12-29 op int ch, code;
350 5c7abf01 2021-12-29 op const char *errstr;
352 21cf735f 2022-10-30 op while ((ch = getopt(argc, argv, "23C:d:H:K:NP:T:")) != -1) {
353 5c7abf01 2021-12-29 op switch (ch) {
361 5c7abf01 2021-12-29 op cert = optarg;
364 5c7abf01 2021-12-29 op debug = parse_debug(optarg);
367 5c7abf01 2021-12-29 op sni = optarg;
370 5c7abf01 2021-12-29 op key = optarg;
373 5c7abf01 2021-12-29 op dont_verify_name = 1;
379 5c7abf01 2021-12-29 op parse_proxy(optarg);
380 5c7abf01 2021-12-29 op dont_verify_name = 1;
383 5c7abf01 2021-12-29 op timer = strtonum(optarg, 1, 1000, &errstr);
384 5c7abf01 2021-12-29 op if (errstr != NULL)
385 5c7abf01 2021-12-29 op errx(1, "timeout is %s: %s",
386 5c7abf01 2021-12-29 op errstr, optarg);
387 5c7abf01 2021-12-29 op signal(SIGALRM, timeout);
388 5c7abf01 2021-12-29 op alarm(timer);
394 5c7abf01 2021-12-29 op argc -= optind;
395 5c7abf01 2021-12-29 op argv += optind;
397 5c7abf01 2021-12-29 op if (flag2 + flag3 > 1) {
398 5c7abf01 2021-12-29 op warnx("only -2 or -3 can be specified at the same time");
402 5c7abf01 2021-12-29 op if ((cert != NULL && key == NULL) ||
403 5c7abf01 2021-12-29 op (cert == NULL && key != NULL)) {
404 5c7abf01 2021-12-29 op warnx("cert or key is missing");
408 b3602923 2022-01-13 op if (argc != 1)
411 5c7abf01 2021-12-29 op load_tls_conf();
413 b3602923 2022-01-13 op signal(SIGPIPE, SIG_IGN);
415 5c7abf01 2021-12-29 op #ifdef __OpenBSD__
416 5c7abf01 2021-12-29 op if (pledge("stdio inet dns", NULL) == -1)
417 5c7abf01 2021-12-29 op err(1, "pledge");
420 5c7abf01 2021-12-29 op code = get(*argv);
422 5c7abf01 2021-12-29 op return code < 20 || code >= 30;