# encoding: UTF-8 require_relative "test_helper" require_relative "../lib/meow/telnet" module MeowTelnetTest Telnet = MeowClient::Telnet def self.bytes(*values) values.pack("C*") end def self.collect(parser, chunks) chunks.each_with_object({:data => +"".b, :replies => [], :events => []}) do |chunk, result| parsed = parser.feed(chunk) result[:data] << parsed.data result[:replies].concat(parsed.replies) result[:events].concat(parsed.events) end end MeowTest.test("Telnet preserves data and unescapes IAC") do parser = Telnet::Parser.new result = parser.feed("look".b + bytes(Telnet::IAC, Telnet::IAC) + "!".b) MeowTest.assert_equal("look".b + bytes(Telnet::IAC) + "!".b, result.data) MeowTest.assert_empty(result.replies) MeowTest.assert_empty(result.events) end MeowTest.test("Telnet negotiation survives every input boundary") do packet = bytes(Telnet::IAC, Telnet::WILL, Telnet::ECHO) whole = collect(Telnet::Parser.new, [packet]) split = collect(Telnet::Parser.new, packet.bytes.map { |byte| bytes(byte) }) MeowTest.assert_equal(whole, split) MeowTest.assert_equal([bytes(Telnet::IAC, Telnet::DO, Telnet::ECHO)], split[:replies]) MeowTest.assert_equal([[:remote_echo, true]], split[:events]) end MeowTest.test("Telnet tracks echo transitions without duplicate replies") do parser = Telnet::Parser.new enabled = parser.feed(bytes(Telnet::IAC, Telnet::WILL, Telnet::ECHO)) duplicate = parser.feed(bytes(Telnet::IAC, Telnet::WILL, Telnet::ECHO)) disabled = parser.feed(bytes(Telnet::IAC, Telnet::WONT, Telnet::ECHO)) disabled_duplicate = parser.feed(bytes(Telnet::IAC, Telnet::WONT, Telnet::ECHO)) MeowTest.assert_equal([bytes(Telnet::IAC, Telnet::DO, Telnet::ECHO)], enabled.replies) MeowTest.assert_empty(duplicate.replies) MeowTest.assert_equal([bytes(Telnet::IAC, Telnet::DONT, Telnet::ECHO)], disabled.replies) MeowTest.assert_equal([[:remote_echo, false]], disabled.events) MeowTest.assert_empty(disabled_duplicate.replies) MeowTest.refute(parser.remote_echo) end MeowTest.test("Telnet accepts supported options and rejects unknown options") do parser = Telnet::Parser.new remote = parser.feed(bytes(Telnet::IAC, Telnet::WILL, Telnet::SUPPRESS_GO_AHEAD)) remote_unknown = parser.feed(bytes(Telnet::IAC, Telnet::WILL, 42)) local = parser.feed(bytes(Telnet::IAC, Telnet::DO, Telnet::TERMINAL_TYPE)) local_unknown = parser.feed(bytes(Telnet::IAC, Telnet::DO, 43)) MeowTest.assert_equal([bytes(Telnet::IAC, Telnet::DO, Telnet::SUPPRESS_GO_AHEAD)], remote.replies) MeowTest.assert_equal([bytes(Telnet::IAC, Telnet::DONT, 42)], remote_unknown.replies) MeowTest.assert_equal([bytes(Telnet::IAC, Telnet::WILL, Telnet::TERMINAL_TYPE)], local.replies) MeowTest.assert_equal([bytes(Telnet::IAC, Telnet::WONT, 43)], local_unknown.replies) end MeowTest.test("Telnet sends terminal type only after accepting the option") do request = bytes(Telnet::IAC, Telnet::SB, Telnet::TERMINAL_TYPE, Telnet::TTYPE_SEND, Telnet::IAC, Telnet::SE) parser = Telnet::Parser.new before = parser.feed(request) parser.feed(bytes(Telnet::IAC, Telnet::DO, Telnet::TERMINAL_TYPE)) after = collect(parser, request.bytes.map { |byte| bytes(byte) }) expected = bytes(Telnet::IAC, Telnet::SB, Telnet::TERMINAL_TYPE, Telnet::TTYPE_IS) + "MEOW".b + bytes(Telnet::IAC, Telnet::SE) MeowTest.assert_empty(before.replies) MeowTest.assert_equal([[:subnegotiation, Telnet::TERMINAL_TYPE]], before.events) MeowTest.assert_equal([expected], after[:replies]) MeowTest.assert_equal([[:terminal_type, "MEOW"]], after[:events]) end MeowTest.test("Telnet handles escaped IAC inside subnegotiation") do parser = Telnet::Parser.new packet = bytes(Telnet::IAC, Telnet::SB, 77, 1, Telnet::IAC, Telnet::IAC, 2, Telnet::IAC, Telnet::SE) result = collect(parser, packet.bytes.map { |byte| bytes(byte) }) MeowTest.assert_empty(result[:data]) MeowTest.assert_empty(result[:replies]) MeowTest.assert_equal([[:subnegotiation, 77]], result[:events]) end MeowTest.test("Telnet ignores unknown commands and resumes ordinary data") do parser = Telnet::Parser.new result = parser.feed("a".b + bytes(Telnet::IAC, 241) + "b".b) MeowTest.assert_equal("ab".b, result.data) MeowTest.assert_empty(result.replies) MeowTest.assert_empty(result.events) end end