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 <fcntl.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 "9pclib.h"
38 #include "kamid.h"
39 #include "log.h"
40 #include "utils.h"
42 #define DEBUG_PACKETS 0
44 #define PROMPT "=% "
46 /* flags */
47 int verbose;
48 int tls;
49 const char *keypath;
50 const char *crtpath;
51 const char *host;
52 const char *port;
54 /* state */
55 struct tls_config *tlsconf;
56 struct tls *ctx;
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_create(const char ** , int);
83 static void excmd_read(const char ** , int);
84 static void excmd_write(const char **, int);
85 static void excmd(const char **, int);
87 static const char *pp_qid_type(uint8_t);
88 static void pp_qid(const uint8_t *, uint32_t);
89 static void pp_msg(uint32_t, uint8_t, uint16_t, const uint8_t *);
90 static void handle_9p(const uint8_t *, size_t);
91 static void clr(void);
92 static void prompt(void);
94 static void ATTR_DEAD
95 usage(int ret)
96 {
97 fprintf(stderr,
98 "usage: %s [-chv] [-C crt] [-K key] [-H host] [-P port]\n",
99 getprogname());
100 fprintf(stderr, PACKAGE_NAME " suite version " PACKAGE_VERSION "\n");
101 exit(ret);
104 static void
105 sig_handler(int sig, short event, void *d)
107 /*
108 * Normal signal handler rules don't apply because libevent
109 * decouples for us.
110 */
112 switch (sig) {
113 case SIGINT:
114 case SIGTERM:
115 clr();
116 log_warnx("Shutting down...");
117 event_loopbreak();
118 return;
119 default:
120 fatalx("unexpected signal %d", sig);
124 static int
125 openconn(void)
127 struct addrinfo hints, *res, *res0;
128 int error;
129 int save_errno;
130 int s;
131 const char *cause = NULL;
133 memset(&hints, 0, sizeof(hints));
134 hints.ai_family = AF_UNSPEC;
135 hints.ai_socktype = SOCK_STREAM;
136 if ((error = getaddrinfo(host, port, &hints, &res0))) {
137 warnx("%s", gai_strerror(error));
138 return -1;
141 s = -1;
142 for (res = res0; res; res = res->ai_next) {
143 s = socket(res->ai_family, res->ai_socktype,
144 res->ai_protocol);
145 if (s == -1) {
146 cause = "socket";
147 continue;
150 if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
151 cause = "connect";
152 save_errno = errno;
153 close(s);
154 errno = save_errno;
155 s = -1;
156 continue;
159 break;
162 freeaddrinfo(res0);
164 if (s == -1)
165 warn("%s", cause);
167 return s;
170 static void
171 mark_nonblock(int fd)
173 int flags;
175 if ((flags = fcntl(fd, F_GETFL)) == -1)
176 fatal("fcntl(F_GETFL)");
177 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
178 fatal("fcntl(F_SETFL)");
181 static void
182 tls_readcb(int fd, short event, void *d)
184 struct bufferevent *bufev = d;
185 char buf[IBUF_READ_SIZE];
186 int what = EVBUFFER_READ;
187 int howmuch = IBUF_READ_SIZE;
188 ssize_t ret;
189 size_t len;
191 if (event == EV_TIMEOUT) {
192 what |= EVBUFFER_TIMEOUT;
193 goto err;
196 if (bufev->wm_read.high != 0)
197 howmuch = MIN(sizeof(buf), bufev->wm_read.high);
199 switch (ret = tls_read(ctx, buf, howmuch)) {
200 case TLS_WANT_POLLIN:
201 case TLS_WANT_POLLOUT:
202 goto retry;
203 case -1:
204 what |= EVBUFFER_ERROR;
205 goto err;
207 len = ret;
209 if (len == 0) {
210 what |= EVBUFFER_EOF;
211 goto err;
214 if (evbuffer_add(bufev->input, buf, len) == -1) {
215 what |= EVBUFFER_ERROR;
216 goto err;
219 event_add(&bufev->ev_read, NULL);
221 len = EVBUFFER_LENGTH(bufev->input);
222 if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
223 return;
224 if (bufev->readcb != NULL)
225 (*bufev->readcb)(bufev, bufev->cbarg);
226 return;
228 retry:
229 event_add(&bufev->ev_read, NULL);
230 return;
232 err:
233 (*bufev->errorcb)(bufev, what, bufev->cbarg);
236 static void
237 tls_writecb(int fd, short event, void *d)
239 struct bufferevent *bufev = d;
240 ssize_t ret;
241 size_t len;
242 short what = EVBUFFER_WRITE;
243 void *data;
245 if (event == EV_TIMEOUT) {
246 what |= EVBUFFER_TIMEOUT;
247 goto err;
250 len = EVBUFFER_LENGTH(bufev->output);
251 if (len != 0) {
252 data = EVBUFFER_DATA(bufev->output);
254 #if DEBUG_PACKETS
255 hexdump("outgoing msg", data, len);
256 #endif
258 switch (ret = tls_write(ctx, data, len)) {
259 case TLS_WANT_POLLIN:
260 case TLS_WANT_POLLOUT:
261 goto retry;
262 case -1:
263 what |= EVBUFFER_ERROR;
264 goto err;
266 evbuffer_drain(bufev->output, ret);
269 if (EVBUFFER_LENGTH(bufev->output) != 0)
270 event_add(&bufev->ev_write, NULL);
272 if (bufev->writecb != NULL &&
273 EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
274 (*bufev->writecb)(bufev, bufev->cbarg);
275 return;
277 retry:
278 event_add(&bufev->ev_write, NULL);
279 return;
280 err:
281 (*bufev->errorcb)(bufev, what, bufev->cbarg);
284 static void
285 client_read(struct bufferevent *bev, void *d)
287 struct evbuffer *src = EVBUFFER_INPUT(bev);
288 uint32_t len;
289 uint8_t *data;
291 for (;;) {
292 if (EVBUFFER_LENGTH(src) < sizeof(len))
293 return;
295 data = EVBUFFER_DATA(src);
297 memcpy(&len, data, sizeof(len));
298 len = le32toh(len);
300 if (len < HEADERSIZE)
301 fatal("incoming message is too small! (%d bytes)",
302 len);
304 if (len > EVBUFFER_LENGTH(src))
305 return;
307 #if DEBUG_PACKETS
308 hexdump("incoming msg", data, len);
309 #endif
311 handle_9p(data, len);
312 evbuffer_drain(src, len);
316 static void
317 client_write(struct bufferevent *bev, void *data)
319 return; /* nothing to do */
322 static void
323 client_error(struct bufferevent *bev, short err, void *data)
325 if (err & EVBUFFER_ERROR)
326 fatal("buffer event error");
328 if (err & EVBUFFER_EOF) {
329 clr();
330 log_info("EOF");
331 event_loopbreak();
332 return;
335 clr();
336 log_warnx("unknown event error");
337 event_loopbreak();
340 static void
341 repl_read(struct bufferevent *bev, void *d)
343 size_t len;
344 int argc;
345 const char *argv[10], **ap;
346 char *line;
348 line = evbuffer_readln(bev->input, &len, EVBUFFER_EOL_LF);
349 if (line == NULL)
350 return;
352 for (argc = 0, ap = argv; ap < &argv[9] &&
353 (*ap = strsep(&line, " \t")) != NULL;) {
354 if (**ap != '\0')
355 ap++, argc++;
358 clr();
359 excmd(argv, argc);
360 prompt();
362 free(line);
365 static void
366 repl_error(struct bufferevent *bev, short error, void *d)
368 fatalx("an error occurred");
371 static inline void
372 do_send(void)
374 bufferevent_write_buffer(bev, evb);
377 /* version [version-str] */
378 static void
379 excmd_version(const char **argv, int argc)
381 const char *s;
383 s = VERSION9P;
384 if (argc == 2)
385 s = argv[1];
387 tversion(s, MSIZE9P);
388 do_send();
391 /* attach fid uname aname */
392 static void
393 excmd_attach(const char **argv, int argc)
395 uint32_t fid;
396 const char *errstr;
398 if (argc != 4)
399 goto usage;
401 fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
402 if (errstr != NULL) {
403 log_warnx("fid is %s: %s", errstr, argv[1]);
404 return;
407 tattach(fid, NOFID, argv[2], argv[3]);
408 do_send();
409 return;
411 usage:
412 log_warnx("usage: attach fid uname aname");
415 /* clunk fid */
416 static void
417 excmd_clunk(const char **argv, int argc)
419 uint32_t fid;
420 const char *errstr;
422 if (argc != 2)
423 goto usage;
425 fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
426 if (errstr != NULL) {
427 log_warnx("fid is %s: %s", errstr, argv[1]);
428 return;
431 tclunk(fid);
432 do_send();
433 return;
435 usage:
436 log_warnx("usage: clunk fid");
439 /* flush oldtag */
440 static void
441 excmd_flush(const char **argv, int argc)
443 uint16_t oldtag;
444 const char *errstr;
446 if (argc != 2)
447 goto usage;
449 oldtag = strtonum(argv[1], 0, UINT16_MAX, &errstr);
450 if (errstr != NULL) {
451 log_warnx("oldtag is %s: %s", errstr, argv[1]);
452 return;
455 tflush(oldtag);
456 do_send();
457 return;
459 usage:
460 log_warnx("usage: flush oldtag");
463 /* walk fid newfid wnames... */
464 static void
465 excmd_walk(const char **argv, int argc)
467 uint32_t fid, newfid;
468 const char *errstr;
470 if (argc < 3)
471 goto usage;
473 fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
474 if (errstr != NULL) {
475 log_warnx("fid is %s: %s", errstr, argv[1]);
476 return;
479 newfid = strtonum(argv[2], 0, UINT32_MAX, &errstr);
480 if (errstr != NULL) {
481 log_warnx("newfid is %s: %s", errstr, argv[1]);
482 return;
485 twalk(fid, newfid, argv + 3, argc - 3);
486 do_send();
487 return;
489 usage:
490 log_warnx("usage: walk fid newfid wnames...");
493 /* open fid mode [flag] */
494 static void
495 excmd_open(const char **argv, int argc)
497 const char *errstr;
498 uint32_t fid;
499 uint8_t mode = 0;
501 if (argc != 3 && argc != 4)
502 goto usage;
504 fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
505 if (errstr != NULL) {
506 log_warnx("fid is %s: %s", errstr, argv[1]);
507 return;
510 /* parse mode */
511 if (!strcmp("read", argv[2]) || !strcmp("r", argv[2]))
512 mode = KOREAD;
513 else if (!strcmp("write", argv[2]) || !strcmp("w", argv[2]))
514 mode = KOWRITE;
515 else if (!strcmp("readwrite", argv[2]) || !strcmp("rw", argv[2]))
516 mode = KORDWR;
517 else {
518 log_warnx("invalid mode %s", argv[2]);
519 return;
522 /* parse flag */
523 if (argv[3] != NULL) {
524 if (!strcmp("trunc", argv[3]))
525 mode |= KOTRUNC;
526 else if (!strcmp("rclose", argv[3]))
527 mode |= KORCLOSE;
528 else {
529 log_warnx("invalid flag %s", argv[3]);
530 return;
534 topen(fid, mode);
535 do_send();
536 return;
538 usage:
539 log_warnx("usage: open fid mode [flag]");
542 /* create fid path perm mode */
543 static void
544 excmd_create(const char **argv, int argc)
546 const char *errstr;
547 uint32_t fid;
548 uint8_t mode = 0;
550 if (argc != 5)
551 goto usage;
553 fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
554 if (errstr != NULL) {
555 log_warnx("fid is %s: %s", errstr, argv[1]);
556 return;
559 /* parse mode */
560 if (!strcmp("write", argv[4]) || !strcmp("w", argv[4]))
561 mode = KOWRITE;
562 else if (!strcmp("readwrite", argv[4]) || !strcmp("rw", argv[4]))
563 mode = KORDWR;
564 else {
565 log_warnx("invalid mode %s for create", argv[4]);
566 return;
569 tcreate(fid, argv[2], 0, mode);
570 do_send();
571 return;
573 usage:
574 log_warnx("usage: create fid path perm mode ; perm is unused");
578 /* read fid offset count */
579 static void
580 excmd_read(const char **argv, int argc)
582 uint64_t off;
583 uint32_t fid, count;
584 const char *errstr;
586 if (argc != 4)
587 goto usage;
589 fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
590 if (errstr != NULL) {
591 log_warnx("fid is %s: %s", errstr, argv[1]);
592 return;
595 /* should really be UNT64_MAX but it fails... */
596 off = strtonum(argv[2], -1, UINT32_MAX, &errstr);
597 if (errstr != NULL) {
598 log_warnx("offset is %s: %s", errstr, argv[2]);
599 return;
602 count = strtonum(argv[3], 0, UINT32_MAX, &errstr);
603 if (errstr != NULL) {
604 log_warnx("count is %s: %s", errstr, argv[3]);
605 return;
608 tread(fid, off, count);
609 do_send();
610 return;
612 usage:
613 log_warnx("usage: read fid offset count");
616 /* write fid offset content */
617 static void
618 excmd_write(const char **argv, int argc)
620 uint64_t off;
621 uint32_t fid, count;
622 const char *errstr;
624 if (argc != 4)
625 goto usage;
627 fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
628 if (errstr != NULL) {
629 log_warnx("fid is %s: %s", errstr, argv[1]);
630 return;
633 /* should really be UINT64_MAX but... */
634 off = strtonum(argv[2], 0, UINT32_MAX, &errstr);
635 if (errstr != NULL) {
636 log_warnx("offset is %s: %s", errstr, argv[2]);
637 return;
640 count = strlen(argv[3]);
641 twrite(fid, off, argv[3], count);
642 do_send();
643 return;
645 usage:
646 log_warnx("usage: write fid offset content");
649 /* remove fid */
650 static void
651 excmd_remove(const char **argv, int argc)
653 const char *errstr;
654 uint32_t fid;
656 if (argc != 2)
657 goto usage;
659 fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
660 if (errstr != NULL) {
661 log_warnx("fid is %s: %s", errstr, argv[1]);
662 return;
665 tremove(fid);
666 do_send();
667 return;
669 usage:
670 log_warnx("usage: remove fid");
673 static void
674 excmd(const char **argv, int argc)
676 struct cmd {
677 const char *name;
678 void (*fn)(const char **, int);
679 } cmds[] = {
680 {"version", excmd_version},
681 {"attach", excmd_attach},
682 {"clunk", excmd_clunk},
683 {"flush", excmd_flush},
684 {"walk", excmd_walk},
685 {"open", excmd_open},
686 {"create", excmd_create},
687 {"read", excmd_read},
688 {"write", excmd_write},
689 /* TODO: stat */
690 {"remove", excmd_remove},
691 };
692 size_t i;
694 if (argc == 0)
695 return;
697 for (i = 0; i < sizeof(cmds)/sizeof(cmds[0]); ++i) {
698 if (!strcmp(cmds[i].name, argv[0])) {
699 cmds[i].fn(argv, argc);
700 return;
704 log_warnx("Unknown command %s", *argv);
707 static const char *
708 pp_qid_type(uint8_t type)
710 switch (type) {
711 case QTDIR: return "dir";
712 case QTAPPEND: return "append-only";
713 case QTEXCL: return "exclusive";
714 case QTMOUNT: return "mounted-channel";
715 case QTAUTH: return "authentication";
716 case QTTMP: return "non-backed-up";
717 case QTSYMLINK: return "symlink";
718 case QTFILE: return "file";
721 return "unknown";
724 static void
725 pp_qid(const uint8_t *d, uint32_t len)
727 uint64_t path;
728 uint32_t vers;
729 uint8_t type;
731 if (len < 13) {
732 printf("invalid");
733 return;
736 type = *d++;
738 memcpy(&vers, d, sizeof(vers));
739 d += sizeof(vers);
740 vers = le64toh(vers);
742 memcpy(&path, d, sizeof(path));
743 d += sizeof(path);
744 path = le64toh(path);
746 printf("qid{path=%"PRIu64" version=%"PRIu32" type=0x%x\"%s\"}",
747 path, vers, type, pp_qid_type(type));
750 static void
751 pp_msg(uint32_t len, uint8_t type, uint16_t tag, const uint8_t *d)
753 uint32_t msize, iounit, count;
754 uint16_t slen;
755 char *v;
757 printf("len=%"PRIu32" type=%d[%s] tag=0x%x[%d] ", len,
758 type, pp_msg_type(type), tag, tag);
760 len -= HEADERSIZE;
762 switch (type) {
763 case Rversion:
764 if (len < 6) {
765 printf("invalid: not enough space for msize "
766 "and version provided.");
767 break;
770 memcpy(&msize, d, sizeof(msize));
771 d += sizeof(msize);
772 len -= sizeof(msize);
773 msize = le32toh(msize);
775 memcpy(&slen, d, sizeof(slen));
776 d += sizeof(slen);
777 len -= sizeof(slen);
778 slen = le16toh(slen);
780 if (len != slen) {
781 printf("invalid: version string length doesn't "
782 "match. Got %d; want %d", slen, len);
783 break;
786 printf("msize=%"PRIu32" version[%"PRIu16"]=\"",
787 msize, slen);
788 fwrite(d, 1, slen, stdout);
789 printf("\"");
791 break;
793 case Rattach:
794 pp_qid(d, len);
795 break;
797 case Rclunk:
798 if (len != 0)
799 printf("invalid Rclunk: %"PRIu32" extra bytes", len);
800 break;
802 case Rflush:
803 if (len != 0)
804 printf("invalid Rflush: %"PRIu32" extra bytes", len);
805 break;
807 case Rwalk:
808 if (len < 2) {
809 printf("invaild Rwalk: less than two bytes (%d)",
810 (int)len);
811 break;
814 memcpy(&slen, d, sizeof(slen));
815 d += sizeof(slen);
816 len -= sizeof(slen);
817 slen = le16toh(slen);
819 if (len != QIDSIZE * slen) {
820 printf("invalid Rwalk: wanted %d bytes for %d qids "
821 "but got %"PRIu32" bytes instead",
822 QIDSIZE*slen, slen, len);
823 break;
826 printf("nwqid=%"PRIu16, slen);
828 for (; slen != 0; slen--) {
829 printf(" ");
830 pp_qid(d, len);
831 d += QIDSIZE;
832 len -= QIDSIZE;
835 break;
837 case Ropen:
838 case Rcreate:
839 if (len != QIDSIZE + 4) {
840 printf("invalid %s: expected %d bytes; "
841 "got %u\n", pp_msg_type(type), QIDSIZE + 4, len);
842 break;
845 pp_qid(d, len);
846 d += QIDSIZE;
847 len -= QIDSIZE;
849 memcpy(&iounit, d, sizeof(iounit));
850 d += sizeof(iounit);
851 len -= sizeof(iounit);
852 iounit = le32toh(iounit);
853 printf(" iounit=%"PRIu32, iounit);
854 break;
856 case Rread:
857 if (len < sizeof(count)) {
858 printf("invalid Rread: expected %zu bytes at least; "
859 "got %u\n", sizeof(count), len);
860 break;
863 memcpy(&count, d, sizeof(count));
864 d += sizeof(count);
865 len -= sizeof(count);
866 count = le32toh(count);
868 if (len != count) {
869 printf("invalid Rread: expected %d data bytes; "
870 "got %u\n", count, len);
871 break;
874 /* allocates three extra bytes, oh well... */
875 if ((v = calloc(count + 1, 4)) == NULL)
876 fatal("calloc");
877 strvisx(v, d, count, VIS_SAFE | VIS_TAB | VIS_NL | VIS_CSTYLE);
878 printf("data=%s", v);
879 free(v);
881 break;
883 case Rwrite:
884 if (len != sizeof(count)) {
885 printf("invalid Rwrite: expected %zu data bytes; "
886 "got %u\n", sizeof(count), len);
887 break;
890 memcpy(&count, d, sizeof(count));
891 d += sizeof(count);
892 len -= sizeof(count);
893 count = le32toh(count);
895 printf("count=%d", count);
896 break;
898 case Rremove:
899 if (len != 0)
900 printf("invalid Rremove: %"PRIu32" extra bytes", len);
901 break;
903 case Rerror:
904 memcpy(&slen, d, sizeof(slen));
905 d += sizeof(slen);
906 len -= sizeof(slen);
907 slen = le16toh(slen);
909 if (slen != len) {
910 printf("invalid: error string length doesn't "
911 "match. Got %d; want %d", slen, len);
912 break;
915 printf("error=\"");
916 fwrite(d, 1, slen, stdout);
917 printf("\"");
919 break;
921 default:
922 if ((v = calloc(len + 1, 4)) == NULL)
923 fatal("calloc");
924 strvisx(v, d, len, VIS_SAFE | VIS_TAB | VIS_NL | VIS_CSTYLE);
925 printf("body=%s", v);
926 free(v);
929 printf("\n");
932 static void
933 handle_9p(const uint8_t *data, size_t size)
935 uint32_t len;
936 uint16_t tag;
937 uint8_t type;
939 assert(size >= HEADERSIZE);
941 memcpy(&len, data, sizeof(len));
942 data += sizeof(len);
944 memcpy(&type, data, sizeof(type));
945 data += sizeof(type);
947 memcpy(&tag, data, sizeof(tag));
948 data += sizeof(tag);
950 len = le32toh(len);
951 /* type is one byte long, no endianness issues */
952 tag = le16toh(tag);
954 clr();
955 pp_msg(len, type, tag, data);
956 prompt();
959 static void
960 clr(void)
962 printf("\r");
963 fflush(stdout);
966 static void
967 prompt(void)
969 printf("%s", PROMPT);
970 fflush(stdout);
973 int
974 main(int argc, char **argv)
976 int ch, sock, handshake;
977 struct event ev_sigint, ev_sigterm;
979 signal(SIGPIPE, SIG_IGN);
981 while ((ch = getopt(argc, argv, "C:cH:hK:P:v")) != -1) {
982 switch (ch) {
983 case 'C':
984 crtpath = optarg;
985 break;
986 case 'c':
987 tls = 1;
988 break;
989 case 'H':
990 host = optarg;
991 break;
992 case 'h':
993 usage(0);
994 break;
995 case 'K':
996 keypath = optarg;
997 break;
998 case 'P':
999 port = optarg;
1000 break;
1001 case 'v':
1002 verbose = 1;
1003 break;
1004 default:
1005 usage(1);
1009 if (host == NULL)
1010 host = "localhost";
1011 if (port == NULL)
1012 port = "1337";
1014 argc -= optind;
1015 argv += optind;
1017 if (argc != 0)
1018 usage(1);
1019 /* if (!tls || (crtpath != NULL || keypath != NULL)) */
1020 /* usage(1); */
1021 if (!tls)
1022 errx(1, "must enable tls (for now)");
1024 log_init(1, LOG_DAEMON);
1025 log_setverbose(verbose);
1026 log_procinit(getprogname());
1028 if ((tlsconf = tls_config_new()) == NULL)
1029 fatalx("tls_config_new");
1030 tls_config_insecure_noverifycert(tlsconf);
1031 tls_config_insecure_noverifyname(tlsconf);
1032 if (tls_config_set_keypair_file(tlsconf, crtpath, keypath) == -1)
1033 fatalx("can't load certs (%s, %s)", crtpath, keypath);
1035 if ((ctx = tls_client()) == NULL)
1036 fatal("tls_client");
1037 if (tls_configure(ctx, tlsconf) == -1)
1038 fatalx("tls_configure: %s", tls_error(ctx));
1040 log_info("connecting to %s:%s...", host, port);
1042 if ((sock = openconn()) == -1)
1043 fatalx("can't connect to %s:%s", host, port);
1045 if (tls_connect_socket(ctx, sock, host) == -1)
1046 fatalx("tls_connect_socket: %s", tls_error(ctx));
1048 for (handshake = 0; !handshake;) {
1049 switch (tls_handshake(ctx)) {
1050 case -1:
1051 fatalx("tls_handshake: %s", tls_error(ctx));
1052 case 0:
1053 handshake = 1;
1054 break;
1058 log_info("connected!");
1060 mark_nonblock(sock);
1062 event_init();
1064 /* initialize global evb */
1065 if ((evb = evbuffer_new()) == NULL)
1066 fatal("evbuffer_new");
1068 signal_set(&ev_sigint, SIGINT, sig_handler, NULL);
1069 signal_set(&ev_sigterm, SIGINT, sig_handler, NULL);
1071 signal_add(&ev_sigint, NULL);
1072 signal_add(&ev_sigterm, NULL);
1074 bev = bufferevent_new(sock, client_read, client_write, client_error,
1075 NULL);
1076 if (bev == NULL)
1077 fatal("bufferevent_new");
1079 /* setup tls/io */
1080 event_set(&bev->ev_read, sock, EV_READ, tls_readcb, bev);
1081 event_set(&bev->ev_write, sock, EV_WRITE, tls_writecb, bev);
1083 bufferevent_enable(bev, EV_READ|EV_WRITE);
1085 mark_nonblock(0);
1086 inbev = bufferevent_new(0, repl_read, NULL, repl_error, NULL);
1087 bufferevent_enable(inbev, EV_READ);
1089 prompt();
1090 event_dispatch();
1092 bufferevent_free(bev);
1093 tls_free(ctx);
1094 tls_config_free(tlsconf);
1095 close(sock);
1097 return 0;