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 <assert.h>
23 #include <netdb.h>
24 #include <limits.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <syslog.h>
29 #include <tls.h>
30 #include <unistd.h>
32 #if HAVE_LIBREADLINE
33 #include <readline/readline.h>
34 #include <readline/history.h>
35 #endif
37 #ifndef nitems
38 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
39 #endif
41 #include "9pclib.h"
42 #include "kamid.h"
43 #include "utils.h"
44 #include "log.h"
46 /* flags */
47 int tls;
48 const char *crtpath;
49 const char *keypath;
51 /* state */
52 struct tls_config *tlsconf;
53 struct tls *ctx;
54 int sock;
55 struct evbuffer *buf;
56 struct evbuffer *dirbuf;
57 uint32_t msize;
58 int bell;
60 #define PWDFID 0
62 #define ASSERT_EMPTYBUF() assert(EVBUFFER_LENGTH(buf) == 0)
64 #if HAVE_LIBREADLINE
65 static char *
66 read_line(const char *prompt)
67 {
68 char *line;
70 again:
71 if ((line = readline(prompt)) == NULL)
72 return NULL;
73 /* XXX: trim spaces? */
74 if (*line == '\0') {
75 free(line);
76 goto again;
77 }
79 add_history(line);
80 return line;
81 }
82 #else
83 static char *
84 read_line(const char *prompt)
85 {
86 char *ch, *line = NULL;
87 size_t linesize = 0;
88 ssize_t linelen;
90 printf("%s", prompt);
91 fflush(stdout);
93 linelen = getline(&line, &linesize, stdin);
94 if (linelen == -1)
95 return NULL;
97 if ((ch = strchr(line, '\n')) != NULL)
98 *ch = '\0';
99 return line;
101 #endif
103 static void __dead
104 usage(int ret)
106 fprintf(stderr, "usage: %s [-c] host[:port] [path]\n",
107 getprogname());
108 fprintf(stderr, PACKAGE_NAME " suite version " PACKAGE VERSION "\n");
109 exit(ret);
112 static void
113 do_send(void)
115 ssize_t r;
117 while (EVBUFFER_LENGTH(evb) != 0) {
118 r = tls_write(ctx, EVBUFFER_DATA(evb), EVBUFFER_LENGTH(evb));
119 switch (r) {
120 case TLS_WANT_POLLIN:
121 case TLS_WANT_POLLOUT:
122 continue;
123 case -1:
124 errx(1, "tls: %s", tls_error(ctx));
125 default:
126 evbuffer_drain(evb, r);
131 static void
132 mustread(void *d, size_t len)
134 ssize_t r;
136 while (len != 0) {
137 switch (r = tls_read(ctx, d, len)) {
138 case TLS_WANT_POLLIN:
139 case TLS_WANT_POLLOUT:
140 continue;
141 case -1:
142 errx(1, "tls: %s", tls_error(ctx));
143 default:
144 d += r;
145 len -= r;
150 static void
151 recv_msg(void)
153 uint32_t len;
154 ssize_t r;
155 char tmp[BUFSIZ];
157 mustread(&len, sizeof(len));
158 len = le32toh(len);
159 if (len < HEADERSIZE)
160 errx(1, "read message of invalid length %d", len);
162 len -= 4; /* skip the length just read */
164 while (len != 0) {
165 switch (r = tls_read(ctx, tmp, sizeof(tmp))) {
166 case TLS_WANT_POLLIN:
167 case TLS_WANT_POLLOUT:
168 continue;
169 case -1:
170 errx(1, "tls: %s", tls_error(ctx));
171 default:
172 len -= r;
173 evbuffer_add(buf, tmp, r);
178 static uint64_t
179 np_read64(struct evbuffer *buf)
181 uint64_t n;
183 evbuffer_remove(buf, &n, sizeof(n));
184 return le64toh(n);
187 static uint32_t
188 np_read32(struct evbuffer *buf)
190 uint32_t n;
192 evbuffer_remove(buf, &n, sizeof(n));
193 return le32toh(n);
196 static uint16_t
197 np_read16(struct evbuffer *buf)
199 uint16_t n;
201 evbuffer_remove(buf, &n, sizeof(n));
202 return le16toh(n);
205 static uint16_t
206 np_read8(struct evbuffer *buf)
208 uint8_t n;
210 evbuffer_remove(buf, &n, sizeof(n));
211 return n;
214 static char *
215 np_readstr(struct evbuffer *buf)
217 uint16_t len;
218 char *str;
220 len = np_read16(buf);
221 assert(EVBUFFER_LENGTH(buf) >= len);
223 if ((str = calloc(1, len+1)) == NULL)
224 err(1, "calloc");
225 evbuffer_remove(buf, str, len);
226 return str;
229 static void
230 np_read_qid(struct evbuffer *buf, struct qid *qid)
232 assert(EVBUFFER_LENGTH(buf) >= QIDSIZE);
234 qid->type = np_read8(buf);
235 qid->vers = np_read32(buf);
236 qid->path = np_read64(buf);
239 static void
240 expect(uint8_t type)
242 uint8_t t;
244 t = np_read8(buf);
245 if (t == type)
246 return;
248 if (t == Terror) {
249 char *err;
251 err = np_readstr(buf);
252 errx(1, "expected %s, got error %s",
253 pp_msg_type(type), err);
256 errx(1, "expected %s, got msg type %s",
257 pp_msg_type(type), pp_msg_type(t));
260 static void
261 expect2(uint8_t type, uint16_t tag)
263 uint16_t t;
265 expect(type);
267 t = np_read16(buf);
268 if (t == tag)
269 return;
271 errx(1, "expected tag 0x%x, got 0x%x", tag, t);
274 static void
275 do_version(void)
277 char *version;
279 tversion(VERSION9P, MSIZE9P);
280 do_send();
281 recv_msg();
282 expect2(Rversion, NOTAG);
284 msize = np_read32(buf);
285 version = np_readstr(buf);
287 if (msize > MSIZE9P)
288 errx(1, "got unexpected msize: %d", msize);
289 if (strcmp(version, VERSION9P))
290 errx(1, "unexpected 9p version: %s", version);
292 free(version);
293 ASSERT_EMPTYBUF();
296 static void
297 do_attach(const char *path)
299 const char *user;
300 struct qid qid;
302 if (path == NULL)
303 path = "/";
304 if ((user = getenv("USER")) == NULL)
305 user = "flan";
307 tattach(PWDFID, NOFID, user, path);
308 do_send();
309 recv_msg();
310 expect2(Rattach, iota_tag);
311 np_read_qid(buf, &qid);
313 ASSERT_EMPTYBUF();
316 static uint32_t
317 do_open(uint32_t fid, uint8_t mode)
319 struct qid qid;
320 uint32_t iounit;
322 topen(fid, mode);
323 do_send();
324 recv_msg();
325 expect2(Ropen, iota_tag);
327 np_read_qid(buf, &qid);
328 iounit = np_read32(buf);
330 ASSERT_EMPTYBUF();
332 return iounit;
335 static void
336 do_clunk(uint32_t fid)
338 tclunk(fid);
339 do_send();
340 recv_msg();
341 expect2(Rclunk, iota_tag);
343 ASSERT_EMPTYBUF();
346 static void
347 dup_fid(int fid, int nfid)
349 uint16_t nwqid;
351 twalk(fid, nfid, NULL, 0);
352 do_send();
353 recv_msg();
354 expect2(Rwalk, iota_tag);
356 nwqid = np_read16(buf);
357 assert(nwqid == 0);
359 ASSERT_EMPTYBUF();
362 static void
363 do_connect(const char *connspec, const char *path)
365 int handshake;
366 char *host, *colon;
367 const char *port;
369 host = xstrdup(connspec);
370 if ((colon = strchr(host, ':')) != NULL) {
371 *colon = '\0';
372 port = ++colon;
373 } else
374 port = "1337";
376 if (!tls)
377 fatalx("non-tls mode is not supported");
379 if ((tlsconf = tls_config_new()) == NULL)
380 fatalx("tls_config_new");
381 tls_config_insecure_noverifycert(tlsconf);
382 tls_config_insecure_noverifyname(tlsconf);
383 if (tls_config_set_keypair_file(tlsconf, crtpath, keypath) == -1)
384 fatalx("can't load certs (%s, %s)", crtpath, keypath);
386 if ((ctx = tls_client()) == NULL)
387 fatal("tls_client");
388 if (tls_configure(ctx, tlsconf) == -1)
389 fatalx("tls_configure: %s", tls_error(ctx));
391 printf("connecting to %s:%s...", host, port);
392 fflush(stdout);
394 if (tls_connect(ctx, host, port) == -1)
395 fatalx("can't connect to %s:%s: %s", host, port,
396 tls_error(ctx));
398 for (handshake = 0; !handshake;) {
399 switch (tls_handshake(ctx)) {
400 case -1:
401 fatalx("tls_handshake: %s", tls_error(ctx));
402 case 0:
403 handshake = 1;
404 break;
408 printf(" done!\n");
410 do_version();
411 do_attach(path);
413 free(host);
416 static void
417 cmd_bell(int argc, const char **argv)
419 if (argc == 0) {
420 bell = !bell;
421 if (bell)
422 puts("bell mode enabled");
423 else
424 puts("bell mode disabled");
425 return;
428 if (argc != 1)
429 goto usage;
431 if (!strcmp(*argv, "on")) {
432 bell = 1;
433 puts("bell mode enabled");
434 return;
437 if (!strcmp(*argv, "off")) {
438 bell = 0;
439 puts("bell mode disabled");
440 return;
443 usage:
444 printf("bell [on | off]\n");
447 static void
448 cmd_bye(int argc, const char **argv)
450 log_warnx("bye\n");
451 exit(0);
454 static void
455 cmd_ls(int argc, const char **argv)
457 uint64_t off = 0;
458 uint32_t len;
460 if (argc != 0) {
461 printf("ls don't take arguments (yet)\n");
462 return;
465 dup_fid(PWDFID, 1);
466 do_open(1, KOREAD);
468 evbuffer_drain(dirbuf, EVBUFFER_LENGTH(dirbuf));
470 for (;;) {
471 tread(1, off, BUFSIZ);
472 do_send();
473 recv_msg();
474 expect2(Rread, iota_tag);
476 len = np_read32(buf);
477 if (len == 0)
478 break;
480 evbuffer_add_buffer(dirbuf, buf);
481 off += len;
483 ASSERT_EMPTYBUF();
486 while (EVBUFFER_LENGTH(dirbuf) != 0) {
487 struct qid qid;
488 uint64_t len;
489 uint16_t size;
490 char *name;
492 size = np_read16(dirbuf);
493 assert(size <= EVBUFFER_LENGTH(dirbuf));
495 np_read16(dirbuf); /* skip type */
496 np_read32(dirbuf); /* skip dev */
498 np_read_qid(dirbuf, &qid);
499 printf("%s ", pp_qid_type(qid.type));
501 np_read32(dirbuf); /* skip mode */
502 np_read32(dirbuf); /* skip atime */
503 np_read32(dirbuf); /* skip mtime */
505 len = np_read64(dirbuf);
506 printf("%llu ", (unsigned long long)len);
508 name = np_readstr(dirbuf);
509 printf("%s\n", name);
510 free(name);
512 free(np_readstr(dirbuf)); /* skip uid */
513 free(np_readstr(dirbuf)); /* skip gid */
514 free(np_readstr(dirbuf)); /* skip muid */
517 do_clunk(1);
520 static void
521 cmd_verbose(int argc, const char **argv)
523 if (argc == 0) {
524 log_setverbose(!log_getverbose());
525 if (log_getverbose())
526 puts("verbose mode enabled");
527 else
528 puts("verbose mode disabled");
529 return;
532 if (argc != 1)
533 goto usage;
535 if (!strcmp(*argv, "on")) {
536 log_setverbose(1);
537 puts("verbose mode enabled");
538 return;
541 if (!strcmp(*argv, "off")) {
542 log_setverbose(0);
543 puts("verbose mode disabled");
544 return;
547 usage:
548 printf("verbose [on | off]\n");
551 static void
552 excmd(int argc, const char **argv)
554 struct cmd {
555 const char *name;
556 void (*fn)(int, const char **);
557 } cmds[] = {
558 {"bell", cmd_bell},
559 {"bye", cmd_bye},
560 {"ls", cmd_ls},
561 {"quit", cmd_bye},
562 {"verbose", cmd_verbose},
563 };
564 size_t i;
566 if (argc == 0)
567 return;
568 for (i = 0; i < nitems(cmds); ++i) {
569 if (!strcmp(cmds[i].name, *argv)) {
570 cmds[i].fn(argc-1, argv+1);
571 return;
575 log_warnx("unknown command %s", *argv);
578 int
579 main(int argc, char **argv)
581 int ch;
583 log_init(1, LOG_DAEMON);
584 log_setverbose(0);
585 log_procinit(getprogname());
587 while ((ch = getopt(argc, argv, "C:cK:")) != -1) {
588 switch (ch) {
589 case 'C':
590 crtpath = optarg;
591 break;
592 case 'c':
593 tls = 1;
594 break;
595 case 'K':
596 keypath = optarg;
597 break;
598 default:
599 usage(1);
602 argc -= optind;
603 argv += optind;
605 if (argc == 0)
606 usage(1);
608 if ((evb = evbuffer_new()) == NULL)
609 fatal("evbuffer_new");
611 if ((buf = evbuffer_new()) == NULL)
612 fatal("evbuffer_new");
614 if ((dirbuf = evbuffer_new()) == NULL)
615 fatal("evbuferr_new");
617 do_connect(argv[0], argv[1]);
619 /* cmd_ls(0, NULL); */
621 for (;;) {
622 int argc = 0;
623 char *line, *argv[16] = {0}, **ap;
625 if ((line = read_line("kamiftp> ")) == NULL)
626 break;
628 for (argc = 0, ap = argv; ap < &argv[15] &&
629 (*ap = strsep(&line, " \t")) != NULL;) {
630 if (**ap != '\0')
631 ap++, argc++;
633 excmd(argc, (const char **)argv);
635 if (bell) {
636 printf("\a");
637 fflush(stdout);
640 free(line);
643 printf("\n");