From 8675580f1c47f7a4d351c308124f2df50faf1fb4 Mon Sep 17 00:00:00 2001 From: Jesse Sanford Date: Mon, 3 Nov 2014 16:55:14 -0500 Subject: [PATCH 1/3] added ability to set target host and port using path --- other/websockify.rb | 106 ++++++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 47 deletions(-) diff --git a/other/websockify.rb b/other/websockify.rb index e54d0dd..a07e1a3 100755 --- a/other/websockify.rb +++ b/other/websockify.rb @@ -15,6 +15,7 @@ require 'optparse' # encoded/decoded to allow binary data to be sent/received to/from # the target. class WebSocketProxy < WebSocketServer + attr_accessor :target_host, :target_port @@Traffic_legend = " Traffic Legend: @@ -33,13 +34,24 @@ Traffic Legend: vmsg "in WebSocketProxy.initialize" super(opts) - + @target_host = opts["target_host"] @target_port = opts["target_port"] end - # Echo back whatever is received + # Echo back whatever is received def new_websocket_client(client) + path = Thread.current[:path] + if path =~ /:/ + msg "Path has a ip and port specified. Using those for target" + possible_target_host = path[/(\d+\.\d+\.\d+\.\d+):(\d+)/,1] + possible_target_port = path[/(\d+\.\d+\.\d+\.\d+):(\d+)/,2].to_i + end + + if possible_target_host and possible_target_port + @target_host = possible_target_host + @target_port = possible_target_port + end msg "connecting to: %s:%s" % [@target_host, @target_port] tsock = TCPSocket.open(@target_host, @target_port) @@ -122,50 +134,50 @@ Traffic Legend: end # Parse parameters -opts = {} -parser = OptionParser.new do |o| - o.on('--verbose', '-v') { |b| opts['verbose'] = b } - o.parse! -end - -if ARGV.length < 2 - puts "Too few arguments" - exit 2 -end - -# Parse host:port and convert ports to numbers -if ARGV[0].count(":") > 0 - opts['listen_host'], _, opts['listen_port'] = ARGV[0].rpartition(':') -else - opts['listen_host'], opts['listen_port'] = nil, ARGV[0] -end - -begin - opts['listen_port'] = opts['listen_port'].to_i -rescue - puts "Error parsing listen port" - exit 2 -end - -if ARGV[1].count(":") > 0 - opts['target_host'], _, opts['target_port'] = ARGV[1].rpartition(':') -else - puts "Error parsing target" - exit 2 -end - -begin - opts['target_port'] = opts['target_port'].to_i -rescue - puts "Error parsing target port" - exit 2 -end - -puts "Starting server on #{opts['listen_host']}:#{opts['listen_port']}" -server = WebSocketProxy.new(opts) -server.start(100) -server.join - -puts "Server has been terminated" +#opts = {} +#parser = OptionParser.new do |o| +# o.on('--verbose', '-v') { |b| opts['verbose'] = b } +# o.parse! +#end +# +#if ARGV.length < 2 +# puts "Too few arguments" +# exit 2 +#end +# +## Parse host:port and convert ports to numbers +#if ARGV[0].count(":") > 0 +# opts['listen_host'], _, opts['listen_port'] = ARGV[0].rpartition(':') +#else +# opts['listen_host'], opts['listen_port'] = nil, ARGV[0] +#end +# +#begin +# opts['listen_port'] = opts['listen_port'].to_i +#rescue +# puts "Error parsing listen port" +# exit 2 +#end +# +#if ARGV[1].count(":") > 0 +# opts['target_host'], _, opts['target_port'] = ARGV[1].rpartition(':') +#else +# puts "Error parsing target" +# exit 2 +#end +# +#begin +# opts['target_port'] = opts['target_port'].to_i +#rescue +# puts "Error parsing target port" +# exit 2 +#end +# +#puts "Starting server on #{opts['listen_host']}:#{opts['listen_port']}" +#server = WebSocketProxy.new(opts) +#server.start(100) +#server.join +# +#puts "Server has been terminated" # vim: sw=2 From a818dd312f9f4a13b99885cc072439fcdd9cd60f Mon Sep 17 00:00:00 2001 From: Jesse Sanford Date: Sun, 14 Dec 2014 13:37:18 -0500 Subject: [PATCH 2/3] added option parsing back in --- other/websockify.rb | 90 ++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/other/websockify.rb b/other/websockify.rb index a07e1a3..16de1ee 100755 --- a/other/websockify.rb +++ b/other/websockify.rb @@ -134,50 +134,50 @@ Traffic Legend: end # Parse parameters -#opts = {} -#parser = OptionParser.new do |o| -# o.on('--verbose', '-v') { |b| opts['verbose'] = b } -# o.parse! -#end -# -#if ARGV.length < 2 -# puts "Too few arguments" -# exit 2 -#end -# -## Parse host:port and convert ports to numbers -#if ARGV[0].count(":") > 0 -# opts['listen_host'], _, opts['listen_port'] = ARGV[0].rpartition(':') -#else -# opts['listen_host'], opts['listen_port'] = nil, ARGV[0] -#end -# -#begin -# opts['listen_port'] = opts['listen_port'].to_i -#rescue -# puts "Error parsing listen port" -# exit 2 -#end -# -#if ARGV[1].count(":") > 0 -# opts['target_host'], _, opts['target_port'] = ARGV[1].rpartition(':') -#else -# puts "Error parsing target" -# exit 2 -#end -# -#begin -# opts['target_port'] = opts['target_port'].to_i -#rescue -# puts "Error parsing target port" -# exit 2 -#end -# -#puts "Starting server on #{opts['listen_host']}:#{opts['listen_port']}" -#server = WebSocketProxy.new(opts) -#server.start(100) -#server.join -# -#puts "Server has been terminated" +opts = {} +parser = OptionParser.new do |o| + o.on('--verbose', '-v') { |b| opts['verbose'] = b } + o.parse! +end + +if ARGV.length < 2 + puts "Too few arguments" + exit 2 +end + +# Parse host:port and convert ports to numbers +if ARGV[0].count(":") > 0 + opts['listen_host'], _, opts['listen_port'] = ARGV[0].rpartition(':') +else + opts['listen_host'], opts['listen_port'] = nil, ARGV[0] +end + +begin + opts['listen_port'] = opts['listen_port'].to_i +rescue + puts "Error parsing listen port" + exit 2 +end + +if ARGV[1].count(":") > 0 + opts['target_host'], _, opts['target_port'] = ARGV[1].rpartition(':') +else + puts "Error parsing target" + exit 2 +end + +begin + opts['target_port'] = opts['target_port'].to_i +rescue + puts "Error parsing target port" + exit 2 +end + +puts "Starting server on #{opts['listen_host']}:#{opts['listen_port']}" +server = WebSocketProxy.new(opts) +server.start(100) +server.join + +puts "Server has been terminated" # vim: sw=2 From 1e260b38134e9bf81826c57824c4e9f44cbc6ea8 Mon Sep 17 00:00:00 2001 From: Jesse Sanford Date: Sat, 14 Feb 2015 14:24:20 -0800 Subject: [PATCH 3/3] now with daemonize and additional options --- other/websocket.rb | 11 ++- other/websockify.rb | 187 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 174 insertions(+), 24 deletions(-) diff --git a/other/websocket.rb b/other/websocket.rb index bb287ea..0961875 100644 --- a/other/websocket.rb +++ b/other/websocket.rb @@ -50,6 +50,10 @@ Sec-WebSocket-Accept: %s\r def initialize(opts) + @verbose = opts['verbose'] + @opts = opts + @@client_id = 0 # Track client number total on class + vmsg "in WebSocketServer.initialize" port = opts['listen_port'] host = opts['listen_host'] || GServer::DEFAULT_HOST @@ -65,11 +69,6 @@ Sec-WebSocket-Accept: %s\r @sslContext.verify_mode = OpenSSL::SSL::VERIFY_NONE @sslContext.verify_depth = 0 end - - @@client_id = 0 # Track client number total on class - - @verbose = opts['verbose'] - @opts = opts end def serve(io) @@ -119,7 +118,7 @@ Sec-WebSocket-Accept: %s\r end def msg(m) - printf("% 3d: %s\n", Thread.current[:my_client_id] || 0, m) + log sprintf("% 3d: %s\n", Thread.current[:my_client_id] || 0, m) end def vmsg(m) diff --git a/other/websockify.rb b/other/websockify.rb index 16de1ee..4d137a5 100755 --- a/other/websockify.rb +++ b/other/websockify.rb @@ -9,13 +9,15 @@ $: << "other" $: << "../other" require 'websocket' require 'optparse' +require 'fileutils' # Proxy traffic to and from a WebSockets client to a normal TCP # socket server target. All traffic to/from the client is base64 # encoded/decoded to allow binary data to be sent/received to/from # the target. class WebSocketProxy < WebSocketServer - attr_accessor :target_host, :target_port + attr_accessor 'target_host', 'target_port' + attr_reader :opts, :quit @@Traffic_legend = " Traffic Legend: @@ -32,21 +34,140 @@ Traffic Legend: def initialize(opts) vmsg "in WebSocketProxy.initialize" - + @opts = opts + super(opts) - @target_host = opts["target_host"] - @target_port = opts["target_port"] + @target_host = opts['target_host'] + @target_port = opts['target_port'] + + #replace the logfile option with the expanded path + opts[:logfile] = File.expand_path(logfile) if logfile? + opts[:pidfile] = File.expand_path(pidfile) if pidfile? + end + + def daemonize? + opts[:daemonize] + end + + def logfile + opts[:logfile] + end + + def pidfile + opts[:pidfile] + end + + def logfile? + !logfile.nil? + end + + def pidfile? + !pidfile.nil? + end + + def write_pid + if pidfile? + begin + File.open(pidfile, ::File::CREAT | ::File::EXCL | ::File::WRONLY){|f| f.write("#{Process.pid}") } + at_exit { File.delete(pidfile) if File.exists?(pidfile) } + rescue Errno::EEXIST + check_pid + retry + end + end + end + + def check_pid + if pidfile? + case pid_status(pidfile) + when :running, :not_owned + log "A server is already running. Check #{pidfile}" + exit(1) + when :dead + File.delete(pidfile) + end + end + end + + def pid_status(pidfile) + return :exited unless File.exists?(pidfile) + pid = ::File.read(pidfile).to_i + return :dead if pid == 0 + Process.kill(0, pid) # check process status + :running + rescue Errno::ESRCH + :dead + rescue Errno::EPERM + :not_owned + end + + def redirect_output + FileUtils.mkdir_p(File.dirname(logfile), :mode => 0755) + FileUtils.touch logfile + File.chmod(0644, logfile) + $stderr.reopen(logfile, 'a') + $stdout.reopen($stderr) + $stdout.sync = $stderr.sync = true + end + + def suppress_output + $stderr.reopen('/dev/null', 'a') + $stdout.reopen($stderr) + end + + def trap_signals + trap(:QUIT) do # kill -9 + log "Hard killing websockify server..." + stop + log "Bye!" + end + + trap(:TERM) do # kill -15 + log "Gracefully shutting down websockify server..." + Thread.new { shutdown; exit } + log "Bye!" + end + end + + def daemonize + exit if fork + Process.setsid + exit if fork + Dir.chdir "/" + end + + def run!(max_connections) + check_pid + daemonize if daemonize? + write_pid + trap_signals + + if logfile? + redirect_output + elsif daemonize? + suppress_output + end + + log "Starting Websockify server on #{opts['listen_host']}:#{opts['listen_port']} and proxying to #{target_host}:#{target_port}" + + #tell gserver to start + start(max_connections) + + #join the gserver thread to this one + join end # Echo back whatever is received def new_websocket_client(client) path = Thread.current[:path] + msg "path = #{path}" if path =~ /:/ - msg "Path has a ip and port specified. Using those for target" + msg "path has a ip and port" possible_target_host = path[/(\d+\.\d+\.\d+\.\d+):(\d+)/,1] possible_target_port = path[/(\d+\.\d+\.\d+\.\d+):(\d+)/,2].to_i end + Thread.current[:path] = "/" if possible_target_host and possible_target_port @target_host = possible_target_host @@ -56,7 +177,7 @@ Traffic Legend: msg "connecting to: %s:%s" % [@target_host, @target_port] tsock = TCPSocket.open(@target_host, @target_port) - if @verbose then puts @@Traffic_legend end + if @verbose then log @@Traffic_legend end begin do_proxy(client, tsock) @@ -134,14 +255,48 @@ Traffic Legend: end # Parse parameters -opts = {} -parser = OptionParser.new do |o| - o.on('--verbose', '-v') { |b| opts['verbose'] = b } - o.parse! -end + +opts = {} +version = "1.0.0" +maxcon_help = "maximum number of connections allowed" +daemonize_help = "run daemonized in the background (default: false)" +pidfile_help = "the pid filename" +logfile_help = "the log filename" +include_help = "an additional $LOAD_PATH" +debug_help = "set $DEBUG to true" +warn_help = "enable warnings" +usage = "Usage: server list_port target_host'target_port' [options]" + +op = OptionParser.new +op.banner = "Websockify Server #{version}" +op.separator "" +op.separator "#{usage}" +op.separator "" + +op.separator "Process options:" +op.on("-m", "--maxcon NUMBER", maxcon_help) { |value| opts[:maxcon] = value || 100 } +op.on("-d", "--daemonize", daemonize_help) { opts[:daemonize] = true } +op.on("-p", "--pid PIDFILE", pidfile_help) { |value| opts[:pidfile] = value } +op.on("-l", "--log LOGFILE", logfile_help) { |value| opts[:logfile] = value } +op.separator "" + +op.separator "Ruby options:" +op.on("-I", "--include PATH", include_help) { |value| $LOAD_PATH.unshift(*value.split(":").map{|v| File.expand_path(v)}) } +op.on( "--debug", debug_help) { $DEBUG = true } +op.on( "--warn", warn_help) { $-w = true } +op.separator "" + +op.separator "Common options:" +op.on("-h", "--help") { puts op.to_s; exit } +op.on("-V", "--version") { puts version; exit } +op.on("-v", "--verbose") { |value| opts['verbose'] = true } +op.separator "" + +op.parse!(ARGV) if ARGV.length < 2 - puts "Too few arguments" + puts "Too few arguments." + puts op.to_s exit 2 end @@ -149,7 +304,7 @@ end if ARGV[0].count(":") > 0 opts['listen_host'], _, opts['listen_port'] = ARGV[0].rpartition(':') else - opts['listen_host'], opts['listen_port'] = nil, ARGV[0] + opts['listen_host'], opts['listen_port'] = '0.0.0.0', ARGV[0] end begin @@ -173,11 +328,7 @@ rescue exit 2 end -puts "Starting server on #{opts['listen_host']}:#{opts['listen_port']}" server = WebSocketProxy.new(opts) -server.start(100) -server.join - -puts "Server has been terminated" +server.run!(opts[:maxcon].to_i) # vim: sw=2