Blob


1 /*
2 * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include "../gmid.h"
19 #include <string.h>
21 int flag2, flag3, bflag, cflag, hflag, Nflag, Oflag, Vflag, vflag;
22 const char *cert, *key;
24 static void
25 timeout(int signo)
26 {
27 dprintf(2, "%s: timer expired\n", getprogname());
28 exit(1);
29 }
31 int
32 main(int argc, char **argv)
33 {
34 struct iri iri;
35 struct tls_config *conf;
36 struct tls *ctx;
37 char iribuf[GEMINI_URL_LEN], buf[GEMINI_URL_LEN];
38 const char *parse_err = "unknown error", *port = "1965", *errstr;
39 const char *hostname;
40 char *t;
41 int ch, handshake, timer;
42 ssize_t len;
44 hostname = NULL;
45 while ((ch = getopt(argc, argv, "23C:cbH:hK:NOT:Vv")) != -1) {
46 switch (ch) {
47 case '2':
48 flag2 = 1;
49 break;
50 case '3':
51 flag3 = 1;
52 break;
53 case 'b':
54 bflag = 1;
55 break;
56 case 'C':
57 cert = optarg;
58 break;
59 case 'c':
60 cflag = 1;
61 break;
62 case 'H':
63 hostname = optarg;
64 break;
65 case 'h':
66 hflag = 1;
67 break;
68 case 'K':
69 key = optarg;
70 break;
71 case 'N':
72 Nflag = 1;
73 break;
74 case 'O':
75 Oflag = 1;
76 break;
77 case 'T':
78 timer = strtonum(optarg, 1, 1000, &errstr);
79 if (errstr != NULL)
80 errx(1, "timeout is %s: %s", errstr, optarg);
81 signal(SIGALRM, timeout);
82 alarm(timer);
83 break;
84 case 'V':
85 Vflag = 1;
86 break;
87 case 'v':
88 vflag = 1;
89 break;
90 default:
91 fprintf(stderr, "USAGE: %s [-23cbhNVv] [-H hostname]\n",
92 *argv);
93 return 1;
94 }
95 }
96 argc -= optind;
97 argv += optind;
99 if ((bflag + cflag + hflag + Vflag) > 1)
100 errx(1, "only one of bchr flags can be used.");
102 if (flag2 + flag3 > 1)
103 errx(1, "only -2 or -3 can be specified at the same time.");
105 if ((cert != NULL && key == NULL) || (cert == NULL && key != NULL))
106 errx(1, "missing certificate or key");
108 if (argc != 1)
109 errx(1, "missing IRI");
111 if (strlcpy(iribuf, argv[0], sizeof(iribuf)) >= sizeof(iribuf))
112 errx(1, "request too long: %s", argv[0]);
113 if (strlcpy(buf, argv[0], sizeof(buf)) >= sizeof(iribuf))
114 errx(1, "request too long: %s", argv[0]);
115 if (strlcat(buf, "\r\n", sizeof(buf)) >= sizeof(buf))
116 errx(1, "request too long: %s", argv[0]);
118 if (!parse_iri(iribuf, &iri, &parse_err))
119 errx(1, "invalid IRI: %s", parse_err);
121 if (Vflag)
122 errx(0, "IRI: OK");
124 if ((conf = tls_config_new()) == NULL)
125 errx(1, "tls_config_new");
127 tls_config_insecure_noverifycert(conf);
128 if (Nflag)
129 tls_config_insecure_noverifyname(conf);
131 if (Oflag)
132 tls_config_ocsp_require_stapling(conf);
134 if (flag2 && tls_config_set_protocols(conf, TLS_PROTOCOL_TLSv1_2) == -1)
135 errx(1, "cannot set TLSv1.2");
136 if (flag3 && tls_config_set_protocols(conf, TLS_PROTOCOL_TLSv1_3) == -1)
137 errx(1, "cannot set TLSv1.3");
139 if (cert != NULL && tls_config_set_keypair_file(conf, cert, key))
140 errx(1, "couldn't load cert: %s", cert);
142 if ((ctx = tls_client()) == NULL)
143 errx(1, "tls_client creation failed");
145 if (tls_configure(ctx, conf) == -1)
146 errx(1, "tls_configure: %s", tls_error(ctx));
148 if (*iri.port != '\0')
149 port = iri.port;
151 if (hostname == NULL)
152 hostname = iri.host;
154 if (tls_connect_servername(ctx, iri.host, port, hostname) == -1)
155 errx(1, "tls_connect: %s", tls_error(ctx));
157 for (handshake = 0; !handshake;) {
158 switch (tls_handshake(ctx)) {
159 case 0:
160 case -1:
161 handshake = 1;
162 break;
166 if (vflag)
167 printf("%s", buf);
168 if (tls_write(ctx, buf, strlen(buf)) == -1)
169 errx(1, "tls_write: %s", tls_error(ctx));
171 for (;;) {
172 switch (len = tls_read(ctx, buf, sizeof(buf))) {
173 case 0:
174 case -1:
175 goto end;
176 case TLS_WANT_POLLIN:
177 case TLS_WANT_POLLOUT:
178 continue;
181 if (bflag) {
182 bflag = 0;
183 if ((t = strchr(buf, '\r')) != NULL)
184 t += 2;
185 else if ((t = strchr(buf, '\n')) != NULL)
186 t += 1;
187 else
188 continue;
189 len -= t - buf;
190 write(1, t, len);
191 continue;
194 if (cflag) {
195 write(1, buf, 2);
196 write(1, "\n", 1);
197 break;
200 if (hflag) {
201 t = strchr(buf, '\r');
202 if (t == NULL)
203 t = strchr(buf, '\n');
204 if (t == NULL)
205 t = &buf[len];
206 write(1, buf, t - buf);
207 write(1, "\n", 1);
208 break;
211 write(1, buf, len);
213 end:
215 tls_close(ctx);
216 tls_free(ctx);
218 return 0;