/* Copyright (C) 2018 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 #include "chat.h" static struct tls *client; int ircConnect(void) { int error; struct tls_config *config = tls_config_new(); error = tls_config_set_ciphers(config, "compat"); if (error) errx(EX_SOFTWARE, "tls_config"); 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(self.host, self.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 = fcntl(sock, F_SETFD, FD_CLOEXEC); if (error) err(EX_IOERR, "fcntl"); error = tls_connect_socket(client, sock, self.host); if (error) errx(EX_PROTOCOL, "tls_connect: %s", tls_error(client)); if (self.auth) ircFmt("CAP REQ :sasl\r\n"); if (self.pass) ircFmt("PASS :%s\r\n", self.pass); ircFmt("NICK %s\r\n", self.nick); ircFmt("USER %s 0 * :%s\r\n", self.user, self.real); return sock; } void ircWrite(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; } } void ircFmt(const char *format, ...) { char *buf; va_list ap; va_start(ap, format); int len = vasprintf(&buf, format, ap); va_end(ap); if (!buf) err(EX_OSERR, "vasprintf"); if (self.raw) { if (!isatty(STDERR_FILENO)) fprintf(stderr, "<<< %.*s\n", len - 2, buf); uiFmt(TagRaw, UICold, "\3%d<<<\3 %.*s", IRCWhite, len - 2, buf); } ircWrite(buf, len); free(buf); } void ircQuit(const char *mesg) { ircFmt("QUIT :%s\r\n", mesg); self.quit = true; } void ircRead(void) { static char buf[4096]; static size_t len; ssize_t read; retry: read = tls_read(client, &buf[len], sizeof(buf) - len); if (read == TLS_WANT_POLLIN || read == TLS_WANT_POLLOUT) goto retry; if (read < 0) errx(EX_IOERR, "tls_read: %s", tls_error(client)); if (!read) { if (!self.quit) errx(EX_PROTOCOL, "unexpected eof"); uiExit(EX_OK); } len += read; char *crlf; char *line = buf; while (NULL != (crlf = memmem(line, &buf[len] - line, "\r\n", 2))) { crlf[0] = '\0'; if (self.raw) { if (!isatty(STDERR_FILENO)) fprintf(stderr, ">>> %s\n", line); uiFmt(TagRaw, UICold, "\3%d>>>\3 %s", IRCGray, line); } handle(line); line = &crlf[2]; } len -= line - buf; memmove(buf, line, len); }