SlideShare a Scribd company logo
1 of 56
Download to read offline
Camping: Going off
the Rails with Ruby
    Adventures in creative coding
  for people who should know better
The weasel words

This presentation contains code
That code is probably broken
If that bothers you - fix it
That’s called a learning experience
Who are these lunatics?
     Eleanor McHugh
     eleanor@games-with-brains.com

     She does real-time systems


     Romek Szczesniak
     romek@spikyblackcat.co.uk

     He does security
Alright, but what are
  they doing here?
Ruby
Pcap & BitStruct
WEBrick
Camping
but no Rails...
No Rails?

That’s right, we don’t use Rails
But we do use Ruby
And we do write web applications
So how is that possible?
Camping!!!
That’s right, we use Camping
It’s by Why The Lucky Stiff
It’s cool
It’s really cool
It’s so damn cool you’d have to be mad
not to use it!!!
It’s this simple!
%w[rubygems active_record markaby metaid ostruct].each {|lib| require lib}
module Camping;C=self;module Models;end;Models::Base=ActiveRecord::Base
module Helpers;def R c,*args;p=/(.+?)/;args.inject(c.urls.detect{|x|x.
scan(p).size==args.size}.dup){|str,a|str.gsub(p,(a.method(a.class.primary_key
)[]rescue a).to_s)};end;def / p;File.join(@root,p) end;end;module Controllers
module Base;include Helpers;attr_accessor :input,:cookies,:headers,:body,
:status,:root;def method_missing(m,*args,&blk);str=m==:render ? markaview(
*args,&blk):eval(quot;markaby.#{m}(*args,&blk)quot;);str=markaview(:layout){str
}rescue nil;r(200,str.to_s);end;def r(s,b,h={});@status=s;@headers.merge!(h)
@body=b;end;def redirect(c,*args);c=R(c,*args)if c.respond_to?:urls;r(302,'',
'Location'=>self/c);end;def service(r,e,m,a);@status,@headers,@root=200,{},e[
'SCRIPT_NAME'];@cookies=C.cookie_parse(e['HTTP_COOKIE']||e['COOKIE']);cook=
@cookies.marshal_dump.dup;if (quot;POSTquot;==e['REQUEST_METHOD'])and %r|Amultipart
/form-data.*boundary=quot;?([^quot;;,]+)quot;?|n.match(e['CONTENT_TYPE']);return r(500,
quot;No multipart/form-data supported.quot;)else;@input=C.qs_parse(e['REQUEST_METHOD'
]==quot;POSTquot;?r.read(e['CONTENT_LENGTH'].to_i):e['QUERY_STRING']);end;@body=
method(m.downcase).call(*a);@headers[quot;Set-Cookiequot;]=@cookies.marshal_dump.map{
|k,v|quot;#{k}=#{C.escape(v)}; path=/quot;if v != cook[k]}.compact;self;end;def to_s
quot;Status: #{@status}n#{{'Content-Type'=>'text/html'}.merge(@headers).map{|k,v|
v.to_a.map{|v2|quot;#{k}: #{v2}quot;}}.flatten.join(quot;nquot;)}nn#{@body}quot;;end;def 
markaby;Class.new(Markaby::Builder){@root=@root;include Views;def tag!(*g,&b)
[:href,:action].each{|a|(g.last[a]=self./(g.last[a]))rescue 0};super end}.new(
instance_variables.map{|iv|[iv[1..-1].intern,instance_variable_get(iv)]},{})
end;def markaview(m,*args,&blk);markaby.instance_eval{Views.instance_method(m
).bind(self).call(*args, &blk);self}.to_s;end;end;class R;include Base end
class NotFound<R;def get(p);r(404,div{h1(quot;#{C} Problem!quot;)+h2(quot;#{p} not foundquot;)
});end end;class ServerError<R;def get(k,m,e);r(500,markaby.div{h1 quot;#{C} Prob
lem!quot;;h2 quot;#{k}.#{m}quot;;h3 quot;#{e.class} #{e.message}:quot;;ul{e.backtrace.each{|bt|li(
bt)}}})end end;class<<self;def R(*urls);Class.new(R){meta_def(:inherited){|c|
c.meta_def(:urls){urls}}};end;def D(path);constants.each{|c|k=const_get(c)
return k,$~[1..-1] if (k.urls rescue quot;/#{c.downcase}quot;).find {|x|path=~/^#{x}
/?$/}};[NotFound,[path]];end end end;class<<self;def escape(s);s.to_s.gsub(
/([^ a-zA-Z0-9_.-]+)/n){'%'+$1.unpack('H2'*$1.size).join('%').upcase}.tr(' ',
'+') end;def unescape(s);s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){[$1.
delete('%')].pack('H*')} end;def qs_parse(qs,d='&;');OpenStruct.new((qs||'').
split(/[#{d}] */n).inject({}){|hsh,p|k,v=p.split('=',2).map{|v|unescape(v)}
hsh[k]=v unless v.empty?;hsh}) end;def cookie_parse(s);c=qs_parse(s,';,') end
def run(r=$stdin,w=$stdout);w<<begin;k,a=Controllers.D quot;/#{ENV['PATH_INFO']}quot;.
gsub(%r!/+!,'/');m=ENV['REQUEST_METHOD']||quot;GETquot;;k.class_eval{include C
include Controllers::Base;include Models};o=k.new;o.service(r,ENV,m,a);rescue
=>e;Controllers::ServerError.new.service(r,ENV,quot;GETquot;,[k,m,e]);end;end;end
module Views; include Controllers; include Helpers end;end
Why?
For fun
For profit
For the satisfaction of knowing exactly
how your application works
For the look on your boss’s face when he
reads the documentation
Earlier...

But let’s not get ahead of ourselves
First we want to take you on a journey
A journey back in time
A journey back to...
January 3rd 2006
Location: The Secret Basement  Lair tm   of
Captain IP and The DNS avengers
Their task: to launch a new Top Level
Domain which DOESN’T RESOLVE
MACHINE ADDRESSES?!?!?!
Their resources? Hands to wave with
and hit keyboards with!
Ruby to the Rescue

It’s easy to learn
It’s quick to code in
It’s pleasing to the eye
It’s fun!
You keep saying that
Yes!!!
Fun makes for better coders
Better coders write good code
Good code stands the test of time
If coding isn’t fun YOU’RE USING THE
WRONG TOOLS!!!!
The console jockeys
let’s write a menu driven calculator
output: puts(), print()
input: gets(), termios library
old-fashioned and unattractive
termios is fiddly
A simple calculator
 #!/usr/bin/env ruby -w
 require 'termios'

 $total = 0
 $menu_entries = [['+', quot;Addquot;], ['-', quot;Subtractquot;], ['*', quot;Multiplyquot;], ['/', quot;Dividequot;], ['c', 'Clear'], ['q',quot;Quitquot;]]

 $commands = $entries.inject([]) { | commands, entry |
 
     commands << entry[0]
 
     }

 $captions = $entries.inject([]) { | captions, entry |
 
       captions << entry[1]
 
       }

 loop do
 
       puts quot;nSimple Calculatornquot;
 
       entries.each { | entry |
 puts quot;#{entry[0]}. #{entry[1]}nquot;
               }

 
         t = Termios.tcgetattr(STDIN)
 
         t.lflag &= ~Termios::ICANON
 
         Termios.tcsetattr(STDIN,0,t)

 
         begin
 
         
        action = STDIN.getc.chr
 
         end until $commands.member?(action)

 
         exit() if action == $commands.last
 
         action = $commands.index(action)
 
         puts quot;n#{$captions[action]}nnquot;

 
         case action
 
         
        when 0
 :
            $total += gets()
 
         
        when 1
 :
            $total -= gets()
 
         
        when 2
 :
            $total *= gets()
 
         
        when 3
 :
            $total /= gets()
 
         
        when 4
 :
            $total = 0
 
         end
 
         puts quot;Total = #{$total}quot;
 end
A Ruby packet reader
The 7 layer IP model
What the heck?
We’re exploring the UDP layer




     We want to look at UDP and DNS traffic
     Our first implementation is console-
     based, so hold on to your hats...
UDP header in Ruby
udpip.rb
require 'bit-struct'                                                     class DNSQueryHeader < BitStruct
                                                                         
       unsigned
     :dns_id,

         16,
     quot;IDquot;
class IP < BitStruct                                                     
       unsigned
     :dns_qr,

         1,
      quot;QRquot;

        unsigned
          :ip_v,
 
          4,
     quot;Versionquot;         
       unsigned
     :dns_opcode,
      4,
      quot;OpCodequot;

        unsigned
          :ip_hl,
 
         4,
     quot;Header lengthquot;   
       unsigned
     :dns_aa,
          1,
      quot;AAquot;

        unsigned
          :ip_tos,
 
        8,
     quot;TOSquot;             
       unsigned
     :dns_tc,

         1,
      quot;TCquot;

        unsigned
          :ip_len,
 
        16,
    quot;Lengthquot;          
       unsigned
     :dns_rd,

         1,
      quot;RDquot;

        unsigned
          :ip_id,
 
         16,
    quot;IDquot;              
       unsigned
     :dns_ra,

         1,
      quot;RAquot;

        unsigned
          :ip_off,
 
        16,
    quot;Frag offsetquot;     
       unsigned
     :dns_z,
 
         3,
      quot;Zquot;

        unsigned
          :ip_ttl,
 
        8,
     quot;TTLquot;             
       unsigned
     :dns_rcode,
       4,
      quot;RCODEquot;

        unsigned
          :ip_p,
 
          8,
     quot;Protocolquot;        
       unsigned
     :dns_qdcount,
     16,
     quot;QDCountquot;

        unsigned
          :ip_sum,

         16,
    quot;Checksumquot;        
       unsigned
     :dns_ancount,
     16,
     quot;ANCountquot;

        octets
 
          :ip_src,
 
        32,
    quot;Source addrquot;     
       unsigned
     :dns_arcount,
     16,
     quot;ARCountquot;

        octets
 
          :ip_dst,
 
        32,
    quot;Dest addrquot;       
       rest
   
     :data,
 
          
        quot;Dataquot;

        rest
     
        :body,
 
          quot;Body of messagequot;         end

        note
     
        quot;rest is application defined message bodyquot;

        initial_value.ip_v = 4                                          class Time

        initial_value.ip_hl = 5                                         
        # tcpdump style format
end                                                                      
        def to_s
                                                                         
        
        sprintf
 quot;%0.2d:%0.2d:%0.2d.%0.6dquot;, hour, min, sec, tv_usec
class UDP < BitStruct                                                    
        end

       unsigned
         :udp_srcport,
     16,
    quot;Source Portquot;       end

       unsigned
         :udp_dstport,
     16,
    quot;Dest Portquot;

       unsigned
         :udp_len,
         16,
    quot;UDP Lengthquot;

       unsigned
         :udp_chksum,
 16,
         quot;UDP Checksumquot;

       rest
   
         :body,
 
          
       quot;Body of messagequot;

       note
   
         quot;rest is application defined message bodyquot;
end
Capturing UDP packets
tcpdump.rb
#!/usr/local/bin/ruby
require 'pcaplet'
include Pcap
require 'udpip'

DIVIDER = quot;-quot; * 50
def print_details(section)

        puts DIVIDER, section, DIVIDER
end

pcaplet = Pcaplet.new('-s 1500')
pcaplet.each_packet { |pkt|

        if pkt.udp?

        
         puts quot;Packet: #{pkt.time} #{pkt}quot;

        
         if (pkt.sport == 53)

        
         
         udp = UDP.new

        
         
         udp.udp_srcport = pkt.sport

        
         
         udp.udp_dstport = pkt.dport

        
         
         udp.udp_len = pkt.udp_len

        
         
         udp.udp_chksum = pkt.udp_sum

        
         
         udp.body = pkt.udp_data

        
         
         print_details udp.inspect_detailed


        
        
        # look for DNS request only

        
        
        dns = DNSQueryHeader.new(pkt.udp_data)

        
        
        bytearray = Array.new

        
        
        udp.body.each_byte { |c|

        
        
        
        bytearray.concat(c.to_s.to_a)

        
        
        
        print c.to_s(16), ' '

        
        
        
        }

        
        
        print_details dns.inspect_detailed

        
        end

        end

        }
pcaplet.close
A live UDP packet
A live DNS packet
Can we have that on
    Windows?
A GUI? You gotta be joking!!
Why do you think we use Macs?
How about we just turn it into a web
application instead?
Sure, we can do that with Ruby
[What have we let ourselves in for...]
The NDA kicks in
Here’s where we hit the brick wall on
what we can talk about
You might imagine a DNS-sniffing web
application, but we couldn’t possibly
comment
So lets get down to some web app basics
And yes, we will be kicking it old-skool...
Introducing WEBrick
WEBrick is an HTTP ser ver library
It’s part of the Ruby 1.8 release
It can ser ve static documents
It can ser ve HTTPS using Ruby/OpenSSL
It can ser ve arbitrary code blocks
It can ser ve ser vlets
Static content
A standard HTTP ser ver
#!/usr/local/bin/ruby
require 'webrick'

server = WEBrick::HTTPServer.new(:Port => 8080, :DocumentRoot => Dir::pwd + quot;/htdocsquot;)

# mount personal directory, generating directory indexes
server.mount(quot;/~eleanorquot;, WEBrick::HTTPServlet::FileHandler, quot;/Users/eleanor/Sitesquot;, true)

# catch keyboard interrupt signal to terminate server
trap(quot;INTquot;){ server.shutdown }
server.start




An HTTPS ser ver
#!/usr/local/bin/ruby
# This requires Ruby/OpenSSL
require 'webrick'
require 'webrick/https'

certificate_name = [ [quot;Cquot;,quot;UKquot;], [quot;Oquot;,quot;games-with-brains.orgquot;], [quot;CNquot;, quot;WWWquot;] ]
server = WEBrick::HTTPServer.new(	

 :DocumentRoot = Dir::pwd + quot;/htdocsquot;, :SSLEnable = true,
	

  	

  	

  	

  	

 	

     :SSLVerifyClient = ::OpenSSL::SSL::VERIFY_NONE, :SSLCertName = certificate_name )
trap(quot;INTquot;){ s.shutdown }
s.start
Ser vlets
A Ruby code block
#!/usr/local/bin/ruby
require 'webrick'

server = WEBrick::GenericServer.new()
trap(quot;INTquot;){ server.shutdown }
server.start{|socket|	

 socket.puts(quot;This is a code blockrquot;)	

   }




A WEBrick ser vlet
#!/usr/local/bin/ruby
require 'webrick'

server = WEBrick::HTTPServer.new()
trap(quot;INTquot;){ server.shutdown }

def generate_response(response)
	

  response.body = quot;HTMLhello, world./HTMLquot;
	

  response['Content-Type'] = quot;text/htmlquot;
end

class   HelloServlet  WEBrick::HTTPServlet::AbstractServlet
	

    def do_GET(request, response)
	

    	

  generate_response(response)
	

    end
end

server.mount_proc(quot;/hello/simplequot;){ | request, response |	

 generate_response(response)	

 }
server.mount(quot;/hello/advancedquot;, HelloServlet)
server.start
It’s that simple?

Yes, it’s that simple
Of course these are trivial examples...
...so let’s build an application ser ver
An application ser ver

Still wondering when we get to the
really good stuff?
Soon, we promise
But first to show you how NOT to do it!
Wrap the request
A basic request context
class RequestContext

       attr_reader
   :request, :response, :servlets, :creation_time


      def initialize(request, response)

      
         @request, @response, = request, response

      
         @creation_time = Time.now()

      end


      def page_not_found

      
       @response.status = WEBrick::HTTPStatus::NotFound.new()

      end


      def response_page(page)

      
       @response['Content-Type'] = page.content_type

      
       @response.body = CGI::pretty(page.to_str())

      end


      def (item)

      
        @response.body  CGI::pretty(item)

      end
end
Ser ve the pages
The application ser ver
IP_ADDRESS_PATTERN = /^d{1,3}.d{1,3}.d{1,3}.d{1,3}/

class ApplicationServer

       attr_reader
      
       
       :web_server, :server_address, :servlets, :pages


       def initialize(parameters = {})

       
         @server_address = parameters[:my_address] or raise “Please supply a server address”

       
         raise “Invalid IP address for server” unless IP_ADDRESS_PATTERN.match(@server_address)

       
         @web_server = WEBrick::HTTPServer.new({:BindAddress = @server_address})

       
         @servlets = {}

       
         @pages = {}

       end


       def start

       
         trap(quot;INTquot;) { @web_server.shutdown }

       
         @web_server.start

       end


       def register_page(path, page)

       
        @pages[path] = page

       
        @web_server.mount_proc(path) { | request, response |

       
        
       context = RequestContext.new(request, response)

       
        
       @pages[request.path] ? context.response_page(@pages[request.path]) : context.page_not_found()

       
        
       }

       end


       def register_method(path, handler)

       
        @servlets[path] = self.method(handler).to_proc

       
        @web_server.mount_proc(path) {
 | request, response |

       
        
       context = RequestContext.new(request, response)

       
        
       @servlets[request.path] ? (context  @servlets[request.path].call(context).to_str()) : context.page_not_found()

       
        
       }

       end
end
Write the application
Revisiting “hello, world”
#!/usr/local/bin/ruby

require 'appserver.rb'

class SimpleServer  ApplicationServer

       def initialize(parameters = {})

       
         super

       
         register_page(quot;/hello/simplequot;, quot;HTMLHello, world/HTMLquot;)

       
         register_method(quot;/hello/advancedquot;, :hello_world)

       end


        def hello_world(context)

        
        quot;HTMLHello, world/HTMLquot;

        end
end

begin

        SimpleServer.new({:my_address = ARGV.shift()}).start()

rescue RuntimeError = e

       $stderr.puts quot;Usage:
    simpleserver
   host-addressquot;

       $stderr.puts quot;
  address must be provided in dotted-quad format (i.e. xxx.xxx.xxx.xxx)quot;
end
What have we done?!?
On the surface this is elegant
But underneath it sucks
There’s no support for HTML
Only methods can be used as ser vlets
We’re tied to WEBrick - which is slow
The road to perdition
So we added an HTML 4 library
And a ser ver pages container
And ActiveRecord
We meta’d the code to death
But it still lacked va-va-voom...
The case for Rails
So perhaps we should have just used
Rails in the first place
We’d be another of those “Rails saved
my career” success stories!
Hindsight’s always 20/20
But we’re old-school coders and it’s far
too user friendly for our comfort
The pressure against

Working at a very low level
Simple code required
Can Rails talk nicely to low-level code?
Strong management resistance - too
high a learning cur ve?
So why Camping?
Camping is beauty incarnate
It’s less than 4K of code
It uses Markaby and ActiveRecord
It runs on JRuby!!!
Oh, and it’s great fun to abuse...
Gratuitous diagram
How Why? The Lucky Stiff teaches it




                               lifted from
     http://redhanded.hobix.com/bits/campingAMicroframework.html
Markaby

An XHTML Domain Specific Language
Allows you to embed XHTML code in
Ruby code without building a complex
object hierarchy
Can be used with Rails
But that’s so simple!
Markaby embedded in Ruby
require 'markaby'

page = Markaby::Builder.new
page.xhtml_strict do
	

   head { title quot;Camping Presentationquot; }
	

   body do
	

   	

   h1.page_heading quot;Camping: Going off the Rails with Rubyquot;
	

   	

   ul.page_index do
	

   	

   	

   li.page_index { a “introduction”, :href = ‘#introduction’ }
	

   	

   	

   li.page_index { a “the presentation”, :href = ‘/presentation’ }
	

   	

   	

   li.page_index { a “comments”, :href = ‘#comments’ }
	

   	

   end
	

   	

   div.introduction! { “Everything will be alright!!!” }
	

   	

   div.comments! { “Have your say” }
	

   end
end
puts page.to_s
                                ?xml version=quot;1.0quot; encoding=quot;UTF-8quot;?
                                !DOCTYPE html PUBLIC quot;-//W3C//DTD XHTML 1.0 Strict//ENquot; quot;DTD/xhtml1-strict.dtdquot;
                                html lang=quot;enquot; xml:lang=quot;enquot; xmlns=quot;http://www.w3.org/1999/xhtmlquot;
                                	

   head
                                	

   	

   meta content=quot;text/html; charset=utf-8quot; http-equiv=quot;Content-Typequot;/
                                	

   	

   titleCamping Presentation/title
                                	

   /head
                                	

   body
                                	

   	

   h1 class=quot;page_headingquot;Camping: Going off the Rails with Ruby/h1
                                	

   	

   ul class=quot;page_indexquot;
                                	

   	

   	

   li class=quot;page_indexquot;a href=quot;#introductionquot;introduction/a/li
Creates this                    	

   	

   	

   li class=quot;page_indexquot;a href=quot;/presentationquot;the presentation/a/li
                                	

   	

   	

   li class=quot;page_indexquot;a href=quot;#commentsquot;comments/a/li
                                	

   	

   /ul

                              	

   	

   div id=quot;introductionquot;Just breathe deeply.../div
                              	

   	

   div id=quot;commentsquot;Have your say/div
                              	

   /body
                              /html
ActiveRecord

An Object-Relational Mapper
Implements the Active Record pattern
Supports many popular databases
A key component of Rails
ORMtastic
Using Active Record
require 'rubygems'
require_gem ‘activerecord’

ActiveRecord::Base.establish_connection(:adapter = “sqlite3”, :host = “localhost”, :database = “test.db”)

class User  ActiveRecord::Base
end

user = User.new()
user.id = “ellie”
user.name = “Eleanor McHugh”
user.password = “somerandomtext”
user.save

user = User.find(“ellie”)
user.destroy()
Totally RAD

Camping builds small applications
Why’s guideline? One file per application
If that’s how you prefer it...
A simple example
Basic setup
#!/usr/bin/env ruby

$:.unshift File.dirname(__FILE__) + quot;/../../libquot;
require 'camping'
require 'camping/session'

Camping.goes :Jotter

module Blog

      include Camping::Session
end




                  Load the camping libraries
                  Define a namespace for the application
                  Include session support (if required)
The data model
Defining the data model
module Jotter::Models

       class Note  Base; end


       class Database  V 1.0

       
       def self.up

       
       
         create_table :jotter_notes, :force = true do |t|

       
       
         
       t.column :id,
     
        :integer,

     :null = false

       
       
         
       t.column :created_at,
      :interger,
     :null = false

       
       
         
       t.column :title,
 
         :string,
 
     :limit = 255

       
       
         
       t.column :body,
 
          :text

       
       
         end

       
       end


       
        def self.down

       
        
         drop_table :jotter_notes

       
        end

       end
end

def Jotter.create

        Jotter::Models.create_schema
end




                We mark our database as version 1.0
                A create method builds the database
The controllers
Adding controllers
module Jotter::Controllers

       class Static  R '/static/(.+)'

       
        MIME_TYPES = {'.css' = 'text/css', '.js' = 'text/javascript', '.jpg' = 'image/jpeg'}

       
        PATH = __FILE__[/(.*)//, 1]


        
        def get(path)

        
        
        @headers['Content-Type'] = MIME_TYPES[path[/.w+$/, 0]] || quot;text/plainquot;

        
        
        @headers['X-Sendfile'] = quot;#{PATH}/static/#{path}quot;

        
        end

        end


        class Index  R '/'

        
        def get

        
        
        @notes = Note.find :all

        
        
        render :index

        
        end

        end


        class View  R '/view/(d+)'

        
        def get note_id

        
        
        @note = Note.find post_id

        
        
        render :view

        
        end

        end


        class Add  R ‘/add/’

        
       def get

        
       
        @note = Note.new

        
       
        render :add

        
       end


        
        def post

        
        
        note = Note.create :title = input.post_title, :body = input.post_body

        
        
        redirect View, post

        
        end

        end
The controllers
Adding controllers
    
   class Edit  R '/edit/(d+)', '/edit'

       
       def get note_id

       
       
         @note = Note.find note_id

       
       
         render :edit

       
       end


       
       def post

       
       
        @note = Note.find input.note_id

       
       
        @note.update_attributes :title = input.post_title, :body = input.post_body

       
       
        redirect View, @note

       
       end

       end


       class Delete  R '/delete/(d+)'

       
       def get note_id

       
       
        @note = Note.find note_id

       
       
        @note.destroy

       
       
        redirect Index

       
       end

       end
end




               Respond to HTTP GET and POST requests
               Perform database operations
The views
Application views
module Jotter::Views

       def layout

       
        xhtml_strict do

       
        
       head do

       
        
       
       title 'blog'

       
        
       
       link :rel = 'stylesheet', :type = 'text/css', :href = '/static/styles.css', :media = 'screen'

       
        
       end


        
        
        body do

        
        
        
       h1.header { a 'jotter', :href = R(Index) }

        
        
        
       div.body do

        
        
        
       
        self  yield

        
        
        
       end

        
        
        end

        
        end

        end


        def index

        
        @notes.empty? (p 'No posts found.') : (ol.row! { _list_notes(@notes) })

        
        p { a 'new note', :href = R(Add) }

        end


        def edit

        
        _form(@note, :action = R(Edit))

        end


        def view

        
        h1 @note.title

        
        h2 @note.created_at

        
        p @note.body

        
        p do

        
        
      [
      a(quot;Viewquot;, :href = R(View, @note)),

        
        
      
       a(quot;Editquot;, :href = R(Edit, @note)),

        
        
      
       a(quot;Deletequot;, :href = R(View, @note))
             ].join quot; | quot;

        
        end

        end
The views
Application views

     def _list_notes(notes)

     
         @notes.each do | note |

     
         
       li do

     
         
       
      ul do

     
         
       
      
            li { a note.title, :href = R(View, note) }

     
         
       
      
            li note.created_at

     
         
       
      
            li { a quot;Editquot;, :href = R(Edit, note) }

     
         
       
      
            li { a quot;Deletequot;, :href = R(Delete, note) }

     
         
       
      end

     
         
       end

     
         end

     end


     def _form(note, opts)

     
        form({:method = 'post'}.merge(opts)) do

     
        label 'Title', :for = 'note_title'; br

     
        input :name = 'note_title', :type = 'text', :value = note.title; br

     
        label 'Body', :for = 'note_body'; br

     
        textarea note.body, :name = 'note_body'; br

     
        input :type = 'hidden', :name = 'note_id', :value = note.id

     
        input :type = 'submit'

     end
end




              Views incorporate Markaby for XHTML
              Have access to controller data
The post-amble
A basic CGI post-amble
if __FILE__ == $0
  Jotter::Models::Base.establish_connection :adapter = 'sqlite3', :database = 'notes.db'
  Jotter::Models::Base.logger = Logger.new('camping.log')
  Jotter.create if Jotter.respond_to? :create
  puts Jotter.run
end




A Mongrel post-amble
if __FILE__ == $0

        Jotter::Models::Base.establish_connection :adapter = 'sqlite3', :database = 'notes.db'

        Jotter::Models::Base.logger = Logger.new('camping.log')

        Jotter::Models::Base.threaded_connections = false

        Jotter.create if Jotter.respond_to? :create

        server = Mongrel::Camping::start(“0.0.0.0”, 3000, “/jotter”, Jotter)

        puts “Jotter application running at http://localhost:3000/jotter”

        server.run.join
end




                 Allows an application to self-execute
                 Can be customised to suit your platform
The style-sheet
A simple style sheet
body {

         font-family: Utopia, Georga, serif;

         }

h1.header {

      background-color: #fef;

      margin: 0;

      padding: 10px;

      }

div.body {

        padding: 10px;

        }

#row ul   {

         list-style: none;

         margin: 0;

         padding: 0;

         padding-top: 4px;

         }

#row li {

         display: inline;

         }

#row a:link, #row a:visited {

        padding: 3px 10px 2px 10px;

        color: #FFFFFF;

        background-color: #B51032;

        text-decoration: none;

        border: 1px solid #711515;

        }
Larger applications
One application per file is a nice idea
But what about large applications?
Each can be broken down into discrete
micro-applications
Each micro-application has its own file
and mount points
Sharing a database
Camping apps keep their database
tables in separate namespaces
Larger applications will want to share
state bet ween micro-applications
We could do some ActiveRecord voodoo
Or we could cheat... guess which?
Camping in the wilds
Installing a database in the framework
require 'rubygems'
require_gem 'camping', '=1.4'
require 'camping/session'

module Camping

      module Models

      
       def self.schema(block)

      
       
         @@schema = block if block_given?

      
       
         @@schema

      
       end


       
       class User  Base

       
       
       validates_uniqueness_of :name, :scope = :id

       
       
       validates_presence_of :password

       
       end

       end


       def self.create

       
         Camping::Models::Session.create_schema

       
         ActiveRecord::Schema.define(Models.schema)

       end


       Models.schema do

       
       unless Models::User.table_exists?

       
       
       create_table :users, :force = true do
   |t|

       
       
       
       t.column :id,
     
        
     :integer,
:null = false

       
       
       
       t.column :created_on,
      
     :integer,
:null = false

       
       
       
       t.column :name,
 
          
     :string,
 :null = false

       
       
       
       t.column :password,
        
     :string,
 :null = false

       
       
       
       t.column :comment,
         
     :string,
 :null = false

       
       
       end


       
       
        execute quot;INSERT INTO users (created_on, name, password, comment) VALUES ('#{Time.now}', 'admin', 'admin', 'system administrator')quot;

       
       end

       end
end
Camping ser ver

The camping ser ver ties together a
series of web applications
A simple implementation ships with the
framework
The ser ver rules
Monitor a directory
load/reload all camping apps that
appear in it or a subdirectory
Mount apps according to the filenames
(i.e. jotter.rb mounts as /jotter)
Run create method on app startup
Support the X-Sendfile header
Summing up
Web applications are useful outside the
usual web app environment
Cross platform is easy when you only
need an XHTML browser
These tasks need a light weight design
Camping is a good way to solve them
And as you can see, Ruby rocks!!!
Where to next?

http://code.whytheluckystiff.net/camping/wiki

http://www.goto.info.waseda.ac.jp/~fukusima/ruby/pcap-e.html

http://raa.ruby-lang.org/project/bit-struct/

http://raa.ruby-lang.org/project/ruby-termios/

More Related Content

What's hot

"Revenge of The Script Kiddies: Current Day Uses of Automated Scripts by Top ...
"Revenge of The Script Kiddies: Current Day Uses of Automated Scripts by Top ..."Revenge of The Script Kiddies: Current Day Uses of Automated Scripts by Top ...
"Revenge of The Script Kiddies: Current Day Uses of Automated Scripts by Top ...PROIDEA
 
Blockchain Cryptography for Developers (Nakov @ BlockWorld 2018, San Jose)
Blockchain Cryptography for Developers (Nakov @ BlockWorld 2018, San Jose)Blockchain Cryptography for Developers (Nakov @ BlockWorld 2018, San Jose)
Blockchain Cryptography for Developers (Nakov @ BlockWorld 2018, San Jose)Svetlin Nakov
 
Cryptography for Java Developers: Nakov jProfessionals (Jan 2019)
Cryptography for Java Developers: Nakov jProfessionals (Jan 2019)Cryptography for Java Developers: Nakov jProfessionals (Jan 2019)
Cryptography for Java Developers: Nakov jProfessionals (Jan 2019)Svetlin Nakov
 
Laura Garcia - Shodan API and Coding Skills [rooted2019]
Laura Garcia - Shodan API and Coding Skills [rooted2019]Laura Garcia - Shodan API and Coding Skills [rooted2019]
Laura Garcia - Shodan API and Coding Skills [rooted2019]RootedCON
 
Blockchain Cryptography for Developers (Nakov @ BGWebSummit 2018)
Blockchain Cryptography for Developers (Nakov @ BGWebSummit 2018)Blockchain Cryptography for Developers (Nakov @ BGWebSummit 2018)
Blockchain Cryptography for Developers (Nakov @ BGWebSummit 2018)Svetlin Nakov
 
Ngrep commands
Ngrep commandsNgrep commands
Ngrep commandsRishu Seth
 
Applying Security Algorithms Using openSSL crypto library
Applying Security Algorithms Using openSSL crypto libraryApplying Security Algorithms Using openSSL crypto library
Applying Security Algorithms Using openSSL crypto libraryPriyank Kapadia
 
[Ruxcon Monthly Sydney 2011] Proprietary Protocols Reverse Engineering : Rese...
[Ruxcon Monthly Sydney 2011] Proprietary Protocols Reverse Engineering : Rese...[Ruxcon Monthly Sydney 2011] Proprietary Protocols Reverse Engineering : Rese...
[Ruxcon Monthly Sydney 2011] Proprietary Protocols Reverse Engineering : Rese...Moabi.com
 
Centralized Logging with syslog
Centralized Logging with syslogCentralized Logging with syslog
Centralized Logging with syslogamiable_indian
 
"A rootkits writer’s guide to defense" - Michal Purzynski
"A rootkits writer’s guide to defense" - Michal Purzynski"A rootkits writer’s guide to defense" - Michal Purzynski
"A rootkits writer’s guide to defense" - Michal PurzynskiPROIDEA
 
9 password security
9   password security9   password security
9 password securitydrewz lin
 
Password Storage and Attacking in PHP
Password Storage and Attacking in PHPPassword Storage and Attacking in PHP
Password Storage and Attacking in PHPAnthony Ferrara
 
Cryptography in PHP: use cases
Cryptography in PHP: use casesCryptography in PHP: use cases
Cryptography in PHP: use casesEnrico Zimuel
 
Nmap not only a port scanner by ravi rajput comexpo security awareness meet
Nmap not only a port scanner by ravi rajput comexpo security awareness meet Nmap not only a port scanner by ravi rajput comexpo security awareness meet
Nmap not only a port scanner by ravi rajput comexpo security awareness meet Ravi Rajput
 
Cryptography for Absolute Beginners (May 2019)
Cryptography for Absolute Beginners (May 2019)Cryptography for Absolute Beginners (May 2019)
Cryptography for Absolute Beginners (May 2019)Svetlin Nakov
 
Francisco Jesús Gómez + Carlos Juan Diaz - Cloud Malware Distribution: DNS wi...
Francisco Jesús Gómez + Carlos Juan Diaz - Cloud Malware Distribution: DNS wi...Francisco Jesús Gómez + Carlos Juan Diaz - Cloud Malware Distribution: DNS wi...
Francisco Jesús Gómez + Carlos Juan Diaz - Cloud Malware Distribution: DNS wi...RootedCON
 

What's hot (20)

"Revenge of The Script Kiddies: Current Day Uses of Automated Scripts by Top ...
"Revenge of The Script Kiddies: Current Day Uses of Automated Scripts by Top ..."Revenge of The Script Kiddies: Current Day Uses of Automated Scripts by Top ...
"Revenge of The Script Kiddies: Current Day Uses of Automated Scripts by Top ...
 
Blockchain Cryptography for Developers (Nakov @ BlockWorld 2018, San Jose)
Blockchain Cryptography for Developers (Nakov @ BlockWorld 2018, San Jose)Blockchain Cryptography for Developers (Nakov @ BlockWorld 2018, San Jose)
Blockchain Cryptography for Developers (Nakov @ BlockWorld 2018, San Jose)
 
Cryptography for Java Developers: Nakov jProfessionals (Jan 2019)
Cryptography for Java Developers: Nakov jProfessionals (Jan 2019)Cryptography for Java Developers: Nakov jProfessionals (Jan 2019)
Cryptography for Java Developers: Nakov jProfessionals (Jan 2019)
 
Laura Garcia - Shodan API and Coding Skills [rooted2019]
Laura Garcia - Shodan API and Coding Skills [rooted2019]Laura Garcia - Shodan API and Coding Skills [rooted2019]
Laura Garcia - Shodan API and Coding Skills [rooted2019]
 
Blockchain Cryptography for Developers (Nakov @ BGWebSummit 2018)
Blockchain Cryptography for Developers (Nakov @ BGWebSummit 2018)Blockchain Cryptography for Developers (Nakov @ BGWebSummit 2018)
Blockchain Cryptography for Developers (Nakov @ BGWebSummit 2018)
 
Ngrep commands
Ngrep commandsNgrep commands
Ngrep commands
 
Interpreter, Compiler, JIT from scratch
Interpreter, Compiler, JIT from scratchInterpreter, Compiler, JIT from scratch
Interpreter, Compiler, JIT from scratch
 
Applying Security Algorithms Using openSSL crypto library
Applying Security Algorithms Using openSSL crypto libraryApplying Security Algorithms Using openSSL crypto library
Applying Security Algorithms Using openSSL crypto library
 
[Ruxcon Monthly Sydney 2011] Proprietary Protocols Reverse Engineering : Rese...
[Ruxcon Monthly Sydney 2011] Proprietary Protocols Reverse Engineering : Rese...[Ruxcon Monthly Sydney 2011] Proprietary Protocols Reverse Engineering : Rese...
[Ruxcon Monthly Sydney 2011] Proprietary Protocols Reverse Engineering : Rese...
 
Centralized Logging with syslog
Centralized Logging with syslogCentralized Logging with syslog
Centralized Logging with syslog
 
"A rootkits writer’s guide to defense" - Michal Purzynski
"A rootkits writer’s guide to defense" - Michal Purzynski"A rootkits writer’s guide to defense" - Michal Purzynski
"A rootkits writer’s guide to defense" - Michal Purzynski
 
9 password security
9   password security9   password security
9 password security
 
Password Storage and Attacking in PHP
Password Storage and Attacking in PHPPassword Storage and Attacking in PHP
Password Storage and Attacking in PHP
 
Cryptography in PHP: use cases
Cryptography in PHP: use casesCryptography in PHP: use cases
Cryptography in PHP: use cases
 
Nmap not only a port scanner by ravi rajput comexpo security awareness meet
Nmap not only a port scanner by ravi rajput comexpo security awareness meet Nmap not only a port scanner by ravi rajput comexpo security awareness meet
Nmap not only a port scanner by ravi rajput comexpo security awareness meet
 
Cryptography for Absolute Beginners (May 2019)
Cryptography for Absolute Beginners (May 2019)Cryptography for Absolute Beginners (May 2019)
Cryptography for Absolute Beginners (May 2019)
 
Php engine
Php enginePhp engine
Php engine
 
Bleeding secrets
Bleeding secretsBleeding secrets
Bleeding secrets
 
Francisco Jesús Gómez + Carlos Juan Diaz - Cloud Malware Distribution: DNS wi...
Francisco Jesús Gómez + Carlos Juan Diaz - Cloud Malware Distribution: DNS wi...Francisco Jesús Gómez + Carlos Juan Diaz - Cloud Malware Distribution: DNS wi...
Francisco Jesús Gómez + Carlos Juan Diaz - Cloud Malware Distribution: DNS wi...
 
Ethical hacking with Python tools
Ethical hacking with Python toolsEthical hacking with Python tools
Ethical hacking with Python tools
 

Similar to Camping: Going off the Rails with Ruby

How to Leverage Go for Your Networking Needs
How to Leverage Go for Your Networking NeedsHow to Leverage Go for Your Networking Needs
How to Leverage Go for Your Networking NeedsDigitalOcean
 
Vc4c development of opencl compiler for videocore4
Vc4c  development of opencl compiler for videocore4Vc4c  development of opencl compiler for videocore4
Vc4c development of opencl compiler for videocore4nomaddo
 
High Availability With DRBD & Heartbeat
High Availability With DRBD & HeartbeatHigh Availability With DRBD & Heartbeat
High Availability With DRBD & HeartbeatChris Barber
 
[2012 CodeEngn Conference 06] pwn3r - Secuinside 2012 CTF 예선 문제풀이
[2012 CodeEngn Conference 06] pwn3r - Secuinside 2012 CTF 예선 문제풀이[2012 CodeEngn Conference 06] pwn3r - Secuinside 2012 CTF 예선 문제풀이
[2012 CodeEngn Conference 06] pwn3r - Secuinside 2012 CTF 예선 문제풀이GangSeok Lee
 
Cs423 raw sockets_bw
Cs423 raw sockets_bwCs423 raw sockets_bw
Cs423 raw sockets_bwjktjpc
 
Endless fun with Arduino and Eventmachine
Endless fun with Arduino and EventmachineEndless fun with Arduino and Eventmachine
Endless fun with Arduino and EventmachineBodo Tasche
 
DEF CON 27 - workshop - HUGO TROVAO and RUSHIKESH NADEDKAR - scapy dojo v1
DEF CON 27 - workshop - HUGO TROVAO and RUSHIKESH NADEDKAR - scapy dojo v1DEF CON 27 - workshop - HUGO TROVAO and RUSHIKESH NADEDKAR - scapy dojo v1
DEF CON 27 - workshop - HUGO TROVAO and RUSHIKESH NADEDKAR - scapy dojo v1Felipe Prado
 
Getting Started with Raspberry Pi - DCC 2013.1
Getting Started with Raspberry Pi - DCC 2013.1Getting Started with Raspberry Pi - DCC 2013.1
Getting Started with Raspberry Pi - DCC 2013.1Tom Paulus
 
How Linux Processes Your Network Packet - Elazar Leibovich
How Linux Processes Your Network Packet - Elazar LeibovichHow Linux Processes Your Network Packet - Elazar Leibovich
How Linux Processes Your Network Packet - Elazar LeibovichDevOpsDays Tel Aviv
 
Xdp and ebpf_maps
Xdp and ebpf_mapsXdp and ebpf_maps
Xdp and ebpf_mapslcplcp1
 
Linux Networking Explained
Linux Networking ExplainedLinux Networking Explained
Linux Networking ExplainedThomas Graf
 
Linux kernel tracing superpowers in the cloud
Linux kernel tracing superpowers in the cloudLinux kernel tracing superpowers in the cloud
Linux kernel tracing superpowers in the cloudAndrea Righi
 
Using Node.js to Build Great Streaming Services - HTML5 Dev Conf
Using Node.js to  Build Great  Streaming Services - HTML5 Dev ConfUsing Node.js to  Build Great  Streaming Services - HTML5 Dev Conf
Using Node.js to Build Great Streaming Services - HTML5 Dev ConfTom Croucher
 
Network Test Automation - Net Ops Coding 2015
Network Test Automation - Net Ops Coding 2015Network Test Automation - Net Ops Coding 2015
Network Test Automation - Net Ops Coding 2015Hiroshi Ota
 
Please help with the below 3 questions, the python script is at the.pdf
Please help with the below 3  questions, the python script is at the.pdfPlease help with the below 3  questions, the python script is at the.pdf
Please help with the below 3 questions, the python script is at the.pdfsupport58
 
100 bugs in Open Source C/C++ projects
100 bugs in Open Source C/C++ projects 100 bugs in Open Source C/C++ projects
100 bugs in Open Source C/C++ projects Andrey Karpov
 
Metasepi team meeting #6: "Snatch-driven development"
Metasepi team meeting #6: "Snatch-driven development"Metasepi team meeting #6: "Snatch-driven development"
Metasepi team meeting #6: "Snatch-driven development"Kiwamu Okabe
 
Perl - laziness, impatience, hubris, and one liners
Perl - laziness, impatience, hubris, and one linersPerl - laziness, impatience, hubris, and one liners
Perl - laziness, impatience, hubris, and one linersKirk Kimmel
 

Similar to Camping: Going off the Rails with Ruby (20)

How to Leverage Go for Your Networking Needs
How to Leverage Go for Your Networking NeedsHow to Leverage Go for Your Networking Needs
How to Leverage Go for Your Networking Needs
 
Test
TestTest
Test
 
Vc4c development of opencl compiler for videocore4
Vc4c  development of opencl compiler for videocore4Vc4c  development of opencl compiler for videocore4
Vc4c development of opencl compiler for videocore4
 
High Availability With DRBD & Heartbeat
High Availability With DRBD & HeartbeatHigh Availability With DRBD & Heartbeat
High Availability With DRBD & Heartbeat
 
[2012 CodeEngn Conference 06] pwn3r - Secuinside 2012 CTF 예선 문제풀이
[2012 CodeEngn Conference 06] pwn3r - Secuinside 2012 CTF 예선 문제풀이[2012 CodeEngn Conference 06] pwn3r - Secuinside 2012 CTF 예선 문제풀이
[2012 CodeEngn Conference 06] pwn3r - Secuinside 2012 CTF 예선 문제풀이
 
Cs423 raw sockets_bw
Cs423 raw sockets_bwCs423 raw sockets_bw
Cs423 raw sockets_bw
 
Endless fun with Arduino and Eventmachine
Endless fun with Arduino and EventmachineEndless fun with Arduino and Eventmachine
Endless fun with Arduino and Eventmachine
 
DEF CON 27 - workshop - HUGO TROVAO and RUSHIKESH NADEDKAR - scapy dojo v1
DEF CON 27 - workshop - HUGO TROVAO and RUSHIKESH NADEDKAR - scapy dojo v1DEF CON 27 - workshop - HUGO TROVAO and RUSHIKESH NADEDKAR - scapy dojo v1
DEF CON 27 - workshop - HUGO TROVAO and RUSHIKESH NADEDKAR - scapy dojo v1
 
Getting Started with Raspberry Pi - DCC 2013.1
Getting Started with Raspberry Pi - DCC 2013.1Getting Started with Raspberry Pi - DCC 2013.1
Getting Started with Raspberry Pi - DCC 2013.1
 
How Linux Processes Your Network Packet - Elazar Leibovich
How Linux Processes Your Network Packet - Elazar LeibovichHow Linux Processes Your Network Packet - Elazar Leibovich
How Linux Processes Your Network Packet - Elazar Leibovich
 
Xdp and ebpf_maps
Xdp and ebpf_mapsXdp and ebpf_maps
Xdp and ebpf_maps
 
Linux Networking Explained
Linux Networking ExplainedLinux Networking Explained
Linux Networking Explained
 
Linux kernel tracing superpowers in the cloud
Linux kernel tracing superpowers in the cloudLinux kernel tracing superpowers in the cloud
Linux kernel tracing superpowers in the cloud
 
A22 Introduction to DTrace by Kyle Hailey
A22 Introduction to DTrace by Kyle HaileyA22 Introduction to DTrace by Kyle Hailey
A22 Introduction to DTrace by Kyle Hailey
 
Using Node.js to Build Great Streaming Services - HTML5 Dev Conf
Using Node.js to  Build Great  Streaming Services - HTML5 Dev ConfUsing Node.js to  Build Great  Streaming Services - HTML5 Dev Conf
Using Node.js to Build Great Streaming Services - HTML5 Dev Conf
 
Network Test Automation - Net Ops Coding 2015
Network Test Automation - Net Ops Coding 2015Network Test Automation - Net Ops Coding 2015
Network Test Automation - Net Ops Coding 2015
 
Please help with the below 3 questions, the python script is at the.pdf
Please help with the below 3  questions, the python script is at the.pdfPlease help with the below 3  questions, the python script is at the.pdf
Please help with the below 3 questions, the python script is at the.pdf
 
100 bugs in Open Source C/C++ projects
100 bugs in Open Source C/C++ projects 100 bugs in Open Source C/C++ projects
100 bugs in Open Source C/C++ projects
 
Metasepi team meeting #6: "Snatch-driven development"
Metasepi team meeting #6: "Snatch-driven development"Metasepi team meeting #6: "Snatch-driven development"
Metasepi team meeting #6: "Snatch-driven development"
 
Perl - laziness, impatience, hubris, and one liners
Perl - laziness, impatience, hubris, and one linersPerl - laziness, impatience, hubris, and one liners
Perl - laziness, impatience, hubris, and one liners
 

More from Eleanor McHugh

[2023] Putting the R! in R&D.pdf
[2023] Putting the R! in R&D.pdf[2023] Putting the R! in R&D.pdf
[2023] Putting the R! in R&D.pdfEleanor McHugh
 
Generics, Reflection, and Efficient Collections
Generics, Reflection, and Efficient CollectionsGenerics, Reflection, and Efficient Collections
Generics, Reflection, and Efficient CollectionsEleanor McHugh
 
The Relevance of Liveness - Biometrics and Data Integrity
The Relevance of Liveness - Biometrics and Data IntegrityThe Relevance of Liveness - Biometrics and Data Integrity
The Relevance of Liveness - Biometrics and Data IntegrityEleanor McHugh
 
The Browser Environment - A Systems Programmer's Perspective [sinatra edition]
The Browser Environment - A Systems Programmer's Perspective [sinatra edition]The Browser Environment - A Systems Programmer's Perspective [sinatra edition]
The Browser Environment - A Systems Programmer's Perspective [sinatra edition]Eleanor McHugh
 
The Browser Environment - A Systems Programmer's Perspective
The Browser Environment - A Systems Programmer's PerspectiveThe Browser Environment - A Systems Programmer's Perspective
The Browser Environment - A Systems Programmer's PerspectiveEleanor McHugh
 
Go for the paranoid network programmer, 3rd edition
Go for the paranoid network programmer, 3rd editionGo for the paranoid network programmer, 3rd edition
Go for the paranoid network programmer, 3rd editionEleanor McHugh
 
An introduction to functional programming with Go [redux]
An introduction to functional programming with Go [redux]An introduction to functional programming with Go [redux]
An introduction to functional programming with Go [redux]Eleanor McHugh
 
An introduction to functional programming with go
An introduction to functional programming with goAn introduction to functional programming with go
An introduction to functional programming with goEleanor McHugh
 
Implementing virtual machines in go & c 2018 redux
Implementing virtual machines in go & c 2018 reduxImplementing virtual machines in go & c 2018 redux
Implementing virtual machines in go & c 2018 reduxEleanor McHugh
 
Identity & trust in Monitored Spaces
Identity & trust in Monitored SpacesIdentity & trust in Monitored Spaces
Identity & trust in Monitored SpacesEleanor McHugh
 
Don't Ask, Don't Tell - The Virtues of Privacy By Design
Don't Ask, Don't Tell - The Virtues of Privacy By DesignDon't Ask, Don't Tell - The Virtues of Privacy By Design
Don't Ask, Don't Tell - The Virtues of Privacy By DesignEleanor McHugh
 
Don't ask, don't tell the virtues of privacy by design
Don't ask, don't tell   the virtues of privacy by designDon't ask, don't tell   the virtues of privacy by design
Don't ask, don't tell the virtues of privacy by designEleanor McHugh
 
Anonymity, identity, trust
Anonymity, identity, trustAnonymity, identity, trust
Anonymity, identity, trustEleanor McHugh
 
Going Loopy - Adventures in Iteration with Google Go
Going Loopy - Adventures in Iteration with Google GoGoing Loopy - Adventures in Iteration with Google Go
Going Loopy - Adventures in Iteration with Google GoEleanor McHugh
 
Distributed Ledgers: Anonymity & Immutability at Scale
Distributed Ledgers: Anonymity & Immutability at ScaleDistributed Ledgers: Anonymity & Immutability at Scale
Distributed Ledgers: Anonymity & Immutability at ScaleEleanor McHugh
 
Go for the paranoid network programmer, 2nd edition
Go for the paranoid network programmer, 2nd editionGo for the paranoid network programmer, 2nd edition
Go for the paranoid network programmer, 2nd editionEleanor McHugh
 
Going Loopy: Adventures in Iteration with Go
Going Loopy: Adventures in Iteration with GoGoing Loopy: Adventures in Iteration with Go
Going Loopy: Adventures in Iteration with GoEleanor McHugh
 
Finding a useful outlet for my many Adventures in go
Finding a useful outlet for my many Adventures in goFinding a useful outlet for my many Adventures in go
Finding a useful outlet for my many Adventures in goEleanor McHugh
 
Anonymity, trust, accountability
Anonymity, trust, accountabilityAnonymity, trust, accountability
Anonymity, trust, accountabilityEleanor McHugh
 

More from Eleanor McHugh (20)

[2023] Putting the R! in R&D.pdf
[2023] Putting the R! in R&D.pdf[2023] Putting the R! in R&D.pdf
[2023] Putting the R! in R&D.pdf
 
Generics, Reflection, and Efficient Collections
Generics, Reflection, and Efficient CollectionsGenerics, Reflection, and Efficient Collections
Generics, Reflection, and Efficient Collections
 
The Relevance of Liveness - Biometrics and Data Integrity
The Relevance of Liveness - Biometrics and Data IntegrityThe Relevance of Liveness - Biometrics and Data Integrity
The Relevance of Liveness - Biometrics and Data Integrity
 
The Browser Environment - A Systems Programmer's Perspective [sinatra edition]
The Browser Environment - A Systems Programmer's Perspective [sinatra edition]The Browser Environment - A Systems Programmer's Perspective [sinatra edition]
The Browser Environment - A Systems Programmer's Perspective [sinatra edition]
 
The Browser Environment - A Systems Programmer's Perspective
The Browser Environment - A Systems Programmer's PerspectiveThe Browser Environment - A Systems Programmer's Perspective
The Browser Environment - A Systems Programmer's Perspective
 
Go for the paranoid network programmer, 3rd edition
Go for the paranoid network programmer, 3rd editionGo for the paranoid network programmer, 3rd edition
Go for the paranoid network programmer, 3rd edition
 
An introduction to functional programming with Go [redux]
An introduction to functional programming with Go [redux]An introduction to functional programming with Go [redux]
An introduction to functional programming with Go [redux]
 
An introduction to functional programming with go
An introduction to functional programming with goAn introduction to functional programming with go
An introduction to functional programming with go
 
Implementing virtual machines in go & c 2018 redux
Implementing virtual machines in go & c 2018 reduxImplementing virtual machines in go & c 2018 redux
Implementing virtual machines in go & c 2018 redux
 
Identity & trust in Monitored Spaces
Identity & trust in Monitored SpacesIdentity & trust in Monitored Spaces
Identity & trust in Monitored Spaces
 
Don't Ask, Don't Tell - The Virtues of Privacy By Design
Don't Ask, Don't Tell - The Virtues of Privacy By DesignDon't Ask, Don't Tell - The Virtues of Privacy By Design
Don't Ask, Don't Tell - The Virtues of Privacy By Design
 
Don't ask, don't tell the virtues of privacy by design
Don't ask, don't tell   the virtues of privacy by designDon't ask, don't tell   the virtues of privacy by design
Don't ask, don't tell the virtues of privacy by design
 
Anonymity, identity, trust
Anonymity, identity, trustAnonymity, identity, trust
Anonymity, identity, trust
 
Going Loopy - Adventures in Iteration with Google Go
Going Loopy - Adventures in Iteration with Google GoGoing Loopy - Adventures in Iteration with Google Go
Going Loopy - Adventures in Iteration with Google Go
 
Distributed Ledgers: Anonymity & Immutability at Scale
Distributed Ledgers: Anonymity & Immutability at ScaleDistributed Ledgers: Anonymity & Immutability at Scale
Distributed Ledgers: Anonymity & Immutability at Scale
 
Hello Go
Hello GoHello Go
Hello Go
 
Go for the paranoid network programmer, 2nd edition
Go for the paranoid network programmer, 2nd editionGo for the paranoid network programmer, 2nd edition
Go for the paranoid network programmer, 2nd edition
 
Going Loopy: Adventures in Iteration with Go
Going Loopy: Adventures in Iteration with GoGoing Loopy: Adventures in Iteration with Go
Going Loopy: Adventures in Iteration with Go
 
Finding a useful outlet for my many Adventures in go
Finding a useful outlet for my many Adventures in goFinding a useful outlet for my many Adventures in go
Finding a useful outlet for my many Adventures in go
 
Anonymity, trust, accountability
Anonymity, trust, accountabilityAnonymity, trust, accountability
Anonymity, trust, accountability
 

Recently uploaded

From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 3652toLead Limited
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Azure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAzure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAndikSusilo4
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024Scott Keck-Warren
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure servicePooja Nehwal
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...shyamraj55
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersThousandEyes
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksSoftradix Technologies
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhisoniya singh
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptxLBM Solutions
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationSafe Software
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesSinan KOZAK
 

Recently uploaded (20)

From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Azure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & ApplicationAzure Monitor & Application Insight to monitor Infrastructure & Application
Azure Monitor & Application Insight to monitor Infrastructure & Application
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping Elbows
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other Frameworks
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptx
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen Frames
 

Camping: Going off the Rails with Ruby

  • 1. Camping: Going off the Rails with Ruby Adventures in creative coding for people who should know better
  • 2. The weasel words This presentation contains code That code is probably broken If that bothers you - fix it That’s called a learning experience
  • 3. Who are these lunatics? Eleanor McHugh eleanor@games-with-brains.com She does real-time systems Romek Szczesniak romek@spikyblackcat.co.uk He does security
  • 4. Alright, but what are they doing here? Ruby Pcap & BitStruct WEBrick Camping but no Rails...
  • 5. No Rails? That’s right, we don’t use Rails But we do use Ruby And we do write web applications So how is that possible?
  • 6. Camping!!! That’s right, we use Camping It’s by Why The Lucky Stiff It’s cool It’s really cool It’s so damn cool you’d have to be mad not to use it!!!
  • 7. It’s this simple! %w[rubygems active_record markaby metaid ostruct].each {|lib| require lib} module Camping;C=self;module Models;end;Models::Base=ActiveRecord::Base module Helpers;def R c,*args;p=/(.+?)/;args.inject(c.urls.detect{|x|x. scan(p).size==args.size}.dup){|str,a|str.gsub(p,(a.method(a.class.primary_key )[]rescue a).to_s)};end;def / p;File.join(@root,p) end;end;module Controllers module Base;include Helpers;attr_accessor :input,:cookies,:headers,:body, :status,:root;def method_missing(m,*args,&#38;blk);str=m==:render ? markaview( *args,&#38;blk):eval(quot;markaby.#{m}(*args,&#38;blk)quot;);str=markaview(:layout){str }rescue nil;r(200,str.to_s);end;def r(s,b,h={});@status=s;@headers.merge!(h) @body=b;end;def redirect(c,*args);c=R(c,*args)if c.respond_to?:urls;r(302,'', 'Location'=&gt;self/c);end;def service(r,e,m,a);@status,@headers,@root=200,{},e[ 'SCRIPT_NAME'];@cookies=C.cookie_parse(e['HTTP_COOKIE']||e['COOKIE']);cook= @cookies.marshal_dump.dup;if (quot;POSTquot;==e['REQUEST_METHOD'])and %r|Amultipart /form-data.*boundary=quot;?([^quot;;,]+)quot;?|n.match(e['CONTENT_TYPE']);return r(500, quot;No multipart/form-data supported.quot;)else;@input=C.qs_parse(e['REQUEST_METHOD' ]==quot;POSTquot;?r.read(e['CONTENT_LENGTH'].to_i):e['QUERY_STRING']);end;@body= method(m.downcase).call(*a);@headers[quot;Set-Cookiequot;]=@cookies.marshal_dump.map{ |k,v|quot;#{k}=#{C.escape(v)}; path=/quot;if v != cook[k]}.compact;self;end;def to_s quot;Status: #{@status}n#{{'Content-Type'=&gt;'text/html'}.merge(@headers).map{|k,v| v.to_a.map{|v2|quot;#{k}: #{v2}quot;}}.flatten.join(quot;nquot;)}nn#{@body}quot;;end;def markaby;Class.new(Markaby::Builder){@root=@root;include Views;def tag!(*g,&#38;b) [:href,:action].each{|a|(g.last[a]=self./(g.last[a]))rescue 0};super end}.new( instance_variables.map{|iv|[iv[1..-1].intern,instance_variable_get(iv)]},{}) end;def markaview(m,*args,&#38;blk);markaby.instance_eval{Views.instance_method(m ).bind(self).call(*args, &#38;blk);self}.to_s;end;end;class R;include Base end class NotFound&lt;R;def get(p);r(404,div{h1(quot;#{C} Problem!quot;)+h2(quot;#{p} not foundquot;) });end end;class ServerError&lt;R;def get(k,m,e);r(500,markaby.div{h1 quot;#{C} Prob lem!quot;;h2 quot;#{k}.#{m}quot;;h3 quot;#{e.class} #{e.message}:quot;;ul{e.backtrace.each{|bt|li( bt)}}})end end;class&lt;&lt;self;def R(*urls);Class.new(R){meta_def(:inherited){|c| c.meta_def(:urls){urls}}};end;def D(path);constants.each{|c|k=const_get(c) return k,$~[1..-1] if (k.urls rescue quot;/#{c.downcase}quot;).find {|x|path=~/^#{x} /?$/}};[NotFound,[path]];end end end;class&lt;&lt;self;def escape(s);s.to_s.gsub( /([^ a-zA-Z0-9_.-]+)/n){'%'+$1.unpack('H2'*$1.size).join('%').upcase}.tr(' ', '+') end;def unescape(s);s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){[$1. delete('%')].pack('H*')} end;def qs_parse(qs,d='&#38;;');OpenStruct.new((qs||''). split(/[#{d}] */n).inject({}){|hsh,p|k,v=p.split('=',2).map{|v|unescape(v)} hsh[k]=v unless v.empty?;hsh}) end;def cookie_parse(s);c=qs_parse(s,';,') end def run(r=$stdin,w=$stdout);w&lt;&lt;begin;k,a=Controllers.D quot;/#{ENV['PATH_INFO']}quot;. gsub(%r!/+!,'/');m=ENV['REQUEST_METHOD']||quot;GETquot;;k.class_eval{include C include Controllers::Base;include Models};o=k.new;o.service(r,ENV,m,a);rescue =&gt;e;Controllers::ServerError.new.service(r,ENV,quot;GETquot;,[k,m,e]);end;end;end module Views; include Controllers; include Helpers end;end
  • 8. Why? For fun For profit For the satisfaction of knowing exactly how your application works For the look on your boss’s face when he reads the documentation
  • 9. Earlier... But let’s not get ahead of ourselves First we want to take you on a journey A journey back in time A journey back to...
  • 10. January 3rd 2006 Location: The Secret Basement Lair tm of Captain IP and The DNS avengers Their task: to launch a new Top Level Domain which DOESN’T RESOLVE MACHINE ADDRESSES?!?!?! Their resources? Hands to wave with and hit keyboards with!
  • 11. Ruby to the Rescue It’s easy to learn It’s quick to code in It’s pleasing to the eye It’s fun!
  • 12. You keep saying that Yes!!! Fun makes for better coders Better coders write good code Good code stands the test of time If coding isn’t fun YOU’RE USING THE WRONG TOOLS!!!!
  • 13. The console jockeys let’s write a menu driven calculator output: puts(), print() input: gets(), termios library old-fashioned and unattractive termios is fiddly
  • 14. A simple calculator #!/usr/bin/env ruby -w require 'termios' $total = 0 $menu_entries = [['+', quot;Addquot;], ['-', quot;Subtractquot;], ['*', quot;Multiplyquot;], ['/', quot;Dividequot;], ['c', 'Clear'], ['q',quot;Quitquot;]] $commands = $entries.inject([]) { | commands, entry | commands << entry[0] } $captions = $entries.inject([]) { | captions, entry | captions << entry[1] } loop do puts quot;nSimple Calculatornquot; entries.each { | entry | puts quot;#{entry[0]}. #{entry[1]}nquot; } t = Termios.tcgetattr(STDIN) t.lflag &= ~Termios::ICANON Termios.tcsetattr(STDIN,0,t) begin action = STDIN.getc.chr end until $commands.member?(action) exit() if action == $commands.last action = $commands.index(action) puts quot;n#{$captions[action]}nnquot; case action when 0 : $total += gets() when 1 : $total -= gets() when 2 : $total *= gets() when 3 : $total /= gets() when 4 : $total = 0 end puts quot;Total = #{$total}quot; end
  • 15. A Ruby packet reader The 7 layer IP model
  • 16. What the heck? We’re exploring the UDP layer We want to look at UDP and DNS traffic Our first implementation is console- based, so hold on to your hats...
  • 17. UDP header in Ruby udpip.rb require 'bit-struct' class DNSQueryHeader < BitStruct unsigned :dns_id, 16, quot;IDquot; class IP < BitStruct unsigned :dns_qr, 1, quot;QRquot; unsigned :ip_v, 4, quot;Versionquot; unsigned :dns_opcode, 4, quot;OpCodequot; unsigned :ip_hl, 4, quot;Header lengthquot; unsigned :dns_aa, 1, quot;AAquot; unsigned :ip_tos, 8, quot;TOSquot; unsigned :dns_tc, 1, quot;TCquot; unsigned :ip_len, 16, quot;Lengthquot; unsigned :dns_rd, 1, quot;RDquot; unsigned :ip_id, 16, quot;IDquot; unsigned :dns_ra, 1, quot;RAquot; unsigned :ip_off, 16, quot;Frag offsetquot; unsigned :dns_z, 3, quot;Zquot; unsigned :ip_ttl, 8, quot;TTLquot; unsigned :dns_rcode, 4, quot;RCODEquot; unsigned :ip_p, 8, quot;Protocolquot; unsigned :dns_qdcount, 16, quot;QDCountquot; unsigned :ip_sum, 16, quot;Checksumquot; unsigned :dns_ancount, 16, quot;ANCountquot; octets :ip_src, 32, quot;Source addrquot; unsigned :dns_arcount, 16, quot;ARCountquot; octets :ip_dst, 32, quot;Dest addrquot; rest :data, quot;Dataquot; rest :body, quot;Body of messagequot; end note quot;rest is application defined message bodyquot; initial_value.ip_v = 4 class Time initial_value.ip_hl = 5 # tcpdump style format end def to_s sprintf quot;%0.2d:%0.2d:%0.2d.%0.6dquot;, hour, min, sec, tv_usec class UDP < BitStruct end unsigned :udp_srcport, 16, quot;Source Portquot; end unsigned :udp_dstport, 16, quot;Dest Portquot; unsigned :udp_len, 16, quot;UDP Lengthquot; unsigned :udp_chksum, 16, quot;UDP Checksumquot; rest :body, quot;Body of messagequot; note quot;rest is application defined message bodyquot; end
  • 18. Capturing UDP packets tcpdump.rb #!/usr/local/bin/ruby require 'pcaplet' include Pcap require 'udpip' DIVIDER = quot;-quot; * 50 def print_details(section) puts DIVIDER, section, DIVIDER end pcaplet = Pcaplet.new('-s 1500') pcaplet.each_packet { |pkt| if pkt.udp? puts quot;Packet: #{pkt.time} #{pkt}quot; if (pkt.sport == 53) udp = UDP.new udp.udp_srcport = pkt.sport udp.udp_dstport = pkt.dport udp.udp_len = pkt.udp_len udp.udp_chksum = pkt.udp_sum udp.body = pkt.udp_data print_details udp.inspect_detailed # look for DNS request only dns = DNSQueryHeader.new(pkt.udp_data) bytearray = Array.new udp.body.each_byte { |c| bytearray.concat(c.to_s.to_a) print c.to_s(16), ' ' } print_details dns.inspect_detailed end end } pcaplet.close
  • 19. A live UDP packet
  • 20. A live DNS packet
  • 21. Can we have that on Windows? A GUI? You gotta be joking!! Why do you think we use Macs? How about we just turn it into a web application instead? Sure, we can do that with Ruby [What have we let ourselves in for...]
  • 22. The NDA kicks in Here’s where we hit the brick wall on what we can talk about You might imagine a DNS-sniffing web application, but we couldn’t possibly comment So lets get down to some web app basics And yes, we will be kicking it old-skool...
  • 23. Introducing WEBrick WEBrick is an HTTP ser ver library It’s part of the Ruby 1.8 release It can ser ve static documents It can ser ve HTTPS using Ruby/OpenSSL It can ser ve arbitrary code blocks It can ser ve ser vlets
  • 24. Static content A standard HTTP ser ver #!/usr/local/bin/ruby require 'webrick' server = WEBrick::HTTPServer.new(:Port => 8080, :DocumentRoot => Dir::pwd + quot;/htdocsquot;) # mount personal directory, generating directory indexes server.mount(quot;/~eleanorquot;, WEBrick::HTTPServlet::FileHandler, quot;/Users/eleanor/Sitesquot;, true) # catch keyboard interrupt signal to terminate server trap(quot;INTquot;){ server.shutdown } server.start An HTTPS ser ver #!/usr/local/bin/ruby # This requires Ruby/OpenSSL require 'webrick' require 'webrick/https' certificate_name = [ [quot;Cquot;,quot;UKquot;], [quot;Oquot;,quot;games-with-brains.orgquot;], [quot;CNquot;, quot;WWWquot;] ] server = WEBrick::HTTPServer.new( :DocumentRoot = Dir::pwd + quot;/htdocsquot;, :SSLEnable = true, :SSLVerifyClient = ::OpenSSL::SSL::VERIFY_NONE, :SSLCertName = certificate_name ) trap(quot;INTquot;){ s.shutdown } s.start
  • 25. Ser vlets A Ruby code block #!/usr/local/bin/ruby require 'webrick' server = WEBrick::GenericServer.new() trap(quot;INTquot;){ server.shutdown } server.start{|socket| socket.puts(quot;This is a code blockrquot;) } A WEBrick ser vlet #!/usr/local/bin/ruby require 'webrick' server = WEBrick::HTTPServer.new() trap(quot;INTquot;){ server.shutdown } def generate_response(response) response.body = quot;HTMLhello, world./HTMLquot; response['Content-Type'] = quot;text/htmlquot; end class HelloServlet WEBrick::HTTPServlet::AbstractServlet def do_GET(request, response) generate_response(response) end end server.mount_proc(quot;/hello/simplequot;){ | request, response | generate_response(response) } server.mount(quot;/hello/advancedquot;, HelloServlet) server.start
  • 26. It’s that simple? Yes, it’s that simple Of course these are trivial examples... ...so let’s build an application ser ver
  • 27. An application ser ver Still wondering when we get to the really good stuff? Soon, we promise But first to show you how NOT to do it!
  • 28. Wrap the request A basic request context class RequestContext attr_reader :request, :response, :servlets, :creation_time def initialize(request, response) @request, @response, = request, response @creation_time = Time.now() end def page_not_found @response.status = WEBrick::HTTPStatus::NotFound.new() end def response_page(page) @response['Content-Type'] = page.content_type @response.body = CGI::pretty(page.to_str()) end def (item) @response.body CGI::pretty(item) end end
  • 29. Ser ve the pages The application ser ver IP_ADDRESS_PATTERN = /^d{1,3}.d{1,3}.d{1,3}.d{1,3}/ class ApplicationServer attr_reader :web_server, :server_address, :servlets, :pages def initialize(parameters = {}) @server_address = parameters[:my_address] or raise “Please supply a server address” raise “Invalid IP address for server” unless IP_ADDRESS_PATTERN.match(@server_address) @web_server = WEBrick::HTTPServer.new({:BindAddress = @server_address}) @servlets = {} @pages = {} end def start trap(quot;INTquot;) { @web_server.shutdown } @web_server.start end def register_page(path, page) @pages[path] = page @web_server.mount_proc(path) { | request, response | context = RequestContext.new(request, response) @pages[request.path] ? context.response_page(@pages[request.path]) : context.page_not_found() } end def register_method(path, handler) @servlets[path] = self.method(handler).to_proc @web_server.mount_proc(path) { | request, response | context = RequestContext.new(request, response) @servlets[request.path] ? (context @servlets[request.path].call(context).to_str()) : context.page_not_found() } end end
  • 30. Write the application Revisiting “hello, world” #!/usr/local/bin/ruby require 'appserver.rb' class SimpleServer ApplicationServer def initialize(parameters = {}) super register_page(quot;/hello/simplequot;, quot;HTMLHello, world/HTMLquot;) register_method(quot;/hello/advancedquot;, :hello_world) end def hello_world(context) quot;HTMLHello, world/HTMLquot; end end begin SimpleServer.new({:my_address = ARGV.shift()}).start() rescue RuntimeError = e $stderr.puts quot;Usage: simpleserver host-addressquot; $stderr.puts quot; address must be provided in dotted-quad format (i.e. xxx.xxx.xxx.xxx)quot; end
  • 31. What have we done?!? On the surface this is elegant But underneath it sucks There’s no support for HTML Only methods can be used as ser vlets We’re tied to WEBrick - which is slow
  • 32. The road to perdition So we added an HTML 4 library And a ser ver pages container And ActiveRecord We meta’d the code to death But it still lacked va-va-voom...
  • 33. The case for Rails So perhaps we should have just used Rails in the first place We’d be another of those “Rails saved my career” success stories! Hindsight’s always 20/20 But we’re old-school coders and it’s far too user friendly for our comfort
  • 34. The pressure against Working at a very low level Simple code required Can Rails talk nicely to low-level code? Strong management resistance - too high a learning cur ve?
  • 35. So why Camping? Camping is beauty incarnate It’s less than 4K of code It uses Markaby and ActiveRecord It runs on JRuby!!! Oh, and it’s great fun to abuse...
  • 36. Gratuitous diagram How Why? The Lucky Stiff teaches it lifted from http://redhanded.hobix.com/bits/campingAMicroframework.html
  • 37. Markaby An XHTML Domain Specific Language Allows you to embed XHTML code in Ruby code without building a complex object hierarchy Can be used with Rails
  • 38. But that’s so simple! Markaby embedded in Ruby require 'markaby' page = Markaby::Builder.new page.xhtml_strict do head { title quot;Camping Presentationquot; } body do h1.page_heading quot;Camping: Going off the Rails with Rubyquot; ul.page_index do li.page_index { a “introduction”, :href = ‘#introduction’ } li.page_index { a “the presentation”, :href = ‘/presentation’ } li.page_index { a “comments”, :href = ‘#comments’ } end div.introduction! { “Everything will be alright!!!” } div.comments! { “Have your say” } end end puts page.to_s ?xml version=quot;1.0quot; encoding=quot;UTF-8quot;? !DOCTYPE html PUBLIC quot;-//W3C//DTD XHTML 1.0 Strict//ENquot; quot;DTD/xhtml1-strict.dtdquot; html lang=quot;enquot; xml:lang=quot;enquot; xmlns=quot;http://www.w3.org/1999/xhtmlquot; head meta content=quot;text/html; charset=utf-8quot; http-equiv=quot;Content-Typequot;/ titleCamping Presentation/title /head body h1 class=quot;page_headingquot;Camping: Going off the Rails with Ruby/h1 ul class=quot;page_indexquot; li class=quot;page_indexquot;a href=quot;#introductionquot;introduction/a/li Creates this li class=quot;page_indexquot;a href=quot;/presentationquot;the presentation/a/li li class=quot;page_indexquot;a href=quot;#commentsquot;comments/a/li /ul div id=quot;introductionquot;Just breathe deeply.../div div id=quot;commentsquot;Have your say/div /body /html
  • 39. ActiveRecord An Object-Relational Mapper Implements the Active Record pattern Supports many popular databases A key component of Rails
  • 40. ORMtastic Using Active Record require 'rubygems' require_gem ‘activerecord’ ActiveRecord::Base.establish_connection(:adapter = “sqlite3”, :host = “localhost”, :database = “test.db”) class User ActiveRecord::Base end user = User.new() user.id = “ellie” user.name = “Eleanor McHugh” user.password = “somerandomtext” user.save user = User.find(“ellie”) user.destroy()
  • 41. Totally RAD Camping builds small applications Why’s guideline? One file per application If that’s how you prefer it...
  • 42. A simple example Basic setup #!/usr/bin/env ruby $:.unshift File.dirname(__FILE__) + quot;/../../libquot; require 'camping' require 'camping/session' Camping.goes :Jotter module Blog include Camping::Session end Load the camping libraries Define a namespace for the application Include session support (if required)
  • 43. The data model Defining the data model module Jotter::Models class Note Base; end class Database V 1.0 def self.up create_table :jotter_notes, :force = true do |t| t.column :id, :integer, :null = false t.column :created_at, :interger, :null = false t.column :title, :string, :limit = 255 t.column :body, :text end end def self.down drop_table :jotter_notes end end end def Jotter.create Jotter::Models.create_schema end We mark our database as version 1.0 A create method builds the database
  • 44. The controllers Adding controllers module Jotter::Controllers class Static R '/static/(.+)' MIME_TYPES = {'.css' = 'text/css', '.js' = 'text/javascript', '.jpg' = 'image/jpeg'} PATH = __FILE__[/(.*)//, 1] def get(path) @headers['Content-Type'] = MIME_TYPES[path[/.w+$/, 0]] || quot;text/plainquot; @headers['X-Sendfile'] = quot;#{PATH}/static/#{path}quot; end end class Index R '/' def get @notes = Note.find :all render :index end end class View R '/view/(d+)' def get note_id @note = Note.find post_id render :view end end class Add R ‘/add/’ def get @note = Note.new render :add end def post note = Note.create :title = input.post_title, :body = input.post_body redirect View, post end end
  • 45. The controllers Adding controllers class Edit R '/edit/(d+)', '/edit' def get note_id @note = Note.find note_id render :edit end def post @note = Note.find input.note_id @note.update_attributes :title = input.post_title, :body = input.post_body redirect View, @note end end class Delete R '/delete/(d+)' def get note_id @note = Note.find note_id @note.destroy redirect Index end end end Respond to HTTP GET and POST requests Perform database operations
  • 46. The views Application views module Jotter::Views def layout xhtml_strict do head do title 'blog' link :rel = 'stylesheet', :type = 'text/css', :href = '/static/styles.css', :media = 'screen' end body do h1.header { a 'jotter', :href = R(Index) } div.body do self yield end end end end def index @notes.empty? (p 'No posts found.') : (ol.row! { _list_notes(@notes) }) p { a 'new note', :href = R(Add) } end def edit _form(@note, :action = R(Edit)) end def view h1 @note.title h2 @note.created_at p @note.body p do [ a(quot;Viewquot;, :href = R(View, @note)), a(quot;Editquot;, :href = R(Edit, @note)), a(quot;Deletequot;, :href = R(View, @note)) ].join quot; | quot; end end
  • 47. The views Application views def _list_notes(notes) @notes.each do | note | li do ul do li { a note.title, :href = R(View, note) } li note.created_at li { a quot;Editquot;, :href = R(Edit, note) } li { a quot;Deletequot;, :href = R(Delete, note) } end end end end def _form(note, opts) form({:method = 'post'}.merge(opts)) do label 'Title', :for = 'note_title'; br input :name = 'note_title', :type = 'text', :value = note.title; br label 'Body', :for = 'note_body'; br textarea note.body, :name = 'note_body'; br input :type = 'hidden', :name = 'note_id', :value = note.id input :type = 'submit' end end Views incorporate Markaby for XHTML Have access to controller data
  • 48. The post-amble A basic CGI post-amble if __FILE__ == $0 Jotter::Models::Base.establish_connection :adapter = 'sqlite3', :database = 'notes.db' Jotter::Models::Base.logger = Logger.new('camping.log') Jotter.create if Jotter.respond_to? :create puts Jotter.run end A Mongrel post-amble if __FILE__ == $0 Jotter::Models::Base.establish_connection :adapter = 'sqlite3', :database = 'notes.db' Jotter::Models::Base.logger = Logger.new('camping.log') Jotter::Models::Base.threaded_connections = false Jotter.create if Jotter.respond_to? :create server = Mongrel::Camping::start(“0.0.0.0”, 3000, “/jotter”, Jotter) puts “Jotter application running at http://localhost:3000/jotter” server.run.join end Allows an application to self-execute Can be customised to suit your platform
  • 49. The style-sheet A simple style sheet body { font-family: Utopia, Georga, serif; } h1.header { background-color: #fef; margin: 0; padding: 10px; } div.body { padding: 10px; } #row ul { list-style: none; margin: 0; padding: 0; padding-top: 4px; } #row li { display: inline; } #row a:link, #row a:visited { padding: 3px 10px 2px 10px; color: #FFFFFF; background-color: #B51032; text-decoration: none; border: 1px solid #711515; }
  • 50. Larger applications One application per file is a nice idea But what about large applications? Each can be broken down into discrete micro-applications Each micro-application has its own file and mount points
  • 51. Sharing a database Camping apps keep their database tables in separate namespaces Larger applications will want to share state bet ween micro-applications We could do some ActiveRecord voodoo Or we could cheat... guess which?
  • 52. Camping in the wilds Installing a database in the framework require 'rubygems' require_gem 'camping', '=1.4' require 'camping/session' module Camping module Models def self.schema(block) @@schema = block if block_given? @@schema end class User Base validates_uniqueness_of :name, :scope = :id validates_presence_of :password end end def self.create Camping::Models::Session.create_schema ActiveRecord::Schema.define(Models.schema) end Models.schema do unless Models::User.table_exists? create_table :users, :force = true do |t| t.column :id, :integer, :null = false t.column :created_on, :integer, :null = false t.column :name, :string, :null = false t.column :password, :string, :null = false t.column :comment, :string, :null = false end execute quot;INSERT INTO users (created_on, name, password, comment) VALUES ('#{Time.now}', 'admin', 'admin', 'system administrator')quot; end end end
  • 53. Camping ser ver The camping ser ver ties together a series of web applications A simple implementation ships with the framework
  • 54. The ser ver rules Monitor a directory load/reload all camping apps that appear in it or a subdirectory Mount apps according to the filenames (i.e. jotter.rb mounts as /jotter) Run create method on app startup Support the X-Sendfile header
  • 55. Summing up Web applications are useful outside the usual web app environment Cross platform is easy when you only need an XHTML browser These tasks need a light weight design Camping is a good way to solve them And as you can see, Ruby rocks!!!