class PolicyServer

Public Class Methods

new(port, host, xml, logger, timeout=10, debug=false) click to toggle source

Synopsis

Initializes the server

Args

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

Public Instance Methods

bogusclient(msg, client) click to toggle source

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
debug(msg) click to toggle source

Log a msg at level DEBUG

# File flashpolicyd.rb, line 164
def debug(msg)
  log(Logger::DEBUG, msg)
end
dumpconnections() click to toggle source

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
dumpthreads() click to toggle source

Dump the current thread list

# File flashpolicyd.rb, line 244
def dumpthreads
  Thread.list.each {|t|
    info("Thread: #{t.id} status #{t.status}")
  }
end
error(msg) click to toggle source

Log a msg at level ERROR

# File flashpolicyd.rb, line 174
def error(msg)
  log(Logger::ERROR, msg)
end
fatal(msg) click to toggle source

Log a msg at level FATAL

# File flashpolicyd.rb, line 169
def fatal(msg)
  log(Logger::FATAL, msg)
end
info(msg) click to toggle source

Log a msg at level INFO

# File flashpolicyd.rb, line 154
def info(msg)
  log(Logger::INFO, msg)
end
log(severity, msg) click to toggle source

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
printstats() click to toggle source

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
serve(connection) click to toggle source

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
start() click to toggle source

Synopsis

Starts the main loop of the server and handles connections, logic is more or less:

  1. Opens the port for listening

  2. Create a new thread so the connection handling happens seperate from the main loop

  3. Create a loop to accept new sessions from the socket, each new sesison gets a new thread

  4. Increment the totalclient variable for stats handling

  5. Create a OpenStruct structure with detail about the current connection and put it in the @connections array

  6. Pass the connection to the serve method for handling

  7. 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
toggledebug() click to toggle source

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
warn(msg) click to toggle source

Log a msg at level WARN

# File flashpolicyd.rb, line 159
def warn(msg)
  log(Logger::WARN, msg)
end