client.rb 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. require 'tempfile'
  2. require 'fiber'
  3. require 'timeout'
  4. require_relative '../monolog'
  5. require_relative '../telnet'
  6. module Woe
  7. class Client
  8. include Monolog
  9. include Telnet::Codes
  10. attr_reader :io
  11. attr_reader :id
  12. # to allow for read timeouts
  13. attr_reader :timeout_at
  14. def initialize(server, id, io)
  15. @server = server
  16. @id = id
  17. @io = io
  18. @fiber = Fiber.new { serve }
  19. @telnet = Telnet.new(self)
  20. @telnet.set_support(TELNET_TELOPT_NAWS)
  21. @telnet.set_support(TELNET_TELOPT_MSSP)
  22. @telnet.set_support(TELNET_TELOPT_TTYPE)
  23. @telnet.set_support(TELNET_TELOPT_ECHO)
  24. @telnet.set_support(TELNET_TELOPT_COMPRESS2)
  25. @busy = true
  26. # telnet event queue
  27. @telnet_events = []
  28. @timeout_at = nil
  29. end
  30. def alive?
  31. @fiber.alive? && @busy
  32. end
  33. def command(cmd, args)
  34. @fiber.resume(cmd, args)
  35. end
  36. def write(data)
  37. @io.write(data)
  38. end
  39. def on_start
  40. p "Starting client fiber"
  41. return nil
  42. end
  43. def on_write(data)
  44. p "On write:"
  45. self.write("Client #{socket}:")
  46. self.write(args)
  47. end
  48. # Telnet event class
  49. class TelnetEvent
  50. attr_accessor :type
  51. attr_accessor :data
  52. def initialize(type, data)
  53. @type = type
  54. @data = data
  55. end
  56. def to_s
  57. "<TelnetEvent #{@type} #{@data}>"
  58. end
  59. end
  60. def telnet_event(type, *data)
  61. # store in the event queue
  62. @telnet_events << TelnetEvent.new(type, data)
  63. p "Received tenet event #{@telnet_events}."
  64. end
  65. def telnet_send_data(buf)
  66. # @telnet_events << TelnetEvent.new(:command, buf)
  67. p "Sending telnet data."
  68. self.write(buf)
  69. end
  70. def process_telnet_events
  71. end
  72. def on_read
  73. data = @io.readpartial(4096)
  74. p "After read: #{data}"
  75. @io.flush
  76. @telnet.telnet_receive(data)
  77. # now, the data and any telnet events are in @telnet_events
  78. return data
  79. end
  80. # Waits for input from the client.
  81. # any
  82. # This is always wrapped as a TelnetEvent.
  83. # Pure commands have the field type == :command
  84. # consisting of a type and a data key in a hash
  85. # Pass in nloops to time out the loop a loop
  86. # has no definite timing.
  87. def wait_for_input(timeout = nil)
  88. loop do
  89. # Timout based on number of loops.
  90. if timeout
  91. @timeout_at = Time.now + timeout
  92. else
  93. @timeout_at = nil
  94. end
  95. unless @telnet_events.empty?
  96. return @telnet_events.shift
  97. end
  98. cmd, arg = Fiber.yield
  99. data = nil
  100. case cmd
  101. when :start
  102. on_start
  103. when :timeout
  104. @timeout_at = nil
  105. return nil
  106. when :read
  107. data = on_read
  108. # all data ends up in he telnet_events queue
  109. unless @telnet_events.empty?
  110. return @telnet_events.shift
  111. end
  112. when :write
  113. on_write(arg)
  114. else
  115. p "Unknown command #{cmd}"
  116. end
  117. end
  118. end
  119. def autohandle_event(tev)
  120. case tev.type
  121. when :naws
  122. @window_h, @window_w = *tev.data
  123. log_info("Client #{@id} window size #{@window_w}x#{@window_h}")
  124. else
  125. log_info('Telnet event #{tev} ignored')
  126. end
  127. end
  128. def wait_for_command(timeout = nil)
  129. loop do
  130. tevent = wait_for_input(timeout)
  131. return nil if tevent.nil?
  132. if tevent.type == :data
  133. return tevent.data.join('').strip
  134. else
  135. autohandle_event(tevent)
  136. end
  137. end
  138. end
  139. def ask_login
  140. @login = nil
  141. while @login.nil? || @login.empty?
  142. write("Login:")
  143. @login = wait_for_command
  144. end
  145. @login.chomp!
  146. true
  147. end
  148. def ask_password
  149. @password = nil
  150. while @password.nil? || @password.empty?
  151. write("\r\nPassword:")
  152. @password = wait_for_command
  153. end
  154. @password.chomp!
  155. true
  156. end
  157. def handle_command
  158. order = wait_for_command
  159. case order
  160. when "/quit"
  161. write("Byebye!\r\n")
  162. @busy = false
  163. else
  164. @server.broadcast("#@login said #{order}\r\n")
  165. end
  166. end
  167. def setup_naws
  168. # Negotiate NAWS (window size) support
  169. @telnet.telnet_send_negotiate(TELNET_DO, TELNET_TELOPT_NAWS)
  170. tev = wait_for_input(0.5)
  171. return nil unless tev
  172. ask, cmd, opt = *tev.data
  173. return tev unless tev.type == :will
  174. tev2 = wait_for_input(0.5)
  175. return tev2 unless tev2 && tev2.type == :naws
  176. @window_h, @window_w = *tev2.data
  177. log_info("Client #{@id} window size #{@window_w}x#{@window_h}")
  178. return nil
  179. end
  180. def setup_telnet
  181. loop do
  182. tev = wait_for_input(0.5)
  183. if tev
  184. p "setup_telnet", tev
  185. else
  186. p "no telnet setup received..."
  187. break
  188. end
  189. end
  190. setup_naws
  191. #p "mssp ev #{tev}"
  192. # @telnet.telnet_send_negotiate(TELNET_WILL, TELNET_TELOPT_MSSP)
  193. # tev = wait_for_input(0.5)
  194. # p "mssp ev #{tev}"
  195. # @telnet.telnet_ttype_send
  196. end
  197. def serve()
  198. setup_telnet
  199. data = nil
  200. lok = ask_login
  201. return false unless lok
  202. pok = ask_password
  203. return false unless pok
  204. write("\r\nWelcome #{@login} #{@password}!\r\n")
  205. while @busy do
  206. handle_command
  207. end
  208. end
  209. =begin
  210. attr_accessor :id
  211. attr_accessor :server
  212. def initialize(server, id, socket)
  213. @id = id
  214. @server = server
  215. @connected = true
  216. @socket = socket
  217. @telnet = Telnet.new(self)
  218. @busy = true
  219. end
  220. # Get some details about the telnet connection
  221. def setup_telnet
  222. @telnet.telnet_send_negotiate(TELNET_DO, TELNET_TELOPT_TTYPE)
  223. @telnet.telnet_ttype_send
  224. type, *args = wait_for_event
  225. p type, args
  226. end
  227. def post_init()
  228. send_data("Welcome!\n")
  229. log_info("Client #{@id} connected.")
  230. self.send_data("Login:")
  231. end
  232. # Send data to the socket
  233. def send_data(data)
  234. @socket.write(data)
  235. end
  236. # Run the client's main loop
  237. def run
  238. post_init
  239. while @connected
  240. data = @socket.readpartial(4096)
  241. unless data.nil? || data.empty?
  242. receive_data(data)
  243. end
  244. p data
  245. end
  246. end
  247. def save
  248. self.send_data("Saving...")
  249. do_save = proc do
  250. begin
  251. f = Tempfile.new('random')
  252. sleep 3
  253. f.write("I'm saving data.")
  254. ensure
  255. f.close
  256. end
  257. end
  258. on_save = proc do
  259. self.send_data("Saved.")
  260. end
  261. Celluloid.defer(do_save, on_save)
  262. end
  263. def wait_for_event
  264. return Fiber.yield
  265. end
  266. # Basically, this method yields the fiber, and will return
  267. # with the input that will cme later when the fiber is resumed, normally
  268. # when more input becomes available from the client.
  269. # Any telnet commands are dispatched to the related telnet handlers.
  270. def wait_for_input
  271. loop do
  272. type, *args = Fiber.yield
  273. if type == :data
  274. return args.first
  275. else
  276. telnet_dispatch(type, *args)
  277. end
  278. end
  279. end
  280. def try
  281. self.send_data("\nOK, let's try. What do you say?:")
  282. try = wait_for_input
  283. self.send_data("\nOK, nice try #{try}.\n")
  284. end
  285. # Fake synchronous handing of input
  286. def handle_input()
  287. setup_telnet
  288. @login = wait_for_input
  289. self.send_data([TELNET_IAC, TELNET_WILL, TELNET_TELOPT_ECHO].pack('c*'))
  290. self.send_data("\nPassword for #{@login}:")
  291. @password = wait_for_input
  292. self.send_data([TELNET_IAC, TELNET_WONT, TELNET_TELOPT_ECHO].pack('c*'))
  293. self.send_data("\nOK #{@password}, switching to command mode.\n")
  294. while @connected
  295. line = wait_for_input
  296. # If the user says 'quit', disconnect them
  297. if line =~ /^\/quit/
  298. @connected = false
  299. close_connection_after_writing
  300. # Shut down the server if we hear 'shutdown'
  301. elsif line =~ /^\/reload/
  302. @server.reload
  303. elsif line =~ /^\/shutdown/
  304. @connected = false
  305. @server.stop
  306. elsif line =~ /^\/save/
  307. self.save
  308. elsif line =~ /^\/try/
  309. self.try
  310. else
  311. @server.broadcast("Client #{id} says #{line}")
  312. end
  313. end
  314. end
  315. def receive_data(data)
  316. # Ignore any input if already requested disconnection
  317. return unless @connected
  318. @telnet.telnet_receive(data)
  319. end
  320. def unbind
  321. log_info("Client #{@id} has left from #{@ip}:#{@port}")
  322. @server.disconnect(@id)
  323. end
  324. # Called when the telnet module wants to send data.
  325. def telnet_send_data(buffer)
  326. p "Sending telnet data #{buffer}"
  327. self.send_data(buffer)
  328. end
  329. # Dispatches a telnet event to a function named telnet_(event_name)
  330. def telnet_dispatch(type, *args)
  331. meth = "telnet_#{type}".to_sym
  332. self.send(meth, *args)
  333. end
  334. # Telnet event handler, called on incoming events.
  335. def telnet_event(type, *args)
  336. log_info("Telnet event received by client #{id}: #{type}, #{args}")
  337. if @fiber
  338. # restart the fiber if available
  339. @fiber.resume(type, *args)
  340. else
  341. # set up a fiber to handle the events
  342. # Like that, the handle_input can be programmed in a fake-syncronous way
  343. @fiber = Fiber.new do
  344. handle_input
  345. end
  346. # Must resume twice becaus of the way telnet_event_fiber works
  347. @fiber.resume()
  348. @fiber.resume(type, *args)
  349. end
  350. end
  351. # Real handler, called inside a fiber
  352. def telnet_event_fiber()
  353. raise "not implemented"
  354. end
  355. def telnet_environment(fb, vars)
  356. p fb,vars
  357. end
  358. def telnet_environment(fb, vars)
  359. p fb,vars
  360. end
  361. def telnet_mssp(vars)
  362. @mssp_vars = vars
  363. end
  364. def telnet_ttype_is(term)
  365. @term = term
  366. self.send_data("\nYou have a #{@term} type terminal.\n")
  367. p "term #{@term}"
  368. end
  369. def telnet_ttype_send(term)
  370. p "term #{term} sent"
  371. end
  372. def telnet_compress(compress)
  373. p "compress #{compress} set"
  374. end
  375. def telnet_subnegotiate(sb_telopt, buffer)
  376. p "received subnegotiate #{sb_telopt} #{buffer}"
  377. end
  378. def do_main
  379. end
  380. def telnet_data(data)
  381. =begin
  382. # send data over telnet protocol. Should arrive below in telnet_data
  383. if @fiber
  384. @fiber.resume(data)
  385. else
  386. # set up a fiber to handle the input
  387. # Like that, the handle_input can be programmed in a fake-syncronous way
  388. @fiber = Fiber.new do
  389. handle_input()
  390. end
  391. # Must resume twice becaus of the way handle_input works
  392. @fiber.resume()
  393. @fiber.resume(data)
  394. end
  395. end
  396. def telnet_iac(byte)
  397. p "received iac #{byte}"
  398. end
  399. =end
  400. end # class Client
  401. end # module Woe