by2 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/by2.rb ADDED
@@ -0,0 +1,69 @@
1
+ require 'rubygems'
2
+ require 'yaml'
3
+ require "optparse"
4
+ require 'ostruct'
5
+ require 'pg'
6
+ require 'active_record'
7
+ require 'composite_primary_keys'
8
+
9
+ require_relative 'by2/version'
10
+ require_relative 'by2/options'
11
+ require_relative 'by2/config_loader'
12
+ require_relative 'by2/utils'
13
+ require_relative 'by2/ext/active_record'
14
+ require_relative 'by2/models'
15
+ require_relative 'by2/client'
16
+
17
+
18
+ module By2
19
+ def self.root
20
+ @root ||= File.expand_path(File.join(__FILE__, '..', '..'))
21
+ end
22
+
23
+ def self.config
24
+ @config ||= ConfigLoader.new
25
+ end
26
+
27
+ def self.fixtures_dir
28
+ File.join(root, "spec", "fixtures")
29
+ end
30
+
31
+ def self.db_connect
32
+ @conn ||= begin
33
+ conn_config = config.load("database.yml")
34
+ #ActiveSupport::Deprecation.silenced = true
35
+ ActiveRecord::Base.default_timezone = :local
36
+ ActiveRecord::Base.establish_connection(conn_config[env])
37
+ ActiveRecord::Base.connection
38
+ ActiveRecord::Base.logger = Logger.new(STDOUT) if debug?
39
+ end
40
+ end
41
+
42
+ def self.env
43
+ @env ||= (ENV['BY2_ENV'] || config.load("env.yml", false) || "development")
44
+ end
45
+
46
+ def self.debug?
47
+ @debug ||= false
48
+ end
49
+
50
+ def self.debug=(d)
51
+ @debug = d
52
+ end
53
+
54
+ def self.debug(msg)
55
+ $stdout.puts(msg) if debug?
56
+ end
57
+ end
58
+
59
+
60
+ By2.db_connect
61
+
62
+ =begin
63
+ require "./lib/by2"
64
+ By2.db_connect
65
+ cli = By2::Client.new([])
66
+ ip = By2::Utils.int32_to_ip(2154959208)
67
+
68
+ q = cli.find_entries({ip: ip});
69
+ =end
data/lib/by2/client.rb ADDED
@@ -0,0 +1,109 @@
1
+ module By2
2
+ class Client
3
+ include Models
4
+
5
+ def initialize(argv=[])
6
+ @opts = Options.parse(argv)
7
+
8
+ By2.debug = @opts.delete(:debug)
9
+ By2.db_connect
10
+ By2.debug(@opts.inspect)
11
+ end
12
+
13
+ ##
14
+ # payload: sid: 1, cid: 5
15
+ # 2149599422 => "128.32.72.190"
16
+ # 2954912804 => "176.32.100.36"
17
+ def find_records(options = {})
18
+ @opts = @opts.merge(options)
19
+ tables = %w(iphdr tcphdr udphdr icmphdr payload)
20
+
21
+ query = Event.
22
+ includes(*tables).
23
+ references(*tables).
24
+ order("event.timestamp")
25
+
26
+ query.
27
+ merge(ip_src_or_dst).
28
+ merge(port_src_or_dst).
29
+ merge(port_src).
30
+ merge(port_dst).
31
+ merge(ip_src).
32
+ merge(ip_dst).
33
+ merge(date).
34
+ merge(date_range)
35
+ end
36
+
37
+ def run
38
+ records = find_records
39
+
40
+ unless @opts[:count]
41
+ records.each { |r| $stdout.puts(terminal(r)) }
42
+ end
43
+
44
+ $stdout.puts(record_separator)
45
+ $stdout.puts(record_count(records.count))
46
+ end
47
+
48
+ private
49
+
50
+ def record_separator
51
+ ("-" * 80)
52
+ end
53
+
54
+ def record_count(n)
55
+ "Total records: #{n}\n\n"
56
+ end
57
+
58
+ def terminal(event)
59
+ e = event
60
+ ("-" * 80) + "\n" \
61
+ "[#{e.timestamp}] #{e.iphdr.ipaddr_src}:#{e.sport} -> #{e.iphdr.ipaddr_dst}:#{e.dport} (#{e.transport})\n\n" \
62
+ "#{(e.payload && e.payload.to_s.strip) || "[no payload]"}\n\n"
63
+ end
64
+
65
+ def all
66
+ Event.all
67
+ end
68
+
69
+ def ip_src_or_dst
70
+ return all if @opts[:ip].nil?
71
+ Iphdr.ip_src_or_dst(@opts[:ip])
72
+ end
73
+
74
+ def port_src_or_dst
75
+ return all if @opts[:port].nil?
76
+ Tcphdr.src_or_dst_port(@opts[:port]).or(Udphdr.src_or_dst_port(@opts[:port]))
77
+ end
78
+
79
+ def port_src
80
+ return all if @opts[:src_port].nil?
81
+ Tcphdr.src_port(@opts[:src_port]).or(Udphdr.src_port(@opts[:src_port]))
82
+ end
83
+
84
+ def port_dst
85
+ return all if @opts[:dst_port].nil?
86
+ Tcphdr.dst_port(@opts[:dst_port]).or(Udphdr.dst_port(@opts[:dst_port]))
87
+ end
88
+
89
+ def ip_src
90
+ return all if @opts[:src_ip].nil?
91
+ Iphdr.ip_src(@opts[:src_ip])
92
+ end
93
+
94
+ def ip_dst
95
+ return all if @opts[:dst_ip].nil?
96
+ Iphdr.ip_dst(@opts[:dst_ip])
97
+ end
98
+
99
+ def date
100
+ return all if @opts[:date].nil?
101
+ Event.on_date(@opts[:date])
102
+ end
103
+
104
+ def date_range
105
+ return all if @opts[:start_date].nil?
106
+ Event.in_date_range(@opts[:start_date], @opts[:end_date])
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,34 @@
1
+ module By2
2
+ class ConfigLoader
3
+ attr_accessor :search_paths
4
+
5
+ def initialize
6
+ @search_paths ||= [app_config, user_config]
7
+ end
8
+
9
+ def load(config_file, required = false)
10
+ search_paths.each do |path|
11
+ file = File.expand_path(File.join(path, config_file))
12
+ By2.debug("Searching for #{file} in #{path}")
13
+
14
+ if File.exists?(file)
15
+ By2.debug("Loaded: #{file}")
16
+ return YAML.load_file(file)
17
+ end
18
+ end
19
+
20
+ By2.debug("Could not load: #{config_file}")
21
+ required ? raise : nil
22
+ end
23
+
24
+ private
25
+
26
+ def app_config
27
+ @app_config ||= File.expand_path(File.join(::By2.root, "config"))
28
+ end
29
+
30
+ def user_config
31
+ @user_config ||= File.expand_path(File.join(ENV['HOME'], ".by2"))
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,105 @@
1
+ # At some point this should be merged into ActiveRecord or a gem should
2
+ # become available with this same functionality.
3
+ #
4
+ # Original gist: https://gist.github.com/j-mcnally/250eaaceef234dd8971b
5
+ # Github Pull Request: https://github.com/rails/rails/pull/9052
6
+
7
+ module ActiveRecord
8
+ module Querying
9
+ delegate :or, :to => :all
10
+ end
11
+ end
12
+
13
+ module ActiveRecord
14
+ module QueryMethods
15
+ # OrChain objects act as placeholder for queries in which #or does not have any parameter.
16
+ # In this case, #or must be chained with any other relation method to return a new relation.
17
+ # It is intended to allow .or.where() and .or.named_scope.
18
+ class OrChain
19
+ def initialize(scope)
20
+ @scope = scope
21
+ end
22
+
23
+ def method_missing(method, *args, &block)
24
+ right_relation = @scope.klass.unscoped do
25
+ @scope.klass.send(method, *args, &block)
26
+ end
27
+ @scope.or(right_relation)
28
+ end
29
+ end
30
+
31
+ # Returns a new relation, which is the result of filtering the current relation
32
+ # according to the conditions in the arguments, joining WHERE clauses with OR
33
+ # operand, contrary to the default behaviour that uses AND.
34
+ #
35
+ # #or accepts conditions in one of several formats. In the examples below, the resulting
36
+ # SQL is given as an illustration; the actual query generated may be different depending
37
+ # on the database adapter.
38
+ #
39
+ # === without arguments
40
+ #
41
+ # If #or is used without arguments, it returns an ActiveRecord::OrChain object that can
42
+ # be used to chain queries with any other relation method, like where:
43
+ #
44
+ # Post.where("id = 1").or.where("id = 2")
45
+ # # SELECT `posts`.* FROM `posts` WHERE (('id = 1' OR 'id = 2'))
46
+ #
47
+ # It can also be chained with a named scope:
48
+ #
49
+ # Post.where("id = 1").or.containing_the_letter_a
50
+ # # SELECT `posts`.* FROM `posts` WHERE (('id = 1' OR 'body LIKE \\'%a%\\''))
51
+ #
52
+ # === ActiveRecord::Relation
53
+ #
54
+ # When #or is used with an ActiveRecord::Relation as an argument, it merges the two
55
+ # relations, with the exception of the WHERE clauses, that are joined using the OR
56
+ # operand.
57
+ #
58
+ # Post.where("id = 1").or(Post.where("id = 2"))
59
+ # # SELECT `posts`.* FROM `posts` WHERE (('id = 1' OR 'id = 2'))
60
+ #
61
+ # === anything you would pass to #where
62
+ #
63
+ # #or also accepts anything that could be passed to the #where method, as
64
+ # a shortcut:
65
+ #
66
+ # Post.where("id = 1").or("id = ?", 2)
67
+ # # SELECT `posts`.* FROM `posts` WHERE (('id = 1' OR 'id = 2'))
68
+ #
69
+ def or(opts = :chain, *rest)
70
+ if opts == :chain
71
+ OrChain.new(self)
72
+ else
73
+ left = with_default_scope
74
+ right = (ActiveRecord::Relation === opts) ? opts : klass.unscoped.where(opts, rest)
75
+
76
+ unless left.where_values.empty? || right.where_values.empty?
77
+ left.where_values = [left.where_ast.or(right.where_ast)]
78
+ right.where_values = []
79
+ end
80
+
81
+ left = left.merge(right)
82
+ end
83
+ end
84
+
85
+
86
+ # Returns an Arel AST containing only where_values
87
+ def where_ast
88
+ arel_wheres = []
89
+
90
+ where_values.each do |where|
91
+ arel_wheres << (String === where ? Arel.sql(where) : where)
92
+ end
93
+
94
+ return Arel::Nodes::And.new(arel_wheres) if arel_wheres.length >= 2
95
+
96
+ if Arel::Nodes::SqlLiteral === arel_wheres.first
97
+ Arel::Nodes::Grouping.new(arel_wheres.first)
98
+ else
99
+ arel_wheres.first
100
+ end
101
+ end
102
+
103
+
104
+ end
105
+ end
data/lib/by2/models.rb ADDED
@@ -0,0 +1,10 @@
1
+ require_relative 'models/event'
2
+ require_relative 'models/payload'
3
+ require_relative 'models/tcphdr'
4
+ require_relative 'models/udphdr'
5
+ require_relative 'models/icmphdr'
6
+ require_relative 'models/iphdr'
7
+
8
+
9
+ module Models
10
+ end
@@ -0,0 +1,50 @@
1
+ module By2
2
+ module Models
3
+
4
+ class Event < ActiveRecord::Base
5
+ self.table_name = 'event'
6
+ self.primary_keys = :sid, :cid
7
+
8
+ has_one :payload, :foreign_key => [:sid, :cid]
9
+ has_one :tcphdr, :foreign_key => [:sid, :cid]
10
+ has_one :udphdr, :foreign_key => [:sid, :cid]
11
+ has_one :icmphdr, :foreign_key => [:sid, :cid]
12
+ has_one :iphdr, :foreign_key => [:sid, :cid]
13
+
14
+
15
+ def self.on_date(date)
16
+ where("cast(\"timestamp\" as date) = ?", date)
17
+ end
18
+
19
+ def self.in_date_range(start_date, end_date)
20
+ query = where("cast(\"timestamp\" as date) >= ?", start_date)
21
+ query = query.merge(where("cast(\"timestamp\" as date) <= ?", end_date)) if end_date
22
+ query
23
+ end
24
+
25
+ def transport
26
+ return "TCP" if tcphdr
27
+ return "UCP" if udphdr
28
+ return "ICMP" if icmphdr
29
+
30
+ "UNKNOWN"
31
+ end
32
+
33
+ def dport
34
+ tcphdr.try(:dport) || udphdr.try(:dport)
35
+ end
36
+
37
+ def sport
38
+ tcphdr.try(:sport) || udphdr.try(:sport)
39
+ end
40
+
41
+ def ip_src
42
+ iphdr.try(:ipaddr_src)
43
+ end
44
+
45
+ def ip_dst
46
+ iphdr.try(:ipaddr_dst)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,10 @@
1
+ module By2
2
+ module Models
3
+ class Icmphdr < ActiveRecord::Base
4
+ self.table_name = 'icmphdr'
5
+ self.primary_keys = :sid, :cid
6
+
7
+ belongs_to :event, :foreign_key => [:sid, :cid]
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,38 @@
1
+ module By2
2
+ module Models
3
+
4
+ class Iphdr < ActiveRecord::Base
5
+ self.table_name = 'iphdr'
6
+ self.primary_keys = :sid, :cid
7
+
8
+ belongs_to :event, :foreign_key => [:sid, :cid]
9
+
10
+
11
+ def self.ip_src_or_dst(ip)
12
+ where("iphdr.ip_dst = ? or iphdr.ip_src = ?", int32(ip), int32(ip))
13
+ end
14
+
15
+ def self.ip_src(ip)
16
+ where("iphdr.ip_src = ?", int32(ip))
17
+ end
18
+
19
+ def self.ip_dst(ip)
20
+ where("iphdr.ip_dst = ?", int32(ip))
21
+ end
22
+
23
+ def ipaddr_src
24
+ Utils.int32_to_ip(self.ip_src)
25
+ end
26
+
27
+ def ipaddr_dst
28
+ Utils.int32_to_ip(self.ip_dst)
29
+ end
30
+
31
+ private
32
+
33
+ def self.int32(ip)
34
+ Utils.ip_to_int32(ip)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,16 @@
1
+ module By2
2
+ module Models
3
+
4
+ class Payload < ActiveRecord::Base
5
+ self.table_name = 'data'
6
+ self.primary_keys = :sid, :cid
7
+
8
+ belongs_to :event, :foreign_key => [:sid, :cid]
9
+
10
+
11
+ def to_s
12
+ Utils.hex_to_ascii(self.data_payload)
13
+ end
14
+ end
15
+ end
16
+ end