by2 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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