Issue #4: add and use telnetRead() function

This commit is contained in:
Matthew R. Wilson committed 2020-12-30 15:55:31 -08:00
1 parent f01cf72d6d
commit cfa06ec4bc
3 files changed
+78 -5

No files matched your search

-1
View File
@@ -35,7 +35,6 @@ I would like to add:
Known Problems
--------------
- The telnet data is not checked for the special telnet byte value, 0xFF, which requires escaping while sending and unescaping while receiving data. If your 3270 data streams contain an FF character, things will probably break. However...I don't think 0xFF should ever appear in the datastream, so we're probably okay.
- The telnet negotiation does not check for any errors or for any responses from the client. We just assume it goes well and we're actually talking to a tn3270 client.
License
+4 -4
View File
@@ -99,7 +99,7 @@ func readResponse(c net.Conn, fm fieldmap) (Response, error) {
func readAID(c net.Conn) (AID, error) {
buf := make([]byte, 1)
for {
_, err := c.Read(buf)
_, err := telnetRead(c, buf)
if err != nil {
return AIDNone, err
}
@@ -122,7 +122,7 @@ func readPosition(c net.Conn) (row, col, addr int, err error) {
// Read two bytes
for i := 0; i < 2; i++ {
if _, err := c.Read(buf); err != nil {
if _, err := telnetRead(c, buf); err != nil {
return 0, 0, 0, err
}
raw[i] = buf[0]
@@ -150,7 +150,7 @@ func readFields(c net.Conn, fm fieldmap) (map[string]string, error) {
// consume bytes until we get 0xffef
for {
// Read a byte
if _, err = c.Read(buf); err != nil {
if _, err = telnetRead(c, buf); err != nil {
return nil, err
}
@@ -163,7 +163,7 @@ func readFields(c net.Conn, fm fieldmap) (map[string]string, error) {
}
// consume the next byte, which is probably 0xef
if _, err = c.Read(buf); err != nil {
if _, err = telnetRead(c, buf); err != nil {
return nil, err
}
return values, nil
+74
View File
@@ -68,3 +68,77 @@ func flushConnection(conn net.Conn, timeout time.Duration) error {
timeout = time.Second / 2
}
}
// telnetRead behaves similarly to conn.Read, except it filters out telnet
// commands. A single call to telnetRead() may perform multiple calls to
// c.Read() if awaiting completion of a command, or to ensure we do not return
// zero bytes in a non-error condition.
func telnetRead(c net.Conn, p []byte) (n int, err error) {
const (
normal = iota
command
subneg
)
// Not the most efficient, but for now to keep the code easier we'll just
// read 1 byte at a time from the connection.
b := make([]byte, 1)
state := normal
for n < len(p) {
bn, berr := c.Read(b)
// When there are no bytes to process and we received an error, we
// are done no matter what state we're in. Any non-command bytes will
// already be in p, so we return.
if bn == 0 && berr != nil {
return n, berr
}
// If we received 0 bytes but no error, we'll just read again.
if bn == 0 {
continue
}
// We got a byte! Let's progress through our state machine.
switch state {
case normal:
if b[0] == iac {
state = command
debugf("entering telnet command state\n")
} else {
p[n] = b[0]
n++
}
case command:
if b[0] == 0xff {
p[n] = 0xff
n++
state = normal
debugf("leaving telnet command state; was an escaped 0xff\n")
} else if b[0] == sb {
state = subneg
debugf("entering telnet command subnegotiation state\n")
} else {
state = normal
debugf("leaving telnet command state; command was %02x\n",
b[0])
}
case subneg:
if b[0] == se {
state = normal
debugf("leaving telnet command subnegotiation state\n")
} else {
// remain in subnegotiation consuming bytes until we get se
debugf("consumed telnet subnegotiation byte: %02x\n", b[0])
}
}
// Now we handle any errors that returned partial data
if berr != nil {
return n, berr
}
}
return n, nil
}