2 2ff1e2a9 2023-07-22 op * Copyright (c) 2023 Omar Polo <op@omarpolo.com>
4 2ff1e2a9 2023-07-22 op * Permission to use, copy, modify, and distribute this software for any
5 2ff1e2a9 2023-07-22 op * purpose with or without fee is hereby granted, provided that the above
6 2ff1e2a9 2023-07-22 op * copyright notice and this permission notice appear in all copies.
8 2ff1e2a9 2023-07-22 op * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 2ff1e2a9 2023-07-22 op * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 2ff1e2a9 2023-07-22 op * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 2ff1e2a9 2023-07-22 op * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 2ff1e2a9 2023-07-22 op * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 2ff1e2a9 2023-07-22 op * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 2ff1e2a9 2023-07-22 op * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 2ff1e2a9 2023-07-22 op #include "config.h"
19 2ff1e2a9 2023-07-22 op #include <sys/stat.h>
20 2ff1e2a9 2023-07-22 op #include <sys/socket.h>
22 2ff1e2a9 2023-07-22 op #include <errno.h>
23 2ff1e2a9 2023-07-22 op #include <fcntl.h>
24 2ff1e2a9 2023-07-22 op #include <netdb.h>
25 2ff1e2a9 2023-07-22 op #include <poll.h>
26 2ff1e2a9 2023-07-22 op #include <stdio.h>
27 2ff1e2a9 2023-07-22 op #include <stdlib.h>
28 2ff1e2a9 2023-07-22 op #include <string.h>
29 2ff1e2a9 2023-07-22 op #include <tls.h>
30 2ff1e2a9 2023-07-22 op #include <unistd.h>
32 2ff1e2a9 2023-07-22 op #include "iri.h"
34 da3fc66e 2023-07-22 op #ifndef INFTIM
35 da3fc66e 2023-07-22 op #define INFTIM -1
38 da3fc66e 2023-07-22 op #ifndef __OpenBSD__
39 da3fc66e 2023-07-22 op #define pledge(a, b) (0)
43 2ff1e2a9 2023-07-22 op dial(const char *hostname, const char *port)
45 2ff1e2a9 2023-07-22 op struct addrinfo hints, *res, *res0;
46 2ff1e2a9 2023-07-22 op int error, save_errno, s;
47 2ff1e2a9 2023-07-22 op const char *cause = NULL;
49 2ff1e2a9 2023-07-22 op if (port == NULL || *port == '\0')
50 2ff1e2a9 2023-07-22 op port = "1965";
52 2ff1e2a9 2023-07-22 op memset(&hints, 0, sizeof(hints));
53 2ff1e2a9 2023-07-22 op hints.ai_family = AF_UNSPEC;
54 2ff1e2a9 2023-07-22 op hints.ai_socktype = SOCK_STREAM;
55 2ff1e2a9 2023-07-22 op error = getaddrinfo(hostname, port, &hints, &res0);
57 2ff1e2a9 2023-07-22 op errx(1, "can't resolve %s: %s", hostname, gai_strerror(error));
60 2ff1e2a9 2023-07-22 op for (res = res0; res; res = res->ai_next) {
61 2ff1e2a9 2023-07-22 op s = socket(res->ai_family, res->ai_socktype,
62 2ff1e2a9 2023-07-22 op res->ai_protocol);
63 2ff1e2a9 2023-07-22 op if (s == -1) {
64 2ff1e2a9 2023-07-22 op cause = "socket";
68 2ff1e2a9 2023-07-22 op if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
69 2ff1e2a9 2023-07-22 op cause = "connect";
70 2ff1e2a9 2023-07-22 op save_errno = errno;
72 2ff1e2a9 2023-07-22 op errno = save_errno;
77 2ff1e2a9 2023-07-22 op if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
78 2ff1e2a9 2023-07-22 op err(1, "fcntl");
79 2ff1e2a9 2023-07-22 op break; /* got one */
82 2ff1e2a9 2023-07-22 op err(1, "%s", cause);
83 2ff1e2a9 2023-07-22 op freeaddrinfo(res0);
87 2ff1e2a9 2023-07-22 op /* returns read bytes, or -1 on error */
88 2ff1e2a9 2023-07-22 op static ssize_t
89 2ff1e2a9 2023-07-22 op iomux(struct tls *ctx, int fd, const char *in, size_t inlen, char *out,
90 2ff1e2a9 2023-07-22 op size_t outlen)
92 2ff1e2a9 2023-07-22 op struct pollfd pfd;
93 2ff1e2a9 2023-07-22 op size_t outwrote = 0;
96 2ff1e2a9 2023-07-22 op memset(&pfd, 0, sizeof(pfd));
98 2ff1e2a9 2023-07-22 op pfd.events = POLLIN|POLLOUT;
101 2ff1e2a9 2023-07-22 op if (poll(&pfd, 1, INFTIM) == -1)
102 2ff1e2a9 2023-07-22 op err(1, "poll");
103 2ff1e2a9 2023-07-22 op if (pfd.revents & (POLLERR|POLLNVAL))
104 2ff1e2a9 2023-07-22 op errx(1, "bad fd %d", pfd.fd);
106 2ff1e2a9 2023-07-22 op /* attempt to read */
107 2ff1e2a9 2023-07-22 op if (out != NULL) {
108 2ff1e2a9 2023-07-22 op switch (ret = tls_read(ctx, out, outlen)) {
109 2ff1e2a9 2023-07-22 op case TLS_WANT_POLLIN:
110 2ff1e2a9 2023-07-22 op case TLS_WANT_POLLOUT:
115 2ff1e2a9 2023-07-22 op return outwrote;
117 2ff1e2a9 2023-07-22 op outwrote += ret;
119 2ff1e2a9 2023-07-22 op outlen -= ret;
123 2ff1e2a9 2023-07-22 op * don't write if we're reading; titan works
126 2ff1e2a9 2023-07-22 op if (outwrote != 0)
130 2ff1e2a9 2023-07-22 op if (inlen == 0 && out == NULL)
132 2ff1e2a9 2023-07-22 op if (inlen == 0)
135 2ff1e2a9 2023-07-22 op switch (ret = tls_write(ctx, in, inlen)) {
136 2ff1e2a9 2023-07-22 op case TLS_WANT_POLLIN:
137 2ff1e2a9 2023-07-22 op case TLS_WANT_POLLOUT:
144 2ff1e2a9 2023-07-22 op inlen -= ret;
151 2ff1e2a9 2023-07-22 op static void __dead
154 2ff1e2a9 2023-07-22 op fprintf(stderr,
155 2ff1e2a9 2023-07-22 op "usage: %s [-C cert] [-K key] [-m mime] [-t token] url file\n",
156 2ff1e2a9 2023-07-22 op getprogname());
161 2ff1e2a9 2023-07-22 op main(int argc, char **argv)
163 2ff1e2a9 2023-07-22 op struct tls_config *config;
164 2ff1e2a9 2023-07-22 op struct tls *ctx;
165 2ff1e2a9 2023-07-22 op struct stat sb;
166 2ff1e2a9 2023-07-22 op struct iri iri;
168 2ff1e2a9 2023-07-22 op const char *cert = NULL, *key = NULL, *mime = NULL, *token = NULL;
169 2ff1e2a9 2023-07-22 op const char *parse_err;
170 2ff1e2a9 2023-07-22 op char iribuf[1025];
171 2ff1e2a9 2023-07-22 op char resbuf[1025];
173 2ff1e2a9 2023-07-22 op int sock, ch;
175 2ff1e2a9 2023-07-22 op if (pledge("stdio rpath inet dns", NULL) == -1)
176 2ff1e2a9 2023-07-22 op err(1, "pledge");
178 2ff1e2a9 2023-07-22 op while ((ch = getopt(argc, argv, "C:K:m:t:")) != -1) {
179 2ff1e2a9 2023-07-22 op switch (ch) {
181 2ff1e2a9 2023-07-22 op cert = optarg;
184 2ff1e2a9 2023-07-22 op key = optarg;
187 2ff1e2a9 2023-07-22 op mime = optarg;
190 2ff1e2a9 2023-07-22 op token = optarg;
194 2ff1e2a9 2023-07-22 op argc -= optind;
195 2ff1e2a9 2023-07-22 op argv += optind;
197 2ff1e2a9 2023-07-22 op if (cert == NULL && key != NULL)
199 2ff1e2a9 2023-07-22 op if (cert != NULL && key == NULL)
202 2ff1e2a9 2023-07-22 op if (argc != 2)
205 2ff1e2a9 2023-07-22 op if ((in = fopen(argv[1], "r")) == NULL)
206 2ff1e2a9 2023-07-22 op err(1, "can't open %s", argv[1]);
208 2ff1e2a9 2023-07-22 op /* drop rpath */
209 2ff1e2a9 2023-07-22 op if (pledge("stdio inet dns", NULL) == -1)
210 2ff1e2a9 2023-07-22 op err(1, "pledge");
212 2ff1e2a9 2023-07-22 op if (fstat(fileno(in), &sb) == -1)
213 2ff1e2a9 2023-07-22 op err(1, "fstat");
215 2ff1e2a9 2023-07-22 op /* prepare the URL */
216 2ff1e2a9 2023-07-22 op if (token && mime) {
217 2ff1e2a9 2023-07-22 op if (asprintf(&req, "%s;size=%lld;token=%s;mime=%s\r\n", argv[0],
218 2ff1e2a9 2023-07-22 op (long long)sb.st_size, token, mime) == -1)
219 2ff1e2a9 2023-07-22 op err(1, "asprintf");
220 2ff1e2a9 2023-07-22 op } else if (token) {
221 2ff1e2a9 2023-07-22 op if (asprintf(&req, "%s;size=%lld;token=%s\r\n", argv[0],
222 2ff1e2a9 2023-07-22 op (long long)sb.st_size, token) == -1)
223 2ff1e2a9 2023-07-22 op err(1, "asprintf");
224 2ff1e2a9 2023-07-22 op } else if (mime) {
225 2ff1e2a9 2023-07-22 op if (asprintf(&req, "%s;size=%lld;mime=%s\r\n", argv[0],
226 2ff1e2a9 2023-07-22 op (long long)sb.st_size, mime) == -1)
227 2ff1e2a9 2023-07-22 op err(1, "asprintf");
229 2ff1e2a9 2023-07-22 op if (asprintf(&req, "%s;size=%lld\r\n", argv[0],
230 2ff1e2a9 2023-07-22 op (long long)sb.st_size) == -1)
231 2ff1e2a9 2023-07-22 op err(1, "asprintf");
234 2ff1e2a9 2023-07-22 op if (strlcpy(iribuf, argv[0], sizeof(iribuf)) >= sizeof(iribuf))
235 2ff1e2a9 2023-07-22 op errx(1, "URL too long");
237 2ff1e2a9 2023-07-22 op if (!parse_iri(iribuf, &iri, &parse_err))
238 2ff1e2a9 2023-07-22 op errx(1, "invalid IRI: %s", parse_err);
240 2ff1e2a9 2023-07-22 op if ((config = tls_config_new()) == NULL)
241 2ff1e2a9 2023-07-22 op err(1, "tls_config_new");
242 2ff1e2a9 2023-07-22 op tls_config_insecure_noverifycert(config);
243 2ff1e2a9 2023-07-22 op tls_config_insecure_noverifyname(config);
245 2ff1e2a9 2023-07-22 op if (cert && tls_config_set_keypair_file(config, cert, key) == -1)
246 2ff1e2a9 2023-07-22 op errx(1, "cant load certificate client %s", cert);
248 2ff1e2a9 2023-07-22 op if ((ctx = tls_client()) == NULL)
249 2ff1e2a9 2023-07-22 op errx(1, "can't create tls context");
251 2ff1e2a9 2023-07-22 op if (tls_configure(ctx, config) == -1)
252 2ff1e2a9 2023-07-22 op errx(1, "tls_configure: %s", tls_error(ctx));
254 2ff1e2a9 2023-07-22 op sock = dial(iri.host, iri.port);
255 2ff1e2a9 2023-07-22 op if (tls_connect_socket(ctx, sock, iri.host) == -1)
256 2ff1e2a9 2023-07-22 op errx(1, "failed to connect to %s:%s: %s", iri.host,
257 2ff1e2a9 2023-07-22 op *iri.port == '\0' ? "1965" : iri.port, tls_error(ctx));
259 2ff1e2a9 2023-07-22 op /* drop inet tls */
260 2ff1e2a9 2023-07-22 op if (pledge("stdio", NULL) == -1)
261 2ff1e2a9 2023-07-22 op err(1, "pledge");
263 2ff1e2a9 2023-07-22 op /* send request */
264 2ff1e2a9 2023-07-22 op if (iomux(ctx, sock, req, strlen(req), NULL, 0) == -1)
265 2ff1e2a9 2023-07-22 op errx(1, "I/O error: %s", tls_error(ctx));
268 2ff1e2a9 2023-07-22 op static char buf[BUFSIZ];
269 2ff1e2a9 2023-07-22 op size_t buflen;
273 2ff1e2a9 2023-07-22 op /* will be zero on EOF */
274 2ff1e2a9 2023-07-22 op buflen = fread(buf, 1, sizeof(buf), in);
276 2ff1e2a9 2023-07-22 op w = iomux(ctx, sock, buf, buflen, resbuf, sizeof(resbuf));
278 2ff1e2a9 2023-07-22 op errx(1, "I/O error: %s", tls_error(ctx));
279 2ff1e2a9 2023-07-22 op if (w != 0) {
280 2ff1e2a9 2023-07-22 op if ((m = memmem(resbuf, w, "\r\n", 2)) == NULL)
281 2ff1e2a9 2023-07-22 op errx(1, "invalid reply");
283 2ff1e2a9 2023-07-22 op /* XXX parse */
284 2ff1e2a9 2023-07-22 op puts(resbuf);
289 2ff1e2a9 2023-07-22 op /* close connection */
291 2ff1e2a9 2023-07-22 op struct pollfd pfd;
293 2ff1e2a9 2023-07-22 op memset(&pfd, 0, sizeof(pfd));
294 2ff1e2a9 2023-07-22 op pfd.fd = sock;
295 2ff1e2a9 2023-07-22 op pfd.events = POLLIN|POLLOUT;
297 2ff1e2a9 2023-07-22 op switch (tls_close(ctx)) {
298 2ff1e2a9 2023-07-22 op case TLS_WANT_POLLIN:
299 2ff1e2a9 2023-07-22 op case TLS_WANT_POLLOUT:
300 2ff1e2a9 2023-07-22 op if (poll(&pfd, 1, INFTIM) == -1)
301 2ff1e2a9 2023-07-22 op err(1, "poll");
304 2ff1e2a9 2023-07-22 op warnx("tls_close: %s", tls_error(ctx));
305 2ff1e2a9 2023-07-22 op /* fallthrough */
307 2ff1e2a9 2023-07-22 op tls_free(ctx);