Initializes the server
port
The port to listen on, if the port is < 1024 server must run as roo
host
The host to listen on, use 0.0.0.0 for all addresses
xml
The XML to serve to clients
logger
An instanse of the Ruby Standard Logger class
timeout
How long does client have to complete the whole process before the socket closes and the thread terminates
debug
Set to true to enable DEBUG level logging from startup
# File flashpolicyd.rb, line 195 def initialize(port, host, xml, logger, timeout=10, debug=false) @logger = logger @connections = [] @@connMutex = Mutex.new @@clientsMutex = Mutex.new @@bogusclients = 0 @@totalclients = 0 @timeout = timeout @@starttime = Time.new @xml = xml @port = port @host = host if debug @logger.level = Logger::DEBUG debug("Starting in DEBUG mode") else @logger.level = Logger::INFO end end
Logs a message passed to it and increment the bogus client counter inside a mutex
# File flashpolicyd.rb, line 258 def bogusclient(msg, client) addr = client.addr warn("Client #{addr[2]} #{msg}") @@clientsMutex.synchronize { @@bogusclients += 1 } end
Log a msg at level DEBUG
# File flashpolicyd.rb, line 164 def debug(msg) log(Logger::DEBUG, msg) end
Walks the list of active connections and dump them to the logger at INFO level
# File flashpolicyd.rb, line 228 def dumpconnections if (@connections.size == 0) info("No active connections to dump") else connections = @connections info("Dumping current #{connections.size} connections:") connections.each{ |c| addr = c.addr info("#{c.thread.object_id} started at #{c.timecreated} currently in #{c.thread.status} status serving #{addr[2]} [#{addr[3]}]") } end end
Dump the current thread list
# File flashpolicyd.rb, line 244 def dumpthreads Thread.list.each {|t| info("Thread: #{t.id} status #{t.status}") } end
Log a msg at level ERROR
# File flashpolicyd.rb, line 174 def error(msg) log(Logger::ERROR, msg) end
Log a msg at level FATAL
# File flashpolicyd.rb, line 169 def fatal(msg) log(Logger::FATAL, msg) end
Log a msg at level INFO
# File flashpolicyd.rb, line 154 def info(msg) log(Logger::INFO, msg) end
Generic logging method that takes a severity constant from the Logger class such as Logger::DEBUG
# File flashpolicyd.rb, line 149 def log(severity, msg) @logger.add(severity) { "#{Thread.current.object_id}: #{msg}" } end
Prints some basic stats about the server so far, bogus client are ones that timeout or otherwise cause problems
# File flashpolicyd.rb, line 251 def printstats u = sec2dhms(Time.new - @@starttime) info("Had #{@@totalclients} clients and #{@@bogusclients} bogus clients. Uptime #{u[0]} days #{u[1]} hours #{u[2]} min. #{@connections.size} connection(s) in use now.") end
The main logic of client handling, waits for @timeout seconds to receive a null terminated request containing “policy-file-request” and sends back the data, else marks the client as bogus and close the connection.
Any exception caught during this should mark a client as bogus
# File flashpolicyd.rb, line 273 def serve(connection) client = connection.client # Flash clients send a null terminate request $/ = "\0000" # run this in a timeout block, clients will have --timeout seconds to complete the transaction or go away begin timeout(@timeout.to_i) do loop do request = client.gets if request =~ /policy-file-request/ client.puts(@xml) debug("Sent xml data to client") break end end end rescue Timeout::Error bogusclient("connection timed out after #{@timeout} seconds", connection) rescue Errno::ENOTCONN => e warn("Unexpected disconnection while handling request") rescue Errno::ECONNRESET => e warn("Connection reset by peer") rescue Exception => e bogusclient("Unexpected #{e.class} exception: #{e}", connection) end end
Starts the main loop of the server and handles connections, logic is more or less:
Opens the port for listening
Create a new thread so the connection handling happens seperate from the main loop
Create a loop to accept new sessions from the socket, each new sesison gets a new thread
Increment the totalclient variable for stats handling
Create a OpenStruct structure with detail about the current connection and put it in the @connections array
Pass the connection to the serve method for handling
Once handling completes, remove the connection from the active list and close the socket
# File flashpolicyd.rb, line 314 def start begin # Disable reverse lookups, makes it all slow down BasicSocket::do_not_reverse_lookup=true server = TCPServer.new(@host, @port) rescue Exception => e fatal("Can't open server: #{e.class} #{e}") exit end begin @serverThread = Thread.new { while (session = server.accept) Thread.new(session) do |client| begin debug("Handling new connection from #{client.peeraddr[2]}, #{Thread.list.size} total threads ") @@clientsMutex.synchronize { @@totalclients += 1 } connection = OpenStruct.new connection.client = client connection.timecreated = Time.new connection.thread = Thread.current connection.addr = client.peeraddr @@connMutex.synchronize { @connections << connection debug("Pushed connection thread to @connections, now #{@connections.size} connections") } debug("Calling serve on connection") serve(connection) client.close @@connMutex.synchronize { @connections.delete(connection) debug("Removed connection from @connections, now #{@connections.size} connections") } rescue Errno::ENOTCONN => e warn("Unexpected disconnection while handling request") rescue Errno::ECONNRESET => e warn("Connection reset by peer") rescue Exception => e error("Unexpected #{e.class} exception while handling client connection: #{e}") error("Unexpected #{e.class} exception while handling client connection: #{e.backtrace.join("\n")}") client.close end # block around main logic end # while end # around Thread.new for client connections } # @serverThread rescue Exception => e fatal("Got #{e.class} exception in main listening thread: #{e}") end end
If the logger instanse is in DEBUG mode, put it into INFO and vica versa
# File flashpolicyd.rb, line 217 def toggledebug if (@logger.debug?) @logger.level = Logger::INFO info("Set logging level to INFO") else @logger.level = Logger::DEBUG info("Set logging level to DEBUG") end end
Log a msg at level WARN
# File flashpolicyd.rb, line 159 def warn(msg) log(Logger::WARN, msg) end