From 16dd18b40015a52cad86cf9c4fd08e2b6e6ab06a Mon Sep 17 00:00:00 2001 From: Laika Date: Sat, 2 Mar 2024 16:04:35 +0100 Subject: [PATCH] Initial commit --- .github/workflows/cargo-audit.yml | 23 + .github/workflows/cargo-test.yml | 32 + .gitignore | 2 + .vscode/launch.json | 14 + .vscode/tasks.json | 30 + Cargo.lock | 7 + Cargo.toml | 21 + LICENSE | 21 + README.md | 32 + resources/ascii.txt | 36 + resources/rfc1184.txt | 1287 +++++++++++++++++++++++++++++ resources/rfc854.txt | 855 +++++++++++++++++++ src/bin/main.rs | 48 ++ src/iter.rs | 84 ++ src/lib.rs | 4 + src/read.rs | 36 + src/telnet/mod.rs | 14 + src/telnet/session.rs | 150 ++++ src/telnet/state.rs | 471 +++++++++++ telnet/Dockerfile | 5 + telnet/run_telnet.sh | 7 + 21 files changed, 3179 insertions(+) create mode 100644 .github/workflows/cargo-audit.yml create mode 100644 .github/workflows/cargo-test.yml create mode 100644 .gitignore create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 resources/ascii.txt create mode 100644 resources/rfc1184.txt create mode 100644 resources/rfc854.txt create mode 100644 src/bin/main.rs create mode 100644 src/iter.rs create mode 100644 src/lib.rs create mode 100644 src/read.rs create mode 100644 src/telnet/mod.rs create mode 100644 src/telnet/session.rs create mode 100644 src/telnet/state.rs create mode 100644 telnet/Dockerfile create mode 100755 telnet/run_telnet.sh diff --git a/.github/workflows/cargo-audit.yml b/.github/workflows/cargo-audit.yml new file mode 100644 index 0000000..e72504c --- /dev/null +++ b/.github/workflows/cargo-audit.yml @@ -0,0 +1,23 @@ +name: Cargo security audit +on: + schedule: + - cron: "31 1 * * *" + + push: + paths: + - "**/Cargo.toml" + - "**/Cargo.lock" +jobs: + audit: + defaults: + run: + working-directory: ./backend + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v5 + + - uses: rustsec/audit-check@v2 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/cargo-test.yml b/.github/workflows/cargo-test.yml new file mode 100644 index 0000000..05a72ad --- /dev/null +++ b/.github/workflows/cargo-test.yml @@ -0,0 +1,32 @@ +name: Cargo test & clippy + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v5 + + - name: Build + run: cargo build --verbose + + - name: Run tests + run: cargo test --verbose + + rust-clippy-analyze: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v5 + + - name: Run rust-clippy + run: cargo clippy --all-features diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2a0038a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +.idea \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..7cec271 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug", + "preLaunchTask": "rust: cargo build", + "program": "target/debug/telnet_server", + "args": [], + "cwd": "${workspaceFolder}" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..b677264 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,30 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "telnet_server: Start TELNET Docker", + "type": "shell", + "command": "./telnet/run_telnet.sh", + "args": [ + "${input:host}", + "${input:port}" + ], + "detail": "Starts a Docker container (in interactive mode) that runs TELNET and connects to given host and port", + "problemMatcher": [] + } + ], + "inputs": [ + { + "id": "host", + "type": "promptString", + "default": "host.docker.internal", + "description": "Which host should the TELNET client connect to?" + }, + { + "id": "port", + "type": "promptString", + "default": "9000", + "description": "Which port should the TELNET client connect to?" + }, + ] +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..80906b9 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "telnet_server" +version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..13b09d5 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "telnet_server" +version = "0.0.1" +edition = "2024" +authors = ["Laika Schmidt "] +description = "A bare TELNET server provider" +readme = "README.md" +repository = "https://github.com/its-laika/telnet_server" +license = "MIT" +keywords = ["TELNET"] +exclude = [".github/", ".vscode/", "resources/", "telnet/", ".gitignore"] + +[lib] +name = "telnet_server" +path = "src/lib.rs" + +[[bin]] +name = "telnet_server" +path = "src/bin/main.rs" + +[dependencies] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..cf3ad66 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 - 2025 Laika Schmidt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a14631d --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# telnet_server +_...because nothing beats the charm of the seventies!_ + +## Goals +This lib should provide everything to build a service (based on TELNET) without +having to touch any TELNET or tcp specifics. Message communication should only +happen between this lib and the code providing the service. + +See [src/bin/main.rs](src/bin/main.rs#l23) on how I think this should work. + +## Status +Non functional and heavily WIP. **DO NOT USE!** + +## Running TELNET +As TELNET is not anymore part of modern operating systems (thank god), I've +created a minimal Dockerfile that let's me use TELNET on CLI. TELNET starts via +this command: + +```sh +./telnet/run_telnet.sh HOST PORT +``` + +For development, _HOST_ is "host.docker.internal" and _PORT_ is 9000. + +## For f*cks sake, why TELNET??? +Because it looked interesting. Honestly, even if I get it finished, the library +likely won't be used _at all_. + +## License +The code is licensed under the [MIT License](LICENSE). The RFCs in the +[resources](resources) folder have a different copyright but are allowed (and +encouraged) to be copied and redistributed if unchanged. \ No newline at end of file diff --git a/resources/ascii.txt b/resources/ascii.txt new file mode 100644 index 0000000..98c56cb --- /dev/null +++ b/resources/ascii.txt @@ -0,0 +1,36 @@ +Source: https://www.cs.cmu.edu/~pattis/15-1XX/common/handouts/ascii.html (downloaded: 2024-03-03 12:24 UTC) + +Dec Char Dec Char Dec Char Dec Char +--------- --------- --------- ---------- + 0 NUL (null) 32 SPACE 64 @ 96 ` + 1 SOH (start of heading) 33 ! 65 A 97 a + 2 STX (start of text) 34 " 66 B 98 b + 3 ETX (end of text) 35 # 67 C 99 c + 4 EOT (end of transmission) 36 $ 68 D 100 d + 5 ENQ (enquiry) 37 % 69 E 101 e + 6 ACK (acknowledge) 38 & 70 F 102 f + 7 BEL (bell) 39 ' 71 G 103 g + 8 BS (backspace) 40 ( 72 H 104 h + 9 TAB (horizontal tab) 41 ) 73 I 105 i + 10 LF (NL line feed, new line) 42 * 74 J 106 j + 11 VT (vertical tab) 43 + 75 K 107 k + 12 FF (NP form feed, new page) 44 , 76 L 108 l + 13 CR (carriage return) 45 - 77 M 109 m + 14 SO (shift out) 46 . 78 N 110 n + 15 SI (shift in) 47 / 79 O 111 o + 16 DLE (data link escape) 48 0 80 P 112 p + 17 DC1 (device control 1) 49 1 81 Q 113 q + 18 DC2 (device control 2) 50 2 82 R 114 r + 19 DC3 (device control 3) 51 3 83 S 115 s + 20 DC4 (device control 4) 52 4 84 T 116 t + 21 NAK (negative acknowledge) 53 5 85 U 117 u + 22 SYN (synchronous idle) 54 6 86 V 118 v + 23 ETB (end of trans. block) 55 7 87 W 119 w + 24 CAN (cancel) 56 8 88 X 120 x + 25 EM (end of medium) 57 9 89 Y 121 y + 26 SUB (substitute) 58 : 90 Z 122 z + 27 ESC (escape) 59 ; 91 [ 123 { + 28 FS (file separator) 60 < 92 \ 124 | + 29 GS (group separator) 61 = 93 ] 125 } + 30 RS (record separator) 62 > 94 ^ 126 ~ + 31 US (unit separator) 63 ? 95 _ 127 DEL diff --git a/resources/rfc1184.txt b/resources/rfc1184.txt new file mode 100644 index 0000000..b12336f --- /dev/null +++ b/resources/rfc1184.txt @@ -0,0 +1,1287 @@ +Source: https://datatracker.ietf.org/doc/html/rfc1184 (downloaded: 2024-03-03 12:22 UTC) + +Network Working Group Telnet Working Group +Request for Comments: 1184 D. Borman, Editor +Obsoletes: RFC 1116 Cray Research, Inc. + October 1990 + + + Telnet Linemode Option + +Status of this Memo + + This memo describes a Draft Standard for the Internet community, and + requests discussion and suggestions for improvements. This RFC + specifies an IAB standards track protocol for the Internet community. + Please refer to the current edition of the "IAB Official Protocol + Standards" for the standardization state and status of this protocol. + Distribution of this memo is unlimited. + +Changes from RFC1116: + + Two new mode bits have been added, SOFT_TAB and LIT_ECHO. These + bits allow the server to give the client some advise on how to + echo tabs and non-printable characters. + + Several new special character mappings have been added for cursor + motion when visual editing is supported. These are: Move cursor + one character left/right (SLC_MCL/SLC_MCR), move cursor one word + left/right (SLC_MCWL/SLC_MCWR), move cursor to begining/end of + line (SLC_MCBOL/SLC_MCEOL), enter insert/overstrike mode + (SLC_INSRT/SLC_OVER), erase one character/word to the right + (SLC_ECR/SLC_EWR), and erase to the beginning/end of the line + (SLC_EBOL/SLC_EEOL). + +Overview + + Linemode Telnet is a way of doing terminal character processing on + the client side of a Telnet connection. While in Linemode with + editing enabled for the local side, network traffic is reduced to a + couple of packets per command line, rather than a couple of packets + per character typed. This is very useful for long delay networks, + because the user has local response time while typing the command + line, and only incurs the network delays after the command is typed. + It is also useful to reduce costs on networks that charge on a per + packet basis. Please send comments to the telnet-ietf@cray.com + mailing list. + + + + + + + +Telnet Working Group [Page 1] + +RFC 1184 Telnet Linemode Option October 1990 + + +Table of Contents + + 1. Command Names and Codes 2 + 2. Command Meanings 3 + 2.1 The LINEMODE function 3 + 2.2 LINEMODE suboption MODE 4 + 2.3 LINEMODE suboption FORWARDMASK 5 + 2.4 LINEMODE suboption SLC, Set Local Characters 6 + 2.5 New control characters 10 + 3. Default Specification 11 + 4. Motivation 11 + 5. Implementation Rules 13 + 5.1 User Interface 13 + 5.2 End of line terminators 14 + 5.3 Output processing 14 + 5.4 A terminal driver in Telnet? 14 + 5.5 Setting of Local Characters 14 + 5.6 FORWARDMASK and SLC_FORW1 and SLC_FORW2 15 + 5.7 Valid and invalid modes and values 16 + 5.8 Flushing input and output 16 + 5.9 State diagram for SLC 18 + 5.10 Example of a connection 19 + 6. Other Telnet options and RFCs 22 + 7. Security Considerations 23 + 8. Author's Address 23 + +1. Command Names and Codes + + LINEMODE 34 + MODE 1 + EDIT 1 + TRAPSIG 2 + MODE_ACK 4 + SOFT_TAB 8 + LIT_ECHO 16 + FORWARDMASK 2 + SLC 3 + SLC_SYNCH 1 + SLC_BRK 2 + SLC_IP 3 + SLC_AO 4 + SLC_AYT 5 + SLC_EOR 6 + SLC_ABORT 7 + SLC_EOF 8 + SLC_SUSP 9 + SLC_EC 10 + SLC_EL 11 + + + +Telnet Working Group [Page 2] + +RFC 1184 Telnet Linemode Option October 1990 + + + SLC_EW 12 + SLC_RP 13 + SLC_LNEXT 14 + SLC_XON 15 + SLC_XOFF 16 + SLC_FORW1 17 + SLC_FORW2 18 + SLC_MCL 19 + SLC_MCR 20 + SLC_MCWL 21 + SLC_MCWR 22 + SLC_MCBOL 23 + SLC_MCEOL 24 + SLC_INSRT 25 + SLC_OVER 26 + SLC_ECR 27 + SLC_EWR 28 + SLC_EBOL 29 + SLC_EEOL 30 + + SLC_DEFAULT 3 + SLC_VALUE 2 + SLC_CANTCHANGE 1 + SLC_NOSUPPORT 0 + SLC_LEVELBITS 3 + + SLC_ACK 128 + SLC_FLUSHIN 64 + SLC_FLUSHOUT 32 + EOF 236 + SUSP 237 + ABORT 238 + +2. Command Meanings + +2.1 The LINEMODE function + + IAC WILL LINEMODE + + The sender of this command REQUESTS permission to begin sub- + negotiation of the editing/signaling status. This should only be + sent by the client side of the connection. + + IAC WONT LINEMODE + + The sender of this command DEMANDS that sub-negotiation of the + editing/signaling status not be allowed. + + + + +Telnet Working Group [Page 3] + +RFC 1184 Telnet Linemode Option October 1990 + + + IAC DO LINEMODE + + The sender of this command REQUESTS that the remote side begin + sub-negotiation of the editing/signaling status. This should only + be sent by the server side of the connection. + + IAC DONT LINEMODE + + The sender of this command DEMANDS that the remote side not begin + sub-negotiation of the editing/signaling status. + +2.2 LINEMODE suboption MODE + + IAC SB LINEMODE MODE mask IAC SE + + The sender of this command CONFIRMS, or REQUESTS permission for, a + switch to the mode defined by "mask". + + The "mask" is a bit mask of various modes that the connection can be + in. Under normal operation, the server side of the connection will + initiate mode changes, and the client will confirm the mode changes. + The currently defined modes are: + + EDIT When set, the client side of the connection should + process all input lines, performing any editing functions, + and only send completed lines to the remote side. When + unset, client side should not process any input from the + user, and the server side should take care of all + character processing that needs to be done. + + TRAPSIG When set, the client side should translate appropriate + interrupts/signals to their Telnet equivalent. (These + would be IP, BRK, AYT, ABORT, EOF, and SUSP) When unset, + the client should pass interrupts/signals as their normal + ASCII values. + + FLOW Logically, this belongs in the "mask". However, this + would overlap the Telnet TOGGLE-FLOW-CONTROL option, so + the Telnet TOGGLE-FLOW-CONTROL option is used instead. + When DO/WILL LINEMODE is negotiated, DO/WILL TOGGLE- + FLOW-CONTROL should also be negotiated. See RFC 1080, + "Telnet Remote Flow Control", for correct usage. + + ECHO Logically, this belongs in the "mask". However, this + would overlap the Telnet ECHO option, so the Telnet ECHO + option is used instead. The client side should never + negotiate "WILL ECHO". When the server has negotiated + "WILL ECHO", the client should not echo data typed by the + + + +Telnet Working Group [Page 4] + +RFC 1184 Telnet Linemode Option October 1990 + + + user back to the user. When the server has negotiated + "WONT ECHO", the the client is responsible for echoing + data typed by the user back to the user. See RFC 857, + "Telnet ECHO OPTION" for a complete discussion on the use + of the Telnet ECHO option. + + SOFT_TAB When set, the client side should expand the Horizontal + Tab (HT) code, USASCII 9, into the appropriate number of + spaces to move the printer to the next horizontal tab + stop. When unset, the client side should allow the + Horizontal Tab code to pass through un-modified. + + LIT_ECHO When set, if the client side is echoing a non-printable + character that the user has typed to the users screen, + the character should be echoed as the literal character. + If the LIT_ECHO bit is not set, then the client side may + echo the character in any manner that it desires. (Many + systems echo unprintable characters as two character + sequences, for example, they will echo "^A" for an + ASCII 1 value.) + + When the client side of a connection receives a MODE command, it MUST + agree with at least the state of the EDIT and TRAPSIG bits. If a + MODE command is received with a mode mask that is currently in use + (ignoring the MODE_ACK bit), the MODE command is ignored. If a MODE + command is received that is different from the current mode mask, + then a reply is sent with either the new mode mask and the MODE_ACK + bit set, or a subset of the new mode mask. The only exception is + that if the server receives a MODE with either the EDIT or TRAPSIG + bits not set, it may set the EDIT and TRAPSIG bits in the response, + and if the client receives a MODE with the EDIT or TRAPSIG bits set, + it may not clear them in the response. + + When a MODE command is received with the MODE_ACK bit set, and the + mode is different that what the current mode is, the client will + ignore the new mode, and the server will switch to the new mode. + This ensures that both sides of the connection will resolve to the + same mode. In all cases, a response is never generated to a MODE + command that has the MODE_ACK bit set. + +2.3 LINEMODE suboption FORWARDMASK + + IAC SB LINEMODE DO FORWARDMASK mask0 mask1 ... mask31 IAC SE + + The sender of this command request that the other side send any + buffered data when any of the ASCII characters defined by the bit + mask are received. Only the side of the connection that sent DO + LINEMODE (the server side) may negotiate this. The mask is up to + + + +Telnet Working Group [Page 5] + +RFC 1184 Telnet Linemode Option October 1990 + + + 32 octets long. Each octet represents 8 ASCII character codes. + The high order bit of mask0 corresponds to an ASCII code of 0. + The low order bit of mask0 corresponds to an ASCII code of 7. The + high order bit of mask1 corresponds to an ASCII code of 8. The + low order bit of mask1 corresponds to an ASCII code of 15, and so + on. The mask list may be terminated before the end of the list, in + which case all the rest of the mask octets are assumed to be reset + (equal to zero). When the server side is in DONT TRANSMIT-BINARY + mode, then only the first 16 octets of the mask (ASCII codes 0 + through 127) are used. If any individual octet of the mask is + equal to IAC, it must be sent as a double IAC. + + IAC SB LINEMODE DONT FORWARDMASK IAC SE + + The sender of this command requests that the other side stop using + the forward mask to determine when to send buffered data. + + IAC SB LINEMODE WILL FORWARDMASK IAC SE + + This command is sent in response to a DO FORWARDMASK command. It + indicates that the forward mask will be used to determine when to + send buffered data. + + IAC SB LINEMODE WONT FORWARDMASK IAC SE + + This command is sent in response to a DO FORWARDMASK command. It + indicates that the forward mask will not be used to determine when + to send buffered data. + +2.4 LINEMODE suboption SLC, Set Local Characters + + The SLC suboption uses a list of octet triplets. The first octet + specifies the function, the second octet specifies modifiers to the + function, and the third octet specifies the ASCII character for the + function. + + IAC SB LINEMODE SLC IAC SE + + The sender of this command REQUESTS that the list of octet + triplets be used to set the local character to be used to send to + perform the specified function. + + There are four levels that a function may be set to. + SLC_NOSUPPORT is the lowest, SLC_CANTCHANGE is the next higher + level, SLC_VALUE is above that, and SLC_DEFAULT is the highest + level. + + If the SLC_LEVELBITS in the second octet are equal to SLC_DEFAULT, + + + +Telnet Working Group [Page 6] + +RFC 1184 Telnet Linemode Option October 1990 + + + then this particular function should use the system default on the + other side of the connection. + + If the SLC_LEVELBITS in the second octet are equal to SLC_VALUE, + then this function is supported, and the current value is + specified by the third octet. + + If the SLC_LEVELBITS in the second octet are equal to + SLC_CANTCHANGE, then this is a function that is supported, but the + value for this function, specified in the third octet, cannot be + changed. + + If the SLC_LEVELBITS in the second octet are equal to + SLC_NOSUPPORT, then this particular function is not supported and + should be disabled by the other side. + + If this is a response to a previous request to change a special + character, and we are agreeing to the change, then the SLC_ACK bit + must be set in the second octet. + + If the SLC_FLUSHIN bit is set in the second octet, then whenever + this function is sent, a Telnet "sync" should be sent at the same + time to flush the input stream. + + If the SLC_FLUSHOUT bit is set in the second octet, then whenever + this function is sent, output data should be flushed. + + Only the client may send an octet triplet with the first octet + equal to zero. In this case, the SLC_LEVELBITS may only be set to + SLC_DEFAULT or SLC_VALUE, and the third octet does not matter. + When the server receives 0 SLC_DEFAULT 0, it should switch to its + system default special character settings, and send all those + special characters to the client. When the server receives 0 + SLC_VALUE 0, it should just send its current special character + settings. Note that if the server does not support some of the + editing functions, they should be sent as XXX SLC_DEFAULT 0, + rather than as XXX SLC_NOSUPPORT 0, so that the client may choose + to use its own values for those functions, rather than have to + disable those functions even if it supports them. + + If any of the octets in the list of octet triplets is equal to + IAC, it must be sent as a double IAC. + + When a connection is established, it is the responsibility of the + client to either request the remote default values for the special + characters, or to send across what all the special characters should + be set to. + + + + +Telnet Working Group [Page 7] + +RFC 1184 Telnet Linemode Option October 1990 + + + The function values can be put into two groups, functions that are to + be translated to their Telnet equivalents before being sent across + the Telnet connection, and functions that are to be recognized and + processed locally. + + First, we have those characters that are to be mapped into their + Telnet equivalents: + + SLC_SYNCH Synch. See RFC 854, "TELNET PROTOCOL SPECIFICATION", + for a complete description. + + SLC_BRK Break. See RFC 854, "TELNET PROTOCOL SPECIFICATION", + for a complete description. + + SLC_IP Interrupt Process. See RFC 854, "TELNET PROTOCOL + SPECIFICATION", for a complete description. + + SLC_AO Abort Output. See RFC 854, "TELNET PROTOCOL + SPECIFICATION", for a complete description. + + SLC_AYT Are You There. See RFC 854, "TELNET PROTOCOL + SPECIFICATION", for a complete description. + + SLC_EOR End of Record. See RFC 885, "TELNET END OF RECORD + OPTION" for a complete description. + + SLC_ABORT Abort. See section 2.5 for a complete description. + + SLC_EOF End of File. See section 2.5 for a complete + description. + + SLC_SUSP Suspend. See section 2.5 for a complete description. + + Next, we have the locally interpreted functions. + + SLC_EC Erase Character. This is the character that is typed to + erase one character from the input stream. See RFC 854, + "TELNET PROTOCOL SPECIFICATION", for a complete + description. + + SLC_EL Erase Line. This is the character that is typed to + erase the entire contents of the current line of input. + See RFC 854, "TELNET PROTOCOL SPECIFICATION", for a + complete description. + + SLC_EW Erase Word. This is the character that is typed to + erase one word from the input stream. + + + + +Telnet Working Group [Page 8] + +RFC 1184 Telnet Linemode Option October 1990 + + + SLC_RP Reprint Line. This is the character that is typed to + cause the current line of input to be reprinted, leaving + the cursor at the end of the line. + + SLC_LNEXT Literal Next. This is the character that is typed to + indicate that the next character is to be taken + literally, no character processing should be done with + it, and if it is a special character that would normally + get mapped into a Telnet option, that mapping should + not be done. + + SLC_XON Start Output. This is the character that is sent to + resume output to the users terminal. + + SLC_XOFF Stop Output. This is the character that is sent to stop + output to the users terminal. + + SLC_FORW1 Forwarding character. This is a character that should + cause all data currently being buffered, and this + character, to be sent immediately. + + SLC_FORW2 Forwarding character. This is another character that is + to be treated in the same manner as SLC_FORW1. + + SLC_MCL Move cursor one character left. When visual editing is + supported, this is the character that, when typed, will + move the cursor one character to the left in the + display. + + SLC_MCR Move cursor one character right. When visual editing is + supported, this is the character that, when typed, will + move the cursor one character to the right in the + display. + + SLC_MCWL Move cursor one word left. When visual editing is + supported, this is the character that, when typed, will + move the cursor one word to the left in the display. + + SLC_MCWR Move cursor one word right. When visual editing is + supported, this is the character that, when typed, will + move the cursor one word to the right in the display. + + SLC_MCBOL Move cursor to the begining of the line. When visual + editing is supported, this is the character that, when + typed, will move the cursor to the begining of the line + that is being edited. + + SLC_MCEOL Move cursor to the end of the line. When visual editing + + + +Telnet Working Group [Page 9] + +RFC 1184 Telnet Linemode Option October 1990 + + + is supported, this is the character that, when typed, + will move the cursor to the end of the line that is + being edited. + + SLC_INSRT Enter insert mode. When visual editing is supported, + after this character is typed, all normal characters + that are subsequently typed will be inserted into the + display. + + SLC_OVER Enter overstrike mode. When visual editing is + supported, after this character is typed, all normal + charactersthat are subsequently typed will overwrite + any characters in the current display. If the + SLC_INSRT and SLC_OVER variables are set to the same + value, then that value is to act as a toggle between + overstrike and insert mode. + + SLC_ECR Erase character to the right. When visual editing is + supported, this is the character that, when typed, will + erase one character to the right of the cursor. + + SLC_EWR Erase word to the right. When visual editing is + supported, this is the character that, when typed, + will erase one word to the right of the cursor. + + SLC_EBOL Erase to the begining of the line. When visual editing + is supported, this is the character that, when typed, + will erase all the characters to the left of the cursor. + + SLC_EEOL Erase to the end of the line. When visual editing is + supported, this is the character that, when typed, will + erase all characters to the right of the cursor. + + For SLC_EEOL, SLC_EWR, and SLC_ECR, if a system has a cursor that is + not diplayed between characters, but is positioned over a character, + that character is assumed to be to the right of the cursor. Thus, + the SLC_ECR will erase the character that is under the current cursor + position. + +2.5 New control characters + + IAC ABORT + + Abort. Similar to "IAC IP", but means only to abort or terminate + the process to which the NVT is connected. (The Telnet spec says + IP may "suspend, interrupt, abort or terminate" the process.) If a + system does not have two methods of interrupting a process, then + ABORT and IP should have the same effect. + + + +Telnet Working Group [Page 10] + +RFC 1184 Telnet Linemode Option October 1990 + + + IAC SUSP + + Suspend the execution of the current process attached to the NVT + in such a way that another process will take over control of the + NVT, and the suspended process can be resumed at a later time. If + the receiving system does not support this functionality, it + should be ignored. + + IAC EOF + + End Of File. The recipient should notify the process connected to + the NVT that an end of file has been reached. This is intended + for systems that support the ability for the user to type in an + EOF character at the keyboard. + +3. Default Specification + + The default specification for this option is + + WONT LINEMODE + + DONT LINEMODE + + meaning there will not be any subnegotiation of the mode of the + connection. + + If WILL LINEMODE is negotiated, the defaults are: + + IAC SB LINEMODE MODE 0 IAC SE + IAC SB LINEMODE WONT FORWARDMASK IAC SE + + If DO LINEMODE is negotiated, the defaults are: + + IAC SB LINEMODE MODE 0 IAC SE + IAC SB LINEMODE DONT FORWARDMASK IAC SE + + Character values for SLC default to SLC_NOSUPPORT. + +4. Motivation + + With increasing Telnet usage, it has become apparent that the ability + to do command line processing on the local machine and send completed + lines to the remote machine is a feature necessary in several + environments. First, in the case of a connection over long delay + equipment, it is very frustrating to the user to have the echoing of + his data take several seconds. Second, some supercomputers, due to + their nature, are not good at handling and processing single + character input. For these machines, it is better to have the front + + + +Telnet Working Group [Page 11] + +RFC 1184 Telnet Linemode Option October 1990 + + + end computer do the character processing, and leave the + supercomputer's cycles available for doing vectorized number + crunching. + + There have been attempts to make local line editing work within the + existing Telnet specs. Indeed, the 4.3 BSD tape includes a version + of Telnet that attempts to do this through recognition of the state + of the ECHO and SUPRESS-GO-AHEAD options; other implementations do + this recognition purely through the ECHO option. + + There are problems with both of these methods. Using just the ECHO + provides no mechanism to have ECHO to the user turned off, and leave + local character processing on, for example, when a user is typing a + password. + + The usage of the SUPRESS-GO-AHEAD comes from reading into RFC 858, + where it states: + + "In many TELNET implementations it will be desirable to couple the + SUPRESS-GO-AHEAD option to the echo option so that when the echo + option is in effect, the SUPPRESS-GO-AHEAD option is in effect + simultaneously: both of these options will normally have to be in + effect simultaneously to effect what it commonly understood to be + character at a time echoing by the remote computer." + + The reverse reading of this is that without the ECHO option or the + SUPPRESS-GO-AHEAD option, you are in line at a time mode, implying + local line editing. This has the obvious problem that that is not + what the SUPPRESS-GO-AHEAD option is supposed to mean. + + Other shortcomings are that the Telnet specification is not rich + enough to handle all of the special characters that some of the + current operating systems support. For example, the ECHO/SGA + implementation supports two ways of interrupting a process, by + borrowing the BRK option for the second interrupt. Some + implementations have taken the EOR option to send an End-Of-File. + Obviously, this is using things for which they were not intended, and + the correct solution would be to define new options. + + Another problem is that some implementations of line mode buffer up + the input until the end of the line, and then send the whole line + across, editing characters and all. No local editing of the line has + been done. + + After examining several implementations, it has become clear that the + correct thing to do is to implement new options to enhance the + current Telnet specification so that it can support local line + editing in a reasonable, reliable, and consistent manner. + + + +Telnet Working Group [Page 12] + +RFC 1184 Telnet Linemode Option October 1990 + + + There are three states that are of interest. + + 1) Local line editing and local signal trapping + + 2) Remote line editing, local signal trapping + + 3) Remote line editing, remote signal trapping + + The case of local line editing and remote signal trapping is not a + very interesting case, because you don't recognize the signals, and + cannot send them to the remote side for it to recognize until the + line has been completed. Also, special signals usually will have an + effect on the line editing function, and if they are not being + trapped locally the desired action will not happen. + + Local line editing means that all normal command line character + processing, like "Erase Character" and "Erase Line", happen on the + local system, and only when "CR LF" (or some other special character) + is encountered is the edited data sent to the remote system. + + Signal trapping means, for example, that if the user types the + character associated with the IP function, then the "IAC IP" function + is sent to the remote side instead of the character typed. Remote + signal trapping means, for example, that if the user types the + character associated with the IP function, then the "IAC IP" function + is not sent to the remote side, but rather the actual character typed + is sent to the remote side. + +5. Implementation Rules + + It is expected that any implementation that supports the Telnet + LINEMODE option will support all of this specification. + +5.1 User Interface + + Normally, the entire user interface is left up to the implementor. + However, there is functionality that the user should be able to + specify on the client side of the connection. During a Telnet + session, the client side should allow some mechanism for the user to + give commands to the local Telnet process. These commands should at + least allow the user to: + + 1) Change the mode of the connection. The user should be able to + attempt to turn EDIT, FLOW, TRAPSIG, and ECHO on and off. The + server may refuse to change the state of the EDIT and TRAPSIG + bits. + + 2) Import or export SLC. The user should be able to tell the + + + +Telnet Working Group [Page 13] + +RFC 1184 Telnet Linemode Option October 1990 + + + local Telnet process whether he wants to use the local or + the current or default remote definitions of the special + characters. + + 3) Manual sending of options. The user should be able to tell + the local Telnet process to explicitly send any of the Telnet + options (like IP, ABORT, AYT, etc). + +5.2 End of line terminators. + + When LINEMODE is turned on, and when in EDIT mode, when any normal + line terminator on the client side operating system is typed, the + line should be transmitted with "CR LF" as the line terminator. When + EDIT mode is turned off, a carriage return should be sent as "CR + NUL", a line feed should be sent as LF, and any other key that cannot + be mapped into an ASCII character, but means the line is complete + (like a DOIT or ENTER key), should be sent as "CR LF". + +5.3 Output processing + + Regardless of what mode has been negotiated, the server side is + responsible for doing all output processing. Specificly, it should + send "CR LF" when it wants the "newline" function, "CR NUL" when it + wants just a carriage return, and "LF" when it wants just a linefeed. + +5.4 A terminal driver in Telnet? + + Conforming implementations need not do all the line editing + themselves. There is nothing wrong with letting the system terminal + driver handle the line editing, and have it hand to the Telnet + application the completed and edited line, which is then sent to the + remote system. + +5.5 Setting of Local Characters + + When this RFC was being developed, the original thought was that both + sides of the connection would use their own defaults for the special + characters, even if they were not the same on both sides of the + connection. If this scheme is used, though, the view that the user + has is that the local special characters are being used, and the + remote character settings don't matter. It was decided that the + client side of the connection should be in control of the character + settings. + + When LINEMODE is negotiated, the client must either export the local + character settings to the server, or send a request (SLC 0 + SLC_DEFAULT 0) to import the servers special characters. The usual + action would be that a client running on a full fledged computer + + + +Telnet Working Group [Page 14] + +RFC 1184 Telnet Linemode Option October 1990 + + + would export the special characters, and a client running where there + are no local defaults (like on some terminal servers) would import + the special characters. + + When an SLC command is received, the action taken should be: + + 1) Ignore it if it is the same as the current settings. + + 2) If the SLC_LEVELBITS are the same as the current level bits, + but the value is different and the SLC_ACK bit is set, no + reply is generated. On the server side, the command is + ignored, and on the client side, a switch is made to the + new value. This is so that if a request to change the same + character is generated by both the server and the client, + they will both settle on the clients requested value. + + 3) If we agree with the new setting, we switch to it and reply + with the same value, but also set the SLC_ACK bit. + + 4) If we don't agree, we send a response with what we think the + value should be. The SLC_ACK bit is NOT set in this case. + You may only disagree with a value by sending a different + value at a lower level. + + If the remote system doesn't support some of the line editing + characters, but the front end does, then the front end may use the + local definitions for those characters when in line mode. In this + case, the server should send "SLC xxx SLC_DEFAULT 0" in response to a + "SLC 0 SLC_DEFAULT 0" request, and just ack whatever value the client + requests to set the function to. + + The SLC_FORW2 character should only be used if SLC_FORW1 is already + in use. + +5.6 FORWARDMASK and SLC_FORW1 and SLC_FORW2 + + To help ease the amount of work needed to implement the client side, + two methods of setting forwarding characters are provided. The + SLC_FORW1 and SLC_FORW2 allow for the setting of two additional + characters on which to forward buffered input data. Since many + terminal drivers have the ability to set one or more line delimiters, + it is fairly easy to support these without having to implement + through the local terminal driver, rather than putting a terminal + driver into Telnet. If the local terminal driver has functionality + that maps easily into the FORWARDMASK, then it can also be easily + supported. If the local terminal driver does not support that, then + it would require more work to support FORWARDMASK. + + + + +Telnet Working Group [Page 15] + +RFC 1184 Telnet Linemode Option October 1990 + + + Also note that the client side is required to forward data when it + sees one of SLC_FORW1, SLC_FORW2, or FORWARDMASK characters, or when + any normal line termination or special signal is encountered. The + client side is also free to forward on other characters that it + chooses. For example, if the server side sent a FORWARDMASK that + asked for data to be forwarded on the first 20 control characters + (ASCII codes 1 through 024), and the client side cannot have its + local terminal driver forward on just the first 20 control + characters, but it can have the local terminal driver forward on any + control character (ASCII codes 1 through 039), then the client side + could validly accept the FORWARDMASK, and forward on any control + character. When in EDIT mode, care should be taken to not forward at + random times, since once that data is forwarded, no more editing on + the forwarded part of the line can be done. The only time (other + than the normal times) that data should be forwarded when in EDIT + mode would be if a single input line is too long to handle locally. + +5.7 Valid and invalid modes and values + + At no time should "DO LINEMODE" be negotiated in both directions of + the Telnet connection. The side that is the "DO LINEMODE" is + considered to be the server side, and the side that is "WILL + LINEMODE" is the client side. + + At no time should "SB LINEMODE DO/DONT FORWARDMASK", be sent unless + "DO LINEMODE" has been previously negotiated. At no time should "SB + LINEMODE WILL/WONT FORWARDMASK", be sent unless "WILL LINEMODE" has + been previously negotiated. + + If an ABORT, EOF or SUSP, is received and the system does not support + that functionality, it may just be ignored. + +5.8 Flushing input and output + + When an IP, BRK or ABORT is sent, it is usually desirable to be able + to flush the input stream, and to flush output to the user until the + IP, BRK, or ABORT is processed. The SLC_FLUSHIN and SLC_FLUSHOUT + bits are used to indicate what action should be done. These bits are + advisory only, but should be honored if possible. The standard + method for processing the SLC_FLUSHIN is to use the Telnet "Synch" + signal, and the SLC_FLUSHOUT is processed using the TIMING-MARK + option. If both are to be sent, the IAC DM is sent before the DO + TIMING-MARK Thus, the sender would send "IAC XXX IAC DM IAC DO + TIMING-MARK", where XXX may be IP, BRK or ABORT, or any other special + character. The IAC DM is sent as TCP urgent data with the DM as the + last (or only) data octet; this is used to flush the input stream. + The "IAC DO TIMING-MARK" is used to tell when to stop flushing + output; once it is sent, all data is discarded until an "IAC WILL + + + +Telnet Working Group [Page 16] + +RFC 1184 Telnet Linemode Option October 1990 + + + TIMING-MARK" or an "IAC WONT TIMING-MARK" is received. + + Since the SLC_FLUSHIN and SLC_FLUSHOUT bit are only advisory, the + user interface should provide a method so that the user can override + the sending (or not sending) of the "Synch" and TIMING-MARK, but the + default action should be to send them according to the SLC_FLUSHIN + and SLC_FLUSHOUT bits. + + Whenever an IAC AO is received, a Synch must be returned. When ever + a Synch is being processed, (by the TCP connection going into Urgent + mode), all data must be discarded (but not Telnet commands!) until an + IAC DM is found, and the connection goes out of Urgent mode. See RFC + 854, "TELNET PROTOCOL SPECIFICATION", for a complete description of + the Synch signal. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Telnet Working Group [Page 17] + +RFC 1184 Telnet Linemode Option October 1990 + + +5.9 State diagram for SLC + + +---------------------------------------------------------------+ + | IDLE | + +----------------------+------+------+-------+-------+---------++ + ^ ^ ^ | | ^ | ^ | ^ | ^ | + | | | v v | | | | | v | | + | | | +------+ +---+--+ | | | | ########### | | + | | | | Get | | Send | | | | | # Get # | | + | | | | SPC0 | | SPC0 | | | | | # 0,DEF,0 # | | + | | | +---+--+ +------+ | | | | ########### | | + | | | | ^ | | | | | | | + | | | v | v | | | v | | + | | | / \ | *********** | | ########### | | + | | | / \ | * Send * | | # Switch # | | + | ********** |Yes/ Same as \ | * 0,VAL,0 * | | # to # | | + | * Change * +--< current? > | *********** | | # default # | | + | * to new * \ / | v | ########### | | + | * value * \ / | *********** | | | + | ********** \ / | * Send * v | | + | ^ |No | * 0,DEF,0 * ######### | | + | |Yes v | *********** # Send #--+ | + | / \ / \ | # SPC-A # | + | / \ / \ | ######### | + | / Is ACK \ Yes/ Same \ | ^ | + |< bit set? ><-< level as > | | | + | \ / \ current?/ | ########### | + | \ / \ / | # Get #<--+ + | \ / \ / +-+---+ # 0,VAL,0 # + | |No |No | Set | ########### + | +--------------+ | ACK | + | v | bit | * - Client side only + | / \ +-----+ # - Server side only + | +------+ / \ ^ + | | Send | No / Do we \ Yes| + +---| SPC1 |<---< agree? >---+ + +------+ \ / + \ / + \ / + + SPC0 Initial setting for a special character + SPC1 A changed special character < SPC0 + SPC-A All current special character settings + VAL SLC_VALUE level + DEF SLC_DEFAULT level + + Levels: DEFAULT, VALUE, CANT_CHANGE, NOSUPPORT + Flags: ACK + + + +Telnet Working Group [Page 18] + +RFC 1184 Telnet Linemode Option October 1990 + + + Receive Response + ------- -------- + f,SLC_DEFAULT,x f,SLC_VALUE,v + f,SLC_CANTCHANGE,v + f,SLC_NOSUPPORT,x + + f,SLC_VALUE,v f,SLC_ACK|SLC_VALUE,v + f,SLC_CANTCHANGE,w + f,SLC_NOSUPPORT,x + + f,SLC_CANTCHANGE,v f,SLC_ACK|SLC_CANTCHANGE,v + f,SLC_NOSUPPORT,x + + f,SLC_NOSUPPORT,x f,SLC_ACK|SLC_NOSUPPORT,x + + x,SLC_ACK|x,x no response + +5.10 Examples of a connection + + In these examples, the symbolic names are used rather than the actual + values, to make them readable. When two or more symbolic names are + joined by a |, it means that the actual value will be the logical + "or" of the values of the symbolic names. In the interest of + clarity, for these examples the leading IAC and IAC SB sequences, and + the trailing IAC SE sequences have been omitted. Also, the SLC_ + prefix has been left off where ever it would normally occur. + + CLIENT SERVER + DO TOGGLE-FLOW-CONTROL + DO LINEMODE + WILL TOGGLE-FLOW-CONTROL + WILL LINEMODE + [ Subnegotiation may now proceed in both directions. The client + sends of the list of special characters. ] + LINEMODE SLC SYNCH DEFAULT 0 IP + VALUE|FLUSHIN|FLUSHOUT 3 AO + VALUE 15 AYT DEFAULT 0 ABORT + VALUE|FLUSHIN|FLUSHOUT 28 EOF + VALUE 4 SUSP VALUE|FLUSHIN 26 EC + VALUE 127 EL VALUE 21 EW VALUE + 23 RP VALUE 18 LNEXT VALUE 22 + XON VALUE 17 XOFF VALUE 19 + [ Now that linemode is enabled, the server sets the initial mode, + and acknowledges the special characters. ] + LINEMODE MODE EDIT + + LINEMODE SLC SYNCH NOSUPPORT 0 + IP VALUE|FLUSHIN|FLUSHOUT|ACK 3 + + + +Telnet Working Group [Page 19] + +RFC 1184 Telnet Linemode Option October 1990 + + + AO NOSUPPORT 0 AYT NOSUPPORT 0 + ABORT VALUE|FLUSHIN|FLUSHOUT|ACK + 28 EOF VALUE|ACK 4 SUSP NOSUP- + PORT 0 EC VALUE|ACK 127 EL + VALUE|ACK 21 EW VALUE|ACK 23 RP + VALUE|ACK 18 LNEXT VALUE|ACK 22 + XON VALUE|ACK 17 XOFF VALUE|ACK + 19 + [ The client gets the mode and ack of the special characters, and + acks the mode and any special characters that the server + changed. ] + LINEMODE MODE EDIT|MODE_ACK + + LINEMODE SLC SYNCH NOSUPPORT|ACK + 0 AO NOSUPPORT|ACK 0 AYT|ACK + NOSUPPORT 0 SUSP NOSUPPORT|ACK 0 + "Login:" + "my_account" + [ Turn off echo to the user. ] + WILL ECHO + DO ECHO + "Password:" + "my_password" + [ Turn back on echo to the user. ] + WONT ECHO + DONT ECHO + [ User does some stuff, and then runs an application that wants + to use single character mode, doing its own echoing of + characters, but keep signal trapping on. ] + WILL ECHO + DO ECHO + LINEMODE MODE TRAPSIG + LINEMODE MODE TRAPSIG|MODE_ACK + [ Application finishes ] + WONT ECHO + DONT ECHO + LINEMODE MODE EDIT|TRAPSIG + LINEMODE MODE + EDIT|TRAPSIG|MODE_ACK + [ Another application, that wants full control of everything. ] + WILL ECHO + DO ECHO + LINEMODE MODE 0 + LINEMODE MODE 0|MODE_ACK + [ Application finishes ] + WONT ECHO + DONT ECHO + LINEMODE MODE EDIT|TRAPSIG + + + +Telnet Working Group [Page 20] + +RFC 1184 Telnet Linemode Option October 1990 + + + LINEMODE MODE + EDIT|TRAPSIG|MODE_ACK + [ The user changes his erase character to ^H. ] + LINEMODE SLC EC VALUE 8 + LINEMODE SLC EC VALUE|ACK 8 + [ The user decides to revert to all the original client side + special characters. ] + LINEMODE SLC SYNCH DEFAULT 0 IP + VALUE|FLUSHIN|FLUSHOUT 3 AO + VALUE 15 AYT DEFAULT 0 ABORT + VALUE|FLUSHIN|FLUSHOUT 28 EOF + VALUE 4 SUSP VALUE|FLUSHIN 26 EC + VALUE 127 EL VALUE 21 EW VALUE + 23 RP VALUE 18 LNEXT VALUE 22 + XON VALUE 17 XOFF VALUE 19 + LINEMODE SLC SYNCH NOSUPPORT 0 + AO NOSUPPORT 15 AYT NOSUPPORT 0 + SUSP NOSUPPORT|FLUSHIN 26 EC + VALUE|ACK 127 EW VALUE|ACK 23 RP + VALUE|ACK 18 LNEXT VALUE|ACK 22 + XON VALUE|ACK 17 XOFF VALUE|ACK + 19 + LINEMODE SLC SYNCH NOSUPPORT|ACK + 0 AO NOSUPPORT|ACK 15 AYT + NOSUPPORT|ACK 0 SUSP + NOSUPPORT|ACK|FLUSHIN 26 + [ The user decides to import the remote sides default special + characters. ] + LINEMODE SLC 0 DEFAULT 0 + LINEMODE SLC IP + VALUE|FLUSHIN|FLUSHOUT 3 ABORT + VALUE|FLUSHIN|FLUSHOUT 28 EOF + VALUE 4 EC VALUE 127 EL VALUE 21 + [ Since these are the same as the current local settings, no + response is generated. ] + [ This next example is what would happen if an editor was fired + up, that wanted to let the client side do the echoing and + buffering of characters, but did not want it to do any line + editing, and only forward the data when got a control + character. Note that we have preceded all the the 0377s + in the forward mask with an IAC. ] + LINEMODE MODE 0 + LINEMODE DO FORWARDMASK IAC 0377 + IAC 0377 IAC 0377 IAC 0377 0 0 0 + 0 0 0 0 0 0 0 0 01 + LINEMODE MODE 0 + LINEMODE WILL FORWARDMASK + [ Application runs to completion, and then things are to be set + + + +Telnet Working Group [Page 21] + +RFC 1184 Telnet Linemode Option October 1990 + + + back to what they were before. ] + LINEMODE MODE EDIT|TRAPSIG + LINEMODE DONT FORWARDMASK + LINEMODE MODE EDIT|TRAPSIG + LINEMODE WONT FORWARDMASK + +6. Other Telnet options and RFCs + + The following is a list of RFCs for various Telnet options that + should be supported along with LINEMODE. + + 1. Postel, J. and Reynolds, J., "TELNET PROTOCOL SPECIFICATION", + RFC 854, ISI, May 1983. + + 2. Postel, J. and Reynolds, J., "TELNET OPTION SPECIFICATIONS", + RFC 855, ISI, May 1983. + + 3. Postel, J. and Reynolds, J., "TELNET BINARY TRANSMISSION", + RFC 856, ISI, May 1983. + + 4. Postel, J. and Reynolds, J., "TELNET ECHO OPTION", RFC 857, + ISI, May 1983. + + 5. Postel, J. and Reynolds, J., "TELNET SUPRESS GO AHEAD OPTION", + RFC 858, ISI, May 1983. + + 6. Postel, J. and Reynolds, J., "TELNET TIMING MARK OPTION", + RFC 860, ISI, May 1983. + + 7. VanBokkeln, J., "Telnet Terminal-Type Option", RFC 1091, + FTP Software, Inc., February 1989. + + 8. Waitzman, D., "Telnet Window Size Option", RFC 1073, BBN STC, + October 1988. + + 9. Hedrick, C., "Telnet Remote Flow Control Option", RFC 1080, + Rutgers University, November, 1988. + + 10. Hedrick, C., "Telnet Terminal Speed Option", RFC 1079, Rutgers + University, December, 1988. + + The following is a list of RFCs that need not be supported for + LINEMODE, but which would enhance any TELNET implementation. + + 11. Postel, J. and Reynolds, J., "TELNET STATUS OPTION", RFC 859, + ISI, May 1983. + + 12. Postel, J. and Reynolds, J., "TELNET END OF RECORD OPTION", + + + +Telnet Working Group [Page 22] + +RFC 1184 Telnet Linemode Option October 1990 + + + RFC 885, ISI, December 1983. + + 13. Silverman, S., "OUTPUT MARKING TELNET OPTION", RFC 933, + MITRE-Washington, January 1985. + + 14. Marcy, G., "Telnet X Display Location Option", RFC 1096, + Carnegie Mellon University, March 1989. + +Security Consideratiions + + Security issues are not discussed in this memo. + +Author's Address + + David A. Borman + Cray Research Inc. + 655F Lone Oak Drive + Eagan, MN 55123 + + Phone: (612) 452-6650 + EMail: dab@CRAY.COM + + IETF Telnet WG Mailing List: telnet-ietf@CRAY.COM + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Telnet Working Group [Page 23] + \ No newline at end of file diff --git a/resources/rfc854.txt b/resources/rfc854.txt new file mode 100644 index 0000000..57c4c65 --- /dev/null +++ b/resources/rfc854.txt @@ -0,0 +1,855 @@ +Source: https://datatracker.ietf.org/doc/html/rfc854 (downloaded: 2024-03-03 12:20 UTC) + +Network Working Group J. Postel +Request for Comments: 854 J. Reynolds + ISI +Obsoletes: NIC 18639 May 1983 + + TELNET PROTOCOL SPECIFICATION + + +This RFC specifies a standard for the ARPA Internet community. Hosts on +the ARPA Internet are expected to adopt and implement this standard. + +INTRODUCTION + + The purpose of the TELNET Protocol is to provide a fairly general, + bi-directional, eight-bit byte oriented communications facility. Its + primary goal is to allow a standard method of interfacing terminal + devices and terminal-oriented processes to each other. It is + envisioned that the protocol may also be used for terminal-terminal + communication ("linking") and process-process communication + (distributed computation). + +GENERAL CONSIDERATIONS + + A TELNET connection is a Transmission Control Protocol (TCP) + connection used to transmit data with interspersed TELNET control + information. + + The TELNET Protocol is built upon three main ideas: first, the + concept of a "Network Virtual Terminal"; second, the principle of + negotiated options; and third, a symmetric view of terminals and + processes. + + 1. When a TELNET connection is first established, each end is + assumed to originate and terminate at a "Network Virtual Terminal", + or NVT. An NVT is an imaginary device which provides a standard, + network-wide, intermediate representation of a canonical terminal. + This eliminates the need for "server" and "user" hosts to keep + information about the characteristics of each other's terminals and + terminal handling conventions. All hosts, both user and server, map + their local device characteristics and conventions so as to appear to + be dealing with an NVT over the network, and each can assume a + similar mapping by the other party. The NVT is intended to strike a + balance between being overly restricted (not providing hosts a rich + enough vocabulary for mapping into their local character sets), and + being overly inclusive (penalizing users with modest terminals). + + NOTE: The "user" host is the host to which the physical terminal + is normally attached, and the "server" host is the host which is + normally providing some service. As an alternate point of view, + + + + +Postel & Reynolds [Page 1] + + + +RFC 854 May 1983 + + + applicable even in terminal-to-terminal or process-to-process + communications, the "user" host is the host which initiated the + communication. + + 2. The principle of negotiated options takes cognizance of the fact + that many hosts will wish to provide additional services over and + above those available within an NVT, and many users will have + sophisticated terminals and would like to have elegant, rather than + minimal, services. Independent of, but structured within the TELNET + Protocol are various "options" that will be sanctioned and may be + used with the "DO, DON'T, WILL, WON'T" structure (discussed below) to + allow a user and server to agree to use a more elaborate (or perhaps + just different) set of conventions for their TELNET connection. Such + options could include changing the character set, the echo mode, etc. + + The basic strategy for setting up the use of options is to have + either party (or both) initiate a request that some option take + effect. The other party may then either accept or reject the + request. If the request is accepted the option immediately takes + effect; if it is rejected the associated aspect of the connection + remains as specified for an NVT. Clearly, a party may always refuse + a request to enable, and must never refuse a request to disable some + option since all parties must be prepared to support the NVT. + + The syntax of option negotiation has been set up so that if both + parties request an option simultaneously, each will see the other's + request as the positive acknowledgment of its own. + + 3. The symmetry of the negotiation syntax can potentially lead to + nonterminating acknowledgment loops -- each party seeing the incoming + commands not as acknowledgments but as new requests which must be + acknowledged. To prevent such loops, the following rules prevail: + + a. Parties may only request a change in option status; i.e., a + party may not send out a "request" merely to announce what mode it + is in. + + b. If a party receives what appears to be a request to enter some + mode it is already in, the request should not be acknowledged. + This non-response is essential to prevent endless loops in the + negotiation. It is required that a response be sent to requests + for a change of mode -- even if the mode is not changed. + + c. Whenever one party sends an option command to a second party, + whether as a request or an acknowledgment, and use of the option + will have any effect on the processing of the data being sent from + the first party to the second, then the command must be inserted + in the data stream at the point where it is desired that it take + + +Postel & Reynolds [Page 2] + + + +RFC 854 May 1983 + + + effect. (It should be noted that some time will elapse between + the transmission of a request and the receipt of an + acknowledgment, which may be negative. Thus, a host may wish to + buffer data, after requesting an option, until it learns whether + the request is accepted or rejected, in order to hide the + "uncertainty period" from the user.) + + Option requests are likely to flurry back and forth when a TELNET + connection is first established, as each party attempts to get the + best possible service from the other party. Beyond that, however, + options can be used to dynamically modify the characteristics of the + connection to suit changing local conditions. For example, the NVT, + as will be explained later, uses a transmission discipline well + suited to the many "line at a time" applications such as BASIC, but + poorly suited to the many "character at a time" applications such as + NLS. A server might elect to devote the extra processor overhead + required for a "character at a time" discipline when it was suitable + for the local process and would negotiate an appropriate option. + However, rather than then being permanently burdened with the extra + processing overhead, it could switch (i.e., negotiate) back to NVT + when the detailed control was no longer necessary. + + It is possible for requests initiated by processes to stimulate a + nonterminating request loop if the process responds to a rejection by + merely re-requesting the option. To prevent such loops from + occurring, rejected requests should not be repeated until something + changes. Operationally, this can mean the process is running a + different program, or the user has given another command, or whatever + makes sense in the context of the given process and the given option. + A good rule of thumb is that a re-request should only occur as a + result of subsequent information from the other end of the connection + or when demanded by local human intervention. + + Option designers should not feel constrained by the somewhat limited + syntax available for option negotiation. The intent of the simple + syntax is to make it easy to have options -- since it is + correspondingly easy to profess ignorance about them. If some + particular option requires a richer negotiation structure than + possible within "DO, DON'T, WILL, WON'T", the proper tack is to use + "DO, DON'T, WILL, WON'T" to establish that both parties understand + the option, and once this is accomplished a more exotic syntax can be + used freely. For example, a party might send a request to alter + (establish) line length. If it is accepted, then a different syntax + can be used for actually negotiating the line length -- such a + "sub-negotiation" might include fields for minimum allowable, maximum + allowable and desired line lengths. The important concept is that + + + + +Postel & Reynolds [Page 3] + + + +RFC 854 May 1983 + + + such expanded negotiations should never begin until some prior + (standard) negotiation has established that both parties are capable + of parsing the expanded syntax. + + In summary, WILL XXX is sent, by either party, to indicate that + party's desire (offer) to begin performing option XXX, DO XXX and + DON'T XXX being its positive and negative acknowledgments; similarly, + DO XXX is sent to indicate a desire (request) that the other party + (i.e., the recipient of the DO) begin performing option XXX, WILL XXX + and WON'T XXX being the positive and negative acknowledgments. Since + the NVT is what is left when no options are enabled, the DON'T and + WON'T responses are guaranteed to leave the connection in a state + which both ends can handle. Thus, all hosts may implement their + TELNET processes to be totally unaware of options that are not + supported, simply returning a rejection to (i.e., refusing) any + option request that cannot be understood. + + As much as possible, the TELNET protocol has been made server-user + symmetrical so that it easily and naturally covers the user-user + (linking) and server-server (cooperating processes) cases. It is + hoped, but not absolutely required, that options will further this + intent. In any case, it is explicitly acknowledged that symmetry is + an operating principle rather than an ironclad rule. + + A companion document, "TELNET Option Specifications," should be + consulted for information about the procedure for establishing new + options. + +THE NETWORK VIRTUAL TERMINAL + + The Network Virtual Terminal (NVT) is a bi-directional character + device. The NVT has a printer and a keyboard. The printer responds + to incoming data and the keyboard produces outgoing data which is + sent over the TELNET connection and, if "echoes" are desired, to the + NVT's printer as well. "Echoes" will not be expected to traverse the + network (although options exist to enable a "remote" echoing mode of + operation, no host is required to implement this option). The code + set is seven-bit USASCII in an eight-bit field, except as modified + herein. Any code conversion and timing considerations are local + problems and do not affect the NVT. + + TRANSMISSION OF DATA + + Although a TELNET connection through the network is intrinsically + full duplex, the NVT is to be viewed as a half-duplex device + operating in a line-buffered mode. That is, unless and until + + + + +Postel & Reynolds [Page 4] + + + +RFC 854 May 1983 + + + options are negotiated to the contrary, the following default + conditions pertain to the transmission of data over the TELNET + connection: + + 1) Insofar as the availability of local buffer space permits, + data should be accumulated in the host where it is generated + until a complete line of data is ready for transmission, or + until some locally-defined explicit signal to transmit occurs. + This signal could be generated either by a process or by a + human user. + + The motivation for this rule is the high cost, to some hosts, + of processing network input interrupts, coupled with the + default NVT specification that "echoes" do not traverse the + network. Thus, it is reasonable to buffer some amount of data + at its source. Many systems take some processing action at the + end of each input line (even line printers or card punches + frequently tend to work this way), so the transmission should + be triggered at the end of a line. On the other hand, a user + or process may sometimes find it necessary or desirable to + provide data which does not terminate at the end of a line; + therefore implementers are cautioned to provide methods of + locally signaling that all buffered data should be transmitted + immediately. + + 2) When a process has completed sending data to an NVT printer + and has no queued input from the NVT keyboard for further + processing (i.e., when a process at one end of a TELNET + connection cannot proceed without input from the other end), + the process must transmit the TELNET Go Ahead (GA) command. + + This rule is not intended to require that the TELNET GA command + be sent from a terminal at the end of each line, since server + hosts do not normally require a special signal (in addition to + end-of-line or other locally-defined characters) in order to + commence processing. Rather, the TELNET GA is designed to help + a user's local host operate a physically half duplex terminal + which has a "lockable" keyboard such as the IBM 2741. A + description of this type of terminal may help to explain the + proper use of the GA command. + + The terminal-computer connection is always under control of + either the user or the computer. Neither can unilaterally + seize control from the other; rather the controlling end must + relinguish its control explicitly. At the terminal end, the + hardware is constructed so as to relinquish control each time + that a "line" is terminated (i.e., when the "New Line" key is + typed by the user). When this occurs, the attached (local) + + +Postel & Reynolds [Page 5] + + + +RFC 854 May 1983 + + + computer processes the input data, decides if output should be + generated, and if not returns control to the terminal. If + output should be generated, control is retained by the computer + until all output has been transmitted. + + The difficulties of using this type of terminal through the + network should be obvious. The "local" computer is no longer + able to decide whether to retain control after seeing an + end-of-line signal or not; this decision can only be made by + the "remote" computer which is processing the data. Therefore, + the TELNET GA command provides a mechanism whereby the "remote" + (server) computer can signal the "local" (user) computer that + it is time to pass control to the user of the terminal. It + should be transmitted at those times, and only at those times, + when the user should be given control of the terminal. Note + that premature transmission of the GA command may result in the + blocking of output, since the user is likely to assume that the + transmitting system has paused, and therefore he will fail to + turn the line around manually. + + The foregoing, of course, does not apply to the user-to-server + direction of communication. In this direction, GAs may be sent at + any time, but need not ever be sent. Also, if the TELNET + connection is being used for process-to-process communication, GAs + need not be sent in either direction. Finally, for + terminal-to-terminal communication, GAs may be required in + neither, one, or both directions. If a host plans to support + terminal-to-terminal communication it is suggested that the host + provide the user with a means of manually signaling that it is + time for a GA to be sent over the TELNET connection; this, + however, is not a requirement on the implementer of a TELNET + process. + + Note that the symmetry of the TELNET model requires that there is + an NVT at each end of the TELNET connection, at least + conceptually. + + STANDARD REPRESENTATION OF CONTROL FUNCTIONS + + As stated in the Introduction to this document, the primary goal + of the TELNET protocol is the provision of a standard interfacing + of terminal devices and terminal-oriented processes through the + network. Early experiences with this type of interconnection have + shown that certain functions are implemented by most servers, but + that the methods of invoking these functions differ widely. For a + human user who interacts with several server systems, these + differences are highly frustrating. TELNET, therefore, defines a + standard representation for five of these functions, as described + + +Postel & Reynolds [Page 6] + + + +RFC 854 May 1983 + + + below. These standard representations have standard, but not + required, meanings (with the exception that the Interrupt Process + (IP) function may be required by other protocols which use + TELNET); that is, a system which does not provide the function to + local users need not provide it to network users and may treat the + standard representation for the function as a No-operation. On + the other hand, a system which does provide the function to a + local user is obliged to provide the same function to a network + user who transmits the standard representation for the function. + + Interrupt Process (IP) + + Many systems provide a function which suspends, interrupts, + aborts, or terminates the operation of a user process. This + function is frequently used when a user believes his process is + in an unending loop, or when an unwanted process has been + inadvertently activated. IP is the standard representation for + invoking this function. It should be noted by implementers + that IP may be required by other protocols which use TELNET, + and therefore should be implemented if these other protocols + are to be supported. + + Abort Output (AO) + + Many systems provide a function which allows a process, which + is generating output, to run to completion (or to reach the + same stopping point it would reach if running to completion) + but without sending the output to the user's terminal. + Further, this function typically clears any output already + produced but not yet actually printed (or displayed) on the + user's terminal. AO is the standard representation for + invoking this function. For example, some subsystem might + normally accept a user's command, send a long text string to + the user's terminal in response, and finally signal readiness + to accept the next command by sending a "prompt" character + (preceded by ) to the user's terminal. If the AO were + received during the transmission of the text string, a + reasonable implementation would be to suppress the remainder of + the text string, but transmit the prompt character and the + preceding . (This is possibly in distinction to the + action which might be taken if an IP were received; the IP + might cause suppression of the text string and an exit from the + subsystem.) + + It should be noted, by server systems which provide this + function, that there may be buffers external to the system (in + + + + +Postel & Reynolds [Page 7] + + + +RFC 854 May 1983 + + + the network and the user's local host) which should be cleared; + the appropriate way to do this is to transmit the "Synch" + signal (described below) to the user system. + + Are You There (AYT) + + Many systems provide a function which provides the user with + some visible (e.g., printable) evidence that the system is + still up and running. This function may be invoked by the user + when the system is unexpectedly "silent" for a long time, + because of the unanticipated (by the user) length of a + computation, an unusually heavy system load, etc. AYT is the + standard representation for invoking this function. + + Erase Character (EC) + + Many systems provide a function which deletes the last + preceding undeleted character or "print position"* from the + stream of data being supplied by the user. This function is + typically used to edit keyboard input when typing mistakes are + made. EC is the standard representation for invoking this + function. + + *NOTE: A "print position" may contain several characters + which are the result of overstrikes, or of sequences such as + BS ... + + Erase Line (EL) + + Many systems provide a function which deletes all the data in + the current "line" of input. This function is typically used + to edit keyboard input. EL is the standard representation for + invoking this function. + + THE TELNET "SYNCH" SIGNAL + + Most time-sharing systems provide mechanisms which allow a + terminal user to regain control of a "runaway" process; the IP and + AO functions described above are examples of these mechanisms. + Such systems, when used locally, have access to all of the signals + supplied by the user, whether these are normal characters or + special "out of band" signals such as those supplied by the + teletype "BREAK" key or the IBM 2741 "ATTN" key. This is not + necessarily true when terminals are connected to the system + through the network; the network's flow control mechanisms may + cause such a signal to be buffered elsewhere, for example in the + user's host. + + + +Postel & Reynolds [Page 8] + + + +RFC 854 May 1983 + + + To counter this problem, the TELNET "Synch" mechanism is + introduced. A Synch signal consists of a TCP Urgent notification, + coupled with the TELNET command DATA MARK. The Urgent + notification, which is not subject to the flow control pertaining + to the TELNET connection, is used to invoke special handling of + the data stream by the process which receives it. In this mode, + the data stream is immediately scanned for "interesting" signals + as defined below, discarding intervening data. The TELNET command + DATA MARK (DM) is the synchronizing mark in the data stream which + indicates that any special signal has already occurred and the + recipient can return to normal processing of the data stream. + + The Synch is sent via the TCP send operation with the Urgent + flag set and the DM as the last (or only) data octet. + + When several Synchs are sent in rapid succession, the Urgent + notifications may be merged. It is not possible to count Urgents + since the number received will be less than or equal the number + sent. When in normal mode, a DM is a no operation; when in urgent + mode, it signals the end of the urgent processing. + + If TCP indicates the end of Urgent data before the DM is found, + TELNET should continue the special handling of the data stream + until the DM is found. + + If TCP indicates more Urgent data after the DM is found, it can + only be because of a subsequent Synch. TELNET should continue + the special handling of the data stream until another DM is + found. + + "Interesting" signals are defined to be: the TELNET standard + representations of IP, AO, and AYT (but not EC or EL); the local + analogs of these standard representations (if any); all other + TELNET commands; other site-defined signals which can be acted on + without delaying the scan of the data stream. + + Since one effect of the SYNCH mechanism is the discarding of + essentially all characters (except TELNET commands) between the + sender of the Synch and its recipient, this mechanism is specified + as the standard way to clear the data path when that is desired. + For example, if a user at a terminal causes an AO to be + transmitted, the server which receives the AO (if it provides that + function at all) should return a Synch to the user. + + Finally, just as the TCP Urgent notification is needed at the + TELNET level as an out-of-band signal, so other protocols which + make use of TELNET may require a TELNET command which can be + viewed as an out-of-band signal at a different level. + + +Postel & Reynolds [Page 9] + + + +RFC 854 May 1983 + + + By convention the sequence [IP, Synch] is to be used as such a + signal. For example, suppose that some other protocol, which uses + TELNET, defines the character string STOP analogously to the + TELNET command AO. Imagine that a user of this protocol wishes a + server to process the STOP string, but the connection is blocked + because the server is processing other commands. The user should + instruct his system to: + + 1. Send the TELNET IP character; + + 2. Send the TELNET SYNC sequence, that is: + + Send the Data Mark (DM) as the only character + in a TCP urgent mode send operation. + + 3. Send the character string STOP; and + + 4. Send the other protocol's analog of the TELNET DM, if any. + + The user (or process acting on his behalf) must transmit the + TELNET SYNCH sequence of step 2 above to ensure that the TELNET IP + gets through to the server's TELNET interpreter. + + The Urgent should wake up the TELNET process; the IP should + wake up the next higher level process. + + THE NVT PRINTER AND KEYBOARD + + The NVT printer has an unspecified carriage width and page length + and can produce representations of all 95 USASCII graphics (codes + 32 through 126). Of the 33 USASCII control codes (0 through 31 + and 127), and the 128 uncovered codes (128 through 255), the + following have specified meaning to the NVT printer: + + NAME CODE MEANING + + NULL (NUL) 0 No Operation + Line Feed (LF) 10 Moves the printer to the + next print line, keeping the + same horizontal position. + Carriage Return (CR) 13 Moves the printer to the left + margin of the current line. + + + + + + + + +Postel & Reynolds [Page 10] + + + +RFC 854 May 1983 + + + In addition, the following codes shall have defined, but not + required, effects on the NVT printer. Neither end of a TELNET + connection may assume that the other party will take, or will + have taken, any particular action upon receipt or transmission + of these: + + BELL (BEL) 7 Produces an audible or + visible signal (which does + NOT move the print head). + Back Space (BS) 8 Moves the print head one + character position towards + the left margin. + Horizontal Tab (HT) 9 Moves the printer to the + next horizontal tab stop. + It remains unspecified how + either party determines or + establishes where such tab + stops are located. + Vertical Tab (VT) 11 Moves the printer to the + next vertical tab stop. It + remains unspecified how + either party determines or + establishes where such tab + stops are located. + Form Feed (FF) 12 Moves the printer to the top + of the next page, keeping + the same horizontal position. + + All remaining codes do not cause the NVT printer to take any + action. + + The sequence "CR LF", as defined, will cause the NVT to be + positioned at the left margin of the next print line (as would, + for example, the sequence "LF CR"). However, many systems and + terminals do not treat CR and LF independently, and will have to + go to some effort to simulate their effect. (For example, some + terminals do not have a CR independent of the LF, but on such + terminals it may be possible to simulate a CR by backspacing.) + Therefore, the sequence "CR LF" must be treated as a single "new + line" character and used whenever their combined action is + intended; the sequence "CR NUL" must be used where a carriage + return alone is actually desired; and the CR character must be + avoided in other contexts. This rule gives assurance to systems + which must decide whether to perform a "new line" function or a + multiple-backspace that the TELNET stream contains a character + following a CR that will allow a rational decision. + + Note that "CR LF" or "CR NUL" is required in both directions + + +Postel & Reynolds [Page 11] + + + +RFC 854 May 1983 + + + (in the default ASCII mode), to preserve the symmetry of the + NVT model. Even though it may be known in some situations + (e.g., with remote echo and suppress go ahead options in + effect) that characters are not being sent to an actual + printer, nonetheless, for the sake of consistency, the protocol + requires that a NUL be inserted following a CR not followed by + a LF in the data stream. The converse of this is that a NUL + received in the data stream after a CR (in the absence of + options negotiations which explicitly specify otherwise) should + be stripped out prior to applying the NVT to local character + set mapping. + + The NVT keyboard has keys, or key combinations, or key sequences, + for generating all 128 USASCII codes. Note that although many + have no effect on the NVT printer, the NVT keyboard is capable of + generating them. + + In addition to these codes, the NVT keyboard shall be capable of + generating the following additional codes which, except as noted, + have defined, but not reguired, meanings. The actual code + assignments for these "characters" are in the TELNET Command + section, because they are viewed as being, in some sense, generic + and should be available even when the data stream is interpreted + as being some other character set. + + Synch + + This key allows the user to clear his data path to the other + party. The activation of this key causes a DM (see command + section) to be sent in the data stream and a TCP Urgent + notification is associated with it. The pair DM-Urgent is to + have required meaning as defined previously. + + Break (BRK) + + This code is provided because it is a signal outside the + USASCII set which is currently given local meaning within many + systems. It is intended to indicate that the Break Key or the + Attention Key was hit. Note, however, that this is intended to + provide a 129th code for systems which require it, not as a + synonym for the IP standard representation. + + Interrupt Process (IP) + + Suspend, interrupt, abort or terminate the process to which the + NVT is connected. Also, part of the out-of-band signal for + other protocols which use TELNET. + + + +Postel & Reynolds [Page 12] + + + +RFC 854 May 1983 + + + Abort Output (AO) + + Allow the current process to (appear to) run to completion, but + do not send its output to the user. Also, send a Synch to the + user. + + Are You There (AYT) + + Send back to the NVT some visible (i.e., printable) evidence + that the AYT was received. + + Erase Character (EC) + + The recipient should delete the last preceding undeleted + character or "print position" from the data stream. + + Erase Line (EL) + + The recipient should delete characters from the data stream + back to, but not including, the last "CR LF" sequence sent over + the TELNET connection. + + The spirit of these "extra" keys, and also the printer format + effectors, is that they should represent a natural extension of + the mapping that already must be done from "NVT" into "local". + Just as the NVT data byte 68 (104 octal) should be mapped into + whatever the local code for "uppercase D" is, so the EC character + should be mapped into whatever the local "Erase Character" + function is. Further, just as the mapping for 124 (174 octal) is + somewhat arbitrary in an environment that has no "vertical bar" + character, the EL character may have a somewhat arbitrary mapping + (or none at all) if there is no local "Erase Line" facility. + Similarly for format effectors: if the terminal actually does + have a "Vertical Tab", then the mapping for VT is obvious, and + only when the terminal does not have a vertical tab should the + effect of VT be unpredictable. + +TELNET COMMAND STRUCTURE + + All TELNET commands consist of at least a two byte sequence: the + "Interpret as Command" (IAC) escape character followed by the code + for the command. The commands dealing with option negotiation are + three byte sequences, the third byte being the code for the option + referenced. This format was chosen so that as more comprehensive use + of the "data space" is made -- by negotiations from the basic NVT, of + course -- collisions of data bytes with reserved command values will + be minimized, all such collisions requiring the inconvenience, and + + + +Postel & Reynolds [Page 13] + + + +RFC 854 May 1983 + + + inefficiency, of "escaping" the data bytes into the stream. With the + current set-up, only the IAC need be doubled to be sent as data, and + the other 255 codes may be passed transparently. + + The following are the defined TELNET commands. Note that these codes + and code sequences have the indicated meaning only when immediately + preceded by an IAC. + + NAME CODE MEANING + + SE 240 End of subnegotiation parameters. + NOP 241 No operation. + Data Mark 242 The data stream portion of a Synch. + This should always be accompanied + by a TCP Urgent notification. + Break 243 NVT character BRK. + Interrupt Process 244 The function IP. + Abort output 245 The function AO. + Are You There 246 The function AYT. + Erase character 247 The function EC. + Erase Line 248 The function EL. + Go ahead 249 The GA signal. + SB 250 Indicates that what follows is + subnegotiation of the indicated + option. + WILL (option code) 251 Indicates the desire to begin + performing, or confirmation that + you are now performing, the + indicated option. + WON'T (option code) 252 Indicates the refusal to perform, + or continue performing, the + indicated option. + DO (option code) 253 Indicates the request that the + other party perform, or + confirmation that you are expecting + the other party to perform, the + indicated option. + DON'T (option code) 254 Indicates the demand that the + other party stop performing, + or confirmation that you are no + longer expecting the other party + to perform, the indicated option. + IAC 255 Data Byte 255. + + + + + + + +Postel & Reynolds [Page 14] + + + +RFC 854 May 1983 + + +CONNECTION ESTABLISHMENT + + The TELNET TCP connection is established between the user's port U + and the server's port L. The server listens on its well known port L + for such connections. Since a TCP connection is full duplex and + identified by the pair of ports, the server can engage in many + simultaneous connections involving its port L and different user + ports U. + + Port Assignment + + When used for remote user access to service hosts (i.e., remote + terminal access) this protocol is assigned server port 23 + (27 octal). That is L=23. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Postel & Reynolds [Page 15] + diff --git a/src/bin/main.rs b/src/bin/main.rs new file mode 100644 index 0000000..36eadd7 --- /dev/null +++ b/src/bin/main.rs @@ -0,0 +1,48 @@ +use std::io::{Error, Write}; +use std::net::{TcpListener, TcpStream}; +use std::thread; +use telnet_server::read::Read; +use telnet_server::telnet::{Session, State, StateConfig}; + +const BIND_ADDRESS: &str = "127.0.0.1:9000"; + +fn main() -> std::io::Result<()> { + let listener = TcpListener::bind(BIND_ADDRESS)?; + + for stream in listener.incoming() { + thread::spawn(move || { + if let Ok(stream) = stream { + let _ = handle_connection(stream); + }; + }); + } + + Ok(()) +} + +fn handle_connection(tcp_stream: TcpStream) -> Result<(), Error> { + // Set up State and Session + let state_config = StateConfig::default(); + let state = State::new(&state_config); + let mut session = Session::new(state, tcp_stream)?; + + // Make the Session listen to incoming TCP data in the background + let session_listen = session.clone(); + let handle = thread::spawn(move || session_listen.listen()); + + loop { + // Handle incoming TELNET messages: + let incoming = session.read_line_waiting()?; + let answer = format!("You sent: {incoming}"); + + if session.write_all(answer.as_bytes()).is_err() { + break; + } + + if session.flush().is_err() { + break; + } + } + + handle.join().expect("Should await thread") +} diff --git a/src/iter.rs b/src/iter.rs new file mode 100644 index 0000000..2c5c0f4 --- /dev/null +++ b/src/iter.rs @@ -0,0 +1,84 @@ +/// Returns whether the sequence `needle` is a part of `haystack`, regardless of +/// its position. +/// +/// # Arguments +/// +/// * `haystack` - The sequence to look for `needle` +/// * `needle` - The sequence that may be a part of `haystack` +/// +/// # Examples +/// +/// ```ignore +/// use telnet_server::iter::contains_sequence; +/// // [1, 2, 3] is included in [1, 2, 3, 4, 5] -> returns true +/// assert!(contains_sequence(&[1, 2, 3, 4, 5], &[1, 2, 3])); +/// // [3, 3, 3] is *NOT* included in [1, 2, 3, 4, 5] -> returns false +/// assert!(!contains_sequence(&[1, 2, 3, 4, 5], &[3, 3, 3])); +/// ``` +pub fn contains_sequence(haystack: &[T], needle: &[T]) -> bool { + let haystack_len = haystack.len(); + let needle_len = needle.len(); + + if needle_len > haystack_len { + return false; + } + + if haystack_len == 0 { + /* this would be an incorrect edge case otherwise */ + return false; + } + + let size_diff = haystack_len - needle_len; + + 'outer: for haystack_start in 0..=size_diff { + for needle_index in 0..needle_len { + if haystack[needle_index + haystack_start] != needle[needle_index] { + continue 'outer; + } + } + + return true; + } + + false +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn finds_match_start() { + assert!(contains_sequence(&[1, 2, 3, 4, 5], &[1, 2, 3])); + } + + #[test] + fn finds_match_middle() { + assert!(contains_sequence(&[1, 2, 3, 4, 5], &[2, 3, 4])); + } + + #[test] + fn finds_match_end() { + assert!(contains_sequence(&[1, 2, 3, 4, 5], &[3, 4, 5])); + } + + #[test] + fn finds_no_match() { + assert!(!contains_sequence(&[1, 2, 3, 4, 5], &[3, 3, 3])); + } + + #[test] + fn finds_match_empty() { + assert!(contains_sequence(&[1, 2, 3, 4, 5], &[])); + } + + #[test] + fn finds_no_match_on_empty_haystack() { + assert!(!contains_sequence(&[], &[1])); + } + + #[test] + fn finds_no_match_on_empty_haystack_and_needle() { + assert!(!contains_sequence::(&[], &[])); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..75ae7df --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,4 @@ +pub(crate) mod iter; + +pub mod read; +pub mod telnet; diff --git a/src/read.rs b/src/read.rs new file mode 100644 index 0000000..9407df1 --- /dev/null +++ b/src/read.rs @@ -0,0 +1,36 @@ +//! Module containing resources for reading data. +//! The reason for this module to exist is "missing" - but mandatory - +//! functionality in the [`std::io::Read`] trait. + +use std::io::Error; + +/// Trait for extending [`std::io::Read`] to add "missing" functionality +pub trait Read { + /// Reads line to a [`String`], ensuring it is never empty. + /// Spins until a `\n` has been found, so that - even if the buffer is empty + /// at some point - the result is always a non-empty line. + /// + /// # Returns + /// + /// * `Ok(String)` with a non-empty string, ending with `\n` + /// * `Err(std::io::Error)` if reading fails + /// + /// # Examples + /// + /// ```ignore + /// use telnet_server::telnet::{Session, State, StateConfig}; + /// use crate::telnet_server::read::Read; + /// + /// let mut session = Session::new(State::new(&StateConfig::default()), tcp_stream)?; + /// + /// // set up session to receive data... + /// + /// let incoming = session.read_line_waiting()?; + /// + /// assert!(incoming.len() > 0); + /// assert!(incoming.ends_with('\n')); + /// + /// Ok(()) + /// ``` + fn read_line_waiting(&mut self) -> Result; +} diff --git a/src/telnet/mod.rs b/src/telnet/mod.rs new file mode 100644 index 0000000..6a6099a --- /dev/null +++ b/src/telnet/mod.rs @@ -0,0 +1,14 @@ +//! TELNET module +//! +//! This module contains state and session handling for TCP connections to a +//! TELNET service. +//! It has two sub modules: +//! * [`Session`] handles the TCP connection +//! * [`State`] handles the internal (TELNET) state of an existing connection +//! * [`StateConfig`] can be used to configure the handling of the [`State`] +//! in specific cases. +pub mod session; +pub mod state; + +pub use session::Session; +pub use state::{State, StateConfig}; diff --git a/src/telnet/session.rs b/src/telnet/session.rs new file mode 100644 index 0000000..d95a7e4 --- /dev/null +++ b/src/telnet/session.rs @@ -0,0 +1,150 @@ +use super::State; +use crate::read; +use std::{ + io::{self, ErrorKind, Read, Result, Write}, + net::TcpStream, + sync::{Arc, Mutex}, +}; + +/// Handles the TCP connection for a TELNET service, allowing reading and +/// writing access while also handling the internal TELNET state. +/// +/// Implements [`std::io::Read`] and [`std::io::Write`] to receive and send +/// messages from/to the connection. +#[derive(Clone)] +pub struct Session { + /// Reference to a TELNET connection [`State`] + state: Arc>, + /// Refence to the TCP connection + tcp_stream: Arc>, +} + +impl Session { + /// Creates new [`Session`] based on given [`TcpStream`] and a fresh + /// [`State`]. + /// Also ensures that the TCP stream is non-blocking as otherwise the + /// session becomes unusable. + /// + /// # Arguments + /// + /// * `state` - A fresh [`State`] + /// * `tcp_stream` - [`TcpStream`] for a TELNET based session. Notice that + /// there's no instant check if this is for TELNET or anthother protocol. + /// + /// # Returns + /// + /// * `Ok(Self)` on success + /// * `Err(std::io::Error)` if `tcp_stream` cannot be set to non-blocking + pub fn new(state: State, tcp_stream: TcpStream) -> Result { + tcp_stream.set_nonblocking(true)?; + + Ok(Self { + state: Arc::new(Mutex::new(state)), + tcp_stream: Arc::new(Mutex::new(tcp_stream)), + }) + } + + /// Listens to and handles incoming TCP data. + /// Should be called in a background thread as it blocks. As the internal + /// TCP stream is set to non-blocking, reading and writing on a cloned + /// [`Session`] is still possible. + /// + /// # Returns + /// + /// Only returns an `Err(std::io::Error)` on TCP errors as it runs + /// indefinitely. + /// + /// # Examples + /// + /// ```ignore + /// use std::thread; + /// use telnet_server::telnet::{Session, State, StateConfig}; + /// + /// let mut session = Session::new(State::new(&StateConfig::default()), tcp_stream)?; + /// + /// let session_listen = session.clone(); + /// let handle = thread::spawn(move || session_listen.listen()); + /// + /// // Send and receive messages here... + /// + /// handle.join().expect("Should await thread"); + /// + /// Ok(()) + /// ``` + pub fn listen(self) -> Result<()> { + let mut buf: [u8; 255] = [0; 255]; + + loop { + let mut tcp_stream = match self.tcp_stream.try_lock() { + Ok(t) => t, + Err(_) => continue, + }; + + let tcp_data = match tcp_stream.read(&mut buf) { + Ok(read_bytes) => &buf[..read_bytes], + Err(e) => { + if e.kind() == ErrorKind::WouldBlock { + continue; + } + + return Err(e); + } + }; + + if let Some(telnet_data) = self + .state + .lock() + .expect("Should lock state") + .write(tcp_data)? + { + tcp_stream.write_all(&telnet_data)?; + tcp_stream.flush()?; + } + } + } +} + +impl io::Write for Session { + fn write(&mut self, buf: &[u8]) -> Result { + self.tcp_stream + .lock() + .expect("Should lock stream") + .write(buf) + } + + fn flush(&mut self) -> Result<()> { + self.tcp_stream.lock().expect("Should lock stream").flush() + } +} + +impl io::Read for Session { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.state.lock().expect("Should lock state").read(buf) + } +} + +impl read::Read for Session { + fn read_line_waiting(&mut self) -> Result { + let mut line = String::new(); + let mut buf: [u8; 1] = [0]; + + loop { + match self.read(&mut buf) { + Ok(1) => { + let next = buf[0] as char; + line.push(next); + if next == '\n' { + break; + } + } + Ok(0) => { + continue; + } + Ok(_) => panic!("Out of range"), + Err(e) => return Err(e), + }; + } + + Ok(line) + } +} diff --git a/src/telnet/state.rs b/src/telnet/state.rs new file mode 100644 index 0000000..f0098b0 --- /dev/null +++ b/src/telnet/state.rs @@ -0,0 +1,471 @@ +use crate::iter::contains_sequence; +use std::{ + cmp::min, + io::{Error, Read}, +}; + +const ECHO: u8 = 1; +const ERASE_LINE: u8 = 248; + +const BEL: u8 = 7; + +const CHAR_BACK_SPACE: u8 = 8; +const CHAR_ESCAPE: u8 = 27; +const CHAR_DELETE: u8 = 127; +const CHAR_ERASE: u8 = 247; + +const IAC: u8 = 255; +/// "IAC SB" +const IAC_SUBNEGOTIATION_START: u8 = 250; +/// "IAC SE" +const IAC_SUBNEGOTIATION_END: u8 = 240; +const IAC_WILL: u8 = 251; +const IAC_WONT: u8 = 252; +const IAC_DO: u8 = 253; +const IAC_DONT: u8 = 254; + +/// Sequence for erasing current line in ANSI terminals +const ANSI_SEQUENCE_ERASE_LINE: [u8; 5] = [CHAR_ESCAPE, 91, 50, 75, 13]; + +/// Any known ending character of ANSI escape sequences +const CHARS_ESCAPE_SEQUENCE_END: [char; 20] = [ + 'A', /* CUU */ + 'B', /* CUD */ + 'C', /* CUF */ + 'D', /* CUB */ + 'E', /* CNL */ + 'F', /* CPL */ + 'G', /* CHA */ + 'H', /* CUP */ + 'J', /* ED */ + 'K', /* EL */ + 'S', /* SU */ + 'T', /* SD */ + 'f', /* HVP */ + 'm', /* SGR */ + 'i', /* AUX */ + 'n', /* DSR */ + 's', /* SCP, SCOSC */ + 'u', /* RCP, SCORC */ + 'h', /* DECTCEM */ + 'l', /* DECTCEM */ +]; + +const CHARS_LINE_BREAK: [u8; 2] = [b'\r', b'\n']; + +/// Type for read-only bytes +pub type Bytes = Box<[u8]>; +pub type BytesResult = Result, Error>; + +/// Struct that holds and handles the current state of a TELNET session. +/// Implements [`Read`] to get the handled readable, non-command data and a fake +/// `Write` to accept incoming TCP data. +/// +/// # Notice +/// +/// This struct is independent of any service related input or TCP at all. It is +/// just a bare state machine. +pub struct State { + /// Buffer of "readable" received data that can be obtained by using the + /// [`Read`] trait + output_buffer: Vec, + /// Current overall mode + mode: Mode, + /// Indicates whether every incoming, non-command char should be echoed back + /// to the connection + is_echoing: bool, + /// If true, ANSI escape sequences will be handled like normal non-command + /// input. Otherwise, sequences will be ignored and a BEL is sent back to + /// notice. + handle_ansi_escape_sequences: bool, +} + +/// Configuration to set up a new [`State`] +#[derive(Default)] +pub struct StateConfig { + /// If true, ANSI escape sequences will be handled like normal non-command + /// input. Otherwise, sequences will be ignored and a BEL is sent back to + /// notice. + pub handle_ansi_escape_sequences: bool, +} + +/// Enumeration of overall modes that a TELNET state may have +enum Mode { + /// Incoming, non-command data (e.g. text) + Idle, + /// Incoming command data (e.g. WILL, WONT, DO, DONT) + Command, + /// Incoming command data for WILL command + CommandWill, + /// Incoming command data for WONT command + CommandWont, + /// Incoming command data for DO command + CommandDo, + /// Incoming command data for DONT command + CommandDont, + /// Incoming command data for sub negotiation command + SubNegotiation, + /// Incoming escape sequence. This is not a "real" mode but we need it as + /// you can choose to ignore ANSI escape sequences because it doesn't really + /// make sense to evaluate these. + AnsiEscapeSequence, +} + +impl State { + /// Creates a new [`State`] with given configuration + /// + /// # Arguments + /// + /// * `config` - [`StateConfig`] to set up the behaviour of the resutlting + /// [`State`] + /// + /// # Returns + /// + /// Fresh [`State`] for a TELNET session + /// + /// # Examples + /// + /// ```rust + /// use telnet_server::telnet::{StateConfig, State}; + /// + /// let config = StateConfig::default(); + /// let state = State::new(&config); + /// ``` + pub fn new(config: &StateConfig) -> Self { + State { + output_buffer: vec![], + mode: Mode::Idle, + is_echoing: false, + handle_ansi_escape_sequences: config.handle_ansi_escape_sequences, + } + } + + /// Writes a buffer of incoming TELNET data into the state, returning an + /// immediate response to the other part. Consumes the whole buffer _unless_ + /// there's an error (e.g. invalid data) which immediatelly results in an + /// [`Error`]. + /// + /// # Arguments + /// + /// * `data` - Incoming TELNET data + /// + /// # Returns + /// + /// * `Err(std::io::Error)` if an error occurs. In this case the internal + /// TELNET state possibly mismatches the "real" state. This _should_ lead + /// to the termination of the TELNET session at all. + /// * `Ok(Some([u8]))` if data should be sent back + /// * `Ok(None)` if everythings fine + /// + /// # Examples + /// + /// ```rust + /// use telnet_server::telnet::{StateConfig, State}; + /// + /// let config = StateConfig::default(); + /// let mut state = State::new(&config); + /// + /// let data = [255, 253, 1]; + /// let result = state.write(&data)?; + /// + /// assert!(result.is_some()); + /// let result = result.unwrap(); + /// + /// // write back result to TCP connection... + /// + /// Ok::<(), std::io::Error>(()) + /// ``` + pub fn write(&mut self, buf: &[u8]) -> BytesResult { + let mut response: Vec = vec![]; + + for &next in buf { + let result = match self.mode { + Mode::Idle => self.next_on_idle(next), + Mode::Command => self.next_as_command(next), + Mode::CommandWill => self.next_as_will(next), + Mode::CommandWont => self.next_as_wont(next), + Mode::CommandDo => self.next_as_do(next), + Mode::CommandDont => self.next_as_dont(next), + Mode::SubNegotiation => self.next_as_sub_negotiation(next), + Mode::AnsiEscapeSequence => self.next_as_escape_sequence(next), + }; + + if let Ok(Some(v)) = result { + response.extend_from_slice(&v); + } else if result.is_err() { + return result; + } + } + + if !response.is_empty() { + Ok(Some(response.into_boxed_slice())) + } else { + Ok(None) + } + } + + /// Handles incoming `next` byte when [`State`] is in idle mode + /// + /// # Returns + /// + /// * `Ok(None)` - Everythings okay, no need to write something back + /// * `Ok(Some(Bytes))` - Everythings okay, something has to be written back + /// * `Err` - Data could not be interpreted + fn next_on_idle(&mut self, next: u8) -> BytesResult { + match next { + IAC => self.mode = Mode::Command, + CHAR_DELETE | CHAR_BACK_SPACE | CHAR_ERASE => { + self.output_buffer.pop(); + + if self.is_echoing { + /* Return fake backspace on echo mode */ + return Ok(Some(Box::new([CHAR_BACK_SPACE, b' ', CHAR_BACK_SPACE]))); + } + } + ERASE_LINE => { + Self::erase_current_line(&mut self.output_buffer); + + if self.is_echoing { + return Ok(Some(ANSI_SEQUENCE_ERASE_LINE.into())); + } + + return Ok(None); + } + CHAR_ESCAPE => { + self.mode = Mode::AnsiEscapeSequence; + + if self.is_echoing { + return Ok(Some(Box::new([next]))); + } + + if self.handle_ansi_escape_sequences { + self.output_buffer.push(next); + } + } + _ => { + self.output_buffer.push(next); + + if self.is_echoing { + return Ok(Some(Box::new([next]))); + } + } + } + + Ok(None) + } + + /// Handles incoming `next` byte when [`State`] is in IAC mode + /// + /// # Returns + /// + /// * `Ok(None)` - Everythings okay, no need to write something back + /// * `Ok(Some(Bytes))` - Everythings okay, something has to be written back + /// * `Err` - Data could not be interpreted + fn next_as_command(&mut self, next: u8) -> BytesResult { + match next { + IAC_WILL => self.mode = Mode::CommandWill, + IAC_WONT => self.mode = Mode::CommandWont, + IAC_DO => self.mode = Mode::CommandDo, + IAC_DONT => self.mode = Mode::CommandDont, + IAC_SUBNEGOTIATION_START => self.mode = Mode::SubNegotiation, + _ => { + return Err(Error::new( + std::io::ErrorKind::InvalidInput, + format!("Unknown command '{next}'"), + )) + } + }; + + Ok(None) + } + + /// Handles incoming `next` byte when [`State`] is in IAC WILL mode + /// + /// # Returns + /// + /// * `Ok(None)` - Everythings okay, no need to write something back + /// * `Ok(Some(Bytes))` - Everythings okay, something has to be written back + /// * `Err` - Data could not be interpreted + fn next_as_will(&mut self, _next: u8) -> BytesResult { + /* Ignore message, just go back to idle state */ + self.mode = Mode::Idle; + Ok(None) + } + + /// Handles incoming `next` byte when [`State`] is in IAC WONT mode + /// + /// # Returns + /// + /// * `Ok(None)` - Everythings okay, no need to write something back + /// * `Ok(Some(Bytes))` - Everythings okay, something has to be written back + /// * `Err` - Data could not be interpreted + fn next_as_wont(&mut self, _next: u8) -> BytesResult { + /* Ignore message, just go back to idle state */ + self.mode = Mode::Idle; + Ok(None) + } + + /// Handles incoming `next` byte when [`State`] is in IAC DO mode + /// + /// # Returns + /// + /// * `Ok(None)` - Everythings okay, no need to write something back + /// * `Ok(Some(Bytes))` - Everythings okay, something has to be written back + /// * `Err` - Data could not be interpreted + fn next_as_do(&mut self, next: u8) -> BytesResult { + self.mode = Mode::Idle; + + if next == ECHO { + self.is_echoing = true; + return Ok(Some(Box::new([IAC, IAC_WILL, ECHO]))); + } + + /* Whatever they're asking for, we're not supporting it probably. */ + Ok(Some(Box::new([IAC, IAC_WONT, next]))) + } + + /// Handles incoming `next` byte when [`State`] is in IAC DONT mode + /// + /// # Returns + /// + /// * `Ok(None)` - Everythings okay, no need to write something back + /// * `Ok(Some(Bytes))` - Everythings okay, something has to be written back + /// * `Err` - Data could not be interpreted + fn next_as_dont(&mut self, next: u8) -> BytesResult { + self.mode = Mode::Idle; + + if next == ECHO { + self.is_echoing = false; + } + + /* Whatever they're asking for, we're not supporting it probably. + * So it's fine to say that we won't do it. */ + Ok(Some(Box::new([IAC, IAC_WONT, next]))) + } + + /// Handles incoming `next` byte when [`State`] is in IAC SB mode + /// + /// # Returns + /// + /// * `Ok(None)` - Everythings okay, no need to write something back + /// * `Ok(Some(Bytes))` - Everythings okay, something has to be written back + /// * `Err` - Data could not be interpreted + fn next_as_sub_negotiation(&mut self, next: u8) -> BytesResult { + /* We're NOT handling sub negotiations right now. */ + if next == IAC_SUBNEGOTIATION_END { + self.mode = Mode::Idle; + } + + Ok(None) + } + + /// Handles incoming `next` byte when [`State`] is in idle mode and in an + /// ANSI escape sequence + /// + /// # Returns + /// + /// * `Ok(None)` - Everythings okay, no need to write something back + /// * `Ok(Some(Bytes))` - Everythings okay, something has to be written back + /// * `Err` - Data could not be interpreted + fn next_as_escape_sequence(&mut self, next: u8) -> BytesResult { + if self.handle_ansi_escape_sequences { + self.output_buffer.push(next); + + if CHARS_ESCAPE_SEQUENCE_END.contains(&(next as char)) { + self.mode = Mode::Idle; + } + + if self.is_echoing { + Ok(Some(Box::new([next]))) + } else { + Ok(None) + } + } else { + if !CHARS_ESCAPE_SEQUENCE_END.contains(&(next as char)) { + return Ok(None); + } + + self.mode = Mode::Idle; + + Ok(Some(Box::new([BEL]))) + } + } + + /// Erases the current line from given text buffer. According to + /// [RFC-854](https://www.rfc-editor.org/rfc/rfc854#page-13), the last + /// CR LF should be kept. + /// + /// # Arguments + /// + /// * `buffer` - Text buffer that should be updated. All current line + /// characters will be removed from the [`Vec`]. + /// + /// # Examples + /// + /// ```ignore + /// use telnet_server::telnet::State; + /// + /// let mut buffer = vec![b'a', b'b', b'c', b'\r', b'\n', b'd', b'e', b'f']; + /// State::erase_current_line(&mut buffer); + /// assert_eq!(buffer, [b'a', b'b', b'c', b'\r', b'\n']); + /// + /// State::erase_current_line(&mut buffer); + /// assert_eq!(buffer, [b'a', b'b', b'c', b'\r', b'\n']); + /// + /// let mut buffer = vec![b'a', b'b', b'c', b'd', b'e', b'f']; + /// State::erase_current_line(&mut buffer); + /// assert!(buffer.is_empty()); + /// ``` + fn erase_current_line(buffer: &mut Vec) { + loop { + let buffer_len = buffer.len(); + + /* Remove all chars until \r\n reached */ + if buffer_len < 2 { + buffer.clear(); + break; + } + + let start_index = buffer_len - 2; + if contains_sequence(&buffer[start_index..], &CHARS_LINE_BREAK) { + break; + } + + buffer.pop(); + } + } +} + +impl Read for State { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + let limit = min(buf.len(), self.output_buffer.len()); + + let (left, _) = buf.split_at_mut(limit); + left.copy_from_slice(&self.output_buffer[..limit]); + self.output_buffer.drain(..limit); + + Ok(limit) + } +} + +mod tests { + #[allow(unused_imports)] + use super::*; + + #[test] + fn erase_current_line_should_work() { + let mut buffer = vec![b'a', b'b', b'c', b'\r', b'\n', b'd', b'e', b'f']; + State::erase_current_line(&mut buffer); + /* RFC-854: 'The recipient should delete characters from the data stream + * back to, but not including, the last "CR LF" sequence sent over the + * TELNET connection.' */ + assert_eq!(buffer, [b'a', b'b', b'c', b'\r', b'\n']); + + State::erase_current_line(&mut buffer); + assert_eq!(buffer, [b'a', b'b', b'c', b'\r', b'\n']); + + let mut buffer = vec![b'a', b'b', b'c', b'd', b'e', b'f']; + State::erase_current_line(&mut buffer); + assert!(buffer.is_empty()); + } +} diff --git a/telnet/Dockerfile b/telnet/Dockerfile new file mode 100644 index 0000000..d296bb9 --- /dev/null +++ b/telnet/Dockerfile @@ -0,0 +1,5 @@ +FROM alpine + +RUN apk update && apk add busybox-extras + +ENTRYPOINT ["/usr/bin/telnet"] \ No newline at end of file diff --git a/telnet/run_telnet.sh b/telnet/run_telnet.sh new file mode 100755 index 0000000..0e17f98 --- /dev/null +++ b/telnet/run_telnet.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +if [ -z "$(docker images -q telnet_docker:latest 2> /dev/null)" ]; then + docker build --tag telnet_docker . --file "$(dirname "$0")/Dockerfile" +fi + +docker run --rm -it telnet_docker:latest "$@" \ No newline at end of file