nio

a simple irc client
git clone git@git.kloet.net/nio.git
Download | Log | Files | Refs | README

commit d17527b115335a229754594c557db7cebea10eda
parent 0e07631cb4b92b74d6fe7c027d41cd7cc80e576d
Author: Andrew Kloet <andrew@kloet.net>
Date:   Sun, 29 Mar 2026 13:49:55 -0400

fix off-by-one and use safer str functions

Diffstat:
Mnio.c | 103++++++++++++++++++++++++++++++++++++++++---------------------------------------
1 file changed, 52 insertions(+), 51 deletions(-)

diff --git a/nio.c b/nio.c @@ -274,7 +274,7 @@ dial(const char *host, const char *service) if (ssl) { SSL_load_error_strings(); SSL_library_init(); - srv.ctx = SSL_CTX_new(SSLv23_client_method()); + srv.ctx = SSL_CTX_new(TLS_client_method()); if (!srv.ctx) return "Could not initialize ssl context."; srv.ssl = SSL_new(srv.ctx); @@ -326,7 +326,7 @@ chadd(const char *name, int joined) return -1; if ((n = chfind(name)) > 0) return n; - strcpy(chl[nch].name, name); + strlcpy(chl[nch].name, name, sizeof(chl[nch].name)); chl[nch].sz = LogSz; chl[nch].buf = malloc(LogSz); if (!chl[nch].buf) @@ -345,13 +345,13 @@ static int chdel(char *name) { int n = chfind(name); - if (n <= 0) return 0; free(chl[n].buf); + + if (n < nch - 1) + memmove(&chl[n], &chl[n + 1], (nch - n - 1) * sizeof(struct Chan)); nch--; - if (n < nch) /* Shift the channel buffers down. */ - memmove(&chl[n], &chl[n + 1], (nch - n) * sizeof(struct Chan)); - if (ch >= n) /* Decrement the selected channel. */ + if (ch > n || ch >= nch) ch = (ch > 0) ? ch - 1 : 0; tdrawbar(); return 1; @@ -431,11 +431,8 @@ pushf(int cn, const char *fmt, ...) gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec, s); fflush(logfp); } - strcat(c->eol, "\n"); - if (n >= LineLen - 1) - c->eol += LineLen - 1; - else - c->eol += n + 1; + strlcat(c->buf, "\n", c->sz); + c->eol = c->buf + strlen(c->buf); if (cn == ch && c->n == 0) { char *p = c->eol - n - 1; @@ -452,7 +449,7 @@ scmd(char *usr, char *cmd, char *par, char *data) IrcCmd type = CMD_UNKNOWN; int c; char *pm = strtok(par, " "); - char *chan; + char *chan; for (size_t i = 0; i < sizeof(cmd_map)/sizeof(cmd_map[0]); i++) { if (!strcmp(cmd, cmd_map[i].name)) { @@ -492,32 +489,32 @@ scmd(char *usr, char *cmd, char *par, char *data) if (pm) pushf(chfind(pm), "-!- %s has %s %s", usr, (type == JOIN ? "joined" : "left"), pm); break; - case QUIT: - break; + case QUIT: + break; case ERR_FORWARD: { - char *oldch = strtok(NULL, " "); - char *newch = strtok(NULL, " "); - if (!oldch || !newch || !(c = chfind(oldch))) - break; - chl[c].name[0] = 0; - strncat(chl[c].name, newch, ChanLen - 1); - tdrawbar(); - break; + char *oldch = strtok(NULL, " "); + char *newch = strtok(NULL, " "); + if (!oldch || !newch || !(c = chfind(oldch))) + break; + chl[c].name[0] = 0; + strlcat(chl[c].name, newch, sizeof(chl[c].name)); + tdrawbar(); + break; } case ERR_FULL: case ERR_INVITE: case ERR_BANNED: case ERR_BADKEY: - if (!(pm = strtok(NULL, " "))) + if (!(pm = strtok(NULL, " "))) break; - chdel(pm); - pushf(0, "-!- Cannot join %s (%s)", pm, cmd); - tredraw(); - break; - case ERR_NICKNAMEINUSE: - strncat(nick, "_", sizeof(nick) - strlen(nick) - 1); - sndf("NICK %s", nick); - break; + chdel(pm); + pushf(0, "-!- Cannot join %s (%s)", pm, cmd); + tredraw(); + break; + case ERR_NICKNAMEINUSE: + strlcat(nick, "_", sizeof(nick)); + sndf("NICK %s", nick); + break; case CAP: if (!(pm = strtok(NULL, " "))) break; if (!strcmp(pm, "LS")) @@ -528,15 +525,15 @@ scmd(char *usr, char *cmd, char *par, char *data) sndf("CAP END"); break; case AUTHENTICATE: - if (!par || *par != '+') break; - if (!is_empty(cert)) { + if (!par || *par != '+') break; + if (!is_empty(cert)) { sndf("AUTHENTICATE +"); - } else { + } else { char raw[512]; int r = snprintf(raw, sizeof(raw), "%s%c%s%c%s", nick, 0, nick, 0, key); sndf("AUTHENTICATE %s", b64_enc((unsigned char *)raw, r)); - } - break; + } + break; case SASL_OK: case SASL_ERR: sndf("CAP END"); @@ -580,14 +577,18 @@ uparse(char *m) case 'l': /* Leave channels. */ p += 1 + (p[1] == ' '); if (!*p) { - if (ch == 0) return; /* Cannot leave server window. */ - p = chl[ch].name; + if (ch == 0) return; + static char buf[ChanLen]; + strlcpy(buf, chl[ch].name, sizeof(buf)); + buf[sizeof(buf) - 1] = '\0'; + p = buf; } - p = strtok(p, " "); - while (p) { - if (chdel(p) && strchr("&#!+", p[0])) - sndf("PART %s", p); /* PART should not be sent for users. */ - p = strtok(0, " "); + char *token = strtok(p, " "); + while (token) { + char *next = strtok(NULL, " "); + if (chdel(token) && strchr("&#!+", token[0])) + sndf("PART %s", token); + token = next; } tredraw(); return; @@ -636,7 +637,7 @@ tinit(void) if (has_colors() == TRUE) { start_color(); use_default_colors(); - init_pair(1, COLOR_WHITE, COLOR_BLUE); + init_pair(1, COLOR_WHITE, COLOR_BLACK); wbkgd(scr.sw, COLOR_PAIR(1)); } } @@ -874,9 +875,8 @@ main(int argc, char *argv[]) panic("fopen: logfile"); break; case 'n': - if (strlen(optarg) >= sizeof nick) + if (strlcpy(nick, optarg, sizeof(nick)) >= sizeof(nick)) goto usage; - strcpy(nick, optarg); break; case 'T': ssl = 0; @@ -891,15 +891,16 @@ main(int argc, char *argv[]) port = optarg; break; case 'c': - snprintf(cert, sizeof(cert), "%s", optarg); + if (strlcpy(cert, optarg, sizeof(cert)) >= sizeof(cert)) + goto usage; break; } if (!user) user = "anonymous"; - if (!nick[0] && ircnick && strlen(ircnick) < sizeof nick) - strcpy(nick, ircnick); - if (!nick[0] && strlen(user) < sizeof nick) - strcpy(nick, user); + if (!nick[0] && ircnick) + strlcpy(nick, ircnick, sizeof(nick)); + if (!nick[0]) + strlcpy(nick, user, sizeof(nick)); if (!nick[0]) goto usage; tinit();