-
-
Save yamamushi/3887428 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'rubygems' | |
require 'eventmachine' | |
module Telnet | |
# :stopdoc: | |
IAC = 255.chr # "\377" # "\xff" # interpret as command | |
DONT = 254.chr # "\376" # "\xfe" # you are not to use option | |
DO = 253.chr # "\375" # "\xfd" # please, you use option | |
WONT = 252.chr # "\374" # "\xfc" # I won't use option | |
WILL = 251.chr # "\373" # "\xfb" # I will use option | |
SB = 250.chr # "\372" # "\xfa" # interpret as subnegotiation | |
GA = 249.chr # "\371" # "\xf9" # you may reverse the line | |
EL = 248.chr # "\370" # "\xf8" # erase the current line | |
EC = 247.chr # "\367" # "\xf7" # erase the current character | |
AYT = 246.chr # "\366" # "\xf6" # are you there | |
AO = 245.chr # "\365" # "\xf5" # abort output--but let prog finish | |
IP = 244.chr # "\364" # "\xf4" # interrupt process--permanently | |
BREAK = 243.chr # "\363" # "\xf3" # break | |
DM = 242.chr # "\362" # "\xf2" # data mark--for connect. cleaning | |
NOP = 241.chr # "\361" # "\xf1" # nop | |
SE = 240.chr # "\360" # "\xf0" # end sub negotiation | |
EOR = 239.chr # "\357" # "\xef" # end of record (transparent mode) | |
ABORT = 238.chr # "\356" # "\xee" # Abort process | |
SUSP = 237.chr # "\355" # "\xed" # Suspend process | |
EOF = 236.chr # "\354" # "\xec" # End of file | |
SYNCH = 242.chr # "\362" # "\xf2" # for telfunc calls | |
OPT_BINARY = 0.chr # "\000" # "\x00" # Binary Transmission | |
OPT_ECHO = 1.chr # "\001" # "\x01" # Echo | |
OPT_RCP = 2.chr # "\002" # "\x02" # Reconnection | |
OPT_SGA = 3.chr # "\003" # "\x03" # Suppress Go Ahead | |
OPT_NAMS = 4.chr # "\004" # "\x04" # Approx Message Size Negotiation | |
OPT_STATUS = 5.chr # "\005" # "\x05" # Status | |
OPT_TM = 6.chr # "\006" # "\x06" # Timing Mark | |
OPT_RCTE = 7.chr # "\a" # "\x07" # Remote Controlled Trans and Echo | |
OPT_NAOL = 8.chr # "\010" # "\x08" # Output Line Width | |
OPT_NAOP = 9.chr # "\t" # "\x09" # Output Page Size | |
OPT_NAOCRD = 10.chr # "\n" # "\x0a" # Output Carriage-Return Disposition | |
OPT_NAOHTS = 11.chr # "\v" # "\x0b" # Output Horizontal Tab Stops | |
OPT_NAOHTD = 12.chr # "\f" # "\x0c" # Output Horizontal Tab Disposition | |
OPT_NAOFFD = 13.chr # "\r" # "\x0d" # Output Formfeed Disposition | |
OPT_NAOVTS = 14.chr # "\016" # "\x0e" # Output Vertical Tabstops | |
OPT_NAOVTD = 15.chr # "\017" # "\x0f" # Output Vertical Tab Disposition | |
OPT_NAOLFD = 16.chr # "\020" # "\x10" # Output Linefeed Disposition | |
OPT_XASCII = 17.chr # "\021" # "\x11" # Extended ASCII | |
OPT_LOGOUT = 18.chr # "\022" # "\x12" # Logout | |
OPT_BM = 19.chr # "\023" # "\x13" # Byte Macro | |
OPT_DET = 20.chr # "\024" # "\x14" # Data Entry Terminal | |
OPT_SUPDUP = 21.chr # "\025" # "\x15" # SUPDUP | |
OPT_SUPDUPOUTPUT = 22.chr # "\026" # "\x16" # SUPDUP Output | |
OPT_SNDLOC = 23.chr # "\027" # "\x17" # Send Location | |
OPT_TTYPE = 24.chr # "\030" # "\x18" # Terminal Type | |
OPT_EOR = 25.chr # "\031" # "\x19" # End of Record | |
OPT_TUID = 26.chr # "\032" # "\x1a" # TACACS User Identification | |
OPT_OUTMRK = 27.chr # "\e" # "\x1b" # Output Marking | |
OPT_TTYLOC = 28.chr # "\034" # "\x1c" # Terminal Location Number | |
OPT_3270REGIME = 29.chr # "\035" # "\x1d" # Telnet 3270 Regime | |
OPT_X3PAD = 30.chr # "\036" # "\x1e" # X.3 PAD | |
OPT_NAWS = 31.chr # "\037" # "\x1f" # Negotiate About Window Size | |
OPT_TSPEED = 32.chr # " " # "\x20" # Terminal Speed | |
OPT_LFLOW = 33.chr # "!" # "\x21" # Remote Flow Control | |
OPT_LINEMODE = 34.chr # "\"" # "\x22" # Linemode | |
OPT_XDISPLOC = 35.chr # "#" # "\x23" # X Display Location | |
OPT_OLD_ENVIRON = 36.chr # "$" # "\x24" # Environment Option | |
OPT_AUTHENTICATION = 37.chr # "%" # "\x25" # Authentication Option | |
OPT_ENCRYPT = 38.chr # "&" # "\x26" # Encryption Option | |
OPT_NEW_ENVIRON = 39.chr # "'" # "\x27" # New Environment Option | |
OPT_EXOPL = 255.chr # "\377" # "\xff" # Extended-Options-List | |
NULL = "\000" | |
CR = "\015" | |
LF = "\012" | |
EOL = CR + LF | |
def preprocess(string) | |
# combine CR+NULL into CR | |
string = string.gsub(/#{CR}#{NULL}/no, CR) | |
# combine EOL into "\n" | |
string = string.gsub(/#{EOL}/no, "\n") | |
# remove NULL | |
string = string.gsub(/#{NULL}/no, '') | |
string.gsub(/#{IAC}( | |
[#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]| | |
[#{DO}#{DONT}#{WILL}#{WONT}] | |
[#{OPT_BINARY}-#{OPT_NEW_ENVIRON}#{OPT_EXOPL}]| | |
#{SB}[^#{IAC}]*#{IAC}#{SE} | |
)/xno) do | |
if IAC == $1 # handle escaped IAC characters | |
IAC | |
elsif AYT == $1 # respond to "IAC AYT" (are you there) | |
send_data("nobody here but us pigeons" + EOL) | |
'' | |
elsif DO[0] == $1[0] # respond to "IAC DO x" | |
if OPT_BINARY[0] == $1[1] | |
@telnet_option["BINARY"] = true | |
send_data(IAC + WILL + OPT_BINARY) | |
else | |
send_data(IAC + WONT + $1[1..1]) | |
end | |
'' | |
elsif DONT[0] == $1[0] # respond to "IAC DON'T x" with "IAC WON'T x" | |
send_data(IAC + WONT + $1[1..1]) | |
'' | |
elsif WILL[0] == $1[0] # respond to "IAC WILL x" | |
if OPT_BINARY[0] == $1[1] | |
send_data(IAC + DO + OPT_BINARY) | |
elsif OPT_ECHO[0] == $1[1] | |
send_data(IAC + DO + OPT_ECHO) | |
elsif OPT_SGA[0] == $1[1] | |
@sga = true | |
send_data(IAC + DO + OPT_SGA) | |
else | |
send_data(IAC + DONT + $1[1..1]) | |
end | |
'' | |
elsif WONT[0] == $1[0] # respond to "IAC WON'T x" | |
if OPT_ECHO[0] == $1[1] | |
send_data(IAC + DONT + OPT_ECHO) | |
elsif OPT_SGA[0] == $1[1] | |
@sga = false | |
send_data(IAC + DONT + OPT_SGA) | |
else | |
send_data(IAC + DONT + $1[1..1]) | |
end | |
'' | |
else | |
'' | |
end | |
end | |
end | |
end | |
module DOSCODE | |
def dos2utf8(s) | |
s.encode(Encoding::UTF_8, Encoding::IBM437, :crlf_newline => true) | |
end | |
def utf82dos(s) | |
s.encode(Encoding::IBM437, Encoding::UTF_8, :crlf_newline => true) | |
end | |
end | |
class BBSTerm < EM::Connection | |
include DOSCODE | |
include Telnet | |
def post_init | |
@initialization = true | |
@read_buffer = "" | |
end | |
def receive_data(data) | |
result = @read_buffer + data | |
@read_buffer.clear | |
if Integer(data.rindex(/#{IAC}#{SE}/no) || 0) < | |
Integer(data.rindex(/#{IAC}#{SB}/no) || 0) | |
result = preprocess(data[0 ... data.rindex(/#{IAC}#{SB}/no)]) | |
@read_buffer += data[data.rindex(/#{IAC}#{SB}/no) .. -1] | |
elsif pt = data.rindex(/#{IAC}[^#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]?\z/no) || | |
data.rindex(/\r\z/no) | |
result = preprocess(data[0 ... pt]) | |
@read_buffer += data[pt .. -1] | |
else | |
result = preprocess(data) | |
rest = '' | |
end | |
result = dos2utf8(result) | |
if @initialization and result =~ /\e\[6n/ | |
@initialization = false | |
send_data "\e[1a\r\n" | |
end | |
$stdout.print result | |
$stdout.flush | |
end | |
end | |
class STDINReader < EM::Connection | |
include DOSCODE | |
def initialize(em) | |
@em = em | |
end | |
def post_init | |
@em.send_data "\xff\xf2" | |
@em.send_data "\xff\xfb\x18" | |
@em.send_data "\xff\xfa\x18\x00ANSI\xff\xf0" | |
end | |
def receive_data(data) | |
if data =~ /\cC/ | |
restore_tty | |
exit | |
end | |
data = utf82dos(data) | |
data.gsub!(/\x7f/) { "\b" } | |
EM.next_tick { @em.send_data(data) } | |
end | |
end | |
$tty = `tty`.chomp | |
$stty = `stty -g`.chomp | |
system("stty raw") | |
def restore_tty | |
system("stty -f #{$tty} '#{$stty}'") | |
end | |
Signal.trap("INT") do | |
restore_tty | |
end | |
at_exit do | |
restore_tty | |
end | |
EM.run do | |
obj = EM.connect(ARGV[0], ARGV[1] || 2002, BBSTerm) | |
EM.open_keyboard(STDINReader, obj) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment