-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathparse_yahoo_im.rb
More file actions
150 lines (130 loc) · 5.5 KB
/
parse_yahoo_im.rb
File metadata and controls
150 lines (130 loc) · 5.5 KB
1
2
3
4
5
6
7
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
84
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
class Protos
def parse_yahoo_im(data, state, dir)
return nil unless data
req = state.app_state[:req_struct]
res = state.app_state[:resp_struct]
pos = 0
# Hand off to the client or server parser as needed.
dir = state.app_state[dir][:type]
obj = (dir == :client ? req : res)
# Check our initialization condition
unless obj
obj = Struct.new(
:state, :buff, :maxlen, :terminator, :service, :version, :user
).new
obj.state = :get_header
_prepare_to_copy(obj, 20)
state.app_state[:resp_struct] = obj if dir == :server
state.app_state[:req_struct] = obj if dir == :client
end
while pos < data.length
case obj.state
# Find the channel header (always six bytes)
when :get_header
pos, ret = _copy_bytes(obj, data, pos)
return true if ret == true
# We have our header, let's dissect it
raise "Yahoo IM traffic out of sync" unless ret[0,4] == 'YMSG'
# Get the version, length, and service
obj.version = _big_endian(ret[4,2]) unless obj.version
len = _big_endian(ret[8,2])
obj.service = _big_endian(ret[10,2])
# Set up the next state
if len > 0
_prepare_to_copy(obj, len)
obj.state = :get_content
# Skip past states we don't care about
unless [6, 85, 241].include?(obj.service)
obj.state = :skip_content
end
else
_prepare_to_copy(obj, 20)
end
# Yahoo IM is pretty simple. Each message is a header (which we
# already have) and an optional content array. Here we're just
# getting, splitting and parsing that array.
when :get_content
pos, ret = _copy_bytes(obj, data, pos)
return true if ret == true
# Go ahead and set up the next state
_prepare_to_copy(obj, 20)
obj.state = :get_header
# Get a content hash out of this silly flat array
content, i = {}, nil
ret.split("\xc0\x80").each do |elm|
if i
content[i], i = elm, nil
else
i = elm.to_i
end
end
# Parse the service messages we care about
case obj.service
when 6 # message
msg = content[14]
next unless msg # What are we doing if there's no message?
recipient = content[5]
sender = content[4] || content[1]
chat_dir = (dir == :client ? :outgoing : :incoming)
@event_collector.send(:yahoo_im_message) do
{ :server_ip => str_ip(state.app_state[:dst]),
:client_ip => str_ip(state.app_state[:src]),
:server_port => state.app_state[:dport],
:client_port => state.app_state[:sport], :dir => dir,
:sender => sender, :recipient => recipient,
:chat_dir => chat_dir, :msg => msg }
end
@event_collector.send(:protos_chat_message) do
{ :server_ip => str_ip(state.app_state[:dst]),
:client_ip => str_ip(state.app_state[:src]),
:server_port => state.app_state[:dport],
:client_port => state.app_state[:sport], :dir => dir,
:sender => sender, :recipient => recipient,
:chat_dir => chat_dir, :msg => msg,
:protocol => :yahoo_im }
end
when 85 # List (denotes sign on)
username = content[3] || content[89]
next unless username
obj.user = username
@event_collector.send(:yahoo_im_login) do
realname = "#{content[216]} #{content[254]}"
realname = nil unless realname.length > 1
{ :server_ip => str_ip(state.app_state[:dst]),
:client_ip => str_ip(state.app_state[:src]),
:server_port => state.app_state[:dport],
:client_port => state.app_state[:sport], :dir => dir,
:username => username, :name => realname }
end
when 241 # Buddy list?
next unless content[65]
next unless content[7] # Have to have at least one friend
# This is a little annoying; redundant array indices!
@event_collector.send(:yahoo_im_friend_list) do
friends, i = [], nil
ret.split("\xc0\x80").each do |elm|
if i
friends << elm if i == 7
i = nil
else
i = elm.to_i
end
end
{ :server_ip => str_ip(state.app_state[:dst]),
:client_ip => str_ip(state.app_state[:src]),
:server_port => state.app_state[:dport],
:client_port => state.app_state[:sport], :dir => dir,
:username => obj.user, :friends => friends }
end
end # of case service
# Just skip past the given number of bytes
when :skip_content
pos, ret = _skip_bytes(obj, data, pos)
return true if ret
_prepare_to_copy(obj, 20)
obj.state = :get_header
end # of case
end # of while data
true
end # of parse_yahoo_im
end # of class Protos