dot11 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ dot11
2
+ =======
3
+
4
+ A simple 802.11 packet parser.
@@ -0,0 +1,17 @@
1
+ class String
2
+ def indent(depth)
3
+ indented = ""
4
+ self.each_line do |line|
5
+ indented += " " * depth + line
6
+ end
7
+
8
+ indented
9
+ end
10
+ end
11
+
12
+ require File.expand_path(File.join(File.dirname(__FILE__), 'dot11', 'packet'))
13
+ require File.expand_path(File.join(File.dirname(__FILE__), 'dot11', 'packetset'))
14
+ require File.expand_path(File.join(File.dirname(__FILE__), 'dot11', 'macaddress'))
15
+ require File.expand_path(File.join(File.dirname(__FILE__), 'dot11', 'dot11'))
16
+ require File.expand_path(File.join(File.dirname(__FILE__), 'dot11', 'raw'))
17
+ require File.expand_path(File.join(File.dirname(__FILE__), 'dot11', 'radiotap'))
@@ -0,0 +1,974 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'macaddress'))
2
+ require File.expand_path(File.join(File.dirname(__FILE__), 'packet'))
3
+ require File.expand_path(File.join(File.dirname(__FILE__), 'raw'))
4
+
5
+ module Dot11
6
+ class Dot11 < Packet
7
+ @@TYPENAMES = [["association request", "association response", "reassociation request", "reassociation response", "probe request", "probe response", "reserved0", "reserved1", "beacon", "ATIM", "disassociation", "authorization", "deauthorization", "reserved2", "reserved3", "reserved4"],
8
+ ["reserved0", "reserved1", "reserved2", "reserved3", "reserved4", "reserved5", "reserved6", "reserved7", "reserved8", "PS-poll", "RTS", "CTS", "ACK", "CF-end", "CF-end + CF-ack"],
9
+ ["data", "data + CF-ack", "data + CF-poll", "data + CF-ack + CF-poll", "null function (no data)", "CF-ack (no data)", "CF-poll (no data)", "CF-ack + CF-poll (no data)", "reserved0", "reserved1", "reserved2", "reserved3", "reserved4", "reserved5", "reserved6", "reserved7"],
10
+ ["reserved0", "reserved1", "reserved2", "reserved3", "reserved4", "reserved5", "reserved6", "reserved7", "reserved8", "reserved9", "reserved10", "reserved11", "reserved12", "reserved13", "reserved14", "reserved15"]]
11
+
12
+ def subtype
13
+ @subtype ||= 0
14
+ end
15
+
16
+ def subtype=(other)
17
+ @subtype = other
18
+ end
19
+
20
+ def type
21
+ @type ||= 0
22
+ end
23
+
24
+ def type=(other)
25
+ @type = other
26
+ end
27
+
28
+ def version
29
+ @version ||= 0
30
+ end
31
+
32
+ def version=(other)
33
+ @version = other
34
+ end
35
+
36
+ def flags
37
+ @flags ||= 0
38
+ end
39
+
40
+ def flags=(other)
41
+ @flags = other
42
+ end
43
+
44
+ def duration
45
+ @duration ||= 0
46
+ end
47
+
48
+ def duration=(other)
49
+ @duration = other
50
+ end
51
+
52
+ def addr1
53
+ @addr1 ||= 0
54
+ end
55
+
56
+ def addr1=(other)
57
+ if other.kind_of?(Integer) || other.kind_of?(String)
58
+ @addr1 = MACAddress.new(other)
59
+ elsif other.kind_of?(MACAddress)
60
+ @addr1 = other
61
+ else
62
+ raise "Unrecognized addr #{other.inspect}"
63
+ end
64
+ end
65
+
66
+ def addr2
67
+ @addr2 ||= 0
68
+ end
69
+
70
+ def addr2=(other)
71
+ if other.kind_of?(Integer) || other.kind_of?(String)
72
+ @addr2 = MACAddress.new(other)
73
+ elsif other.kind_of?(MACAddress)
74
+ @addr2 = other
75
+ else
76
+ raise "Unrecognized addr #{other.inspect}"
77
+ end
78
+ end
79
+
80
+ def addr3
81
+ @addr3 ||= 0
82
+ end
83
+
84
+ def addr3=(other)
85
+ if other.kind_of?(Integer) || other.kind_of?(String)
86
+ @addr3 = MACAddress.new(other)
87
+ elsif other.kind_of?(MACAddress)
88
+ @addr3 = other
89
+ else
90
+ raise "Unrecognized addr #{other.inspect}"
91
+ end
92
+ end
93
+
94
+ def sc
95
+ @sc ||= 0
96
+ end
97
+
98
+ def sc=(other)
99
+ @sc = other
100
+ end
101
+
102
+ def addr4
103
+ @addr4 ||= 0
104
+ end
105
+
106
+ def addr4=
107
+ if other.kind_of?(Integer) || other.kind_of?(String)
108
+ @addr4 = MACAddress.new(other)
109
+ elsif other.kind_of?(MACAddress)
110
+ @addr4 = other
111
+ else
112
+ raise "Unrecognized addr #{other.inspect}"
113
+ end
114
+ end
115
+
116
+ def payload=(other)
117
+ @payload = other
118
+ end
119
+
120
+ def data
121
+ buffer = [(subtype << 4) | (type << 2) | version, flags, duration].concat(addr1.to_arr).pack("CCSC6")
122
+
123
+ if (type == 1 && [0x0a, 0x0b, 0x0e, 0x0f].include?(subtype)) || (type != 1)
124
+ buffer += addr2.to_arr.pack("C6")
125
+ end
126
+
127
+ if [0, 2].include?(type)
128
+ buffer += addr3.to_arr.pack("C6")
129
+ end
130
+
131
+ if type != 1
132
+ buffer += [sc].pack("v")
133
+ end
134
+
135
+ if type == 2 && flags & 0x03 == 0x03
136
+ buffer += addr4.to_arr.pack("C6")
137
+ end
138
+
139
+ if payload
140
+ buffer += payload.data
141
+ end
142
+
143
+ buffer
144
+ end
145
+
146
+ def ==(other)
147
+ eql?(other)
148
+ end
149
+
150
+ def eql?(other)
151
+ return false unless other.kind_of?(Dot11)
152
+
153
+ basics = subtype.eql?(other.subtype) && type.eql?(other.type) && version.eql?(other.version) &&
154
+ flags.eql?(other.flags) && duration.eql?(other.duration) && addr1.eql?(other.addr1)
155
+
156
+ return false unless basics
157
+
158
+ if (type == 1 && [0x0a, 0x0b, 0x0e, 0x0f].include?(subtype)) || (type != 1)
159
+ return false unless addr2.eql?(other.addr2)
160
+ end
161
+
162
+ if [0, 2].include?(type)
163
+ return false unless addr3.eql?(other.addr3)
164
+ end
165
+
166
+ if type != 1
167
+ return false unless sc.eql?(other.sc)
168
+ end
169
+
170
+ if type == 2 && flags & 0x03 == 0x03
171
+ return false unless addr4.eql?(other.addr4)
172
+ end
173
+
174
+ return true
175
+ end
176
+
177
+ def /(other)
178
+ if @payload.nil?
179
+ @payload = other
180
+ return self
181
+ end
182
+
183
+ if @payload.respond_to?(:elements)
184
+ @payload.elements << other
185
+ end
186
+
187
+ self
188
+ end
189
+
190
+ alias to_s data
191
+
192
+ def inspect
193
+ binary_flags = flags.to_s(2)
194
+ flag_names = ['to-DS', 'from-DS', 'MF', 'retry', 'pw-mgt', 'MD', 'wep', 'order']
195
+ set_flags = []
196
+
197
+ 8.times do |i|
198
+ set_flags << flag_names[i] if binary_flags[7 - i] == ?1
199
+ end
200
+
201
+ "Dot11\n" + if @corrupt then " (corrupt)" else "" end +
202
+ "----------------\n" +
203
+ "type: ...... #{type} (#{%w(management control data reserved)[type]})\n" +
204
+ "subtype: ... #{subtype} (#{@@TYPENAMES[type][subtype]})\n" +
205
+ "version: ... #{version}\n" +
206
+ "flags: ..... #{"%#02x" % flags} (#{"0" * (8 - flags.to_s(2).length) + flags.to_s(2)}#{if set_flags.size > 0 then ' : ' + set_flags.join(', ') else '' end})\n" +
207
+ "duration: .. #{duration}\n" +
208
+ "addr1: ..... #{addr1}\n" +
209
+ (if addr2 then "addr2: ..... #{addr2}\n" else "" end) +
210
+ (if addr3 then "addr3: ..... #{addr3}\n" else "" end) +
211
+ (if sc then "sc: ........ #{"%#02x" % sc} (fragment: #{sc & 0x0F}; sequence: #{(sc & 0xFFF0) >> 4})\n" else "" end) +
212
+ (if addr4 then "addr4: ..... #{addr4}\n" else "" end) +
213
+ (if payload then "payload:\n#{payload.to_s.indent(6)}" else "" end)
214
+ end
215
+
216
+ # Lazily dissect the payload
217
+ def payload
218
+ return @payload if @payload
219
+
220
+ payload_class = if (@flags & 0x40) == 0x40
221
+ Dot11WEP
222
+ elsif @type == 0
223
+ [ Dot11AssoReq, Dot11AssoResp, Dot11ReassoReq, Dot11ReassoResp, Dot11ProbeReq, Dot11ProbeResp, nil, nil,
224
+ Dot11Beacon, Dot11ATIM, Dot11Disas, Dot11Auth, Dot11Deauth, nil, nil, nil,
225
+ nil, nil, nil, nil, nil, nil, nil, nil,
226
+ nil, nil, nil, nil, nil, nil, nil ][@subtype]
227
+ elsif @type == 2
228
+ if @subtype == 0
229
+ Dot11Data
230
+ elsif @subtype == 4
231
+ Dot11NullData
232
+ end
233
+ end
234
+
235
+ return nil if payload_class.nil?
236
+
237
+ @payload = payload_class.new(@rest) unless (payload_class == Dot11NullData || @rest.nil? || @rest.empty?)
238
+ end
239
+
240
+ def self.fields()
241
+ # Why can't ruby have ordered maps??
242
+ # The offsets are unnecessary but removing them would have been even more complicated
243
+ # TODO: remove offsets and compute possible offsets for each field automatically
244
+ [[:subtype, {:type => :int, :offset => 0, :size => 1, :bitrange => 4..7}],
245
+ [:type, {:type => :int, :offset => 0, :size => 1, :bitrange => 2..3}],
246
+ [:version, {:type => :int, :offset => 0, :size => 1, :bitrange => 0..1}],
247
+ [:flags, {:type => :int, :offset => 1, :size => 1}],
248
+ [:duration, {:type => :int, :offset => 2, :size => 2}],
249
+ [:addr1, {:type => :mac, :offset => 4, :size => 6}],
250
+ # Ugly syntax! TODO: come up with a better way to represent negation than a one-element array
251
+ [:addr2, {:type => :mac, :offset => 10, :size => 6, :condition => [{:subtype => [0x0a, 0x0b, 0x0e, 0x0f]}, {:type => [1]}]}],
252
+ [:addr3, {:type => :mac, :offset => {[{:subtype => [0x0a, 0x0b, 0x0e, 0x0f]}, {:type => [1]}] => 16, :else => 10}, :size => 6, :condition => {:type => [0, 2]}}],
253
+ #[:sc, {:type => :int, :size => 2, :offset => {}, => :condition => {:type => [1]}}]
254
+ ]
255
+ end
256
+
257
+ private
258
+
259
+ def dissect(data)
260
+ fields = data.unpack("CCSC6")
261
+
262
+ @subtype = (fields[0] & 0xF0) >> 4
263
+ @type = (fields[0] & 0x0C) >> 2
264
+ @version = fields[0] & 0x03
265
+
266
+ @flags = fields[1]
267
+ @duration = fields[2]
268
+
269
+ # The array2mac calculations could be lazy if we really needed speed
270
+ @addr1 = MACAddress.new(fields[3..-1])
271
+
272
+ @rest = data[10..-1]
273
+
274
+ if (@type == 1 && [0x0a, 0x0b, 0x0e, 0x0f].include?(@subtype)) || (@type != 1)
275
+ if !@rest || @rest.empty?
276
+ @corrupt = true
277
+ return
278
+ end
279
+
280
+ @addr2 = MACAddress.new(@rest.unpack("C6"))
281
+ @rest = @rest[6..-1]
282
+ end
283
+
284
+ if [0, 2].include?(@type)
285
+ if !@rest || @rest.empty?
286
+ @corrupt = true
287
+ return
288
+ end
289
+
290
+ @addr3 = MACAddress.new(@rest.unpack("C6"))
291
+ @rest = @rest[6..-1]
292
+ end
293
+
294
+ if @type != 1
295
+ if !@rest || @rest.empty?
296
+ @corrupt = true
297
+ return
298
+ end
299
+
300
+ @sc = @rest.unpack("v")[0]
301
+ @rest = @rest[2..-1]
302
+ end
303
+
304
+ if @type == 2 && @flags & 0x03 == 0x03
305
+ if !@rest || @rest.empty?
306
+ @corrupt = true
307
+ return
308
+ end
309
+
310
+ @addr4 = MACAddress.new(@rest.unpack("C6"))
311
+ @rest = @rest[6..-1]
312
+ end
313
+ end
314
+
315
+ class Dot11Elt < Packet
316
+ attr_accessor :id, :info_length, :info
317
+
318
+ def Dot11Elt.register_element(id, klass)
319
+ @@registered_elements ||= {}
320
+
321
+ @@registered_elements[id] = klass
322
+ end
323
+
324
+ def data
325
+ buffer = [id, info_length].pack("CC")
326
+
327
+ buffer += info
328
+ end
329
+
330
+ def to_s
331
+ "Dot11Elt\n" +
332
+ "------------\n" +
333
+ "id: ............ #{id}\n" +
334
+ "info_length: ... #{info_length}\n" +
335
+ "info: .......... #{info.inspect}\n"
336
+ end
337
+
338
+ private
339
+
340
+ def dissect(data)
341
+ fields = data.unpack("CC")
342
+
343
+ @id = fields[0]
344
+ @info_length = fields[1]
345
+
346
+ @info = data[2, @info_length]
347
+
348
+ @rest = data[2 + @info_length..-1]
349
+ end
350
+
351
+ class << self
352
+ # Hook into new to "subclass on the fly"
353
+ alias old_new new
354
+
355
+ def new(parameters)
356
+ return old_new(parameters) if self != Dot11Elt
357
+
358
+ if parameters.kind_of?(String)
359
+ elt_id = parameters.unpack("C")[0]
360
+
361
+ if @@registered_elements && @@registered_elements[elt_id]
362
+ return @@registered_elements[elt_id].new(parameters)
363
+ else
364
+ elt = Dot11Elt.allocate
365
+ elt.send(:initialize, parameters)
366
+
367
+ return elt
368
+ end
369
+
370
+ elsif parameters.kind_of?(Hash)
371
+
372
+ if @@registered_elements && @@registered_elements[parameters[:id]]
373
+ return @@registered_elements[parameters[:id]].new(parameters)
374
+ else
375
+ elt = Dot11Elt.allocate
376
+ elt.send(:initialize, parameters)
377
+
378
+ return elt
379
+ end
380
+ end
381
+
382
+ end
383
+
384
+ def element_id(id)
385
+ @id = id
386
+ Dot11Elt.register_element(id, self)
387
+ end
388
+ end
389
+ end
390
+
391
+ class Dot11EltSSID < Dot11Elt
392
+ element_id 0
393
+
394
+ def essid
395
+ return @info
396
+ end
397
+
398
+ def to_s
399
+ "Dot11EltSSID\n" +
400
+ "-------------\n" +
401
+ "id: ............ 0\n" +
402
+ "info_length: ... #{info_length}\n" +
403
+ "essid: ......... #{info.inspect} (#{essid})\n"
404
+ end
405
+ end
406
+
407
+ class Dot11EltRates < Dot11Elt
408
+ element_id 1
409
+
410
+ def rates
411
+ return @rates if @rates
412
+
413
+ @rates = []
414
+
415
+ @info.each_byte do |b|
416
+ @rates << (b & 0x7f) / 2
417
+ end
418
+
419
+ @rates
420
+ end
421
+
422
+ def to_s
423
+ "Dot11EltRates\n" +
424
+ "------------------\n" +
425
+ "id: ............ 1\n" +
426
+ "info_length: ... #{info_length}\n" +
427
+ "rates: ......... #{info.inspect} (#{rates.join(', ')})\n"
428
+ end
429
+ end
430
+
431
+ class Dot11EltESR < Dot11Elt
432
+ element_id 50
433
+
434
+ def rates
435
+ return @rates if @rates
436
+
437
+ @rates = []
438
+
439
+ @info.each_byte do |b|
440
+ @rates << (b & 0x7f) / 2
441
+ end
442
+
443
+ @rates
444
+ end
445
+
446
+ def to_s
447
+ "Dot11EltESR\n" +
448
+ "------------------\n" +
449
+ "id: ............ 50\n" +
450
+ "info_length: ... #{info_length}\n" +
451
+ "rates: ......... #{info.inspect} (#{rates.join(', ')})\n"
452
+ end
453
+ end
454
+
455
+ module Dot11EltContainer
456
+ def elements_by_id
457
+ hash = {}
458
+
459
+ elements.each do |element|
460
+ hash[element.id] = element
461
+ end
462
+
463
+ hash
464
+ end
465
+
466
+ def elements
467
+ if @elements.nil?
468
+ @elements = []
469
+
470
+ if @rest
471
+ dissect_elements(@rest)
472
+ end
473
+
474
+ return @elements
475
+ end
476
+
477
+ @elements
478
+ end
479
+
480
+ def element_data
481
+ buffer = ""
482
+
483
+ elements.each do |element|
484
+ buffer += element.data
485
+ end
486
+
487
+ buffer
488
+ end
489
+
490
+ def element_to_s
491
+ buffer = ""
492
+
493
+ elements.each do |element|
494
+ buffer += element.to_s + "\n"
495
+ end
496
+
497
+ buffer
498
+ end
499
+
500
+ def /(other)
501
+ elements << other
502
+
503
+ self
504
+ end
505
+
506
+ private
507
+
508
+ def dissect_elements(data)
509
+ @elements = []
510
+
511
+ current_pos = 0
512
+
513
+ while current_pos < data.length
514
+ info_length = data[current_pos, 2].unpack("xC")[0]
515
+ total_elt_length = 2 + info_length
516
+
517
+ @elements << Dot11Elt.new(data[current_pos, total_elt_length])
518
+
519
+ current_pos += total_elt_length
520
+ end
521
+
522
+ end
523
+ end
524
+
525
+ class Dot11Beacon < Packet
526
+ attr_accessor :timestamp, :beacon_interval, :capabilities
527
+
528
+ include Dot11EltContainer
529
+
530
+ def data
531
+ buffer = [timestamp & 0xFFFFFFFF, (timestamp & 0xFFFFFFFF00000000) >> 32, beacon_interval, capabilities].pack("V2vn")
532
+
533
+ buffer += element_data
534
+ end
535
+
536
+ def to_s
537
+ binary_caps = capabilities.to_s(2)
538
+ cap_names = ['ESS', 'IBSS', 'CF Pollable', 'CF Poll Request', 'Privacy', 'Reserved5', 'Reserved6', 'Reserved7', 'Reserved8', 'Reserved9', 'Reserved10', 'Reserved11', 'Reserved12', 'Reserved13', 'Reserved14', 'Reserved15', ]
539
+ set_caps = []
540
+
541
+ 16.times do |i|
542
+ set_caps << cap_names[i] if binary_caps[15 -i] == ?1
543
+ end
544
+
545
+ "Dot11Beacon\n" +
546
+ "-------------------------\n" +
547
+ "timestamp: ......... #{timestamp}\n" +
548
+ "beacon_interval: ... #{beacon_interval} (#{beacon_interval * 0.001024} seconds)\n" +
549
+ "capabilities: ...... #{capabilities} (#{"0" * (16 - binary_caps.length) + binary_caps}#{if set_caps.size > 0 then ' : ' + set_caps.join(', ') else '' end})\n" +
550
+ "elements:\n" +
551
+ element_to_s.indent(7)
552
+ end
553
+
554
+ private
555
+
556
+ def dissect(data)
557
+ fields = data.unpack("V2vn")
558
+
559
+ p fields
560
+
561
+ @timestamp = (fields[1] << 32) | fields[0]
562
+ @beacon_interval = fields[2]
563
+ @capabilities = fields[3]
564
+
565
+ @rest = data[12..-1]
566
+ end
567
+ end
568
+
569
+ class Dot11ATIM < Packet
570
+ def dissect(data)
571
+ raise "Not implemented"
572
+ end
573
+ end
574
+
575
+ class Dot11Disas < Packet
576
+ attr_accessor :reason
577
+
578
+ def data
579
+ [reason].pack("v")
580
+ end
581
+
582
+ def to_s
583
+ "Dot11Disas\n" +
584
+ "-------------\n" +
585
+ "reason: #{reason}\n"
586
+ end
587
+
588
+ private
589
+
590
+ def dissect(data)
591
+ @reason = data.unpack("v")[0]
592
+ end
593
+ end
594
+
595
+ class Dot11AssoReq < Packet
596
+ attr_accessor :capabilities, :listen_interval
597
+
598
+ include Dot11EltContainer
599
+
600
+ def data
601
+ buffer = [capabilities, listen_interval].pack("nv")
602
+
603
+ buffer += element_data
604
+ end
605
+
606
+ def to_s
607
+ "Dot11AssoReq\n" +
608
+ "---------------\n" +
609
+ "capabilities: ...... #{capabilities}\n" +
610
+ "listen_interval: ... #{listen_interval}\n" +
611
+ "elements:\n" +
612
+ element_to_s.indent(7)
613
+ end
614
+
615
+ private
616
+
617
+ def dissect(data)
618
+ fields = data.unpack("nv")
619
+
620
+ @capabilities = fields[0]
621
+ @listen_interval = fields[1]
622
+
623
+ @rest = data[4..-1]
624
+ end
625
+ end
626
+
627
+ class Dot11AssoResp < Packet
628
+ attr_accessor :capabilities, :status, :aid
629
+
630
+ include Dot11EltContainer
631
+
632
+ def data
633
+ buffer = [capabilities, status, aid].pack("nvv")
634
+
635
+ buffer += element_data
636
+ end
637
+
638
+ def to_s
639
+ "Dot11AssoResp\n" +
640
+ "---------------\n" +
641
+ "capabilities: ... #{capabilities}\n" +
642
+ "status: ......... #{status}\n" +
643
+ "aid: ............ #{aid}\n" +
644
+ "elements:\n" +
645
+ element_to_s.indent(7)
646
+ end
647
+
648
+ private
649
+
650
+ def dissect(data)
651
+ fields = data.unpack("nvv")
652
+
653
+ @capabilities = fields[0]
654
+ @status = fields[1]
655
+ @aid = fields[2]
656
+
657
+ @rest = data[6..-1]
658
+ end
659
+ end
660
+
661
+ class Dot11ReassoReq < Packet
662
+ attr_accessor :capabilities, :current_ap, :listen_interval
663
+
664
+ include Dot11EltContainer
665
+
666
+ def data
667
+ buffer = [capabilities].concat(mac2array(current_ap)).concat([listen_interval]).pack("nC6v")
668
+
669
+ buffer += element_data
670
+ end
671
+
672
+ def to_s
673
+ "Dot11ReassoReq\n" +
674
+ "---------------\n" +
675
+ "capabilities: ...... #{capabilities}\n" +
676
+ "current_ap: ........ #{current_ap}\n" +
677
+ "listen_interval: ... #{listen_interval}\n" +
678
+ "elements:\n" +
679
+ element_to_s.indent(7)
680
+ end
681
+
682
+ private
683
+
684
+ def dissect(data)
685
+ fields = data.unpack("nC6v")
686
+
687
+ @capabilities = fields[0]
688
+ @current_ap = Packet.array2mac(fields[1, 6])
689
+ @listen_interval = fields[7]
690
+
691
+ @rest = data[10..-1]
692
+ end
693
+ end
694
+
695
+ class Dot11ReassoResp < Packet
696
+ include Dot11EltContainer
697
+
698
+ def data
699
+ element_data
700
+ end
701
+
702
+ def to_s
703
+ "Dot11ReassoResp\n" +
704
+ "---------------\n" +
705
+ "elements:\n" +
706
+ element_to_s.indent(7)
707
+ end
708
+
709
+ private
710
+
711
+ def dissect(data)
712
+ @rest = data
713
+ end
714
+ end
715
+
716
+ class Dot11ProbeReq < Packet
717
+ include Dot11EltContainer
718
+
719
+ def data
720
+ element_data
721
+ end
722
+
723
+ def to_s
724
+ "Dot11ProbeReq\n" +
725
+ "---------------\n" +
726
+ "elements:\n" +
727
+ element_to_s.indent(7)
728
+ end
729
+
730
+ private
731
+
732
+ def dissect(data)
733
+ @rest = data
734
+ end
735
+ end
736
+
737
+ class Dot11ProbeResp < Packet
738
+ attr_accessor :timestamp, :beacon_interval, :capabilities
739
+
740
+ include Dot11EltContainer
741
+
742
+ def data
743
+ buffer = [timestamp & 0xFFFFFFFF, (timestamp & 0xFFFFFFFF00000000) >> 32, beacon_interval, capabilities].pack("V2vn")
744
+
745
+ buffer += element_data
746
+ end
747
+
748
+ def to_s
749
+ binary_caps = capabilities.to_s(2)
750
+ cap_names = ['ESS', 'IBSS', 'CF Pollable', 'CF Poll Request', 'Privacy', 'Reserved5', 'Reserved6', 'Reserved7', 'Reserved8', 'Reserved9', 'Reserved10', 'Reserved11', 'Reserved12', 'Reserved13', 'Reserved14', 'Reserved15', ]
751
+ set_caps = []
752
+
753
+ 16.times do |i|
754
+ set_caps << cap_names[i] if binary_caps[15 -i] == ?1
755
+ end
756
+
757
+ "Dot11ProbeResp\n" +
758
+ "-------------------------\n" +
759
+ "timestamp: ......... #{timestamp}\n" +
760
+ "beacon_interval: ... #{beacon_interval} (#{beacon_interval * 0.001024} seconds)\n" +
761
+ "capabilities: ...... #{capabilities} (#{"0" * (16 - binary_caps.length) + binary_caps}#{if set_caps.size > 0 then ' : ' + set_caps.join(', ') else '' end})\n" +
762
+ "elements:\n" +
763
+ element_to_s.indent(7)
764
+ end
765
+
766
+ private
767
+
768
+ def dissect(data)
769
+ fields = data.unpack("V2vn")
770
+
771
+ @timestamp = (fields[1] << 32) | fields[0]
772
+ @beacon_interval = fields[2]
773
+ @capabilities = fields[3]
774
+
775
+ @rest = data[12..-1]
776
+ end
777
+ end
778
+
779
+ class Dot11Auth < Packet
780
+ attr_accessor :algo, :seqnum, :status
781
+
782
+ include Dot11EltContainer
783
+
784
+ def data
785
+ buffer = [algo, seqnum, status].pack("vvv")
786
+
787
+ buffer += element_data
788
+ end
789
+
790
+ def to_s
791
+ "Dot11Auth\n" +
792
+ "-------------\n" +
793
+ "algo: #{algo}\n" +
794
+ "seqnum: #{seqnum}\n" +
795
+ "status: #{status}\n"
796
+ end
797
+
798
+ private
799
+
800
+ def dissect(data)
801
+ fields = data.unpack("vvv")
802
+
803
+ @algo = fields[0] || 0
804
+ @seqnum = fields[1] || 0
805
+ @status = fields[2] || 0
806
+
807
+ @rest = data[6..-1]
808
+ end
809
+ end
810
+
811
+ class Dot11Deauth < Packet
812
+ attr_accessor :reason
813
+
814
+ def data
815
+ [reason].pack("v")
816
+ end
817
+
818
+ def to_s
819
+ "Dot11Deauth\n" +
820
+ "-------------\n" +
821
+ "reason: #{reason}\n"
822
+ end
823
+
824
+ private
825
+
826
+ def dissect(data)
827
+ @reason = data.unpack("v")[0]
828
+ end
829
+ end
830
+
831
+ class Dot11Data < Packet
832
+ attr_accessor :payload
833
+
834
+ def data
835
+ payload.data
836
+ end
837
+
838
+ def to_s
839
+ "Dot11Data\n" +
840
+ "-------------\n" +
841
+ "payload: \n#{payload.to_s.indent(6)}\n"
842
+ end
843
+
844
+ def payload
845
+ return @payload if @payload
846
+
847
+ @payload = LLC.new(@rest)
848
+ end
849
+
850
+ def /(other)
851
+ @payload = other
852
+ self
853
+ end
854
+
855
+ private
856
+
857
+ def dissect(data)
858
+ @rest = data
859
+ end
860
+ end
861
+
862
+ class Dot11NullData < Packet
863
+ def data
864
+ ""
865
+ end
866
+
867
+ def to_s
868
+ "Dot11NullData\n"
869
+ end
870
+
871
+ private
872
+
873
+ def dissect(data)
874
+ @rest = data
875
+ end
876
+ end
877
+
878
+ class Dot11WEP < Packet
879
+ def data
880
+
881
+ end
882
+
883
+ def to_s
884
+ "Dot11WEP\n" +
885
+ "-------------\n" +
886
+ "unknown\n"
887
+ end
888
+
889
+ private
890
+
891
+ def dissect(data)
892
+ @rest = data
893
+ end
894
+ end
895
+
896
+ class LLC < Packet
897
+ attr_accessor :dsap, :ssap, :control, :payload
898
+
899
+ def data
900
+ [dsap, ssap, control].pack("CCC") + payload.data
901
+ end
902
+
903
+ def to_s
904
+ "LLC\n" +
905
+ "-------------\n" +
906
+ "dsap: #{dsap}\n" +
907
+ "ssap: #{ssap}\n" +
908
+ "control: #{control}\n" +
909
+ (if payload then "payload:\n#{payload.to_s.indent(6)}" else "" end)
910
+ end
911
+
912
+ def payload
913
+ return @payload if @payload
914
+
915
+ @payload = SNAP.new(@rest)
916
+ end
917
+
918
+ def /(other)
919
+ @payload = other
920
+ self
921
+ end
922
+
923
+ private
924
+
925
+ def dissect(data)
926
+ fields = data.unpack("CCC")
927
+
928
+ @dsap = fields[0]
929
+ @ssap = fields[1]
930
+ @control = fields[2]
931
+
932
+ @rest = data[3..-1]
933
+ end
934
+ end
935
+
936
+ class SNAP < Packet
937
+ attr_accessor :oui, :code, :payload
938
+
939
+ def data
940
+ [oui, code].pack("QXv") + payload.data
941
+ end
942
+
943
+ def to_s
944
+ "SNAP\n" +
945
+ "-------\n" +
946
+ "oui: #{oui}\n" +
947
+ "code: #{code}\n" +
948
+ (if payload then "payload:\n#{payload.to_s.indent(6)}" else "" end)
949
+ end
950
+
951
+ def payload
952
+ return @payload if @payload
953
+
954
+ return @payload = Raw.new(@rest)
955
+ end
956
+
957
+ def /(other)
958
+ @payload = other
959
+ self
960
+ end
961
+
962
+ private
963
+
964
+ def dissect(data)
965
+ fields = data.unpack("CCCv")
966
+
967
+ @oui = fields[0] << 16 | fields[1] << 8 | fields[2]
968
+ @code = fields[3]
969
+
970
+ @rest = data[4..-1]
971
+ end
972
+ end
973
+ end
974
+ end