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 "compat.h"
19 #include <sys/types.h>
20 #include <sys/socket.h>
22 #include <netdb.h>
24 #include <assert.h>
25 #include <endian.h>
26 #include <errno.h>
27 #include <event.h>
28 #include <inttypes.h>
29 #include <signal.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <syslog.h>
34 #include <tls.h>
35 #include <unistd.h>
37 #include "kamid.h"
38 #include "log.h"
39 #include "utils.h"
41 #define PROMPT "> "
43 /* flags */
44 int verbose;
45 int tls;
46 const char *keypath;
47 const char *crtpath;
48 const char *host;
49 const char *port;
51 /* state */
52 struct tls_config *tlsconf;
53 struct tls *ctx;
55 static void ATTR_DEAD usage(int);
57 static void sig_handler(int, short, void *);
59 static int openconn(void);
61 static void tls_readcb(int, short, void *);
62 static void tls_writecb(int, short, void *);
64 static void client_read(struct bufferevent *, void *);
65 static void client_write(struct bufferevent *, void *);
66 static void client_error(struct bufferevent *, short, void *);
68 static void readcmd(int, short, void *);
70 static void handle_9p(const void *, size_t);
71 static void prompt(void);
73 static void ATTR_DEAD
74 usage(int ret)
75 {
76 fprintf(stderr,
77 "usage: %s [-chv] [-C crt] [-K key] [-H host] [-P port]\n",
78 getprogname());
79 fprintf(stderr, PACKAGE_NAME " suite version " PACKAGE_VERSION "\n");
80 exit(ret);
81 }
83 static void
84 sig_handler(int sig, short event, void *d)
85 {
86 /*
87 * Normal signal handler rules don't apply because libevent
88 * decouples for us.
89 */
91 switch (sig) {
92 case SIGINT:
93 case SIGTERM:
94 log_warnx("\rShutting down...");
95 event_loopbreak();
96 return;
97 default:
98 fatalx("unexpected signal %d", sig);
99 }
102 static int
103 openconn(void)
105 struct addrinfo hints, *res, *res0;
106 int error;
107 int save_errno;
108 int s;
109 const char *cause = NULL;
111 memset(&hints, 0, sizeof(hints));
112 hints.ai_family = AF_UNSPEC;
113 hints.ai_socktype = SOCK_STREAM;
114 if ((error = getaddrinfo(host, port, &hints, &res0))) {
115 warnx("%s", gai_strerror(error));
116 return -1;
119 s = -1;
120 for (res = res0; res; res = res->ai_next) {
121 s = socket(res->ai_family, res->ai_socktype,
122 res->ai_protocol);
123 if (s == -1) {
124 cause = "socket";
125 continue;
128 if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
129 cause = "connect";
130 save_errno = errno;
131 close(s);
132 errno = save_errno;
133 s = -1;
134 continue;
137 break;
140 freeaddrinfo(res0);
142 if (s == -1)
143 warn("%s", cause);
145 return s;
148 static void
149 tls_readcb(int fd, short event, void *d)
151 struct bufferevent *bufev = d;
152 char buf[IBUF_READ_SIZE];
153 int what = EVBUFFER_READ;
154 int howmuch = IBUF_READ_SIZE;
155 ssize_t ret;
156 size_t len;
158 if (event == EV_TIMEOUT) {
159 what |= EVBUFFER_TIMEOUT;
160 goto err;
163 if (bufev->wm_read.high != 0)
164 howmuch = MIN(sizeof(buf), bufev->wm_read.high);
166 switch (ret = tls_read(ctx, buf, howmuch)) {
167 case TLS_WANT_POLLIN:
168 case TLS_WANT_POLLOUT:
169 goto retry;
170 case -1:
171 what |= EVBUFFER_ERROR;
172 goto err;
174 len = ret;
176 if (len == 0) {
177 what |= EVBUFFER_EOF;
178 goto err;
181 if (evbuffer_add(bufev->input, buf, len) == -1) {
182 what |= EVBUFFER_ERROR;
183 goto err;
186 event_add(&bufev->ev_read, NULL);
188 len = EVBUFFER_LENGTH(bufev->input);
189 if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
190 return;
191 if (bufev->readcb != NULL)
192 (*bufev->readcb)(bufev, bufev->cbarg);
193 return;
195 retry:
196 event_add(&bufev->ev_read, NULL);
197 return;
199 err:
200 (*bufev->errorcb)(bufev, what, bufev->cbarg);
203 static void
204 tls_writecb(int fd, short event, void *d)
206 struct bufferevent *bufev = d;
207 ssize_t ret;
208 short what = EVBUFFER_WRITE;
210 if (event == EV_TIMEOUT) {
211 what |= EVBUFFER_TIMEOUT;
212 goto err;
215 if (EVBUFFER_LENGTH(bufev->output) != 0) {
216 ret = tls_write(ctx,
217 EVBUFFER_DATA(bufev->output),
218 EVBUFFER_LENGTH(bufev->output));
219 switch (ret) {
220 case TLS_WANT_POLLIN:
221 case TLS_WANT_POLLOUT:
222 goto retry;
223 case -1:
224 what |= EVBUFFER_ERROR;
225 goto err;
227 evbuffer_drain(bufev->output, ret);
230 if (EVBUFFER_LENGTH(bufev->output) != 0)
231 event_add(&bufev->ev_write, NULL);
233 if (bufev->writecb != NULL &&
234 EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
235 (*bufev->writecb)(bufev, bufev->cbarg);
236 return;
238 retry:
239 event_add(&bufev->ev_write, NULL);
240 return;
241 err:
242 (*bufev->errorcb)(bufev, what, bufev->cbarg);
245 static void
246 client_read(struct bufferevent *bev, void *data)
248 struct evbuffer *src = EVBUFFER_INPUT(bev);
249 uint32_t len;
251 for (;;) {
252 if (EVBUFFER_LENGTH(src) < 4)
253 return;
255 memcpy(&len, EVBUFFER_DATA(src), sizeof(len));
256 len = le32toh(len);
258 if (len > EVBUFFER_LENGTH(src))
259 return;
261 handle_9p(EVBUFFER_DATA(src), len);
262 evbuffer_drain(src, len);
266 static void
267 client_write(struct bufferevent *bev, void *data)
269 return; /* nothing to do */
272 static void
273 client_error(struct bufferevent *bev, short err, void *data)
275 if (err & EVBUFFER_ERROR)
276 fatal("buffer event error");
278 if (err & EVBUFFER_EOF) {
279 printf("\r");
280 log_info("EOF");
281 event_loopbreak();
282 return;
285 printf("\r");
286 log_warnx("unknown event error");
287 event_loopbreak();
290 static void
291 readcmd(int fd, short event, void *data)
293 char *line = NULL;
294 size_t linesize = 0;
295 ssize_t linelen;
297 if ((linelen = getline(&line, &linesize, stdin)) != -1) {
298 line[linelen-1] = '\0';
300 printf("\r");
301 log_info("TODO: parse `%s'", line);
302 prompt();
305 free(line);
306 if (ferror(stdin))
307 fatal("getline");
310 static void
311 handle_9p(const void *data, size_t len)
313 struct np_msg_header hdr;
315 assert(len >= sizeof(hdr));
316 memcpy(&hdr, data, sizeof(hdr));
318 hdr.len = le32toh(hdr.len);
319 /* type is one byte long, no endianness issues */
320 hdr.tag = le16toh(hdr.tag);
322 printf("\r");
323 log_info("[%d] type=%s len=%"PRIu32, hdr.tag, pp_msg_type(hdr.type),
324 hdr.len);
325 prompt();
328 static void
329 prompt(void)
331 printf("%s", PROMPT);
332 fflush(stdout);
335 int
336 main(int argc, char **argv)
338 int ch, sock, handshake;
339 struct bufferevent *bev;
340 struct event inev, ev_sigint, ev_sigterm;
342 signal(SIGPIPE, SIG_IGN);
344 while ((ch = getopt(argc, argv, "C:cH:hK:P:v")) != -1) {
345 switch (ch) {
346 case 'C':
347 crtpath = optarg;
348 break;
349 case 'c':
350 tls = 1;
351 break;
352 case 'H':
353 host = optarg;
354 break;
355 case 'h':
356 usage(0);
357 break;
358 case 'K':
359 keypath = optarg;
360 break;
361 case 'P':
362 port = optarg;
363 break;
364 case 'v':
365 verbose = 1;
366 break;
367 default:
368 usage(1);
372 if (host == NULL)
373 host = "localhost";
374 if (port == NULL)
375 port = "1337";
377 argc -= optind;
378 argv += optind;
380 if (argc != 0)
381 usage(1);
382 /* if (!tls || (crtpath != NULL || keypath != NULL)) */
383 /* usage(1); */
384 if (!tls)
385 errx(1, "must enable tls (for now)");
387 log_init(1, LOG_DAEMON);
388 log_setverbose(verbose);
389 log_procinit(getprogname());
391 if ((tlsconf = tls_config_new()) == NULL)
392 fatalx("tls_config_new");
393 tls_config_insecure_noverifycert(tlsconf);
394 tls_config_insecure_noverifyname(tlsconf);
395 if (tls_config_set_keypair_file(tlsconf, crtpath, keypath) == -1)
396 fatalx("can't load certs (%s, %s)", crtpath, keypath);
398 if ((ctx = tls_client()) == NULL)
399 fatal("tls_client");
400 if (tls_configure(ctx, tlsconf) == -1)
401 fatalx("tls_configure: %s", tls_error(ctx));
403 log_info("connecting to %s:%s...", host, port);
405 if ((sock = openconn()) == -1)
406 fatalx("can't connect to %s:%s", host, port);
408 if (tls_connect_socket(ctx, sock, host) == -1)
409 fatalx("tls_connect_socket: %s", tls_error(ctx));
411 for (handshake = 0; !handshake;) {
412 switch (tls_handshake(ctx)) {
413 case -1:
414 fatalx("tls_handshake: %s", tls_error(ctx));
415 case 0:
416 handshake = 1;
417 break;
421 log_info("connected!");
423 event_init();
425 signal_set(&ev_sigint, SIGINT, sig_handler, NULL);
426 signal_set(&ev_sigterm, SIGINT, sig_handler, NULL);
428 signal_add(&ev_sigint, NULL);
429 signal_add(&ev_sigterm, NULL);
431 bev = bufferevent_new(sock, client_read, client_write, client_error,
432 NULL);
433 if (bev == NULL)
434 fatal("bufferevent_new");
436 /* setup tls/io */
437 event_set(&bev->ev_read, sock, EV_READ, tls_readcb, bev);
438 event_set(&bev->ev_write, sock, EV_WRITE, tls_writecb, bev);
440 bufferevent_setwatermark(bev, EV_READ|EV_WRITE,
441 sizeof(struct np_msg_header), 0);
442 bufferevent_enable(bev, EV_READ|EV_WRITE);
444 event_set(&inev, 0, EV_READ, readcmd, NULL);
445 event_add(&inev, NULL);
447 prompt();
448 event_dispatch();
450 bufferevent_free(bev);
451 tls_free(ctx);
452 tls_config_free(tlsconf);
453 close(sock);
455 return 0;