summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Pointner <equinox@helsinki.at>2015-12-23 06:11:54 (GMT)
committerChristian Pointner <equinox@helsinki.at>2015-12-23 06:11:54 (GMT)
commitde51aad67a1a63f1e6e00cc60d2b4e86bf2bbcbe (patch)
tree38e1a08e5b1b4da084695e7f93e8732866c37b73
parent511a0720ea9f4278cd6c491bbb1e8c895e91e342 (diff)
improved IAC handling which treats telnet commands as OOB messages
-rw-r--r--src/helsinki.at/rhimportd/ctrlTelnet.go139
-rw-r--r--src/helsinki.at/rhimportd/main.go2
2 files changed, 101 insertions, 40 deletions
diff --git a/src/helsinki.at/rhimportd/ctrlTelnet.go b/src/helsinki.at/rhimportd/ctrlTelnet.go
index 74e396f..55701cd 100644
--- a/src/helsinki.at/rhimportd/ctrlTelnet.go
+++ b/src/helsinki.at/rhimportd/ctrlTelnet.go
@@ -53,6 +53,7 @@ type TelnetClient struct {
conf *rhimport.Config
rddb *rhimport.RdDbChan
ctx *rhimport.ImportContext
+ iacout chan []byte
}
func (c *TelnetClient) write_string(text string) {
@@ -348,30 +349,19 @@ func (c *TelnetClient) handle_cmd(cmdstr string, done chan<- bool, cancel <-chan
done <- false
}
-func (c *TelnetClient) handle_iac(iac []byte, cancel chan<- bool) bool {
- if len(iac) < 2 {
- return false // this shouldn't happen
- }
-
+func handleIac(iac []byte, iacout chan<- []byte) {
switch iac[1] {
case WILL, WONT: // Don't accept any proposed options
iac[1] = DONT
case DO, DONT:
iac[1] = WONT
case IP:
- select {
- case cancel <- true:
- default: // process got canceled already
- }
- return false
+ // pass this through to client.handle which will cancel the process
default:
rhdl.Printf("ignoring unimplemented telnet command: %X", iac[1])
- return false
+ return
}
- c.writer.Write(iac)
- c.writer.Flush()
-
- return false
+ iacout <- iac
}
func dropCR(data []byte) []byte {
@@ -381,6 +371,37 @@ func dropCR(data []byte) []byte {
return data
}
+func dropIAC(data []byte) []byte {
+ var token []byte
+ iiac := 0
+ for {
+ niiac := bytes.IndexByte(data[iiac:], IAC)
+ if niiac >= 0 {
+ token = append(token, data[iiac:iiac+niiac]...)
+ iiac += niiac
+ if (len(data) - iiac) < 2 {
+ return token
+ }
+ switch data[iiac+1] {
+ case DONT, DO, WONT, WILL:
+ if (len(data) - iiac) < 3 {
+ return token
+ }
+ iiac += 3
+ case IAC:
+ token = append(token, IAC)
+ fallthrough
+ default:
+ iiac += 2
+ }
+ } else {
+ token = append(token, data[iiac:]...)
+ break
+ }
+ }
+ return token
+}
+
func compare_idx(a, b int) int {
if a < 0 {
a = int(^uint(0) >> 1)
@@ -391,35 +412,52 @@ func compare_idx(a, b int) int {
return a - b
}
-func ScanLinesTelnet(data []byte, atEOF bool) (advance int, token []byte, err error) {
+func ScanLinesTelnet(data []byte, atEOF bool, iacout chan<- []byte, lastiiac *int) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
inl := bytes.IndexByte(data, '\n') // index of first newline character
ieot := bytes.IndexByte(data, EOT) // index of first End of Transmission
- iiac := bytes.IndexByte(data, IAC) // index of first telnet IAC
- if inl >= 0 && compare_idx(inl, ieot) < 0 && compare_idx(inl, iiac) < 0 {
- return inl + 1, dropCR(data[0:inl]), nil // found a complete line
- }
- if ieot >= 0 && compare_idx(ieot, iiac) < 0 {
- return ieot + 1, data[ieot : ieot+1], nil // found a EOT (aka Ctrl-D)
- }
- if iiac >= 0 {
- l := 2
- if (len(data) - iiac) < 2 {
- return 0, nil, nil // data does not yet contain the telnet command code -> need more data
+ iiac := *lastiiac
+ for {
+ niiac := bytes.IndexByte(data[iiac:], IAC) // index of first/next telnet IAC
+ if niiac >= 0 {
+ iiac += niiac
+ } else {
+ iiac = niiac
}
- switch data[iiac+1] {
- case DONT, DO, WONT, WILL:
- if (len(data) - iiac) < 3 {
- return 0, nil, nil // this is a 3-byte command and data does not yet contain the option code -> need more data
+
+ if inl >= 0 && compare_idx(inl, ieot) < 0 && compare_idx(inl, iiac) < 0 {
+ *lastiiac = 0
+ return inl + 1, dropCR(dropIAC(data[0:inl])), nil // found a complete line
+ }
+ if ieot >= 0 && compare_idx(ieot, iiac) < 0 {
+ *lastiiac = 0
+ return ieot + 1, data[ieot : ieot+1], nil // found a EOT (aka Ctrl-D)
+ }
+ if iiac >= 0 {
+ l := 2
+ if (len(data) - iiac) < 2 {
+ return 0, nil, nil // data does not yet contain the telnet command code -> need more data
}
- l = 3
+ switch data[iiac+1] {
+ case DONT, DO, WONT, WILL:
+ if (len(data) - iiac) < 3 {
+ return 0, nil, nil // this is a 3-byte command and data does not yet contain the option code -> need more data
+ }
+ l = 3
+ case IAC:
+ iiac += 2
+ continue
+ }
+ handleIac(data[iiac:iiac+l], iacout)
+ iiac += l
+ *lastiiac = iiac
+ } else {
+ break
}
- // TODO: this doesn't handle escaped IAC bytes correctly: this means utf-8 might be broken...
- return iiac + l, data[iiac : iiac+l], nil // found a Telnet Command
}
if atEOF {
return len(data), dropCR(data), nil // allow last line to have no new line
@@ -460,6 +498,8 @@ func (c *TelnetClient) handle() {
}
}()
+ var cmd_backlog []string
+ busy := false
c.write_string(prompt)
for {
select {
@@ -468,11 +508,11 @@ func (c *TelnetClient) handle() {
return
}
if len(cmd) > 0 {
- switch cmd[0] {
- case IAC:
- c.handle_iac([]byte(cmd), cancel)
- default:
+ if !busy {
go c.handle_cmd(cmd, done, cancel)
+ busy = true
+ } else {
+ cmd_backlog = append(cmd_backlog, cmd)
}
} else {
c.write_string(prompt)
@@ -482,6 +522,22 @@ func (c *TelnetClient) handle() {
return
}
c.write_string(prompt)
+ if len(cmd_backlog) > 0 {
+ go c.handle_cmd(cmd_backlog[0], done, cancel)
+ cmd_backlog = cmd_backlog[1:]
+ } else {
+ busy = false
+ }
+ case iac := <-c.iacout:
+ if iac[1] == IP {
+ select {
+ case cancel <- true:
+ default: // process got canceled already
+ }
+ } else {
+ c.writer.Write(iac)
+ c.writer.Flush()
+ }
}
}
}
@@ -491,11 +547,16 @@ func newTelnetClient(conn net.Conn, conf *rhimport.Config, rddb *rhimport.RdDbCh
c = &TelnetClient{}
c.conn = conn
c.scanner = bufio.NewScanner(conn)
- c.scanner.Split(ScanLinesTelnet)
c.writer = bufio.NewWriter(conn)
c.conf = conf
c.rddb = rddb
c.ctx = nil
+ // the telnet split function needs some closures to handle OOB telnet commands
+ c.iacout = make(chan []byte)
+ lastiiac := 0
+ c.scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
+ return ScanLinesTelnet(data, atEOF, c.iacout, &lastiiac)
+ })
return c
}
diff --git a/src/helsinki.at/rhimportd/main.go b/src/helsinki.at/rhimportd/main.go
index 56fe612..362e8dc 100644
--- a/src/helsinki.at/rhimportd/main.go
+++ b/src/helsinki.at/rhimportd/main.go
@@ -164,7 +164,7 @@ func main() {
}
defer rddb.Cleanup()
- go session_test(conf, rddb.GetInterface())
+ // go session_test(conf, rddb.GetInterface())
var wg sync.WaitGroup