Steven Jewel Blog RSS Feed

29 Jan 2014

My Stripe CTF3 level3 solution

Stripe CTF is a yearly programming contest. This year the problems were distributed programming problems.

Level 3 was a distributed search engine. However, when looking at the problem, I thought I had an easy way to handle all the searches on the main node.

Instead of using a more complex algorithm, I put all the words into a string and then relied on ruby's .index() to find matches quickly:

#!/usr/bin/env ruby

port = 9090

exit 0 unless ARGV.member?( '--master' )

ARGV.shift while ARGV.size > 0

require 'sinatra'
require 'json'
Thread.abort_on_exception = true

set :port, port
disable :sessions, :logging, :show_exceptions

get '/healthcheck' do
  { success: true }.to_json
end

indexed = false

require 'find'

master = String.new
words = {}
word_locations = {}

get '/index' do
  path = params[:path]

  Thread.new do
    warn "Indexing #{path}"
    Find.find( path ) do |file|
      next if File.directory? file
      relative_path = file[(path.size+1)..-1]
      warn "#{words.size} - Adding #{relative_path}, of size #{File.size(file)}"
      data = File.readlines( file )
      data.each_with_index do |line,number|
        line.split( /\W+/ ).each do |word|
          next if word == ""
          if !words[word]
            words[word] = true
            master << "#{word}\n"
          end
          word_locations[word] ||= []
          word_locations[word] << relative_path.to_sym
          word_locations[word] << number+1
        end
      end
    end

    warn "Master size: #{master.size} bytes"

    indexed = true
  end

  "OK"
end

get '/isIndexed' do
  {success: indexed}.to_json
end

get '/' do
  query = params[:q]
  warn "Searching for #{query.inspect}"
  res = []
  prev = 0
  while pos = master.index( query, prev )
    start = master.rindex "\n", pos
    finish = master.index "\n", pos
    word = master[(start+1)...finish]

    word_locations[word].each_slice(2) do |pair|
      res << pair.join(":")
    end

    prev = finish
  end

  { success: true, results: res.uniq }.to_json
end