From b6d2f4a5ab6089cbcf8f2e1de40d292baa5b3a83 Mon Sep 17 00:00:00 2001 From: Curtis McEnroe Date: Sat, 4 May 2019 17:50:38 -0400 Subject: Move relay to bin --- bin/.gitignore | 2 + bin/Makefile | 41 +++++++---- bin/README | 3 +- bin/bin.7 | 5 +- bin/irc/.gitignore | 1 - bin/irc/Makefile | 13 ---- bin/irc/relay.1 | 48 ------------- bin/irc/relay.c | 204 ----------------------------------------------------- bin/man1/relay.1 | 48 +++++++++++++ bin/relay.c | 204 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 289 insertions(+), 280 deletions(-) delete mode 100644 bin/irc/.gitignore delete mode 100644 bin/irc/Makefile delete mode 100644 bin/irc/relay.1 delete mode 100644 bin/irc/relay.c create mode 100644 bin/man1/relay.1 create mode 100644 bin/relay.c diff --git a/bin/.gitignore b/bin/.gitignore index 4e25a4fd..ce3a976f 100644 --- a/bin/.gitignore +++ b/bin/.gitignore @@ -5,6 +5,7 @@ atch beef bri brot +config.mk dtch fbatt fbclock @@ -20,6 +21,7 @@ pbpaste pngo psf2png psfed +relay scheme scheme.h scheme.png diff --git a/bin/Makefile b/bin/Makefile index 16a5ba9e..528a70b2 100644 --- a/bin/Makefile +++ b/bin/Makefile @@ -1,13 +1,22 @@ -PREFIX = ~/.local -MANDIR = $(PREFIX)/share/man GFX = cocoa +LIBRESSL_PREFIX = /usr/local + +PREFIX = ~/.local +MANIDR = $(PREFIX)/share/man CFLAGS += -Wall -Wextra -Wpedantic LDLIBS = -lm -lutil -lz + +CFLAGS_tls = $(CFLAGS) -I$(LIBRESSL_PREFIX)/include +LDFLAGS_tls = $(LDFLAGS) -L$(LIBRESSL_PREFIX)/lib +LDLIBS_tls = $(LDLIBS) -ltls + LDLIBS_cocoa = $(LDLIBS) -framework Cocoa LDLIBS_fb = $(LDLIBS) LDLIBS_x11 = $(LDLIBS) -lX11 +-include config.mk + BINS += aes BINS += dtch BINS += glitch @@ -32,24 +41,28 @@ LINKS += pbpaste BINS_BSD += beef BINS_BSD += wat +BINS_GFX += brot +BINS_GFX += gfxx + BINS_LINUX += bri BINS_LINUX += fbatt BINS_LINUX += fbclock BINS_LINUX += psfed -BINS_GFX += brot -BINS_GFX += gfxx +BINS_TLS += relay -BINS_ALL = $(BINS) $(BINS_BSD) $(BINS_LINUX) $(BINS_GFX) +BINS_ALL = $(BINS) $(BINS_BSD) $(BINS_GFX) $(BINS_LINUX) $(BINS_TLS) MAN1_ALL = $(BINS_ALL:%=man1/%.1) any: .gitignore tags $(BINS) $(LINKS) -bsd: any $(BINS_BSD) +bsd: $(BINS_BSD) + +gfx: $(BINS_GFX) -linux: any $(BINS_LINUX) +linux: $(BINS_LINUX) -gfx: any $(BINS_GFX) +tls: $(BINS_TLS) .o: $(CC) $(LDFLAGS) $< $(LDLIBS) -o $@ @@ -75,22 +88,26 @@ hi: hi.c $(CC) $(CFLAGS) $(LDFLAGS) hi.c $(LDLIBS) -o $@ ./hi -c +relay: relay.c + $(CC) $(CFLAGS_tls) $(LDFLAGS_tls) relay.c $(LDLIBS_tls) -o $@ + atch: dtch ln -f dtch atch open pbcopy pbpaste: pbd ln -f pbd $@ -.gitignore: Makefile - echo $(BINS_ALL) $(LINKS) tags scheme.h scheme.png '*.o' '*.html' \ - | tr ' ' '\n' | sort > .gitignore - tags: *.h *.c ctags -w *.h *.c scheme.png: scheme ./scheme -gt > scheme.png +IGNORE = '*.o' '*.html' config.mk tags scheme.h scheme.png $(BINS_ALL) $(LINKS) + +.gitignore: Makefile + echo $(IGNORE) | tr ' ' '\n' | sort > .gitignore + HTMLS = $(BINS_ALL:%=%.html) HTMLS += Makefile.html HTMLS += gfx-fb.html diff --git a/bin/README b/bin/README index bd12df94..d3d05ade 100644 --- a/bin/README +++ b/bin/README @@ -23,6 +23,7 @@ DESCRIPTION pngo(1) PNG optimizer psf2png(1) PSF2 to PNG renderer psfed(1) PSF2 font editor + relay(1) IRC relay scheme(1) color scheme ttpre(1) man output to HTML up(1) upload file @@ -36,4 +37,4 @@ DESCRIPTION GFX=fb GFX=x11 -Causal Agency Feburary 12, 2019 Causal Agency +Causal Agency May 4, 2019 Causal Agency diff --git a/bin/bin.7 b/bin/bin.7 index e6ab3cc0..0c7c3184 100644 --- a/bin/bin.7 +++ b/bin/bin.7 @@ -1,4 +1,4 @@ -.Dd Feburary 12, 2019 +.Dd May 4, 2019 .Dt BIN 7 .Os "Causal Agency" . @@ -64,6 +64,9 @@ PSF2 to PNG renderer .It Xr psfed 1 PSF2 font editor . +.It Xr relay 1 +IRC relay +. .It Xr scheme 1 color scheme . diff --git a/bin/irc/.gitignore b/bin/irc/.gitignore deleted file mode 100644 index 32e541af..00000000 --- a/bin/irc/.gitignore +++ /dev/null @@ -1 +0,0 @@ -relay diff --git a/bin/irc/Makefile b/bin/irc/Makefile deleted file mode 100644 index c909f067..00000000 --- a/bin/irc/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -LIBRESSL_PREFIX = /usr/local - -CFLAGS += -Wall -Wextra -Wpedantic -CFLAGS += -I$(LIBRESSL_PREFIX)/include -LDFLAGS += -L$(LIBRESSL_PREFIX)/lib -LDLIBS = -ltls - --include config.mk - -relay: - -clean: - rm -f relay diff --git a/bin/irc/relay.1 b/bin/irc/relay.1 deleted file mode 100644 index 402c4726..00000000 --- a/bin/irc/relay.1 +++ /dev/null @@ -1,48 +0,0 @@ -.Dd April 28, 2019 -.Dt RELAY 1 -.Os -. -.Sh NAME -.Nm relay -.Nd IRC relay bot -. -.Sh SYNOPSIS -.Nm -.Ar host -.Ar port -.Ar nick -.Ar chan -. -.Sh DESCRIPTION -.Nm -is one half of an IRC relay pair. -It connects to -.Ar host Ns : Ns Ar port -over TLS -as -.Ar nick -and joins -.Ar chan . -. -.Pp -.Nm -outputs messages from -.Ar chan -to standard output -and sends messages to -.Ar chan -from standard input. -Two -.Nm -processes can be connected with -.Xr mkfifo 1 . -. -.Sh EXAMPLES -.Bd -literal -offset indent -mkfifo a b -relay a.example.com 6697 relay '#example' <>a >b -relay b.example.com 6697 relay '#example' <>b >a -.Ed -. -.Sh SEE ALSO -.Xr mkfifo 1 diff --git a/bin/irc/relay.c b/bin/irc/relay.c deleted file mode 100644 index caf9062f..00000000 --- a/bin/irc/relay.c +++ /dev/null @@ -1,204 +0,0 @@ -/* Copyright (C) 2019 C. McEnroe - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __FreeBSD__ -#include -#endif - -static void clientWrite(struct tls *client, const char *ptr, size_t len) { - while (len) { - ssize_t ret = tls_write(client, ptr, len); - if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) continue; - if (ret < 0) errx(EX_IOERR, "tls_write: %s", tls_error(client)); - ptr += ret; - len -= ret; - } -} - -static void clientFormat(struct tls *client, const char *format, ...) { - char buf[1024]; - va_list ap; - va_start(ap, format); - int len = vsnprintf(buf, sizeof(buf), format, ap); - va_end(ap); - if ((size_t)len > sizeof(buf) - 1) errx(EX_DATAERR, "message too large"); - clientWrite(client, buf, len); -} - -static void clientHandle(struct tls *client, const char *chan, char *line) { - char *prefix = NULL; - if (line[0] == ':') { - prefix = strsep(&line, " ") + 1; - if (!line) errx(EX_PROTOCOL, "unexpected eol"); - } - - char *command = strsep(&line, " "); - if (!strcmp(command, "001") || !strcmp(command, "INVITE")) { - clientFormat(client, "JOIN :%s\r\n", chan); - } else if (!strcmp(command, "PING")) { - clientFormat(client, "PONG %s\r\n", line); - } - if (strcmp(command, "PRIVMSG") && strcmp(command, "NOTICE")) return; - - if (!prefix) errx(EX_PROTOCOL, "message without prefix"); - char *nick = strsep(&prefix, "!"); - - if (!line) errx(EX_PROTOCOL, "message without destination"); - char *dest = strsep(&line, " "); - if (strcmp(dest, chan)) return; - - if (!line || line[0] != ':') errx(EX_PROTOCOL, "message without message"); - line = &line[1]; - - if (!strncmp(line, "\1ACTION ", 8)) { - line = &line[8]; - size_t len = strcspn(line, "\1"); - printf("* %c\u200B%s %.*s\n", nick[0], &nick[1], (int)len, line); - } else if (command[0] == 'N') { - printf("-%c\u200B%s- %s\n", nick[0], &nick[1], line); - } else { - printf("<%c\u200B%s> %s\n", nick[0], &nick[1], line); - } -} - -int main(int argc, char *argv[]) { - int error; - - if (argc < 5) return EX_USAGE; - const char *host = argv[1]; - const char *port = argv[2]; - const char *nick = argv[3]; - const char *chan = argv[4]; - - setlinebuf(stdout); - - struct tls_config *config = tls_config_new(); - if (!config) errx(EX_SOFTWARE, "tls_config_new"); - - error = tls_config_set_ciphers(config, "compat"); - if (error) { - errx(EX_SOFTWARE, "tls_config_set_ciphers: %s", tls_config_error(config)); - } - - struct tls *client = tls_client(); - if (!client) errx(EX_SOFTWARE, "tls_client"); - - error = tls_configure(client, config); - if (error) errx(EX_SOFTWARE, "tls_configure: %s", tls_error(client)); - tls_config_free(config); - - struct addrinfo *head; - struct addrinfo hints = { - .ai_family = AF_UNSPEC, - .ai_socktype = SOCK_STREAM, - .ai_protocol = IPPROTO_TCP, - }; - error = getaddrinfo(host, port, &hints, &head); - if (error) errx(EX_NOHOST, "getaddrinfo: %s", gai_strerror(error)); - - int sock = -1; - for (struct addrinfo *ai = head; ai; ai = ai->ai_next) { - sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (sock < 0) err(EX_OSERR, "socket"); - - error = connect(sock, ai->ai_addr, ai->ai_addrlen); - if (!error) break; - - close(sock); - sock = -1; - } - if (sock < 0) err(EX_UNAVAILABLE, "connect"); - freeaddrinfo(head); - - error = tls_connect_socket(client, sock, host); - if (error) errx(EX_PROTOCOL, "tls_connect: %s", tls_error(client)); - -#ifdef __FreeBSD__ - cap_rights_t rights; - - error = cap_enter(); - if (error) err(EX_OSERR, "cap_enter"); - - cap_rights_init(&rights, CAP_READ, CAP_EVENT); - error = cap_rights_limit(STDIN_FILENO, &rights); - if (error) err(EX_OSERR, "cap_rights_limit"); - - cap_rights_init(&rights, CAP_WRITE); - error = cap_rights_limit(STDOUT_FILENO, &rights); - if (error) err(EX_OSERR, "cap_rights_limit"); - - error = cap_rights_limit(STDERR_FILENO, &rights); - if (error) err(EX_OSERR, "cap_rights_limit"); - - cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT); - error = cap_rights_limit(sock, &rights); - if (error) err(EX_OSERR, "cap_rights_limit"); -#endif - - clientFormat(client, "NICK :%s\r\nUSER %s 0 * :%s\r\n", nick, nick, nick); - - char *input = NULL; - size_t cap = 0; - - char buf[4096]; - size_t len = 0; - - struct pollfd fds[2] = { - { .events = POLLIN, .fd = STDIN_FILENO }, - { .events = POLLIN, .fd = sock }, - }; - while (0 < poll(fds, 2, -1)) { - if (fds[0].revents) { - ssize_t len = getline(&input, &cap, stdin); - if (len < 0) err(EX_IOERR, "getline"); - input[len - 1] = '\0'; - clientFormat(client, "NOTICE %s :%s\r\n", chan, input); - } - if (!fds[1].revents) continue; - - ssize_t read = tls_read(client, &buf[len], sizeof(buf) - len); - if (read == TLS_WANT_POLLIN || read == TLS_WANT_POLLOUT) continue; - if (read < 0) errx(EX_IOERR, "tls_read: %s", tls_error(client)); - if (!read) return EX_UNAVAILABLE; - len += read; - - char *crlf; - char *line = buf; - for (;;) { - crlf = memmem(line, &buf[len] - line, "\r\n", 2); - if (!crlf) break; - crlf[0] = '\0'; - clientHandle(client, chan, line); - line = &crlf[2]; - } - len -= line - buf; - memmove(buf, line, len); - } - err(EX_IOERR, "poll"); -} diff --git a/bin/man1/relay.1 b/bin/man1/relay.1 new file mode 100644 index 00000000..402c4726 --- /dev/null +++ b/bin/man1/relay.1 @@ -0,0 +1,48 @@ +.Dd April 28, 2019 +.Dt RELAY 1 +.Os +. +.Sh NAME +.Nm relay +.Nd IRC relay bot +. +.Sh SYNOPSIS +.Nm +.Ar host +.Ar port +.Ar nick +.Ar chan +. +.Sh DESCRIPTION +.Nm +is one half of an IRC relay pair. +It connects to +.Ar host Ns : Ns Ar port +over TLS +as +.Ar nick +and joins +.Ar chan . +. +.Pp +.Nm +outputs messages from +.Ar chan +to standard output +and sends messages to +.Ar chan +from standard input. +Two +.Nm +processes can be connected with +.Xr mkfifo 1 . +. +.Sh EXAMPLES +.Bd -literal -offset indent +mkfifo a b +relay a.example.com 6697 relay '#example' <>a >b +relay b.example.com 6697 relay '#example' <>b >a +.Ed +. +.Sh SEE ALSO +.Xr mkfifo 1 diff --git a/bin/relay.c b/bin/relay.c new file mode 100644 index 00000000..caf9062f --- /dev/null +++ b/bin/relay.c @@ -0,0 +1,204 @@ +/* Copyright (C) 2019 C. McEnroe + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __FreeBSD__ +#include +#endif + +static void clientWrite(struct tls *client, const char *ptr, size_t len) { + while (len) { + ssize_t ret = tls_write(client, ptr, len); + if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) continue; + if (ret < 0) errx(EX_IOERR, "tls_write: %s", tls_error(client)); + ptr += ret; + len -= ret; + } +} + +static void clientFormat(struct tls *client, const char *format, ...) { + char buf[1024]; + va_list ap; + va_start(ap, format); + int len = vsnprintf(buf, sizeof(buf), format, ap); + va_end(ap); + if ((size_t)len > sizeof(buf) - 1) errx(EX_DATAERR, "message too large"); + clientWrite(client, buf, len); +} + +static void clientHandle(struct tls *client, const char *chan, char *line) { + char *prefix = NULL; + if (line[0] == ':') { + prefix = strsep(&line, " ") + 1; + if (!line) errx(EX_PROTOCOL, "unexpected eol"); + } + + char *command = strsep(&line, " "); + if (!strcmp(command, "001") || !strcmp(command, "INVITE")) { + clientFormat(client, "JOIN :%s\r\n", chan); + } else if (!strcmp(command, "PING")) { + clientFormat(client, "PONG %s\r\n", line); + } + if (strcmp(command, "PRIVMSG") && strcmp(command, "NOTICE")) return; + + if (!prefix) errx(EX_PROTOCOL, "message without prefix"); + char *nick = strsep(&prefix, "!"); + + if (!line) errx(EX_PROTOCOL, "message without destination"); + char *dest = strsep(&line, " "); + if (strcmp(dest, chan)) return; + + if (!line || line[0] != ':') errx(EX_PROTOCOL, "message without message"); + line = &line[1]; + + if (!strncmp(line, "\1ACTION ", 8)) { + line = &line[8]; + size_t len = strcspn(line, "\1"); + printf("* %c\u200B%s %.*s\n", nick[0], &nick[1], (int)len, line); + } else if (command[0] == 'N') { + printf("-%c\u200B%s- %s\n", nick[0], &nick[1], line); + } else { + printf("<%c\u200B%s> %s\n", nick[0], &nick[1], line); + } +} + +int main(int argc, char *argv[]) { + int error; + + if (argc < 5) return EX_USAGE; + const char *host = argv[1]; + const char *port = argv[2]; + const char *nick = argv[3]; + const char *chan = argv[4]; + + setlinebuf(stdout); + + struct tls_config *config = tls_config_new(); + if (!config) errx(EX_SOFTWARE, "tls_config_new"); + + error = tls_config_set_ciphers(config, "compat"); + if (error) { + errx(EX_SOFTWARE, "tls_config_set_ciphers: %s", tls_config_error(config)); + } + + struct tls *client = tls_client(); + if (!client) errx(EX_SOFTWARE, "tls_client"); + + error = tls_configure(client, config); + if (error) errx(EX_SOFTWARE, "tls_configure: %s", tls_error(client)); + tls_config_free(config); + + struct addrinfo *head; + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, + .ai_protocol = IPPROTO_TCP, + }; + error = getaddrinfo(host, port, &hints, &head); + if (error) errx(EX_NOHOST, "getaddrinfo: %s", gai_strerror(error)); + + int sock = -1; + for (struct addrinfo *ai = head; ai; ai = ai->ai_next) { + sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (sock < 0) err(EX_OSERR, "socket"); + + error = connect(sock, ai->ai_addr, ai->ai_addrlen); + if (!error) break; + + close(sock); + sock = -1; + } + if (sock < 0) err(EX_UNAVAILABLE, "connect"); + freeaddrinfo(head); + + error = tls_connect_socket(client, sock, host); + if (error) errx(EX_PROTOCOL, "tls_connect: %s", tls_error(client)); + +#ifdef __FreeBSD__ + cap_rights_t rights; + + error = cap_enter(); + if (error) err(EX_OSERR, "cap_enter"); + + cap_rights_init(&rights, CAP_READ, CAP_EVENT); + error = cap_rights_limit(STDIN_FILENO, &rights); + if (error) err(EX_OSERR, "cap_rights_limit"); + + cap_rights_init(&rights, CAP_WRITE); + error = cap_rights_limit(STDOUT_FILENO, &rights); + if (error) err(EX_OSERR, "cap_rights_limit"); + + error = cap_rights_limit(STDERR_FILENO, &rights); + if (error) err(EX_OSERR, "cap_rights_limit"); + + cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT); + error = cap_rights_limit(sock, &rights); + if (error) err(EX_OSERR, "cap_rights_limit"); +#endif + + clientFormat(client, "NICK :%s\r\nUSER %s 0 * :%s\r\n", nick, nick, nick); + + char *input = NULL; + size_t cap = 0; + + char buf[4096]; + size_t len = 0; + + struct pollfd fds[2] = { + { .events = POLLIN, .fd = STDIN_FILENO }, + { .events = POLLIN, .fd = sock }, + }; + while (0 < poll(fds, 2, -1)) { + if (fds[0].revents) { + ssize_t len = getline(&input, &cap, stdin); + if (len < 0) err(EX_IOERR, "getline"); + input[len - 1] = '\0'; + clientFormat(client, "NOTICE %s :%s\r\n", chan, input); + } + if (!fds[1].revents) continue; + + ssize_t read = tls_read(client, &buf[len], sizeof(buf) - len); + if (read == TLS_WANT_POLLIN || read == TLS_WANT_POLLOUT) continue; + if (read < 0) errx(EX_IOERR, "tls_read: %s", tls_error(client)); + if (!read) return EX_UNAVAILABLE; + len += read; + + char *crlf; + char *line = buf; + for (;;) { + crlf = memmem(line, &buf[len] - line, "\r\n", 2); + if (!crlf) break; + crlf[0] = '\0'; + clientHandle(client, chan, line); + line = &crlf[2]; + } + len -= line - buf; + memmove(buf, line, len); + } + err(EX_IOERR, "poll"); +} -- cgit 1.4.1