diff options
| author | user <user@clank> | 2026-07-17 23:03:43 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-07-17 23:03:43 +0200 |
| commit | 3b856b3d009b99d01cb84727b803735937a44d16 (patch) | |
| tree | 30c12ac18a260dea12f27688013cbd46870248e0 /c | |
| parent | gbhub: control socket + gbtype to inject shell input without the OSK (diff) | |
| download | gbos-3b856b3d009b99d01cb84727b803735937a44d16.tar.gz gbos-3b856b3d009b99d01cb84727b803735937a44d16.tar.xz gbos-3b856b3d009b99d01cb84727b803735937a44d16.zip | |
irc: strip mIRC formatting, drop 004/005 noise, fix a RAM-bank-swap landmine
Against the real irc.sl0p.foo (via a TLS-stripping socat proxy) the
client's screen filled with garbage after connect. Two causes:
1. Color/format codes. The MOTD is a mIRC-color ASCII-art logo. We
dropped the \x03 control byte (term ignores <32) but left its numeric
*arguments*, so the logo rendered as digit soup ('09> stream the
work', '030903 030903 ...'). strip_fmt() now removes \x03 color (+its
fg[,bg] digits), \x04 hex color, and the \x02/\x0f/\x11/\x16/\x1d/
\x1e/\x1f toggles, applied to the trailing text of every line (leaving
\x01 for CTCP). Standalone digits like '3 apples' are untouched.
2. The real bug: handle() defaulted pfx/txt to a read-only "" literal,
and both bang(pfx) and strip_fmt(txt) write a NUL terminator into it.
That literal lives in ROM (000-FFF); on MBC5 a write there is a
RAM-bank-select, silently swapping the cart-RAM bank - where
every static lives - out from under the program. One prefix-less or
empty-trailing server line (real ircds send them: NOTICE AUTH, ERROR,
registration PING) and all state turns to garbage. Fixed with a
writable 1-byte 'empty' default (zeroed in main; gbos doesn't clear
BSS).
Also suppress 004 (MYINFO) and 005 (ISUPPORT) - pure noise on 40 cols,
and InspIRCd splits ISUPPORT across several lines.
Verified end to end against the live server: full InspIRCd MOTD (logo,
LUSERS, links) renders clean and stable; host unit tests cover color/
bold/CTCP-action stripping and prefix-less NOTICE/ERROR/PING.
Diffstat (limited to '')
| -rw-r--r-- | c/irc.c | 43 |
1 files changed, 42 insertions, 1 deletions
@@ -30,6 +30,12 @@ static unsigned char namebuf[64]; static unsigned char dst[4]; static unsigned char sock; static unsigned char quitting; +/* A WRITABLE empty string for parse defaults. handle() writes terminators + into pfx (bang) and txt (strip_fmt); a read-only "" literal lives in ROM + ($4000-$5FFF), where a write is an MBC5 *RAM-bank-select* - it would swap + the whole $A000 data bank out from under every static (instant garbage). + A prefix-less or empty-trailing server line is enough to trigger it. */ +static char empty[1]; /* ---- tiny string helpers (SDCC libc is us) ------------------------------- */ static unsigned char sceq(const char *a, const char *b) { /* case-insens. */ @@ -107,9 +113,41 @@ static void bang(char *p) { static unsigned char isnum(const char *c) { return c[0] >= '0' && c[0] <= '9'; } +static unsigned char ishex(char c) { + return (c>='0'&&c<='9')||(c>='a'&&c<='f')||(c>='A'&&c<='F'); +} + +/* Strip mIRC/IRC formatting in place - crucially their *arguments*, or a + color logo (a real MOTD!) leaves its \x03 color numbers on screen as digit + soup. \x03 = color (up to 2 digits[,up to 2 digits]); \x04 = hex color + (up to 6 hex[,6 hex]); \x02 bold \x0f reset \x11 mono \x16 reverse + \x1d italic \x1e strike \x1f underline are lone toggles. Leaves \x01 + (CTCP) intact for the PRIVMSG path. */ +static void strip_fmt(char *s) { + char *w = s, *r = s; + unsigned char c, d; + while (*r) { + c = (unsigned char)*r; + if (c == 3) { + r++; d = 0; + while (*r>='0'&&*r<='9'&&d<2) { r++; d++; } + if (*r==','&&r[1]>='0'&&r[1]<='9') { r++; d=0; while(*r>='0'&&*r<='9'&&d<2){r++;d++;} } + continue; + } + if (c == 4) { + r++; d = 0; + while (ishex(*r)&&d<6) { r++; d++; } + if (*r==','&&ishex(r[1])) { r++; d=0; while(ishex(*r)&&d<6){r++;d++;} } + continue; + } + if (c==2||c==15||c==17||c==22||c==29||c==30||c==31) { r++; continue; } + *w++ = *r++; + } + *w = 0; +} static void handle(char *l) { - char *pfx = (char *)"", *cmd, *arg, *txt = (char *)""; + char *pfx = empty, *cmd, *arg, *txt = empty; char *p = l; if (*p == ':') { /* :prefix */ @@ -127,6 +165,7 @@ static void handle(char *l) { p++; } /* arg = first param (NUL-joined list), txt = trailing */ + strip_fmt(txt); /* kill color/format codes + args */ if (sceq(cmd, "PING")) { slen = 0; sc("PONG :"); sc(txt[0] ? txt : arg); sfin(); @@ -210,6 +249,7 @@ static void handle(char *l) { return; } if (cmd[1]=='3'&&cmd[0]=='3') return; /* 333 topic-by: skip */ + if (cmd[0]=='0'&&cmd[1]=='0'&&(cmd[2]=='4'||cmd[2]=='5')) return; /* MYINFO/ISUPPORT noise */ if (txt[0]) say2("-!- ", txt); /* motd & friends */ return; } @@ -315,6 +355,7 @@ void main(void) { parent's RAM bank) - initialize every piece of state explicitly */ llen = inlen = drawn = slen = quitting = 0; target[0] = 0; + empty[0] = 0; /* gbos doesn't zero BSS */ argp = getargs(); /* copy args before seg is used */ for (i = 0; argp[i] && i < 63; i++) namebuf[i] = (unsigned char)argp[i]; |
