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 <errno.h>
26 #include <fcntl.h>
27 #include <inttypes.h>
28 #include <signal.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <syslog.h>
33 #include <tls.h>
34 #include <unistd.h>
36 #include "kami.h"
37 #include "log.h"
38 #include "utils.h"
39 #include "9pclib.h"
41 #define DEBUG_PACKETS 0
43 #define PROMPT "=% "
45 /* flags */
46 int verbose;
47 int tls;
48 const char *keypath;
49 const char *crtpath;
50 const char *host;
51 const char *port;
53 /* state */
54 struct tls_config *tlsconf;
55 struct tls *ctx;
56 struct bufferevent *bev, *inbev;
58 static void __dead usage(int);
60 static void sig_handler(int, short, void *);
62 static int openconn(void);
63 static void mark_nonblock(int);
65 static void tls_readcb(int, short, void *);
66 static void tls_writecb(int, short, void *);
68 static void client_read(struct bufferevent *, void *);
69 static void client_write(struct bufferevent *, void *);
70 static void client_error(struct bufferevent *, short, void *);
72 static void repl_read(struct bufferevent *, void *);
73 static void repl_error(struct bufferevent *, short, void *);
75 static void excmd_version(const char **, int);
76 static void excmd_attach(const char **, int);
77 static void excmd_clunk(const char **, int);
78 static void excmd_flush(const char **, int);
79 static void excmd_walk(const char ** , int);
80 static void excmd_open(const char ** , int);
81 static void excmd_create(const char ** , int);
82 static void excmd_read(const char ** , int);
83 static void excmd_write(const char **, int);
84 static void excmd(const char **, int);
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);
92 static void __dead
93 usage(int ret)
94 {
95 fprintf(stderr,
96 "usage: %s [-chv] [-C crt] [-K key] [-H host] [-P port]\n",
97 getprogname());
98 fprintf(stderr, "kamid suite version " KAMID_VERSION "\n");
99 exit(ret);
102 static void
103 sig_handler(int sig, short event, void *d)
105 /*
106 * Normal signal handler rules don't apply because libevent
107 * decouples for us.
108 */
110 switch (sig) {
111 case SIGINT:
112 case SIGTERM:
113 clr();
114 log_warnx("Shutting down...");
115 event_loopbreak();
116 return;
117 default:
118 fatalx("unexpected signal %d", sig);
122 static int
123 openconn(void)
125 struct addrinfo hints, *res, *res0;
126 int error;
127 int save_errno;
128 int s;
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));
136 return -1;
139 s = -1;
140 for (res = res0; res; res = res->ai_next) {
141 s = socket(res->ai_family, res->ai_socktype,
142 res->ai_protocol);
143 if (s == -1) {
144 cause = "socket";
145 continue;
148 if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
149 cause = "connect";
150 save_errno = errno;
151 close(s);
152 errno = save_errno;
153 s = -1;
154 continue;
157 break;
160 freeaddrinfo(res0);
162 if (s == -1)
163 warn("%s", cause);
165 return s;
168 static void
169 mark_nonblock(int fd)
171 int flags;
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)");
179 static void
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;
186 ssize_t ret;
187 size_t len;
189 if (event == EV_TIMEOUT) {
190 what |= EVBUFFER_TIMEOUT;
191 goto err;
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:
200 goto retry;
201 case -1:
202 what |= EVBUFFER_ERROR;
203 goto err;
205 len = ret;
207 if (len == 0) {
208 what |= EVBUFFER_EOF;
209 goto err;
212 if (evbuffer_add(bufev->input, buf, len) == -1) {
213 what |= EVBUFFER_ERROR;
214 goto err;
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)
221 return;
222 if (bufev->readcb != NULL)
223 (*bufev->readcb)(bufev, bufev->cbarg);
224 return;
226 retry:
227 event_add(&bufev->ev_read, NULL);
228 return;
230 err:
231 (*bufev->errorcb)(bufev, what, bufev->cbarg);
234 static void
235 tls_writecb(int fd, short event, void *d)
237 struct bufferevent *bufev = d;
238 ssize_t ret;
239 size_t len;
240 short what = EVBUFFER_WRITE;
241 void *data;
243 if (event == EV_TIMEOUT) {
244 what |= EVBUFFER_TIMEOUT;
245 goto err;
248 len = EVBUFFER_LENGTH(bufev->output);
249 if (len != 0) {
250 data = EVBUFFER_DATA(bufev->output);
252 #if DEBUG_PACKETS
253 hexdump("outgoing msg", data, len);
254 #endif
256 switch (ret = tls_write(ctx, data, len)) {
257 case TLS_WANT_POLLIN:
258 case TLS_WANT_POLLOUT:
259 goto retry;
260 case -1:
261 what |= EVBUFFER_ERROR;
262 goto err;
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);
273 return;
275 retry:
276 event_add(&bufev->ev_write, NULL);
277 return;
278 err:
279 (*bufev->errorcb)(bufev, what, bufev->cbarg);
282 static void
283 client_read(struct bufferevent *bev, void *d)
285 struct evbuffer *src = EVBUFFER_INPUT(bev);
286 uint32_t len;
287 uint8_t *data;
289 for (;;) {
290 if (EVBUFFER_LENGTH(src) < sizeof(len))
291 return;
293 data = EVBUFFER_DATA(src);
295 memcpy(&len, data, sizeof(len));
296 len = le32toh(len);
298 if (len < HEADERSIZE)
299 fatal("incoming message is too small! (%d bytes)",
300 len);
302 if (len > EVBUFFER_LENGTH(src))
303 return;
305 #if DEBUG_PACKETS
306 hexdump("incoming msg", data, len);
307 #endif
309 handle_9p(data, len);
310 evbuffer_drain(src, len);
314 static void
315 client_write(struct bufferevent *bev, void *data)
317 return; /* nothing to do */
320 static void
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) {
327 clr();
328 log_info("EOF");
329 event_loopbreak();
330 return;
333 clr();
334 log_warnx("unknown event error");
335 event_loopbreak();
338 static void
339 repl_read(struct bufferevent *bev, void *d)
341 size_t len;
342 int argc;
343 const char *argv[10], **ap;
344 char *line;
346 line = evbuffer_readln(bev->input, &len, EVBUFFER_EOL_LF);
347 if (line == NULL)
348 return;
350 for (argc = 0, ap = argv; ap < &argv[9] &&
351 (*ap = strsep(&line, " \t")) != NULL;) {
352 if (**ap != '\0')
353 ap++, argc++;
356 clr();
357 excmd(argv, argc);
358 prompt();
360 free(line);
363 static void
364 repl_error(struct bufferevent *bev, short error, void *d)
366 fatalx("an error occurred");
369 static inline void
370 do_send(void)
372 bufferevent_write_buffer(bev, evb);
375 /* version [version-str] */
376 static void
377 excmd_version(const char **argv, int argc)
379 const char *s;
381 s = VERSION9P;
382 if (argc == 2)
383 s = argv[1];
385 tversion(s, MSIZE9P);
386 do_send();
389 /* attach fid uname aname */
390 static void
391 excmd_attach(const char **argv, int argc)
393 uint32_t fid;
394 const char *errstr;
396 if (argc != 4)
397 goto usage;
399 fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
400 if (errstr != NULL) {
401 log_warnx("fid is %s: %s", errstr, argv[1]);
402 return;
405 tattach(fid, NOFID, argv[2], argv[3]);
406 do_send();
407 return;
409 usage:
410 log_warnx("usage: attach fid uname aname");
413 /* clunk fid */
414 static void
415 excmd_clunk(const char **argv, int argc)
417 uint32_t fid;
418 const char *errstr;
420 if (argc != 2)
421 goto usage;
423 fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
424 if (errstr != NULL) {
425 log_warnx("fid is %s: %s", errstr, argv[1]);
426 return;
429 tclunk(fid);
430 do_send();
431 return;
433 usage:
434 log_warnx("usage: clunk fid");
437 /* flush oldtag */
438 static void
439 excmd_flush(const char **argv, int argc)
441 uint16_t oldtag;
442 const char *errstr;
444 if (argc != 2)
445 goto usage;
447 oldtag = strtonum(argv[1], 0, UINT16_MAX, &errstr);
448 if (errstr != NULL) {
449 log_warnx("oldtag is %s: %s", errstr, argv[1]);
450 return;
453 tflush(oldtag);
454 do_send();
455 return;
457 usage:
458 log_warnx("usage: flush oldtag");
461 /* walk fid newfid wnames... */
462 static void
463 excmd_walk(const char **argv, int argc)
465 uint32_t fid, newfid;
466 const char *errstr;
468 if (argc < 3)
469 goto usage;
471 fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
472 if (errstr != NULL) {
473 log_warnx("fid is %s: %s", errstr, argv[1]);
474 return;
477 newfid = strtonum(argv[2], 0, UINT32_MAX, &errstr);
478 if (errstr != NULL) {
479 log_warnx("newfid is %s: %s", errstr, argv[1]);
480 return;
483 twalk(fid, newfid, argv + 3, argc - 3);
484 do_send();
485 return;
487 usage:
488 log_warnx("usage: walk fid newfid wnames...");
491 /* open fid mode [flag] */
492 static void
493 excmd_open(const char **argv, int argc)
495 const char *errstr;
496 uint32_t fid;
497 uint8_t mode = 0;
499 if (argc != 3 && argc != 4)
500 goto usage;
502 fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
503 if (errstr != NULL) {
504 log_warnx("fid is %s: %s", errstr, argv[1]);
505 return;
508 /* parse mode */
509 if (!strcmp("read", argv[2]) || !strcmp("r", argv[2]))
510 mode = KOREAD;
511 else if (!strcmp("write", argv[2]) || !strcmp("w", argv[2]))
512 mode = KOWRITE;
513 else if (!strcmp("readwrite", argv[2]) || !strcmp("rw", argv[2]))
514 mode = KORDWR;
515 else {
516 log_warnx("invalid mode %s", argv[2]);
517 return;
520 /* parse flag */
521 if (argv[3] != NULL) {
522 if (!strcmp("trunc", argv[3]))
523 mode |= KOTRUNC;
524 else if (!strcmp("rclose", argv[3]))
525 mode |= KORCLOSE;
526 else {
527 log_warnx("invalid flag %s", argv[3]);
528 return;
532 topen(fid, mode);
533 do_send();
534 return;
536 usage:
537 log_warnx("usage: open fid mode [flag]");
540 /* create fid path perm mode */
541 static void
542 excmd_create(const char **argv, int argc)
544 const char *errstr;
545 uint32_t fid;
546 uint8_t mode = 0;
548 if (argc != 5)
549 goto usage;
551 fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
552 if (errstr != NULL) {
553 log_warnx("fid is %s: %s", errstr, argv[1]);
554 return;
557 /* parse mode */
558 if (!strcmp("write", argv[4]) || !strcmp("w", argv[4]))
559 mode = KOWRITE;
560 else if (!strcmp("readwrite", argv[4]) || !strcmp("rw", argv[4]))
561 mode = KORDWR;
562 else {
563 log_warnx("invalid mode %s for create", argv[4]);
564 return;
567 tcreate(fid, argv[2], 0, mode);
568 do_send();
569 return;
571 usage:
572 log_warnx("usage: create fid path perm mode ; perm is unused");
576 /* read fid offset count */
577 static void
578 excmd_read(const char **argv, int argc)
580 uint64_t off;
581 uint32_t fid, count;
582 const char *errstr;
584 if (argc != 4)
585 goto usage;
587 fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
588 if (errstr != NULL) {
589 log_warnx("fid is %s: %s", errstr, argv[1]);
590 return;
593 /* should really be UNT64_MAX but it fails... */
594 off = strtonum(argv[2], -1, UINT32_MAX, &errstr);
595 if (errstr != NULL) {
596 log_warnx("offset is %s: %s", errstr, argv[2]);
597 return;
600 count = strtonum(argv[3], 0, UINT32_MAX, &errstr);
601 if (errstr != NULL) {
602 log_warnx("count is %s: %s", errstr, argv[3]);
603 return;
606 tread(fid, off, count);
607 do_send();
608 return;
610 usage:
611 log_warnx("usage: read fid offset count");
614 /* write fid offset content */
615 static void
616 excmd_write(const char **argv, int argc)
618 uint64_t off;
619 uint32_t fid, count;
620 const char *errstr;
622 if (argc != 4)
623 goto usage;
625 fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
626 if (errstr != NULL) {
627 log_warnx("fid is %s: %s", errstr, argv[1]);
628 return;
631 /* should really be UINT64_MAX but... */
632 off = strtonum(argv[2], 0, UINT32_MAX, &errstr);
633 if (errstr != NULL) {
634 log_warnx("offset is %s: %s", errstr, argv[2]);
635 return;
638 count = strlen(argv[3]);
639 twrite(fid, off, argv[3], count);
640 do_send();
641 return;
643 usage:
644 log_warnx("usage: write fid offset content");
647 /* remove fid */
648 static void
649 excmd_remove(const char **argv, int argc)
651 const char *errstr;
652 uint32_t fid;
654 if (argc != 2)
655 goto usage;
657 fid = strtonum(argv[1], 0, UINT32_MAX, &errstr);
658 if (errstr != NULL) {
659 log_warnx("fid is %s: %s", errstr, argv[1]);
660 return;
663 tremove(fid);
664 do_send();
665 return;
667 usage:
668 log_warnx("usage: remove fid");
671 static void
672 excmd(const char **argv, int argc)
674 struct cmd {
675 const char *name;
676 void (*fn)(const char **, int);
677 } cmds[] = {
678 {"version", excmd_version},
679 {"attach", excmd_attach},
680 {"clunk", excmd_clunk},
681 {"flush", excmd_flush},
682 {"walk", excmd_walk},
683 {"open", excmd_open},
684 {"create", excmd_create},
685 {"read", excmd_read},
686 {"write", excmd_write},
687 /* TODO: stat */
688 {"remove", excmd_remove},
689 };
690 size_t i;
692 if (argc == 0)
693 return;
695 for (i = 0; i < sizeof(cmds)/sizeof(cmds[0]); ++i) {
696 if (!strcmp(cmds[i].name, argv[0])) {
697 cmds[i].fn(argv, argc);
698 return;
702 log_warnx("Unknown command %s", *argv);
705 static void
706 pp_qid(const uint8_t *d, uint32_t len)
708 uint64_t path;
709 uint32_t vers;
710 uint8_t type;
712 if (len < 13) {
713 printf("invalid");
714 return;
717 type = *d++;
719 memcpy(&vers, d, sizeof(vers));
720 d += sizeof(vers);
721 vers = le64toh(vers);
723 memcpy(&path, d, sizeof(path));
724 d += sizeof(path);
725 path = le64toh(path);
727 printf("qid{path=%"PRIu64" version=%"PRIu32" type=0x%x\"%s\"}",
728 path, vers, type, pp_qid_type(type));
731 static void
732 pp_msg(uint32_t len, uint8_t type, uint16_t tag, const uint8_t *d)
734 uint32_t msize, iounit, count;
735 uint16_t slen;
736 char *v;
738 printf("len=%"PRIu32" type=%d[%s] tag=0x%x[%d] ", len,
739 type, pp_msg_type(type), tag, tag);
741 len -= HEADERSIZE;
743 switch (type) {
744 case Rversion:
745 if (len < 6) {
746 printf("invalid: not enough space for msize "
747 "and version provided.");
748 break;
751 memcpy(&msize, d, sizeof(msize));
752 d += sizeof(msize);
753 len -= sizeof(msize);
754 msize = le32toh(msize);
756 memcpy(&slen, d, sizeof(slen));
757 d += sizeof(slen);
758 len -= sizeof(slen);
759 slen = le16toh(slen);
761 if (len != slen) {
762 printf("invalid: version string length doesn't "
763 "match. Got %d; want %d", slen, len);
764 break;
767 printf("msize=%"PRIu32" version[%"PRIu16"]=\"",
768 msize, slen);
769 fwrite(d, 1, slen, stdout);
770 printf("\"");
772 break;
774 case Rattach:
775 pp_qid(d, len);
776 break;
778 case Rclunk:
779 case Rflush:
780 case Rremove:
781 if (len != 0)
782 printf("invalid %s: %"PRIu32" extra bytes",
783 pp_msg_type(type), len);
784 break;
786 case Rwalk:
787 if (len < 2) {
788 printf("invaild Rwalk: less than two bytes (%d)",
789 (int)len);
790 break;
793 memcpy(&slen, d, sizeof(slen));
794 d += sizeof(slen);
795 len -= sizeof(slen);
796 slen = le16toh(slen);
798 if (len != QIDSIZE * slen) {
799 printf("invalid Rwalk: wanted %d bytes for %d qids "
800 "but got %"PRIu32" bytes instead",
801 QIDSIZE*slen, slen, len);
802 break;
805 printf("nwqid=%"PRIu16, slen);
807 for (; slen != 0; slen--) {
808 printf(" ");
809 pp_qid(d, len);
810 d += QIDSIZE;
811 len -= QIDSIZE;
814 break;
816 case Ropen:
817 case Rcreate:
818 if (len != QIDSIZE + 4) {
819 printf("invalid %s: expected %d bytes; "
820 "got %u\n", pp_msg_type(type), QIDSIZE + 4, len);
821 break;
824 pp_qid(d, len);
825 d += QIDSIZE;
826 len -= QIDSIZE;
828 memcpy(&iounit, d, sizeof(iounit));
829 d += sizeof(iounit);
830 len -= sizeof(iounit);
831 iounit = le32toh(iounit);
832 printf(" iounit=%"PRIu32, iounit);
833 break;
835 case Rread:
836 if (len < sizeof(count)) {
837 printf("invalid Rread: expected %zu bytes at least; "
838 "got %u\n", sizeof(count), len);
839 break;
842 memcpy(&count, d, sizeof(count));
843 d += sizeof(count);
844 len -= sizeof(count);
845 count = le32toh(count);
847 if (len != count) {
848 printf("invalid Rread: expected %d data bytes; "
849 "got %u\n", count, len);
850 break;
853 /* allocates three extra bytes, oh well... */
854 if ((v = calloc(count + 1, 4)) == NULL)
855 fatal("calloc");
856 strvisx(v, d, count, VIS_SAFE | VIS_TAB | VIS_NL | VIS_CSTYLE);
857 printf("data=%s", v);
858 free(v);
860 break;
862 case Rwrite:
863 if (len != sizeof(count)) {
864 printf("invalid Rwrite: expected %zu data bytes; "
865 "got %u\n", sizeof(count), len);
866 break;
869 memcpy(&count, d, sizeof(count));
870 d += sizeof(count);
871 len -= sizeof(count);
872 count = le32toh(count);
874 printf("count=%d", count);
875 break;
877 case Rerror:
878 memcpy(&slen, d, sizeof(slen));
879 d += sizeof(slen);
880 len -= sizeof(slen);
881 slen = le16toh(slen);
883 if (slen != len) {
884 printf("invalid: error string length doesn't "
885 "match. Got %d; want %d", slen, len);
886 break;
889 printf("error=\"");
890 fwrite(d, 1, slen, stdout);
891 printf("\"");
893 break;
895 default:
896 if ((v = calloc(len + 1, 4)) == NULL)
897 fatal("calloc");
898 strvisx(v, d, len, VIS_SAFE | VIS_TAB | VIS_NL | VIS_CSTYLE);
899 printf("body=%s", v);
900 free(v);
903 printf("\n");
906 static void
907 handle_9p(const uint8_t *data, size_t size)
909 uint32_t len;
910 uint16_t tag;
911 uint8_t type;
913 assert(size >= HEADERSIZE);
915 memcpy(&len, data, sizeof(len));
916 data += sizeof(len);
918 memcpy(&type, data, sizeof(type));
919 data += sizeof(type);
921 memcpy(&tag, data, sizeof(tag));
922 data += sizeof(tag);
924 len = le32toh(len);
925 /* type is one byte long, no endianness issues */
926 tag = le16toh(tag);
928 clr();
929 pp_msg(len, type, tag, data);
930 prompt();
933 static void
934 clr(void)
936 printf("\r");
937 fflush(stdout);
940 static void
941 prompt(void)
943 printf("%s", PROMPT);
944 fflush(stdout);
947 int
948 main(int argc, char **argv)
950 int ch, sock, handshake;
951 struct event ev_sigint, ev_sigterm;
953 signal(SIGPIPE, SIG_IGN);
955 while ((ch = getopt(argc, argv, "C:cH:hK:P:v")) != -1) {
956 switch (ch) {
957 case 'C':
958 crtpath = optarg;
959 break;
960 case 'c':
961 tls = 1;
962 break;
963 case 'H':
964 host = optarg;
965 break;
966 case 'h':
967 usage(0);
968 break;
969 case 'K':
970 keypath = optarg;
971 break;
972 case 'P':
973 port = optarg;
974 break;
975 case 'v':
976 verbose = 1;
977 break;
978 default:
979 usage(1);
983 if (host == NULL)
984 host = "localhost";
985 if (port == NULL)
986 port = "1337";
988 argc -= optind;
989 argv += optind;
991 if (argc != 0)
992 usage(1);
993 /* if (!tls || (crtpath != NULL || keypath != NULL)) */
994 /* usage(1); */
995 if (!tls)
996 errx(1, "must enable tls (for now)");
998 log_init(1, LOG_DAEMON);
999 log_setverbose(verbose);
1000 log_procinit(getprogname());
1002 if ((tlsconf = tls_config_new()) == NULL)
1003 fatalx("tls_config_new");
1004 tls_config_insecure_noverifycert(tlsconf);
1005 tls_config_insecure_noverifyname(tlsconf);
1006 if (tls_config_set_keypair_file(tlsconf, crtpath, keypath) == -1)
1007 fatalx("can't load certs (%s, %s)", crtpath, keypath);
1009 if ((ctx = tls_client()) == NULL)
1010 fatal("tls_client");
1011 if (tls_configure(ctx, tlsconf) == -1)
1012 fatalx("tls_configure: %s", tls_error(ctx));
1014 log_info("connecting to %s:%s...", host, port);
1016 if ((sock = openconn()) == -1)
1017 fatalx("can't connect to %s:%s", host, port);
1019 if (tls_connect_socket(ctx, sock, host) == -1)
1020 fatalx("tls_connect_socket: %s", tls_error(ctx));
1022 for (handshake = 0; !handshake;) {
1023 switch (tls_handshake(ctx)) {
1024 case -1:
1025 fatalx("tls_handshake: %s", tls_error(ctx));
1026 case 0:
1027 handshake = 1;
1028 break;
1032 log_info("connected!");
1034 mark_nonblock(sock);
1036 event_init();
1038 /* initialize global evb */
1039 if ((evb = evbuffer_new()) == NULL)
1040 fatal("evbuffer_new");
1042 signal_set(&ev_sigint, SIGINT, sig_handler, NULL);
1043 signal_set(&ev_sigterm, SIGINT, sig_handler, NULL);
1045 signal_add(&ev_sigint, NULL);
1046 signal_add(&ev_sigterm, NULL);
1048 bev = bufferevent_new(sock, client_read, client_write, client_error,
1049 NULL);
1050 if (bev == NULL)
1051 fatal("bufferevent_new");
1053 /* setup tls/io */
1054 event_set(&bev->ev_read, sock, EV_READ, tls_readcb, bev);
1055 event_set(&bev->ev_write, sock, EV_WRITE, tls_writecb, bev);
1057 bufferevent_enable(bev, EV_READ|EV_WRITE);
1059 mark_nonblock(0);
1060 inbev = bufferevent_new(0, repl_read, NULL, repl_error, NULL);
1061 bufferevent_enable(inbev, EV_READ);
1063 prompt();
1064 event_dispatch();
1066 bufferevent_free(bev);
1067 tls_free(ctx);
1068 tls_config_free(tlsconf);
1069 close(sock);
1071 return 0;