2 * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
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.
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.
19 #include <sys/types.h>
20 #include <sys/socket.h>
42 #define DEBUG_PACKETS 0
55 struct tls_config *tlsconf;
57 struct bufferevent *bev, *inbev;
59 static void ATTR_DEAD usage(int);
61 static void sig_handler(int, short, void *);
63 static int openconn(void);
64 static void mark_nonblock(int);
66 static void tls_readcb(int, short, void *);
67 static void tls_writecb(int, short, void *);
69 static void client_read(struct bufferevent *, void *);
70 static void client_write(struct bufferevent *, void *);
71 static void client_error(struct bufferevent *, short, void *);
73 static void repl_read(struct bufferevent *, void *);
74 static void repl_error(struct bufferevent *, short, void *);
76 static void excmd_version(const char **, int);
77 static void excmd_attach(const char **, int);
78 static void excmd_clunk(const char **, int);
79 static void excmd_flush(const char **, int);
80 static void excmd_walk(const char ** , int);
81 static void excmd_open(const char ** , int);
82 static void excmd_read(const char ** , int);
83 static void excmd(const char **, int);
85 static const char *pp_qid_type(uint8_t);
86 static void pp_qid(const uint8_t *, uint32_t);
87 static void pp_msg(uint32_t, uint8_t, uint16_t, const uint8_t *);
88 static void handle_9p(const uint8_t *, size_t);
89 static void clr(void);
90 static void prompt(void);
96 "usage: %s [-chv] [-C crt] [-K key] [-H host] [-P port]\n",
98 fprintf(stderr, PACKAGE_NAME " suite version " PACKAGE_VERSION "\n");
103 sig_handler(int sig, short event, void *d)
106 * Normal signal handler rules don't apply because libevent
114 log_warnx("Shutting down...");
118 fatalx("unexpected signal %d", sig);
125 struct addrinfo hints, *res, *res0;
129 const char *cause = NULL;
131 memset(&hints, 0, sizeof(hints));
132 hints.ai_family = AF_UNSPEC;
133 hints.ai_socktype = SOCK_STREAM;
134 if ((error = getaddrinfo(host, port, &hints, &res0))) {
135 warnx("%s", gai_strerror(error));
140 for (res = res0; res; res = res->ai_next) {
141 s = socket(res->ai_family, res->ai_socktype,
148 if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
169 mark_nonblock(int fd)
173 if ((flags = fcntl(fd, F_GETFL)) == -1)
174 fatal("fcntl(F_GETFL)");
175 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
176 fatal("fcntl(F_SETFL)");
180 tls_readcb(int fd, short event, void *d)
182 struct bufferevent *bufev = d;
183 char buf[IBUF_READ_SIZE];
184 int what = EVBUFFER_READ;
185 int howmuch = IBUF_READ_SIZE;
189 if (event == EV_TIMEOUT) {
190 what |= EVBUFFER_TIMEOUT;
194 if (bufev->wm_read.high != 0)
195 howmuch = MIN(sizeof(buf), bufev->wm_read.high);
197 switch (ret = tls_read(ctx, buf, howmuch)) {
198 case TLS_WANT_POLLIN:
199 case TLS_WANT_POLLOUT:
202 what |= EVBUFFER_ERROR;
208 what |= EVBUFFER_EOF;
212 if (evbuffer_add(bufev->input, buf, len) == -1) {
213 what |= EVBUFFER_ERROR;
217 event_add(&bufev->ev_read, NULL);
219 len = EVBUFFER_LENGTH(bufev->input);
220 if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
222 if (bufev->readcb != NULL)
223 (*bufev->readcb)(bufev, bufev->cbarg);
227 event_add(&bufev->ev_read, NULL);
231 (*bufev->errorcb)(bufev, what, bufev->cbarg);
235 tls_writecb(int fd, short event, void *d)
237 struct bufferevent *bufev = d;
240 short what = EVBUFFER_WRITE;
243 if (event == EV_TIMEOUT) {
244 what |= EVBUFFER_TIMEOUT;
248 len = EVBUFFER_LENGTH(bufev->output);
250 data = EVBUFFER_DATA(bufev->output);
253 hexdump("outgoing msg", data, len);
256 switch (ret = tls_write(ctx, data, len)) {
257 case TLS_WANT_POLLIN:
258 case TLS_WANT_POLLOUT:
261 what |= EVBUFFER_ERROR;
264 evbuffer_drain(bufev->output, ret);
267 if (EVBUFFER_LENGTH(bufev->output) != 0)
268 event_add(&bufev->ev_write, NULL);
270 if (bufev->writecb != NULL &&
271 EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
272 (*bufev->writecb)(bufev, bufev->cbarg);
276 event_add(&bufev->ev_write, NULL);
279 (*bufev->errorcb)(bufev, what, bufev->cbarg);
283 client_read(struct bufferevent *bev, void *d)
285 struct evbuffer *src = EVBUFFER_INPUT(bev);
290 if (EVBUFFER_LENGTH(src) < sizeof(len))
293 data = EVBUFFER_DATA(src);
295 memcpy(&len, data, sizeof(len));
298 if (len < HEADERSIZE)
299 fatal("incoming message is too small! (%d bytes)",
302 if (len > EVBUFFER_LENGTH(src))
306 hexdump("incoming msg", data, len);
309 handle_9p(data, len);
310 evbuffer_drain(src, len);
315 client_write(struct bufferevent *bev, void *data)
317 return; /* nothing to do */
321 client_error(struct bufferevent *bev, short err, void *data)
323 if (err & EVBUFFER_ERROR)
324 fatal("buffer event error");
326 if (err & EVBUFFER_EOF) {
334 log_warnx("unknown event error");
339 repl_read(struct bufferevent *bev, void *d)
343 const char *argv[10], **ap;
346 line = evbuffer_readln(bev->input, &len, EVBUFFER_EOL_LF);
350 for (argc = 0, ap = argv; ap < &argv[9] &&
351 (*ap = strsep(&line, " \t")) != NULL;) {
364 repl_error(struct bufferevent *bev, short error, void *d)
366 fatalx("an error occurred");
372 bufferevent_write_buffer(bev, evb);
375 /* version [version-str] */
377 excmd_version(const char **argv, int argc)
385 tversion(s, MSIZE9P);
389 /* attach fid uname aname */
391 excmd_attach(const char **argv, int argc)
399 fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
400 if (errstr != NULL) {
401 log_warnx("fid is %s: %s", errstr, argv[1]);
405 tattach(fid, NOFID, argv[2], argv[3]);
410 log_warnx("usage: attach fid uname aname");
415 excmd_clunk(const char **argv, int argc)
423 fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
424 if (errstr != NULL) {
425 log_warnx("fid is %s: %s", errstr, argv[1]);
434 log_warnx("usage: clunk fid");
439 excmd_flush(const char **argv, int argc)
447 oldtag = strtonum(argv[1], 0, UINT16_MAX, &errstr);
448 if (errstr != NULL) {
449 log_warnx("oldtag is %s: %s", errstr, argv[1]);
458 log_warnx("usage: flush oldtag");
461 /* walk fid newfid wnames... */
463 excmd_walk(const char **argv, int argc)
465 uint32_t fid, newfid;
471 fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
472 if (errstr != NULL) {
473 log_warnx("fid is %s: %s", errstr, argv[1]);
477 newfid = strtonum(argv[2], 0, UINT32_MAX, &errstr);
478 if (errstr != NULL) {
479 log_warnx("newfid is %s: %s", errstr, argv[1]);
483 twalk(fid, newfid, argv + 3, argc - 3);
488 log_warnx("usage: walk fid newfid wnames...");
491 /* open fid mode [flag] */
493 excmd_open(const char **argv, int argc)
499 if (argc != 3 && argc != 4)
502 fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
503 if (errstr != NULL) {
504 log_warnx("fid is %s: %s", errstr, argv[1]);
509 if (!strcmp("read", argv[2]) || !strcmp("r", argv[2]))
511 else if (!strcmp("write", argv[2]) || !strcmp("w", argv[2]))
513 else if (!strcmp("readwrite", argv[2]) || !strcmp("rw", argv[2]))
516 log_warnx("invalid mode %s", argv[2]);
521 if (argv[3] != NULL) {
522 if (!strcmp("trunc", argv[3]))
524 else if (!strcmp("rclose", argv[3]))
527 log_warnx("invalid flag %s", argv[3]);
537 log_warnx("usage: open fid mode [flag]");
540 /* read fid offset count */
542 excmd_read(const char **argv, int argc)
551 fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
552 if (errstr != NULL) {
553 log_warnx("fid is %s: %s", errstr, argv[1]);
557 /* should really be UNT64_MAX but it fails... */
558 off = strtonum(argv[2], -1, UINT32_MAX, &errstr);
559 if (errstr != NULL) {
560 log_warnx("offset is %s: %s", errstr, argv[2]);
564 count = strtonum(argv[3], 0, UINT32_MAX, &errstr);
565 if (errstr != NULL) {
566 log_warnx("count is %s: %s", errstr, argv[3]);
570 tread(fid, off, count);
575 log_warnx("usage: read fid offset count");
579 excmd(const char **argv, int argc)
583 void (*fn)(const char **, int);
585 {"version", excmd_version},
586 {"attach", excmd_attach},
587 {"clunk", excmd_clunk},
588 {"flush", excmd_flush},
589 {"walk", excmd_walk},
590 {"open", excmd_open},
591 {"read", excmd_read},
598 for (i = 0; i < sizeof(cmds)/sizeof(cmds[0]); ++i) {
599 if (!strcmp(cmds[i].name, argv[0])) {
600 cmds[i].fn(argv, argc);
605 log_warnx("Unknown command %s", *argv);
609 pp_qid_type(uint8_t type)
612 case QTDIR: return "dir";
613 case QTAPPEND: return "append-only";
614 case QTEXCL: return "exclusive";
615 case QTMOUNT: return "mounted-channel";
616 case QTAUTH: return "authentication";
617 case QTTMP: return "non-backed-up";
618 case QTSYMLINK: return "symlink";
619 case QTFILE: return "file";
626 pp_qid(const uint8_t *d, uint32_t len)
639 memcpy(&vers, d, sizeof(vers));
641 vers = le64toh(vers);
643 memcpy(&path, d, sizeof(path));
645 path = le64toh(path);
647 printf("qid{path=%"PRIu64" version=%"PRIu32" type=0x%x\"%s\"}",
648 path, vers, type, pp_qid_type(type));
652 pp_msg(uint32_t len, uint8_t type, uint16_t tag, const uint8_t *d)
654 uint32_t msize, iounit, count;
658 printf("len=%"PRIu32" type=%d[%s] tag=0x%x[%d] ", len,
659 type, pp_msg_type(type), tag, tag);
666 printf("invalid: not enough space for msize "
667 "and version provided.");
671 memcpy(&msize, d, sizeof(msize));
673 len -= sizeof(msize);
674 msize = le32toh(msize);
676 memcpy(&slen, d, sizeof(slen));
679 slen = le16toh(slen);
682 printf("invalid: version string length doesn't "
683 "match. Got %d; want %d", slen, len);
687 printf("msize=%"PRIu32" version[%"PRIu16"]=\"",
689 fwrite(d, 1, slen, stdout);
700 printf("invalid Rclunk: %"PRIu32" extra bytes", len);
705 printf("invalid Rflush: %"PRIu32" extra bytes", len);
710 printf("invaild Rwalk: less than two bytes (%d)",
715 memcpy(&slen, d, sizeof(slen));
718 slen = le16toh(slen);
720 if (len != QIDSIZE * slen) {
721 printf("invalid Rwalk: wanted %d bytes for %d qids "
722 "but got %"PRIu32" bytes instead",
723 QIDSIZE*slen, slen, len);
727 printf("nwqid=%"PRIu16, slen);
729 for (; slen != 0; slen--) {
739 if (len != QIDSIZE + 4) {
740 printf("invalid Ropen: expected %d bytes; "
741 "got %u\n", QIDSIZE + 4, len);
749 memcpy(&iounit, d, sizeof(iounit));
751 len -= sizeof(iounit);
752 iounit = le32toh(iounit);
753 printf(" iounit=%"PRIu32, iounit);
757 if (len < sizeof(count)) {
758 printf("invalid Rread: expected %zu bytes at least; "
759 "got %u\n", sizeof(count), len);
763 memcpy(&count, d, sizeof(count));
765 len -= sizeof(count);
766 count = le32toh(count);
769 printf("invalid Rread: expected %d data bytes; "
770 "got %u\n", count, len);
774 /* allocates three extra bytes, oh well... */
775 if ((v = calloc(count + 1, 4)) == NULL)
777 strvisx(v, d, count, VIS_SAFE | VIS_TAB | VIS_NL | VIS_CSTYLE);
778 printf("data=%s", v);
784 memcpy(&slen, d, sizeof(slen));
787 slen = le16toh(slen);
790 printf("invalid: error string length doesn't "
791 "match. Got %d; want %d", slen, len);
796 fwrite(d, 1, slen, stdout);
802 if ((v = calloc(len + 1, 4)) == NULL)
804 strvisx(v, d, len, VIS_SAFE | VIS_TAB | VIS_NL | VIS_CSTYLE);
805 printf("body=%s", v);
813 handle_9p(const uint8_t *data, size_t size)
819 assert(size >= HEADERSIZE);
821 memcpy(&len, data, sizeof(len));
824 memcpy(&type, data, sizeof(type));
825 data += sizeof(type);
827 memcpy(&tag, data, sizeof(tag));
831 /* type is one byte long, no endianness issues */
835 pp_msg(len, type, tag, data);
849 printf("%s", PROMPT);
854 main(int argc, char **argv)
856 int ch, sock, handshake;
857 struct event ev_sigint, ev_sigterm;
859 signal(SIGPIPE, SIG_IGN);
861 while ((ch = getopt(argc, argv, "C:cH:hK:P:v")) != -1) {
899 /* if (!tls || (crtpath != NULL || keypath != NULL)) */
902 errx(1, "must enable tls (for now)");
904 log_init(1, LOG_DAEMON);
905 log_setverbose(verbose);
906 log_procinit(getprogname());
908 if ((tlsconf = tls_config_new()) == NULL)
909 fatalx("tls_config_new");
910 tls_config_insecure_noverifycert(tlsconf);
911 tls_config_insecure_noverifyname(tlsconf);
912 if (tls_config_set_keypair_file(tlsconf, crtpath, keypath) == -1)
913 fatalx("can't load certs (%s, %s)", crtpath, keypath);
915 if ((ctx = tls_client()) == NULL)
917 if (tls_configure(ctx, tlsconf) == -1)
918 fatalx("tls_configure: %s", tls_error(ctx));
920 log_info("connecting to %s:%s...", host, port);
922 if ((sock = openconn()) == -1)
923 fatalx("can't connect to %s:%s", host, port);
925 if (tls_connect_socket(ctx, sock, host) == -1)
926 fatalx("tls_connect_socket: %s", tls_error(ctx));
928 for (handshake = 0; !handshake;) {
929 switch (tls_handshake(ctx)) {
931 fatalx("tls_handshake: %s", tls_error(ctx));
938 log_info("connected!");
944 /* initialize global evb */
945 if ((evb = evbuffer_new()) == NULL)
946 fatal("evbuffer_new");
948 signal_set(&ev_sigint, SIGINT, sig_handler, NULL);
949 signal_set(&ev_sigterm, SIGINT, sig_handler, NULL);
951 signal_add(&ev_sigint, NULL);
952 signal_add(&ev_sigterm, NULL);
954 bev = bufferevent_new(sock, client_read, client_write, client_error,
957 fatal("bufferevent_new");
960 event_set(&bev->ev_read, sock, EV_READ, tls_readcb, bev);
961 event_set(&bev->ev_write, sock, EV_WRITE, tls_writecb, bev);
963 bufferevent_enable(bev, EV_READ|EV_WRITE);
966 inbev = bufferevent_new(0, repl_read, NULL, repl_error, NULL);
967 bufferevent_enable(inbev, EV_READ);
972 bufferevent_free(bev);
974 tls_config_free(tlsconf);