Class PolicyServer
In: flashpolicyd.rb
Parent: Object

Methods

bogusclient   debug   dumpconnections   dumpthreads   error   fatal   info   log   new   printstats   serve   start   toggledebug   warn  

Public Class methods

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

[Source]

     # File flashpolicyd.rb, line 172
172:   def initialize(port, host, xml, logger, timeout=10, debug=false)
173:     @logger = logger
174:     @connections = []
175:     @@connMutex = Mutex.new
176:     @@clientsMutex = Mutex.new
177:     @@bogusclients = 0
178:     @@totalclients = 0
179:     @timeout = timeout
180:     @@starttime = Time.new
181:     @xml = xml
182:     @port = port
183:     @host = host
184: 
185:     if debug
186:       @logger.level = Logger::DEBUG
187:       debug("Starting in DEBUG mode")
188:     else
189:       @logger.level = Logger::INFO
190:     end
191:   end

Public Instance methods

Logs a message passed to it and increment the bogus client counter inside a mutex

[Source]

     # File flashpolicyd.rb, line 235
235:   def bogusclient(msg, client)
236:     addr = client.addr
237:     
238:     warn("Client #{addr[2]} #{msg}")
239: 
240:     @@clientsMutex.synchronize {
241:       @@bogusclients += 1
242:     }
243:   end

Log a msg at level DEBUG

[Source]

     # File flashpolicyd.rb, line 141
141:   def debug(msg)
142:     log(Logger::DEBUG, msg)
143:   end

Walks the list of active connections and dump them to the logger at INFO level

[Source]

     # File flashpolicyd.rb, line 205
205:   def dumpconnections
206:     if (@connections.size == 0)
207:       info("No active connections to dump")
208:     else 
209:       connections = @connections
210:       
211:       info("Dumping current #{connections.size} connections:")
212:     
213:       connections.each{ |c|
214:         addr = c.addr
215:         info("#{c.thread.object_id} started at #{c.timecreated} currently in #{c.thread.status} status serving #{addr[2]} [#{addr[3]}]")
216:       }
217:     end
218:   end

Dump the current thread list

[Source]

     # File flashpolicyd.rb, line 221
221:   def dumpthreads 
222:     Thread.list.each {|t|
223:       info("Thread: #{t.id} status #{t.status}")
224:     }
225:   end

Log a msg at level ERROR

[Source]

     # File flashpolicyd.rb, line 151
151:   def error(msg)
152:     log(Logger::ERROR, msg)
153:   end

Log a msg at level FATAL

[Source]

     # File flashpolicyd.rb, line 146
146:   def fatal(msg)
147:     log(Logger::FATAL, msg)
148:   end

Log a msg at level INFO

[Source]

     # File flashpolicyd.rb, line 131
131:   def info(msg)
132:     log(Logger::INFO, msg)
133:   end

Generic logging method that takes a severity constant from the Logger class such as Logger::DEBUG

[Source]

     # File flashpolicyd.rb, line 126
126:   def log(severity, msg)
127:     @logger.add(severity) { "#{Thread.current.object_id}: #{msg}" }
128:   end

Prints some basic stats about the server so far, bogus client are ones that timeout or otherwise cause problems

[Source]

     # File flashpolicyd.rb, line 228
228:   def printstats
229:     u = sec2dhms(Time.new - @@starttime)
230:     
231:     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.")
232:   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

[Source]

     # File flashpolicyd.rb, line 250
250:   def serve(connection)
251:     client = connection.client
252:         
253:     # Flash clients send a null terminate request
254:     $/ = "\000"
255: 
256:     # run this in a timeout block, clients will have --timeout seconds to complete the transaction or go away
257:     begin
258:       timeout(@timeout.to_i) do
259:         loop do
260:           request = client.gets
261: 
262:           if request =~ /policy-file-request/
263:             client.puts(@xml)
264:             
265:             debug("Sent xml data to client")
266:             break
267:           end
268:         end
269:       end
270:     rescue Timeout::Error
271:       bogusclient("connection timed out after #{@timeout} seconds", connection)
272:     rescue Errno::ENOTCONN => e
273:       warn("Unexpected disconnection while handling request")
274:     rescue Errno::ECONNRESET => e
275:       warn("Connection reset by peer")
276:     rescue Exception => e
277:       bogusclient("Unexpected #{e.class} exception: #{e}", connection)
278:     end
279:   end

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

[Source]

     # File flashpolicyd.rb, line 291
291:   def start
292:     begin
293:       # Disable reverse lookups, makes it all slow down
294:       BasicSocket::do_not_reverse_lookup=true
295:       server = TCPServer.new(@host, @port)
296:     rescue Exception => e
297:       fatal("Can't open server: #{e.class} #{e}")
298:       exit
299:     end
300:     
301:     begin
302:       @serverThread = Thread.new {
303:         while (session = server.accept)
304:           Thread.new(session) do |client| 
305:             begin 
306:               debug("Handling new connection from #{client.peeraddr[2]}, #{Thread.list.size} total threads ")
307: 
308:               @@clientsMutex.synchronize {
309:                 @@totalclients += 1
310:               }
311: 
312:               connection = OpenStruct.new
313:               connection.client = client
314:               connection.timecreated = Time.new
315:               connection.thread = Thread.current
316:               connection.addr = client.peeraddr
317:           
318:               @@connMutex.synchronize {
319:                 @connections << connection
320:                 debug("Pushed connection thread to @connections, now #{@connections.size} connections")
321:               }
322:               
323:               debug("Calling serve on connection")
324:               serve(connection)
325:           
326:               client.close
327:           
328:               @@connMutex.synchronize {
329:                 @connections.delete(connection)
330:                 debug("Removed connection from @connections, now #{@connections.size} connections")
331:               }
332:           
333:             rescue Errno::ENOTCONN => e
334:               warn("Unexpected disconnection while handling request")
335:             rescue Errno::ECONNRESET => e
336:               warn("Connection reset by peer")
337:             rescue Exception => e
338:               error("Unexpected #{e.class} exception while handling client connection: #{e}")
339:               error("Unexpected #{e.class} exception while handling client connection: #{e.backtrace.join("\n")}")
340:               client.close
341:             end # block around main logic 
342:           end # while
343:         end # around Thread.new for client connections
344:       } # @serverThread
345:     rescue Exception => e
346:       fatal("Got #{e.class} exception in main listening thread: #{e}")
347:     end
348:   end

If the logger instanse is in DEBUG mode, put it into INFO and vica versa

[Source]

     # File flashpolicyd.rb, line 194
194:   def toggledebug
195:     if (@logger.debug?)
196:       @logger.level = Logger::INFO
197:       info("Set logging level to INFO")
198:     else
199:       @logger.level = Logger::DEBUG
200:       info("Set logging level to DEBUG")
201:     end
202:   end

Log a msg at level WARN

[Source]

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

[Validate]