woolen_common 0.0.1

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.
Files changed (86) hide show
  1. checksums.yaml +15 -0
  2. data/README.md +31 -0
  3. data/ext/woolen_common/extconf.rb +26 -0
  4. data/ext/woolen_common/linux.h +3 -0
  5. data/ext/woolen_common/win.c +18 -0
  6. data/ext/woolen_common/win.h +4 -0
  7. data/ext/woolen_common/win/puts_color.c +139 -0
  8. data/ext/woolen_common/win/puts_color.h +5 -0
  9. data/ext/woolen_common/woolen_common.c +20 -0
  10. data/ext/woolen_common/woolen_common.h +7 -0
  11. data/lib/woolen_common.rb +39 -0
  12. data/lib/woolen_common/abstract_middleware/builder.rb +138 -0
  13. data/lib/woolen_common/abstract_middleware/map_cfg_manager.rb +52 -0
  14. data/lib/woolen_common/abstract_middleware/runner.rb +72 -0
  15. data/lib/woolen_common/action_pool_proxy.rb +28 -0
  16. data/lib/woolen_common/actionpool.rb +10 -0
  17. data/lib/woolen_common/actionpool/pool.rb +295 -0
  18. data/lib/woolen_common/actionpool/queue.rb +41 -0
  19. data/lib/woolen_common/actionpool/thread.rb +181 -0
  20. data/lib/woolen_common/addr_helper.rb +93 -0
  21. data/lib/woolen_common/cache.rb +305 -0
  22. data/lib/woolen_common/common_helper.rb +42 -0
  23. data/lib/woolen_common/config_manager.rb +36 -0
  24. data/lib/woolen_common/drb_helper.rb +125 -0
  25. data/lib/woolen_common/ffi/win32_kernel32.rb +86 -0
  26. data/lib/woolen_common/logger.rb +419 -0
  27. data/lib/woolen_common/pcap/mu/fixnum_ext.rb +8 -0
  28. data/lib/woolen_common/pcap/mu/pcap/ethernet.rb +164 -0
  29. data/lib/woolen_common/pcap/mu/pcap/header.rb +76 -0
  30. data/lib/woolen_common/pcap/mu/pcap/io_pair.rb +68 -0
  31. data/lib/woolen_common/pcap/mu/pcap/io_wrapper.rb +77 -0
  32. data/lib/woolen_common/pcap/mu/pcap/ip.rb +62 -0
  33. data/lib/woolen_common/pcap/mu/pcap/ipv4.rb +274 -0
  34. data/lib/woolen_common/pcap/mu/pcap/ipv6.rb +149 -0
  35. data/lib/woolen_common/pcap/mu/pcap/packet.rb +106 -0
  36. data/lib/woolen_common/pcap/mu/pcap/pkthdr.rb +162 -0
  37. data/lib/woolen_common/pcap/mu/pcap/reader.rb +62 -0
  38. data/lib/woolen_common/pcap/mu/pcap/reader/http_family.rb +175 -0
  39. data/lib/woolen_common/pcap/mu/pcap/sctp.rb +369 -0
  40. data/lib/woolen_common/pcap/mu/pcap/sctp/chunk.rb +124 -0
  41. data/lib/woolen_common/pcap/mu/pcap/sctp/chunk/data.rb +135 -0
  42. data/lib/woolen_common/pcap/mu/pcap/sctp/chunk/init.rb +101 -0
  43. data/lib/woolen_common/pcap/mu/pcap/sctp/chunk/init_ack.rb +69 -0
  44. data/lib/woolen_common/pcap/mu/pcap/sctp/parameter.rb +111 -0
  45. data/lib/woolen_common/pcap/mu/pcap/sctp/parameter/ip_address.rb +49 -0
  46. data/lib/woolen_common/pcap/mu/pcap/stream_packetizer.rb +74 -0
  47. data/lib/woolen_common/pcap/mu/pcap/tcp.rb +522 -0
  48. data/lib/woolen_common/pcap/mu/pcap/udp.rb +81 -0
  49. data/lib/woolen_common/pcap/mu/scenario/pcap.rb +175 -0
  50. data/lib/woolen_common/pcap/mu/scenario/pcap/fields.rb +51 -0
  51. data/lib/woolen_common/pcap/mu/scenario/pcap/rtp.rb +72 -0
  52. data/lib/woolen_common/pcap/pcap.rb +115 -0
  53. data/lib/woolen_common/pcap/readme.md +72 -0
  54. data/lib/woolen_common/ruby_ext/blank.rb +126 -0
  55. data/lib/woolen_common/ruby_ext/drb_ext.rb +7 -0
  56. data/lib/woolen_common/ruby_ext/string.rb +116 -0
  57. data/lib/woolen_common/ruby_ext/win32_ole.rb +4 -0
  58. data/lib/woolen_common/ruby_proxy.rb +5 -0
  59. data/lib/woolen_common/ruby_proxy/client.rb +305 -0
  60. data/lib/woolen_common/ruby_proxy/config.rb +44 -0
  61. data/lib/woolen_common/ruby_proxy/exceptions.rb +17 -0
  62. data/lib/woolen_common/ruby_proxy/proxy.rb +157 -0
  63. data/lib/woolen_common/ruby_proxy/proxy_global_set.rb +44 -0
  64. data/lib/woolen_common/ruby_proxy/proxy_load.rb +34 -0
  65. data/lib/woolen_common/ruby_proxy/server.rb +53 -0
  66. data/lib/woolen_common/splib.rb +36 -0
  67. data/lib/woolen_common/splib/Array.rb +33 -0
  68. data/lib/woolen_common/splib/CodeReloader.rb +59 -0
  69. data/lib/woolen_common/splib/Constants.rb +44 -0
  70. data/lib/woolen_common/splib/Conversions.rb +47 -0
  71. data/lib/woolen_common/splib/Exec.rb +132 -0
  72. data/lib/woolen_common/splib/Float.rb +13 -0
  73. data/lib/woolen_common/splib/HumanIdealRandomIterator.rb +40 -0
  74. data/lib/woolen_common/splib/Monitor.rb +214 -0
  75. data/lib/woolen_common/splib/PriorityQueue.rb +110 -0
  76. data/lib/woolen_common/splib/Sleep.rb +10 -0
  77. data/lib/woolen_common/splib/UrlShorteners.rb +48 -0
  78. data/lib/woolen_common/ssh_proxy.rb +146 -0
  79. data/lib/woolen_common/system_helper.rb +123 -0
  80. data/lib/woolen_common/system_monitor.rb +23 -0
  81. data/lib/woolen_common/system_monitor/linux_monitor.rb +250 -0
  82. data/lib/woolen_common/system_monitor/windows_monitor.rb +145 -0
  83. data/lib/woolen_common/type_helper.rb +42 -0
  84. data/lib/woolen_common/ver_ctrl_middle_ware.rb +92 -0
  85. data/lib/woolen_common/version.rb +3 -0
  86. metadata +210 -0
@@ -0,0 +1,49 @@
1
+ # -*- encoding : utf-8 -*-
2
+ # http://www.mudynamics.com
3
+ # http://labs.mudynamics.com
4
+ # http://www.pcapr.net
5
+
6
+ module Mu
7
+ class Pcap
8
+ class SCTP
9
+ class Parameter
10
+
11
+ class IpAddress < Parameter
12
+ attr_accessor :value
13
+
14
+ def initialize
15
+ super
16
+
17
+ @value = nil
18
+ end
19
+
20
+ def self.from_bytes type, size, bytes
21
+ # Basic validation
22
+ if PARAM_IPV4 == type
23
+ Pcap.assert(size == 8, "Invalid IPv4 address: 4 != #{size}")
24
+ else
25
+ Pcap.assert(size == 20, "Invalid IPv6 address: 16 != #{size}")
26
+ end
27
+
28
+ # Create IP address parameter
29
+ ip_address = IpAddress.new
30
+ ip_address.type = type
31
+ ip_address.size = size
32
+ ip_address.value = IPAddr.new_ntoh(bytes[0, size - 4])
33
+
34
+ # Set raw payload
35
+ ip_address.payload_raw = bytes[0, size - 4]
36
+
37
+ # Return the result
38
+ return ip_address
39
+ end
40
+
41
+ def to_s
42
+ return "address(%s)" % [@value]
43
+ end
44
+ end # class IpAddress
45
+
46
+ end # class Parameter
47
+ end # class SCTP
48
+ end # class Pcap
49
+ end # module Mu
@@ -0,0 +1,74 @@
1
+ # -*- encoding : utf-8 -*-
2
+ # http://www.mudynamics.com
3
+ # http://labs.mudynamics.com
4
+ # http://www.pcapr.net
5
+
6
+ require 'mu/pcap/io_pair'
7
+ require 'mu/pcap/io_wrapper'
8
+
9
+ module Mu
10
+ class Pcap
11
+ class StreamPacketizer
12
+ attr_reader :io_pair, :parser
13
+
14
+ def initialize parser
15
+ @parser = parser
16
+ @key_to_idx = Hash.new do |hash, key|
17
+ if hash.size >= 2
18
+ raise ArgumentError, "Only two endpoints are allowed in a transaction"
19
+ end
20
+ hash[key] = hash.size
21
+ end
22
+ @sent_messages = [[], []].freeze
23
+ @inner_pair = IOPair.stream_pair
24
+ @io_pair = @inner_pair.map { |io| IOWrapper.new io, parser }.freeze
25
+ end
26
+
27
+ def msg_count key
28
+ key = key.inspect
29
+ widx = @key_to_idx[key]
30
+ messages = @sent_messages[widx]
31
+ messages.size
32
+ end
33
+
34
+ def extra_bytes w_key
35
+ w_key = w_key.inspect
36
+
37
+ ridx = @key_to_idx[w_key] ^ 1
38
+ reader = @io_pair[ridx]
39
+ incomplete = reader.unread
40
+ incomplete.empty? ? nil : incomplete.dup
41
+ end
42
+
43
+ def push key, bytes
44
+ key = key.inspect
45
+ widx = @key_to_idx[key]
46
+ writer = @io_pair[widx]
47
+ raw_writer = @inner_pair[widx]
48
+ raw_writer.write bytes
49
+
50
+ messages = @sent_messages[widx]
51
+
52
+ ridx = widx ^ 1
53
+ reader = @io_pair[ridx]
54
+ while msg = reader.read
55
+ messages << msg
56
+ writer.record_write bytes
57
+ end
58
+
59
+ nil
60
+ end
61
+
62
+ def next_msg key
63
+ key = key.inspect
64
+ idx = @key_to_idx[key]
65
+ if m = @sent_messages[idx].shift
66
+ return m.dup
67
+ else
68
+ nil
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+
@@ -0,0 +1,522 @@
1
+ # -*- encoding : utf-8 -*-
2
+ # http://www.mudynamics.com
3
+ # http://labs.mudynamics.com
4
+ # http://www.pcapr.net
5
+
6
+ require 'mu/pcap/reader'
7
+ require 'mu/pcap/stream_packetizer'
8
+
9
+ module Mu
10
+ class Pcap
11
+
12
+ class TCP < Packet
13
+ attr_accessor :src_port, :dst_port, :seq, :ack, :flags, :window, :urgent, :mss, :proto_family,:tcp_options
14
+
15
+ TH_FIN = 0x01
16
+ TH_SYN = 0x02
17
+ TH_RST = 0x04
18
+ TH_PUSH = 0x08
19
+ TH_ACK = 0x10
20
+ TH_URG = 0x20
21
+ TH_ECE = 0x40
22
+ TH_CWR = 0x80
23
+
24
+ MSS = 2
25
+
26
+ def initialize
27
+ super
28
+ @src_port = 0
29
+ @dst_port = 0
30
+ @seq = 0
31
+ @ack = 0
32
+ @flags = 0
33
+ @window = 0
34
+ @urgent = 0
35
+ @mss = 0
36
+ @proto_family = nil
37
+ end
38
+
39
+ def flow_id
40
+ return [:tcp, @src_port, @dst_port]
41
+ end
42
+
43
+ def self.from_bytes bytes
44
+ Pcap.assert bytes.length >= 20, 'Truncated TCP header: ' +
45
+ "expected 20 bytes, got #{bytes.length} bytes"
46
+ sport, dport, seq, ack, offset, flags, win, sum, urp =
47
+ bytes.unpack('nnNNCCnnn')
48
+ offset = (offset >> 4) * 4
49
+ Pcap.assert offset >= 20, 'Truncated TCP header: ' +
50
+ "expected at least 20 bytes, got #{offset} bytes"
51
+ Pcap.assert bytes.length >= offset, 'Truncated TCP header: ' +
52
+ "expected at least #{offset} bytes, got #{bytes.length} bytes"
53
+
54
+ if TH_SYN == flags
55
+ ss = TCP.get_option bytes[20, offset-20], MSS
56
+ tcp_options = bytes[20, offset-20]
57
+ else
58
+ tcp_options = ''
59
+ ss = 0
60
+ end
61
+
62
+ IPv4.check_options bytes[20, offset-20], 'TCP'
63
+
64
+ tcp = TCP.new
65
+ tcp.src_port = sport
66
+ tcp.dst_port = dport
67
+ tcp.seq = seq
68
+ tcp.ack = ack
69
+ tcp.flags = flags
70
+ tcp.window = win
71
+ tcp.urgent = urp
72
+ tcp.mss = ss
73
+ tcp.tcp_options = tcp_options
74
+ tcp.payload = tcp.payload_raw = bytes[offset..-1]
75
+ return tcp
76
+ end
77
+
78
+ def self.get_option options, option_type
79
+ while not options.empty?
80
+ type = options.slice!(0, 1)[0].ord
81
+ if type == 0 or type == 1
82
+ next
83
+ end
84
+ length = options.slice!(0, 1)[0].ord
85
+ if 2 < length
86
+ case length
87
+ when 3
88
+ format = "C"
89
+ when 4
90
+ format = "n"
91
+ when 6
92
+ format = "N"
93
+ when 10
94
+ format = "Q"
95
+ else
96
+ Pcap.warning "Bad TCP option length: #{length}"
97
+ end
98
+ option = options.slice!(0, length - 2).unpack(format)[0]
99
+ end
100
+ if option_type == type
101
+ return option
102
+ end
103
+ end
104
+ return 0
105
+ end
106
+
107
+ def write io, ip
108
+ if @payload.bytesize + 40 > 65535
109
+ raise NotImplementedError, "TCP segment too large"
110
+ end
111
+ options = ''
112
+ options = @tcp_options if @tcp_options != nil
113
+ all_head_length = 20 + options.bytesize
114
+ pseudo_header = ip.pseudo_header(all_head_length + @payload.bytesize)
115
+ head_length_to_pack = (all_head_length / 4).to_int << 4
116
+ @src_port = @src_port.to_i if @src_port.is_a? String
117
+ @dst_port = @dst_port.to_i if @dst_port.is_a? String
118
+ @flags = @flags.to_i if @flags.is_a? String
119
+ @window = @window.to_i if @window.is_a? String
120
+ @urgent = @urgent.to_i if @urgent.is_a? String
121
+ header = [@src_port, @dst_port, @seq, @ack, head_length_to_pack, @flags, @window,
122
+ 0, @urgent].pack('nnNNCCnnn')
123
+ checksum = IP.checksum pseudo_header + header + options + @payload
124
+ header = [@src_port, @dst_port, @seq, @ack, head_length_to_pack, @flags, @window,
125
+ checksum, @urgent].pack('nnNNCCnnn')
126
+ io.write header
127
+ io.write options
128
+ io.write @payload
129
+ end
130
+
131
+ class ReorderError < StandardError;
132
+ end
133
+
134
+ ReorderState = ::Struct.new(:next_seq, :queued)
135
+
136
+ # Reorder packets by TCP sequence number. TCP packets are assumed to
137
+ # be over IP over Ethernet.
138
+ def self.reorder packets
139
+ packets = packets.dup
140
+ reordered_packets = []
141
+ flow_to_state = {}
142
+ while not packets.empty?
143
+ packet = packets.shift
144
+ # Don't reorder non-TCP packets
145
+ if not tcp? packet
146
+ reordered_packets << packet
147
+ next
148
+ end
149
+ # Sanity check: must not be a fragment
150
+ if packet.payload.v4? and packet.payload.fragment?
151
+ raise ReorderError, "TCP stream contains IP fragments"
152
+ end
153
+ tcp = packet.payload.payload
154
+ # Must not contain urgent data
155
+ if tcp.flags & TH_URG != 0
156
+ raise ReorderError, "TCP stream contains urgent data: "+
157
+ pretty_flow_name(packet)
158
+ end
159
+ # Get/create state
160
+ if flow_to_state.member? packet.flow_id
161
+ state = flow_to_state[packet.flow_id]
162
+ else
163
+ state = ReorderState.new nil, []
164
+ flow_to_state[packet.flow_id] = state
165
+ end
166
+ if not state.next_seq
167
+ # First packet in TCP stream
168
+ reordered_packets << packet
169
+ state.next_seq = tcp.seq + tcp.payload.length
170
+ if tcp.flags & TCP::TH_SYN != 0
171
+ state.next_seq += 1
172
+ end
173
+ if tcp.flags & TCP::TH_FIN != 0
174
+ state.next_seq += 1
175
+ end
176
+ state.next_seq %= 2**32
177
+ elsif seq_eq(tcp.seq, state.next_seq)
178
+ # Next expected sequence number in TCP stream
179
+
180
+ # SYN must not appear in middle of stream
181
+ if tcp.flags & TCP::TH_SYN != 0
182
+ raise ReorderError, "SYN in middle of TCP stream " +
183
+ pretty_flow_name(packet)
184
+ end
185
+
186
+ reordered_packets << packet
187
+ state.next_seq += tcp.payload.length
188
+ if tcp.flags & TCP::TH_FIN != 0
189
+ state.next_seq += 1
190
+ end
191
+ state.next_seq %= 2**32
192
+
193
+ # Reinject any packets in the queue into the packet stream
194
+ if not state.queued.empty?
195
+ packets.unshift(*state.queued)
196
+ state.queued.clear
197
+ end
198
+ elsif seq_lt(tcp.seq, state.next_seq)
199
+ # Old sequence number
200
+ if seq_lte(tcp.seq + tcp.payload.length, state.next_seq)
201
+ # No overlap: retransmitted packet, ignore
202
+ else
203
+ # Overlap: reassembler must slice in overlapping data
204
+ reordered_packets << packet
205
+ end
206
+ else
207
+ # Future sequence number - queue
208
+ state.queued << packet
209
+ end
210
+ end
211
+
212
+ flow_to_state.each do |flow_id, state|
213
+ if not state.queued.empty?
214
+ raise ReorderError, "Data missing from TCP stream "+
215
+ pretty_flow_name(state.queued[0]) + ': ' +
216
+ "expecting sequence number #{state.next_seq}"
217
+ end
218
+ end
219
+
220
+ return reordered_packets
221
+ end
222
+
223
+ class MergeError < StandardError;
224
+ end
225
+
226
+ # Merge adjacent TCP packets. Non-data TCP packets are also removed.
227
+ # reorder() should be run first. This can create packets that are larger
228
+ # than the maximum possible IPv4 packet - use split() to make them smaller.
229
+ def self.merge packets
230
+ merged_packets = []
231
+ merged_packet = nil
232
+ next_seq = nil
233
+ packets.each do |packet|
234
+ if not tcp? packet
235
+ # Skip non-TCP packets.
236
+ if merged_packet
237
+ merged_packets << merged_packet
238
+ merged_packet = nil
239
+ end
240
+ merged_packets << packet
241
+ elsif packet.payload.v4? and packet.payload.fragment?
242
+ # Sanity check: must not be a fragment
243
+ raise MergeError, 'TCP stream contains IP fragments'
244
+ else
245
+ tcp = packet.payload.payload
246
+ if tcp.flags & TCP::TH_SYN == 0 and tcp.payload == ''
247
+ # Ignore non-data packets. SYNs are kept so the TCP
248
+ # transport is created at the correct spot.
249
+ elsif not merged_packet or
250
+ merged_packet.flow_id != packet.flow_id
251
+ # New TCP stream
252
+ if merged_packet
253
+ merged_packets << merged_packet
254
+ end
255
+ merged_packet = packet.deepdup
256
+ next_seq = tcp.seq + tcp.payload.length
257
+ elsif seq_eq tcp.seq, next_seq
258
+ # Next expected sequence number
259
+ merged_packet.payload.payload.payload << tcp.payload
260
+ next_seq += tcp.payload.length
261
+ elsif seq_lte(tcp.seq + tcp.payload.length, next_seq)
262
+ # Old data: ignore
263
+ elsif seq_lt tcp.seq, next_seq
264
+ # Overlapping segment: merge newest part
265
+ length = seq_sub(tcp.seq + tcp.payload.length, next_seq)
266
+ bytes = tcp.payload[-length..-1]
267
+ merged_packet.payload.payload.payload << bytes
268
+ next_seq += length
269
+ else
270
+ # Error (sanify check, reorder_tcp will raise an error)
271
+ raise MergeError, 'TCP stream is missing segments'
272
+ end
273
+ if next_seq
274
+ if tcp.flags & TCP::TH_SYN != 0
275
+ next_seq += 1
276
+ end
277
+ if tcp.flags & TCP::TH_FIN != 0
278
+ next_seq += 1
279
+ end
280
+ next_seq %= 2**32
281
+ end
282
+ end
283
+ end
284
+ if merged_packet
285
+ merged_packets << merged_packet
286
+ end
287
+
288
+ merged_packets = create_message_boundaries(merged_packets)
289
+
290
+ return merged_packets
291
+ end
292
+
293
+ def self.create_message_boundaries packets
294
+ # Get complete bytes for each tcp flow before trying to
295
+ # identify the protocol.
296
+ flow_to_bytes = {}
297
+ packets.each do |packet|
298
+ if tcp? packet
299
+ tcp = packet.payload.payload
300
+ flow = packet.flow_id
301
+ bytes = flow_to_bytes[flow] ||= ""
302
+ bytes << tcp.payload.to_s
303
+ end
304
+ end
305
+
306
+ # If any proto plugin can parse a message off of the stream we will
307
+ # use that plugin to detect message boundaries and guide message
308
+ # reassembly.
309
+ flow_to_packetizer = {}
310
+ flow_to_bytes.each_pair do |flow, bytes|
311
+ [Reader::HttpFamily].each do |klass|
312
+ reader = klass.new
313
+ reader.pcap2scenario = true
314
+ if reader.read_message bytes
315
+ tx_key = flow.flatten.sort_by { |o| o.to_s }
316
+
317
+ tx = flow_to_packetizer[tx_key] ||= StreamPacketizer.new(klass.new)
318
+ break
319
+ end
320
+ end
321
+ end
322
+
323
+ # Merge/split packets along message boundaries. This is done as an
324
+ # atomic transaction per tcp connection. The loop below adds merged
325
+ # packets alongside the original unmerged packets. If the stream
326
+ # is completely merged (no fragments left at end) we remove the
327
+ # original packets otherwise we rollback by removing the newly
328
+ # created packets.
329
+ changes = Hash.new do |hash, key|
330
+ # tuple of original/replacement packets per flow.
331
+ hash[key] = [[], []]
332
+ end
333
+ rollback_list = []
334
+
335
+ merged = []
336
+ partial_messages = Hash.new { |hash, key| hash[key] = [] }
337
+ packets.each do |packet|
338
+ merged << packet
339
+
340
+ next if not tcp? packet
341
+ tcp = packet.payload.payload
342
+
343
+ flow = packet.flow_id
344
+
345
+ # Check if we have message boundaries for this flow
346
+ tx_key = flow.flatten.sort_by { |o| o.to_s }
347
+ if not tx = flow_to_packetizer[tx_key]
348
+ next
349
+ end
350
+
351
+ # Keep track of new vs orig packets so we can delete one set at the end.
352
+ orig_packets, new_packets = changes[flow]
353
+ orig_packets << packet
354
+
355
+ if tcp.payload.empty?
356
+ p = packet.deepdup
357
+ new_packets << p
358
+ p.payload.payload.proto_family = tx.parser.family
359
+ next
360
+ end
361
+
362
+ # Does the current packet result in any completed messages?
363
+ tx.push(flow, tcp.payload)
364
+ fragments = partial_messages[flow]
365
+ if tx.msg_count(flow) == 0
366
+ # No, record packet as a fragment and move to next packet.
367
+ fragments << packet
368
+ next
369
+ end
370
+
371
+ # Yes, packet did result in completed messages. Create a new
372
+ # tcp packet for each higher level protocol message.
373
+ first_inc_packet = (fragments.empty? ? packet : fragments[0])
374
+ next_seq = first_inc_packet.payload.payload.seq
375
+ while tcp_payload = tx.next_msg(flow)
376
+ if tcp_payload.size > MAX_SEGMENT_PAYLOAD
377
+ # Abort merging for this flow because this packet
378
+ # will be split and result in a scenario where
379
+ # we send one logical message but try and receive
380
+ # two.
381
+ rollback_list << tx_key
382
+ $stderr.puts "Warning: Message too big, cannot enforce " \
383
+ "message boundaries."
384
+ end
385
+ next_packet = packet.deepdup
386
+ new_packets << next_packet
387
+ next_tcp = next_packet.payload.payload
388
+ next_tcp.seq = next_seq
389
+ next_tcp.payload = tcp_payload
390
+ next_tcp.proto_family = tx.parser.family
391
+ next_seq += tcp_payload.size
392
+ merged << next_packet
393
+ end
394
+ fragments.clear
395
+
396
+ # If there are unconsumed bytes then add a fragment to the
397
+ # incomplete list.
398
+ if extra_bytes = tx.extra_bytes(flow)
399
+ frag = packet.deepdup
400
+ new_packets << frag
401
+ fragments << frag
402
+ tcp = frag.payload.payload
403
+ tcp.payload = extra_bytes
404
+ tcp.seq = next_seq
405
+ tcp.proto_family = tx.parser.family
406
+ end
407
+ end
408
+
409
+ # Figure out which connections have incompletely merged flows.
410
+ # Rollback for those and commit the rest.
411
+ partial_messages.each_pair do |flow, list|
412
+ if not list.empty?
413
+ tx_key = flow.flatten.sort_by { |o| o.to_s }
414
+ $stderr.puts "Warning: Left over fragments, cannot force message boundaries."
415
+ rollback_list << tx_key
416
+ end
417
+ end
418
+ changes.each_pair do |flow, orig_new|
419
+ orig, new = orig_new
420
+ tx_key = flow.flatten.sort_by { |o| o.to_s }
421
+ if rollback_list.include?(tx_key)
422
+ new.each { |p| p.payload = :remove }
423
+ else
424
+ orig.each { |p| p.payload = :remove }
425
+ end
426
+ end
427
+ merged.reject! { |p| p.payload == :remove }
428
+
429
+ merged
430
+ end
431
+
432
+
433
+ # Split-up TCP packets that are too large to serialize. (I.e., total
434
+ # length including all headers greater than 65535 - 20 - 20 - 14.)
435
+ MAX_SEGMENT_PAYLOAD = 65535 - 20 - 20 - 14
436
+
437
+ def self.split packets
438
+ split_packets = []
439
+ packets.each do |packet|
440
+ if not tcp? packet
441
+ # Skip non-TCP packets.
442
+ split_packets << packet
443
+ next
444
+ elsif packet.payload.v4? and packet.payload.fragment?
445
+ # Sanity check: must not be a fragment
446
+ raise MergeError, 'TCP stream contains IP fragments'
447
+ elsif packet.payload.payload.payload.length <= MAX_SEGMENT_PAYLOAD
448
+ split_packets << packet
449
+ else
450
+ tcp = packet.payload.payload
451
+ payload = tcp.payload
452
+ tcp.payload = payload.slice! 0, MAX_SEGMENT_PAYLOAD
453
+ next_seq = tcp.seq + tcp.payload.length
454
+ split_packets << packet
455
+ while payload != ''
456
+ next_packet = packet.deepdup
457
+ next_tcp = next_packet.payload.payload
458
+ next_tcp.seq = next_seq
459
+ next_tcp.payload = payload.slice! 0, MAX_SEGMENT_PAYLOAD
460
+ next_seq += next_tcp.payload.length
461
+ split_packets << next_packet
462
+ end
463
+ end
464
+ end
465
+ return split_packets
466
+ end
467
+
468
+ def self.tcp? packet
469
+ return packet.is_a?(Ethernet) &&
470
+ packet.payload.is_a?(IP) &&
471
+ packet.payload.payload.is_a?(TCP)
472
+ end
473
+
474
+ # Subtract two sequence numbers module 2**32.
475
+ def self.seq_sub a, b
476
+ if a - b > 2**31
477
+ return -((b - a) % 2**32)
478
+ elsif a - b < -2**31
479
+ return (a - b) % 2**32
480
+ else
481
+ return a - b
482
+ end
483
+ end
484
+
485
+ # Compare TCP sequence numbers modulo 2**32.
486
+ def self.seq_eq a, b
487
+ return seq_sub(a, b) == 0
488
+ end
489
+
490
+ def self.seq_lt a, b
491
+ return seq_sub(a, b) < 0
492
+ end
493
+
494
+ def self.seq_lte a, b
495
+ return seq_sub(a, b) <= 0
496
+ end
497
+
498
+ # Generate a pretty name for a TCP flow
499
+ def self.pretty_flow_name packet
500
+ ip = packet.payload
501
+ return "#{ip.src}:#{ip.payload.src_port} <-> " +
502
+ "#{ip.dst}:#{ip.payload.dst_port}"
503
+ end
504
+
505
+ def to_s
506
+ return "tcp(%d, %d, %s)" % [@src_port, @dst_port, @payload.inspect]
507
+ end
508
+
509
+ def == other
510
+ return super &&
511
+ self.src_port == other.src_port &&
512
+ self.dst_port == other.dst_port &&
513
+ self.seq == other.seq &&
514
+ self.ack == other.ack &&
515
+ self.flags == other.flags &&
516
+ self.window == other.window &&
517
+ self.urgent == other.urgent
518
+ end
519
+ end
520
+
521
+ end
522
+ end