Class: PStream

Inherits:
Object
  • Object
show all
Defined in:
lib/pstream.rb

Defined Under Namespace

Classes: CipherNegotiation, Error, Stream

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(pcap, hilight = false) ⇒ PStream

Returns a new instance of PStream.



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/pstream.rb', line 182

def initialize(pcap, hilight = false)
    if (ScoobyDoo.where_are_you("tshark").nil?)
        raise PStream::Error::TsharkNotFound.new
    end

    @@hilight = hilight
    @pcap = Pathname.new(pcap).expand_path

    if (!@pcap.exist?)
        raise PStream::Error::PcapNotFound.new(@pcap)
    elsif (!@pcap.readable?)
        raise PStream::Error::PcapNotReadable.new(@pcap)
    end

    @streams = Hash.new
    ["tcp", "udp"].each do |prot|
        @streams[prot] = get_streams(prot)
    end
end

Instance Attribute Details

#streamsObject (readonly)

Returns the value of attribute streams.



6
7
8
# File 'lib/pstream.rb', line 6

def streams
  @streams
end

Class Method Details

.hilight?Boolean

Returns:

  • (Boolean)


140
141
142
143
# File 'lib/pstream.rb', line 140

def self.hilight?
    @@hilight ||= false
    return @@hilight
end

Instance Method Details

#cipher_negotiationsObject



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/pstream.rb', line 8

def cipher_negotiations
    negotiations = Hash.new
    negotiation = nil
    hello = nil

    # List ciphers during ssl handshake
    %x(
        tshark -r #{@pcap} -Y ssl.handshake.ciphersuite -V 2>&1 \
            | \grep -E "(Handshake|Internet) Prot|Cipher Suite"
    ).split("\n").each do |line|
        case line.gsub(/^ +/, "")
        when /^Cipher Suite:/
            m = line.match(/Cipher Suite: ([^ ]+) (.*)$/)
            case hello
            when "Client"
                case m[1]
                when "Unknown"
                    negotiation.suites.push("#{m[1]} #{m[2]}")
                else
                    negotiation.suites.push(m[1])
                end
            when "Server"
                id = "#{negotiation.dst} <-> #{negotiation.src}"
                # Ignore partial handshakes that are server side
                # only
                if (negotiations[id])
                    case m[1]
                    when "Unknown"
                        negotiations[id].suite = "#{m[1]} #{m[2]}"
                    else
                        negotiations[id].suite = m[1]
                    end
                end
                negotiation = nil
            end
        when /^Cipher Suites Length:/
            m = line.match(/Cipher Suites Length: ([0-9]+)$/)
            negotiation.length = m[1].to_i
        when /^Handshake Protocol: [^ ]+ Hello$/
            m = line.match(/Handshake Protocol: ([^ ]+) Hello$/)
            hello = m[1]
        when /^Handshake Protocol: Certificate$/
            # TODO extract cert info
        when /^Handshake Protocol: Server Key Exchange$/
            # TODO extract key info
        when /^Internet Protocol Version/
            if (negotiation)
                id = "#{negotiation.src} <-> #{negotiation.dst}"
                negotiations[id] = negotiation
            end

            m = line.gsub("Internet Protocol Version", "").match(
                /(4|6), Src: ([^,]+), Dst: (.*)$/
            )

            ipv = m[1]
            src = m[2]
            dst = m[3]

            negotiation = PStream::CipherNegotiation.new(
                self,
                ipv,
                src,
                dst
            )
        end
    end

    # Keep parital handshakes that are client side only
    if (negotiation)
        id = "#{negotiation.src} <-> #{negotiation.dst}"
        negotiations[id] = negotiation
    end

    return negotiations.values
end

#get_stream(stream, prot = "tcp") ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/pstream.rb', line 85

def get_stream(stream, prot = "tcp")
    case prot
    when /^tcp$/i
        if (@streams["tcp"].empty? && !@streams["udp"].empty?)
            return get_stream(stream, "udp")
        end
        if (stream >= @streams["tcp"].length)
            if (stream < @streams["udp"].length)
                return @streams["udp"][stream]
            end
            raise PStream::Error::StreamNotFound.new(stream, prot)
        end
        return @streams["tcp"][stream]
    when /^udp$/i
        if (@streams["udp"].empty? && !@streams["tcp"].empty?)
            return get_stream(stream, "tcp")
        end
        if (stream >= @streams["udp"].length)
            if (stream < @streams["tcp"].length)
                return @streams["tcp"][stream]
            end
            raise PStream::Error::StreamNotFound.new(stream, prot)
        end
        return @streams["udp"][stream]
    else
        raise PStream::Error::ProtocolNotSupported.new(prot)
    end
end

#hilight_cipher_suite(suite) ⇒ Object



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/pstream.rb', line 145

def hilight_cipher_suite(suite)
    return suite if (!@@hilight)

    case suite
    when /Unknown/
        # Unknown
        return suite.light_yellow
    when /NULL|MD5|RC4|anon/
        # Bad cipher suites
        return suite.light_red
    when /E?(EC)?DHE?|AES_256/
        # Great cipher suites
        return suite.light_green
    else
        # Maybe OK
        return suite.light_white
    end
end

#to_sObject



226
227
228
# File 'lib/pstream.rb', line 226

def to_s
    return summary
end