aboutsummaryrefslogtreecommitdiff
path: root/internal/net
diff options
context:
space:
mode:
Diffstat (limited to 'internal/net')
-rw-r--r--internal/net/conn.go32
-rw-r--r--internal/net/iac_test.go17
2 files changed, 36 insertions, 13 deletions
diff --git a/internal/net/conn.go b/internal/net/conn.go
index 4aa4db4..ca40354 100644
--- a/internal/net/conn.go
+++ b/internal/net/conn.go
@@ -165,23 +165,29 @@ func stripIAC(s string) string {
i := 0
iacSE := []byte{iac, se}
for i < len(b) {
- if b[i] == iac && i+2 < len(b) {
- if b[i+1] == sb {
- end := bytes.Index(b[i+2:], iacSE)
- if end >= 0 {
- i += end + 4
- continue
- }
- }
- i += 3
+ if b[i] != iac {
+ out = append(out, b[i])
+ i++
continue
}
- if b[i] == iac {
- i++
+ // IAC begins a telnet command. If the line ends mid-command, drop the
+ // remainder: leaking the option byte into player input would corrupt
+ // the command/password with a stray non-UTF-8 byte.
+ if i+1 >= len(b) {
+ break
+ }
+ if b[i+1] == sb {
+ end := bytes.Index(b[i+2:], iacSE)
+ if end < 0 {
+ break
+ }
+ i += end + 4
continue
}
- out = append(out, b[i])
- i++
+ i += 3
+ if i > len(b) {
+ i = len(b)
+ }
}
return string(out)
}
diff --git a/internal/net/iac_test.go b/internal/net/iac_test.go
new file mode 100644
index 0000000..71570db
--- /dev/null
+++ b/internal/net/iac_test.go
@@ -0,0 +1,17 @@
+package net
+
+import "testing"
+
+// Regression: stripIAC required a third byte, so a trailing 2-byte IAC command
+// leaked its option byte into player input.
+func TestStripIACDropsTruncatedSequence(t *testing.T) {
+ got := stripIAC("abc" + string([]byte{iac, 0xf1}))
+ if got != "abc" {
+ t.Fatalf("stripIAC(truncated) = %q, want %q", got, "abc")
+ }
+ // A full subnegotiation is still removed.
+ got = stripIAC("x" + string([]byte{iac, sb, 1, 2}) + string([]byte{iac, se}) + "y")
+ if got != "xy" {
+ t.Fatalf("stripIAC(subnegotiation) = %q, want xy", got)
+ }
+}