|
@@ -0,0 +1,739 @@
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+$:.unshift "lib" if !$:.include? "lib"
|
|
|
+$:.unshift "vendor" if !$:.include? "vendor"
|
|
|
+
|
|
|
+require 'strscan'
|
|
|
+require 'ostruct'
|
|
|
+require 'network/protocol/filter'
|
|
|
+require 'network/protocol/telnetcodes'
|
|
|
+require 'network/protocol/asciicodes'
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+class Telnet
|
|
|
+ include Telnet::Codes
|
|
|
+
|
|
|
+
|
|
|
+ def initialize(server)
|
|
|
+ @server = server
|
|
|
+ @mode = :normal
|
|
|
+ @state = {}
|
|
|
+ @sc = nil
|
|
|
+ @sneg_opts = [ TTYPE, ZMP ]
|
|
|
+ @ttype = []
|
|
|
+ @init_tries = 0
|
|
|
+ @synch = false
|
|
|
+ log.debug "telnet filter initialized - #{@init_tries}"
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+ def wait_for_input
|
|
|
+ return Fiber.yield
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+ def client_to_server(data)
|
|
|
+
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+ def server_to_client(data)
|
|
|
+
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ def init(args)
|
|
|
+ if @server.service_type == :client
|
|
|
+
|
|
|
+ @wopts.each do |key,val|
|
|
|
+ case key
|
|
|
+ when ECHO, SGA, BINARY, ZMP, EOREC
|
|
|
+ ask_him(key,val)
|
|
|
+ else
|
|
|
+ offer_us(key,val)
|
|
|
+ end
|
|
|
+ end
|
|
|
+ else
|
|
|
+
|
|
|
+ @wopts.each do |key,val|
|
|
|
+ case key
|
|
|
+ when ECHO, SGA, BINARY, ZMP, EOREC
|
|
|
+ offer_us(key,val)
|
|
|
+ else
|
|
|
+ ask_him(key,val)
|
|
|
+ end
|
|
|
+ end
|
|
|
+ end
|
|
|
+ true
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ def filter_in(str)
|
|
|
+
|
|
|
+ return "" if str.nil? || str.empty?
|
|
|
+ buf = ""
|
|
|
+
|
|
|
+ @sc ? @sc.concat(str) : @sc = StringScanner.new(str)
|
|
|
+ while b = @sc.get_byte
|
|
|
+
|
|
|
+
|
|
|
+ if @pstack.urgent_on || b.getbyte(0) == DM
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) Sync mode on")
|
|
|
+ @pstack.urgent_on = false
|
|
|
+ @synch = true
|
|
|
+ break
|
|
|
+ end
|
|
|
+
|
|
|
+ case mode?
|
|
|
+ when :normal
|
|
|
+ case b.getbyte(0)
|
|
|
+ when CR
|
|
|
+ next if @synch
|
|
|
+ set_mode(:cr) if !@pstack.binary_on
|
|
|
+ when LF
|
|
|
+ next if @synch
|
|
|
+ set_mode(:lf) if !@pstack.binary_on
|
|
|
+ buf << LF.chr
|
|
|
+ echo(CR.chr + LF.chr)
|
|
|
+ when IAC
|
|
|
+ set_mode(:cmd)
|
|
|
+ when NUL
|
|
|
+ next if @synch
|
|
|
+ if @pstack.binary_on
|
|
|
+ buf << b
|
|
|
+ echo(b)
|
|
|
+ else
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) unexpected NUL found in stream")
|
|
|
+ end
|
|
|
+ when BS, DEL
|
|
|
+ next if @synch
|
|
|
+
|
|
|
+ buf << b
|
|
|
+ echo(BS.chr)
|
|
|
+ else
|
|
|
+ next if @synch
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ buf << b
|
|
|
+ echo(b)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ end
|
|
|
+ when :cr
|
|
|
+
|
|
|
+ case b.getbyte(0)
|
|
|
+ when LF
|
|
|
+ buf << LF.chr
|
|
|
+ echo(CR.chr + LF.chr)
|
|
|
+ when NUL
|
|
|
+ if @server.service_type == :client
|
|
|
+ buf << CR.chr
|
|
|
+ echo(CR.chr)
|
|
|
+ else
|
|
|
+ buf << LF.chr
|
|
|
+ echo(CR.chr + LF.chr)
|
|
|
+ end
|
|
|
+ else
|
|
|
+ buf << b
|
|
|
+ echo(b)
|
|
|
+ end
|
|
|
+ set_mode(:normal)
|
|
|
+ when :lf
|
|
|
+
|
|
|
+ case b.getbyte(0)
|
|
|
+ when CR
|
|
|
+ else
|
|
|
+ buf << b
|
|
|
+ echo(b)
|
|
|
+ end
|
|
|
+ set_mode(:normal)
|
|
|
+ when :cmd
|
|
|
+ case b.getbyte(0)
|
|
|
+ when IAC
|
|
|
+
|
|
|
+ buf << IAC.chr
|
|
|
+ set_mode(:normal)
|
|
|
+ when AYT
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) AYT sent - Msg returned")
|
|
|
+ @pstack.conn.sock.send("WOE is here.\n",0)
|
|
|
+ set_mode(:normal)
|
|
|
+ when AO
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) AO sent - Synch returned")
|
|
|
+ @pstack.conn.sockio.write_flush
|
|
|
+ @pstack.conn.sock.send(IAC.chr + DM.chr, 0)
|
|
|
+ @pstack.conn.sockio.write_urgent(DM.chr)
|
|
|
+ set_mode(:normal)
|
|
|
+ when IP
|
|
|
+ @pstack.conn.sockio.read_flush
|
|
|
+ @pstack.conn.sockio.write_flush
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) IP sent")
|
|
|
+ set_mode(:normal)
|
|
|
+ when GA, NOP, BRK
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) GA, NOP or BRK sent")
|
|
|
+ set_mode(:normal)
|
|
|
+ when DM
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) Synch mode off")
|
|
|
+ @synch = false
|
|
|
+ set_mode(:normal)
|
|
|
+ when EC
|
|
|
+ next if @synch
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) EC sent")
|
|
|
+ if buf.size > 1
|
|
|
+ buf.slice!(-1)
|
|
|
+ elsif @pstack.conn.inbuffer.size > 0
|
|
|
+ @pstack.conn.inbuffer.slice(-1)
|
|
|
+ end
|
|
|
+ set_mode(:normal)
|
|
|
+ when EL
|
|
|
+ next if @synch
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) EL sent")
|
|
|
+ p = buf.rindex("\n")
|
|
|
+ if p
|
|
|
+ buf.slice!(p+1..-1)
|
|
|
+ else
|
|
|
+ buf = ""
|
|
|
+ p = @pstack.conn.inbuffer.rindex("\n")
|
|
|
+ if p
|
|
|
+ @pstack.conn.inbuffer.slice!(p+1..-1)
|
|
|
+ end
|
|
|
+ end
|
|
|
+ set_mode(:normal)
|
|
|
+ when DO, DONT, WILL, WONT
|
|
|
+ if @sc.eos?
|
|
|
+ @sc.unscan
|
|
|
+ break
|
|
|
+ end
|
|
|
+ opt = @sc.get_byte
|
|
|
+ case b.getbyte(0)
|
|
|
+ when WILL
|
|
|
+ replies_him(opt.getbyte(0),true)
|
|
|
+ when WONT
|
|
|
+ replies_him(opt.getbyte(0),false)
|
|
|
+ when DO
|
|
|
+ requests_us(opt.getbyte(0),true)
|
|
|
+ when DONT
|
|
|
+ requests_us(opt.getbyte(0),false)
|
|
|
+ end
|
|
|
+
|
|
|
+ case opt.getbyte(0)
|
|
|
+ when ECHO
|
|
|
+ @pstack.echo_on = enabled?(ECHO, :us)
|
|
|
+ when BINARY
|
|
|
+ @pstack.binary_on = enabled?(BINARY, :us)
|
|
|
+ when ZMP
|
|
|
+ @pstack.zmp_on = enabled?(ZMP, :us)
|
|
|
+ end
|
|
|
+ set_mode(:normal)
|
|
|
+ when SB
|
|
|
+ @sc.unscan
|
|
|
+ break if @sc.check_until(/#{IAC.chr}#{SE.chr}/).nil?
|
|
|
+ @sc.get_byte
|
|
|
+ opt = @sc.get_byte
|
|
|
+ data = @sc.scan_until(/#{IAC.chr}#{SE.chr}/).chop.chop
|
|
|
+ parse_subneg(opt.getbyte(0),data)
|
|
|
+ set_mode(:normal)
|
|
|
+ else
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) Unknown Telnet command - #{b.getbyte(0)}")
|
|
|
+ set_mode(:normal)
|
|
|
+ end
|
|
|
+ end
|
|
|
+ end
|
|
|
+
|
|
|
+ @sc = nil if @sc.eos?
|
|
|
+ buf
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ def filter_out(str)
|
|
|
+ return '' if str.nil? || str.empty?
|
|
|
+ if !@pstack.binary_on
|
|
|
+ str.gsub!(/\n/, "\r\n")
|
|
|
+ end
|
|
|
+ str
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ def enabled?(opt, who)
|
|
|
+ option(opt)
|
|
|
+ e = @state[opt].send(who)
|
|
|
+ e == :yes ? true : false
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ def desired?(opt)
|
|
|
+ st = @wopts[opt]
|
|
|
+ st = false if st.nil?
|
|
|
+ st
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ def echo(ch)
|
|
|
+ return if @server.service_type == :client
|
|
|
+
|
|
|
+ if @pstack.echo_on
|
|
|
+ if @pstack.hide_on && ch.getbyte(0) != CR
|
|
|
+ @pstack.conn.sock.send('*',0)
|
|
|
+ else
|
|
|
+ @pstack.conn.sock.send(ch,0)
|
|
|
+ end
|
|
|
+ end
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ def init_subneg
|
|
|
+ return if @init_tries > 20
|
|
|
+ @init_tries += 1
|
|
|
+ @wopts.each_key do |opt|
|
|
|
+ next if !@sneg_opts.include?(opt)
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) Subnegotiation attempt for option #{opt}.")
|
|
|
+ case opt
|
|
|
+ when TTYPE
|
|
|
+ who = :him
|
|
|
+ else
|
|
|
+ who = :us
|
|
|
+ end
|
|
|
+ if desired?(opt) == enabled?(opt, who)
|
|
|
+ case opt
|
|
|
+ when TTYPE
|
|
|
+ @pstack.conn.sendmsg(IAC.chr + SB.chr + TTYPE.chr + 1.chr + IAC.chr + SE.chr)
|
|
|
+ when ZMP
|
|
|
+ log.info("(#{@pstack.conn.object_id}) ZMP successfully negotiated." )
|
|
|
+ @pstack.conn.sendmsg("#{IAC.chr}#{SB.chr}#{ZMP.chr}" +
|
|
|
+ "zmp.check#{NUL.chr}color.#{NUL.chr}" +
|
|
|
+ "#{IAC.chr}#{SE.chr}")
|
|
|
+ @pstack.conn.sendmsg("#{IAC.chr}#{SB.chr}#{ZMP.chr}" +
|
|
|
+ "zmp.ident#{NUL.chr}WOE#{NUL.chr}#{Version}#{NUL.chr}A mud based on the TeensyMUD server#{NUL.chr}" +
|
|
|
+ "#{IAC.chr}#{SE.chr}")
|
|
|
+ @pstack.conn.sendmsg("#{IAC.chr}#{SB.chr}#{ZMP.chr}" +
|
|
|
+ "zmp.ping#{NUL.chr}" +
|
|
|
+ "#{IAC.chr}#{SE.chr}")
|
|
|
+ @pstack.conn.sendmsg("#{IAC.chr}#{SB.chr}#{ZMP.chr}" +
|
|
|
+ "zmp.input#{NUL.chr}\n I see you support...\n ZMP protocol\n#{NUL.chr}" +
|
|
|
+ "#{IAC.chr}#{SE.chr}")
|
|
|
+ end
|
|
|
+ @sneg_opts.delete(opt)
|
|
|
+ end
|
|
|
+ end
|
|
|
+
|
|
|
+ if @init_tries > 20
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) Telnet init_subneg option - Timed out after #{@init_tries} tries.")
|
|
|
+ @sneg_opts = []
|
|
|
+ @pstack.conn.set_initdone
|
|
|
+ if !@pstack.terminal or @pstack.terminal.empty?
|
|
|
+ @pstack.terminal = "dumb"
|
|
|
+ end
|
|
|
+ end
|
|
|
+ end
|
|
|
+
|
|
|
+ def send_naws
|
|
|
+ return if !enabled?(NAWS, :us)
|
|
|
+ ts = @pstack.query(:termsize)
|
|
|
+ data = [ts[0]].pack('n') + [ts[1]].pack('n')
|
|
|
+ data.gsub!(/#{IAC}/, IAC.chr + IAC.chr)
|
|
|
+ @pstack.conn.sendmsg(IAC.chr + SB.chr + NAWS.chr + data + IAC.chr + SE.chr)
|
|
|
+ end
|
|
|
+
|
|
|
+private
|
|
|
+
|
|
|
+
|
|
|
+ def getopts(wopts)
|
|
|
+
|
|
|
+ wopts.each do |op|
|
|
|
+ case op
|
|
|
+ when :ttype
|
|
|
+ @wopts[TTYPE] = true
|
|
|
+ when :echo
|
|
|
+ @wopts[ECHO] = true
|
|
|
+ when :sga
|
|
|
+ @wopts[SGA] = true
|
|
|
+ when :naws
|
|
|
+ @wopts[NAWS] = true
|
|
|
+ when :eorec
|
|
|
+ @wopts[EOREC] = true
|
|
|
+ when :binary
|
|
|
+ @wopts[BINARY] = true
|
|
|
+ when :zmp
|
|
|
+ @wopts[ZMP] = true
|
|
|
+ end
|
|
|
+ end
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ def parse_subneg(opt,data)
|
|
|
+ data.gsub!(/#{IAC}#{IAC}/, IAC.chr)
|
|
|
+ case opt
|
|
|
+ when NAWS
|
|
|
+ @pstack.twidth = data[0..1].unpack('n')[0]
|
|
|
+ @pstack.theight = data[2..3].unpack('n')[0]
|
|
|
+ @pstack.conn.publish(:termsize)
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) Terminal width #{@pstack.twidth} / height #{@pstack.theight}")
|
|
|
+ when TTYPE
|
|
|
+ if data.getbyte(0) == 0
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) Terminal type - #{data[1..-1]}")
|
|
|
+ if !@ttype.include?(data[1..-1])
|
|
|
+
|
|
|
+ if data[1..-1].downcase == 'zmud'
|
|
|
+ @ttype << data[1..-1]
|
|
|
+ @pstack.terminal = 'zmud'
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) Terminal choice - #{@pstack.terminal} in list #{@ttype.inspect}")
|
|
|
+ end
|
|
|
+
|
|
|
+ if data[1..-1].downcase == 'vt100'
|
|
|
+ @ttype << data[1..-1]
|
|
|
+ @pstack.terminal = 'vt100'
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) Terminal choice - #{@pstack.terminal} in list #{@ttype.inspect}")
|
|
|
+ end
|
|
|
+ return if @pstack.terminal
|
|
|
+ @ttype << data[1..-1]
|
|
|
+ @pstack.conn.sendmsg(IAC.chr + SB.chr + TTYPE.chr + 1.chr + IAC.chr + SE.chr)
|
|
|
+ else
|
|
|
+ return if @pstack.terminal
|
|
|
+ choose_terminal
|
|
|
+ end
|
|
|
+ elsif data.getbyte(0) == 1
|
|
|
+ return if !@pstack.terminal
|
|
|
+ @pstack.conn.sendmsg(IAC.chr + SB.chr + TTYPE.chr + 0.chr + @pstack.terminal + IAC.chr + SE.chr)
|
|
|
+ end
|
|
|
+ when ZMP
|
|
|
+ args = data.split("\0")
|
|
|
+ cmd = args.shift
|
|
|
+ handle_zmp(cmd,args)
|
|
|
+ end
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ def choose_terminal
|
|
|
+ if @ttype.empty?
|
|
|
+ @pstack.terminal = "dumb"
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+ @pstack.terminal = @ttype.find {|t| t =~ /mushclient/i } if !@pstack.terminal
|
|
|
+ @pstack.terminal = @ttype.find {|t| t =~ /simplemu/i } if !@pstack.terminal
|
|
|
+ @pstack.terminal = @ttype.find {|t| t =~ /(zmud).*/i } if !@pstack.terminal
|
|
|
+ @pstack.terminal = @ttype.find {|t| t =~ /linux/i } if !@pstack.terminal
|
|
|
+ @pstack.terminal = @ttype.find {|t| t =~ /cygwin/i } if !@pstack.terminal
|
|
|
+ @pstack.terminal = @ttype.find {|t| t =~ /(cons25).*/i } if !@pstack.terminal
|
|
|
+ @pstack.terminal = @ttype.find {|t| t =~ /(xterm).*/i } if !@pstack.terminal
|
|
|
+ @pstack.terminal = @ttype.find {|t| t =~ /(vt)[-]?100/i } if !@pstack.terminal
|
|
|
+ @pstack.terminal = @ttype.find {|t| t =~ /(vt)[-]?\d+/i } if !@pstack.terminal
|
|
|
+ @pstack.terminal = @ttype.find {|t| t =~ /(ansi).*/i } if !@pstack.terminal
|
|
|
+
|
|
|
+ if @pstack.terminal && @ttype.last != @pstack.terminal
|
|
|
+ @ttype.each do |t|
|
|
|
+ @pstack.conn.sendmsg(IAC.chr + SB.chr + TTYPE.chr + 1.chr + IAC.chr + SE.chr)
|
|
|
+ break if t == @pstack.terminal
|
|
|
+ end
|
|
|
+ elsif @ttype.last != @pstack.terminal
|
|
|
+ @pstack.terminal = 'dumb'
|
|
|
+ end
|
|
|
+
|
|
|
+ @pstack.terminal.downcase!
|
|
|
+
|
|
|
+
|
|
|
+ case @pstack.terminal
|
|
|
+ when /cygwin/i, /cons25/i, /linux/i, /dec-vt/i
|
|
|
+ @pstack.terminal = 'vt100'
|
|
|
+ when /ansis/i then
|
|
|
+ @pstack.terminal = 'ansi'
|
|
|
+ end
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) Terminal set to - #{@pstack.terminal} from list #{@ttype.inspect}")
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ def mode?
|
|
|
+ return @mode
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ def set_mode(m)
|
|
|
+ @mode = m
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+ def option(opt)
|
|
|
+ return if @state.key?(opt)
|
|
|
+ o = OpenStruct.new
|
|
|
+ o.us = :no
|
|
|
+ o.him = :no
|
|
|
+ o.usq = :empty
|
|
|
+ o.himq = :empty
|
|
|
+ @state[opt] = o
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ def ask_him(opt, enable)
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) Requested Telnet option #{opt.to_s} set to #{enable.to_s}")
|
|
|
+ initiate(opt, enable, :him)
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ def offer_us(opt, enable)
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) Offered Telnet option #{opt.to_s} set to #{enable.to_s}")
|
|
|
+ initiate(opt, enable, :us)
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ def initiate(opt, enable, who)
|
|
|
+ option(opt)
|
|
|
+
|
|
|
+ case who
|
|
|
+ when :him
|
|
|
+ willdo = DO.chr
|
|
|
+ wontdont = DONT.chr
|
|
|
+ whoq = :himq
|
|
|
+ when :us
|
|
|
+ willdo = WILL.chr
|
|
|
+ wontdont = WONT.chr
|
|
|
+ whoq = :usq
|
|
|
+ else
|
|
|
+
|
|
|
+ end
|
|
|
+
|
|
|
+ case @state[opt].send(who)
|
|
|
+ when :no
|
|
|
+ if enable
|
|
|
+ @state[opt].send("#{who}=", :wantyes)
|
|
|
+ @pstack.conn.sendmsg(IAC.chr + willdo + opt.chr)
|
|
|
+ else
|
|
|
+
|
|
|
+ log.error("(#{@pstack.conn.object_id}) Telnet negotiation: option #{opt.to_s} already disabled")
|
|
|
+ end
|
|
|
+ when :yes
|
|
|
+ if enable
|
|
|
+
|
|
|
+ log.error("(#{@pstack.conn.object_id}) Telnet negotiation: option #{opt.to_s} already enabled")
|
|
|
+ else
|
|
|
+ @state[opt].send("#{who}=", :wantno)
|
|
|
+ @pstack.conn.sendmsg(IAC.chr + wontdont + opt.chr)
|
|
|
+ end
|
|
|
+ when :wantno
|
|
|
+ if enable
|
|
|
+ case @state[opt].send(whoq)
|
|
|
+ when :empty
|
|
|
+ @state[opt].send("#{whoq}=", :opposite)
|
|
|
+ when :opposite
|
|
|
+
|
|
|
+ log.error("(#{@pstack.conn.object_id}) Telnet negotiation: option #{opt.to_s} already queued enable request")
|
|
|
+ end
|
|
|
+ else
|
|
|
+ case @state[opt].send(whoq)
|
|
|
+ when :empty
|
|
|
+
|
|
|
+ log.error("(#{@pstack.conn.object_id}) Telnet negotiation: option #{opt.to_s} already negotiating for disable")
|
|
|
+ when :opposite
|
|
|
+ @state[opt].send("#{whoq}=", :empty)
|
|
|
+ end
|
|
|
+ end
|
|
|
+ when :wantyes
|
|
|
+ if enable
|
|
|
+ case @state[opt].send(whoq)
|
|
|
+ when :empty
|
|
|
+
|
|
|
+ log.error("(#{@pstack.conn.object_id}) Telnet negotiation: option #{opt.to_s} already negotiating for enable")
|
|
|
+ when :opposite
|
|
|
+ @state[opt].send("#{whoq}=", :empty)
|
|
|
+ end
|
|
|
+ else
|
|
|
+ case @state[opt].send(whoq)
|
|
|
+ when :empty
|
|
|
+ @state[opt].send("#{whoq}=", :opposite)
|
|
|
+ when :opposite
|
|
|
+
|
|
|
+ log.error("(#{@pstack.conn.object_id}) Telnet negotiation: option #{opt.to_s} already queued for disable request")
|
|
|
+ end
|
|
|
+ end
|
|
|
+ end
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ def replies_him(opt, enable)
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) Client replies to Telnet option #{opt.to_s} set to #{enable.to_s}")
|
|
|
+ response(opt, enable, :him)
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ def requests_us(opt, enable)
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) Client requests Telnet option #{opt.to_s} set to #{enable.to_s}")
|
|
|
+ response(opt, enable, :us)
|
|
|
+ end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ def response(opt, enable, who)
|
|
|
+ option(opt)
|
|
|
+
|
|
|
+ case who
|
|
|
+ when :him
|
|
|
+ willdo = DO.chr
|
|
|
+ wontdont = DONT.chr
|
|
|
+ whoq = :himq
|
|
|
+ when :us
|
|
|
+ willdo = WILL.chr
|
|
|
+ wontdont = WONT.chr
|
|
|
+ whoq = :usq
|
|
|
+ else
|
|
|
+
|
|
|
+ end
|
|
|
+
|
|
|
+ case @state[opt].send(who)
|
|
|
+ when :no
|
|
|
+ if enable
|
|
|
+ if desired?(opt)
|
|
|
+
|
|
|
+ @state[opt].send("#{who}=", :yes)
|
|
|
+ @pstack.conn.sendmsg(IAC.chr + willdo + opt.chr)
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) Telnet negotiation: agreed to enable option #{opt}")
|
|
|
+ else
|
|
|
+
|
|
|
+ @pstack.conn.sendmsg(IAC.chr + wontdont + opt.chr)
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) Telnet negotiation: disagreed to enable option #{opt}")
|
|
|
+ end
|
|
|
+ else
|
|
|
+
|
|
|
+ end
|
|
|
+ when :yes
|
|
|
+ if enable
|
|
|
+
|
|
|
+ else
|
|
|
+ @state[opt].send("#{who}=", :no)
|
|
|
+ @pstack.conn.sendmsg(IAC.chr + wontdont + opt.chr)
|
|
|
+ end
|
|
|
+ when :wantno
|
|
|
+ if enable
|
|
|
+ case @state[opt].send(whoq)
|
|
|
+ when :empty
|
|
|
+
|
|
|
+ @state[opt].send("#{who}=", :no)
|
|
|
+ when :opposite
|
|
|
+
|
|
|
+ @state[opt].send("#{who}=", :yes)
|
|
|
+ @state[opt].send("#{whoq}=", :empty)
|
|
|
+ end
|
|
|
+ log.error("(#{@pstack.conn.object_id}) Telnet negotiation: option #{opt.to_s} DONT/WONT answered by WILL/DO")
|
|
|
+ else
|
|
|
+ case @state[opt].send(whoq)
|
|
|
+ when :empty
|
|
|
+ @state[opt].send("#{who}=", :no)
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) Telnet negotiation: agreed to disable option #{opt}")
|
|
|
+ when :opposite
|
|
|
+ @state[opt].send("#{who}=", :wantyes)
|
|
|
+ @state[opt].send("#{whoq}=", :empty)
|
|
|
+ @pstack.conn.sendmsg(IAC.chr + willdo + opt.chr)
|
|
|
+ end
|
|
|
+ end
|
|
|
+ when :wantyes
|
|
|
+ if enable
|
|
|
+ case @state[opt].send(whoq)
|
|
|
+ when :empty
|
|
|
+ @state[opt].send("#{who}=", :yes)
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) Telnet negotiation: agreed to enable option #{opt}")
|
|
|
+ when :opposite
|
|
|
+ @state[opt].send("#{who}=", :wantno)
|
|
|
+ @state[opt].send("#{whoq}=", :empty)
|
|
|
+ @pstack.conn.sendmsg(IAC.chr + wontdont + opt.chr)
|
|
|
+ end
|
|
|
+ else
|
|
|
+ case @state[opt].send(whoq)
|
|
|
+ when :empty
|
|
|
+ @state[opt].send("#{who}=", :no)
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) Telnet negotiation: agreed to disable option #{opt}")
|
|
|
+ when :opposite
|
|
|
+ @state[opt].send("#{who}=", :no)
|
|
|
+ @state[opt].send("#{whoq}=", :empty)
|
|
|
+ end
|
|
|
+ end
|
|
|
+ end
|
|
|
+ end
|
|
|
+
|
|
|
+ def handle_zmp(cmd,args)
|
|
|
+ log.debug("(#{@pstack.conn.object_id}) ZMP command recieved - '#{cmd}' args: #{args.inspect}" )
|
|
|
+ case cmd
|
|
|
+ when "zmp.ping"
|
|
|
+ @pstack.conn.sendmsg("#{IAC.chr}#{SB.chr}#{ZMP.chr}" +
|
|
|
+ "zmp.time#{NUL.chr}#{Time.now.utc.strftime("%Y-%m-%d %H:%M:%S")}#{NUL.chr}" +
|
|
|
+ "#{IAC.chr}#{SE.chr}")
|
|
|
+ when "zmp.time"
|
|
|
+ when "zmp.ident"
|
|
|
+
|
|
|
+ when "zmp.check"
|
|
|
+ case args[0]
|
|
|
+ when /zmp.*/
|
|
|
+
|
|
|
+ @pstack.conn.sendmsg("#{IAC.chr}#{SB.chr}#{ZMP.chr}" +
|
|
|
+ "zmp.support#{NUL.chr}#{args[0]}{NUL.chr}" +
|
|
|
+ "#{IAC.chr}#{SE.chr}")
|
|
|
+ else
|
|
|
+ @pstack.conn.sendmsg("#{IAC.chr}#{SB.chr}#{ZMP.chr}" +
|
|
|
+ "zmp.no-support#{NUL.chr}#{args[0]}#{NUL.chr}" +
|
|
|
+ "#{IAC.chr}#{SE.chr}")
|
|
|
+ end
|
|
|
+ when "zmp.support"
|
|
|
+ when "zmp.no-support"
|
|
|
+ when "zmp.input"
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ @pstack.conn.publish(args[0])
|
|
|
+ end
|
|
|
+ end
|
|
|
+
|
|
|
+end
|