packetfu 1.1.8 → 1.1.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. data/README.rdoc +11 -12
  2. data/bench/octets.rb +9 -9
  3. data/examples/100kpackets.rb +13 -12
  4. data/examples/ackscan.rb +17 -16
  5. data/examples/arp.rb +36 -35
  6. data/examples/arphood.rb +37 -36
  7. data/examples/dissect_thinger.rb +7 -6
  8. data/examples/ethernet.rb +1 -0
  9. data/examples/examples.rb +1 -0
  10. data/examples/ifconfig.rb +1 -0
  11. data/examples/new-simple-stats.rb +24 -23
  12. data/examples/packetfu-shell.rb +26 -25
  13. data/examples/simple-sniffer.rb +10 -9
  14. data/examples/simple-stats.rb +24 -23
  15. data/examples/slammer.rb +4 -3
  16. data/lib/packetfu.rb +128 -127
  17. data/lib/packetfu/capture.rb +170 -169
  18. data/lib/packetfu/config.rb +53 -52
  19. data/lib/packetfu/inject.rb +57 -56
  20. data/lib/packetfu/packet.rb +529 -528
  21. data/lib/packetfu/pcap.rb +580 -579
  22. data/lib/packetfu/protos/arp.rb +91 -90
  23. data/lib/packetfu/protos/arp/header.rb +159 -158
  24. data/lib/packetfu/protos/arp/mixin.rb +37 -36
  25. data/lib/packetfu/protos/eth.rb +45 -44
  26. data/lib/packetfu/protos/eth/header.rb +244 -243
  27. data/lib/packetfu/protos/eth/mixin.rb +4 -3
  28. data/lib/packetfu/protos/hsrp.rb +70 -69
  29. data/lib/packetfu/protos/hsrp/header.rb +108 -107
  30. data/lib/packetfu/protos/hsrp/mixin.rb +30 -29
  31. data/lib/packetfu/protos/icmp.rb +72 -71
  32. data/lib/packetfu/protos/icmp/header.rb +83 -82
  33. data/lib/packetfu/protos/icmp/mixin.rb +15 -14
  34. data/lib/packetfu/protos/invalid.rb +50 -49
  35. data/lib/packetfu/protos/ip.rb +70 -69
  36. data/lib/packetfu/protos/ip/header.rb +292 -291
  37. data/lib/packetfu/protos/ip/mixin.rb +41 -40
  38. data/lib/packetfu/protos/ipv6.rb +51 -50
  39. data/lib/packetfu/protos/ipv6/header.rb +189 -188
  40. data/lib/packetfu/protos/ipv6/mixin.rb +30 -29
  41. data/lib/packetfu/protos/lldp.rb +3 -1
  42. data/lib/packetfu/protos/lldp/header.rb +1 -0
  43. data/lib/packetfu/protos/lldp/mixin.rb +1 -0
  44. data/lib/packetfu/protos/tcp.rb +177 -176
  45. data/lib/packetfu/protos/tcp/ecn.rb +36 -35
  46. data/lib/packetfu/protos/tcp/flags.rb +75 -74
  47. data/lib/packetfu/protos/tcp/header.rb +269 -268
  48. data/lib/packetfu/protos/tcp/hlen.rb +33 -32
  49. data/lib/packetfu/protos/tcp/mixin.rb +47 -46
  50. data/lib/packetfu/protos/tcp/option.rb +322 -321
  51. data/lib/packetfu/protos/tcp/options.rb +96 -95
  52. data/lib/packetfu/protos/tcp/reserved.rb +36 -35
  53. data/lib/packetfu/protos/udp.rb +117 -116
  54. data/lib/packetfu/protos/udp/header.rb +92 -91
  55. data/lib/packetfu/protos/udp/mixin.rb +4 -3
  56. data/lib/packetfu/structfu.rb +281 -280
  57. data/lib/packetfu/utils.rb +211 -208
  58. data/lib/packetfu/version.rb +42 -41
  59. data/packetfu.gemspec +1 -1
  60. data/spec/ethpacket_spec.rb +48 -48
  61. data/spec/packet_spec.rb +57 -57
  62. data/spec/packet_subclasses_spec.rb +8 -8
  63. data/spec/packetfu_spec.rb +59 -59
  64. data/spec/structfu_spec.rb +268 -268
  65. data/spec/tcp_spec.rb +75 -75
  66. data/test/all_tests.rb +13 -13
  67. data/test/func_lldp.rb +3 -3
  68. data/test/ptest.rb +2 -2
  69. data/test/test_arp.rb +116 -116
  70. data/test/test_capture.rb +45 -45
  71. data/test/test_eth.rb +68 -68
  72. data/test/test_hsrp.rb +9 -9
  73. data/test/test_icmp.rb +52 -52
  74. data/test/test_inject.rb +18 -18
  75. data/test/test_invalid.rb +16 -16
  76. data/test/test_ip.rb +36 -36
  77. data/test/test_ip6.rb +48 -48
  78. data/test/test_octets.rb +21 -21
  79. data/test/test_packet.rb +154 -154
  80. data/test/test_pcap.rb +170 -170
  81. data/test/test_structfu.rb +97 -97
  82. data/test/test_tcp.rb +320 -320
  83. data/test/test_udp.rb +76 -76
  84. metadata +2 -2
@@ -1,107 +1,108 @@
1
+ # -*- coding: binary -*-
1
2
  module PacketFu
2
3
 
3
- # UDPHeader is a complete UDP struct, used in UDPPacket. Many Internet-critical protocols
4
- # rely on UDP, such as DNS and World of Warcraft.
5
- #
6
- # For more on UDP packets, see http://www.networksorcery.com/enp/protocol/udp.htm
7
- #
8
- # ==== Header Definition
9
- # Int16 :udp_src
10
- # Int16 :udp_dst
11
- # Int16 :udp_len Default: calculated
12
- # Int16 :udp_sum Default: 0. Often calculated.
13
- # String :body
14
- class UDPHeader < Struct.new(:udp_src, :udp_dst, :udp_len, :udp_sum, :body)
4
+ # UDPHeader is a complete UDP struct, used in UDPPacket. Many Internet-critical protocols
5
+ # rely on UDP, such as DNS and World of Warcraft.
6
+ #
7
+ # For more on UDP packets, see http://www.networksorcery.com/enp/protocol/udp.htm
8
+ #
9
+ # ==== Header Definition
10
+ # Int16 :udp_src
11
+ # Int16 :udp_dst
12
+ # Int16 :udp_len Default: calculated
13
+ # Int16 :udp_sum Default: 0. Often calculated.
14
+ # String :body
15
+ class UDPHeader < Struct.new(:udp_src, :udp_dst, :udp_len, :udp_sum, :body)
15
16
 
16
- include StructFu
17
+ include StructFu
17
18
 
18
- def initialize(args={})
19
- super(
20
- Int16.new(args[:udp_src]),
21
- Int16.new(args[:udp_dst]),
22
- Int16.new(args[:udp_len] || udp_calc_len),
23
- Int16.new(args[:udp_sum]),
24
- StructFu::String.new.read(args[:body])
25
- )
26
- end
19
+ def initialize(args={})
20
+ super(
21
+ Int16.new(args[:udp_src]),
22
+ Int16.new(args[:udp_dst]),
23
+ Int16.new(args[:udp_len] || udp_calc_len),
24
+ Int16.new(args[:udp_sum]),
25
+ StructFu::String.new.read(args[:body])
26
+ )
27
+ end
27
28
 
28
- # Returns the object in string form.
29
- def to_s
30
- self.to_a.map {|x| x.to_s}.join
31
- end
29
+ # Returns the object in string form.
30
+ def to_s
31
+ self.to_a.map {|x| x.to_s}.join
32
+ end
32
33
 
33
- # Reads a string to populate the object.
34
- def read(str)
35
- force_binary(str)
36
- return self if str.nil?
37
- self[:udp_src].read(str[0,2])
38
- self[:udp_dst].read(str[2,2])
39
- self[:udp_len].read(str[4,2])
40
- self[:udp_sum].read(str[6,2])
41
- self[:body].read(str[8,str.size])
42
- self
43
- end
34
+ # Reads a string to populate the object.
35
+ def read(str)
36
+ force_binary(str)
37
+ return self if str.nil?
38
+ self[:udp_src].read(str[0,2])
39
+ self[:udp_dst].read(str[2,2])
40
+ self[:udp_len].read(str[4,2])
41
+ self[:udp_sum].read(str[6,2])
42
+ self[:body].read(str[8,str.size])
43
+ self
44
+ end
44
45
 
45
- # Setter for the UDP source port.
46
- def udp_src=(i); typecast i; end
47
- # Getter for the UDP source port.
48
- def udp_src; self[:udp_src].to_i; end
49
- # Setter for the UDP destination port.
50
- def udp_dst=(i); typecast i; end
51
- # Getter for the UDP destination port.
52
- def udp_dst; self[:udp_dst].to_i; end
53
- # Setter for the length field. Usually should be recalc()'ed instead.
54
- def udp_len=(i); typecast i; end
55
- # Getter for the length field.
56
- def udp_len; self[:udp_len].to_i; end
57
- # Setter for the checksum. Usually should be recalc()'ed instad.
58
- def udp_sum=(i); typecast i; end
59
- # Getter for the checksum.
60
- def udp_sum; self[:udp_sum].to_i; end
46
+ # Setter for the UDP source port.
47
+ def udp_src=(i); typecast i; end
48
+ # Getter for the UDP source port.
49
+ def udp_src; self[:udp_src].to_i; end
50
+ # Setter for the UDP destination port.
51
+ def udp_dst=(i); typecast i; end
52
+ # Getter for the UDP destination port.
53
+ def udp_dst; self[:udp_dst].to_i; end
54
+ # Setter for the length field. Usually should be recalc()'ed instead.
55
+ def udp_len=(i); typecast i; end
56
+ # Getter for the length field.
57
+ def udp_len; self[:udp_len].to_i; end
58
+ # Setter for the checksum. Usually should be recalc()'ed instad.
59
+ def udp_sum=(i); typecast i; end
60
+ # Getter for the checksum.
61
+ def udp_sum; self[:udp_sum].to_i; end
61
62
 
62
- # Returns the true length of the UDP packet.
63
- def udp_calc_len
64
- body.to_s.size + 8
65
- end
63
+ # Returns the true length of the UDP packet.
64
+ def udp_calc_len
65
+ body.to_s.size + 8
66
+ end
66
67
 
67
- # Recalculates calculated fields for UDP.
68
- def udp_recalc(args=:all)
69
- arg = arg.intern if arg.respond_to? :intern
70
- case args
71
- when :udp_len
72
- self.udp_len = udp_calc_len
73
- when :all
74
- self.udp_recalc(:udp_len)
75
- else
76
- raise ArgumentError, "No such field `#{arg}'"
77
- end
78
- end
68
+ # Recalculates calculated fields for UDP.
69
+ def udp_recalc(args=:all)
70
+ arg = arg.intern if arg.respond_to? :intern
71
+ case args
72
+ when :udp_len
73
+ self.udp_len = udp_calc_len
74
+ when :all
75
+ self.udp_recalc(:udp_len)
76
+ else
77
+ raise ArgumentError, "No such field `#{arg}'"
78
+ end
79
+ end
79
80
 
80
- # Equivalent to udp_src.to_i
81
- def udp_sport
82
- self.udp_src
83
- end
81
+ # Equivalent to udp_src.to_i
82
+ def udp_sport
83
+ self.udp_src
84
+ end
84
85
 
85
- # Equivalent to udp_src=
86
- def udp_sport=(arg)
87
- self.udp_src=(arg)
88
- end
86
+ # Equivalent to udp_src=
87
+ def udp_sport=(arg)
88
+ self.udp_src=(arg)
89
+ end
89
90
 
90
- # Equivalent to udp_dst
91
- def udp_dport
92
- self.udp_dst
93
- end
94
-
95
- # Equivalent to udp_dst=
96
- def udp_dport=(arg)
97
- self.udp_dst=(arg)
98
- end
91
+ # Equivalent to udp_dst
92
+ def udp_dport
93
+ self.udp_dst
94
+ end
95
+
96
+ # Equivalent to udp_dst=
97
+ def udp_dport=(arg)
98
+ self.udp_dst=(arg)
99
+ end
99
100
 
100
- # Readability aliases
101
+ # Readability aliases
101
102
 
102
- def udp_sum_readable
103
- "0x%04x" % udp_sum
104
- end
103
+ def udp_sum_readable
104
+ "0x%04x" % udp_sum
105
+ end
105
106
 
106
- end
107
+ end
107
108
  end
@@ -1,7 +1,8 @@
1
+ # -*- coding: binary -*-
1
2
  module PacketFu
2
- # This Mixin simplifies access to the UDPHeaders. Mix this in with your
3
- # packet interface, and it will add methods that essentially delegate to
4
- # the 'udp_header' method (assuming that it is a UDPHeader object)
3
+ # This Mixin simplifies access to the UDPHeaders. Mix this in with your
4
+ # packet interface, and it will add methods that essentially delegate to
5
+ # the 'udp_header' method (assuming that it is a UDPHeader object)
5
6
  module UDPHeaderMixin
6
7
  def udp_src=(v); self.udp_header.udp_src= v; end
7
8
  def udp_src; self.udp_header.udp_src; end
@@ -1,293 +1,294 @@
1
+ # -*- coding: binary -*-
1
2
  # StructFu, a nifty way to leverage Ruby's built in Struct class
2
3
  # to create meaningful binary data.
3
4
 
4
5
  module StructFu
5
-
6
- # Normally, self.size and self.length will refer to the Struct
7
- # size as an array. It's a hassle to redefine, so this introduces some
8
- # shorthand to get at the size of the resultant string.
9
- def sz
10
- self.to_s.size
11
- end
12
-
13
- alias len sz
14
-
15
- # Typecast is used mostly by packet header classes, such as IPHeader,
16
- # TCPHeader, and the like. It takes an argument, and casts it to the
17
- # expected type for that element.
18
- def typecast(i)
19
- c = caller[0].match(/.*`([^']+)='/)[1]
20
- self[c.intern].read i
21
- end
22
-
23
- # Used like typecast(), but specifically for casting Strings to StructFu::Strings.
24
- def body=(i)
25
- if i.kind_of? ::String
26
- typecast(i)
27
- elsif i.kind_of? StructFu
28
- self[:body] = i
29
- elsif i.nil?
30
- self[:body] = StructFu::String.new.read("")
31
- else
32
- raise ArgumentError, "Can't cram a #{i.class} into a StructFu :body"
33
- end
34
- end
35
-
36
- # Handle deep copies correctly. Marshal in 1.9, re-read myself on 1.8
37
- def clone
38
- begin
39
- Marshal.load(Marshal.dump(self))
40
- rescue
41
- self.class.new.read(self.to_s)
42
- end
43
- end
44
-
45
- # Ints all have a value, an endianness, and a default value.
46
- # Note that the signedness of Int values are implicit as
47
- # far as the subclasses are concerned; to_i and to_f will
48
- # return Integer/Float versions of the input value, instead
49
- # of attempting to unpack the pack value. (This can be a useful
50
- # hint to other functions).
51
- #
52
- # ==== Header Definition
53
- #
54
- # Fixnum :value
55
- # Symbol :endian
56
- # Fixnum :width
57
- # Fixnum :default
58
- class Int < Struct.new(:value, :endian, :width, :default)
59
- alias :v= :value=
60
- alias :v :value
61
- alias :e= :endian=
62
- alias :e :endian
63
- alias :w= :width=
64
- alias :w :width
65
- alias :d= :default=
66
- alias :d :default
67
-
68
- # This is a parent class definition and should not be used directly.
69
- def to_s
70
- raise StandardError, "StructFu::Int#to_s accessed, must be redefined."
71
- end
72
-
73
- # Returns the Int as an Integer.
74
- def to_i
75
- (self.v || self.d).to_i
76
- end
77
-
78
- # Returns the Int as a Float.
79
- def to_f
80
- (self.v || self.d).to_f
81
- end
82
-
83
- def initialize(value=nil, endian=nil, width=nil, default=nil)
84
- super(value,endian,width,default=0)
85
- end
86
-
87
- # Reads either an Integer or a packed string, and populates the value accordingly.
88
- def read(i)
89
- self.v = i.kind_of?(Integer) ? i.to_i : i.to_s.unpack(@packstr).first
90
- self
91
- end
92
-
93
- end
94
-
95
- # Int8 is a one byte value.
96
- class Int8 < Int
97
-
98
- def initialize(v=nil)
99
- super(v,nil,w=1)
100
- @packstr = "C"
101
- end
102
-
103
- # Returns a one byte value as a packed string.
104
- def to_s
105
- [(self.v || self.d)].pack("C")
106
- end
107
-
108
- end
109
-
110
- # Int16 is a two byte value.
111
- class Int16 < Int
112
- def initialize(v=nil, e=:big)
113
- super(v,e,w=2)
114
- @packstr = (self.e == :big) ? "n" : "v"
115
- end
116
-
117
- # Returns a two byte value as a packed string.
118
- def to_s
119
- @packstr = (self.e == :big) ? "n" : "v"
120
- [(self.v || self.d)].pack(@packstr)
121
- end
122
-
123
- end
124
6
 
125
- # Int16be is a two byte value in big-endian format. The endianness cannot be altered.
126
- class Int16be < Int16
127
- undef :endian=
128
- end
129
-
130
- # Int16le is a two byte value in little-endian format. The endianness cannot be altered.
131
- class Int16le < Int16
132
- undef :endian=
133
- def initialize(v=nil, e=:little)
134
- super(v,e)
135
- @packstr = (self.e == :big) ? "n" : "v"
136
- end
137
- end
138
-
139
- # Int32 is a four byte value.
140
- class Int32 < Int
141
- def initialize(v=nil, e=:big)
142
- super(v,e,w=4)
143
- @packstr = (self.e == :big) ? "N" : "V"
144
- end
145
-
146
- # Returns a four byte value as a packed string.
147
- def to_s
148
- @packstr = (self.e == :big) ? "N" : "V"
149
- [(self.v || self.d)].pack(@packstr)
150
- end
151
-
152
- end
153
-
154
- # Int32be is a four byte value in big-endian format. The endianness cannot be altered.
155
- class Int32be < Int32
156
- undef :endian=
157
- end
158
-
159
- # Int32le is a four byte value in little-endian format. The endianness cannot be altered.
160
- class Int32le < Int32
161
- undef :endian=
162
- def initialize(v=nil, e=:little)
163
- super(v,e)
164
- end
165
- end
166
-
167
- # Strings are just like regular strings, except it comes with a read() function
168
- # so that it behaves like other StructFu elements.
169
- class String < ::String
170
- def read(str)
171
- str = str.to_s
172
- self.replace str
173
- self
174
- end
175
- end
176
-
177
- # Provides a primitive for creating strings, preceeded by
178
- # an Int type of length. By default, a string of length zero with
179
- # a one-byte length is presumed.
180
- #
181
- # Note that IntStrings aren't used for much, but it seemed like a good idea at the time.
182
- class IntString < Struct.new(:int, :string, :mode)
183
-
184
- def initialize(string='',int=Int8,mode=nil)
185
- if int < Int
186
- super(int.new,string,mode)
187
- calc
188
- else
189
- raise "IntStrings need a StructFu::Int for a length."
190
- end
191
- end
192
-
193
- # Calculates the size of a string, and sets it as the value.
194
- def calc
195
- int.v = string.to_s.size
196
- self.to_s
197
- end
198
-
199
- # Returns the object as a string, depending on the mode set upon object creation.
200
- def to_s
201
- if mode == :parse
202
- "#{int}" + [string].pack("a#{len}")
203
- elsif mode == :fix
204
- self.int.v = string.size
205
- "#{int}#{string}"
206
- else
207
- "#{int}#{string}"
208
- end
209
- end
210
-
211
- # By redefining #string=, we can ensure the correct value
212
- # is calculated upon assignment. If you'd prefer to have
213
- # an incorrect value, use the syntax, obj[:string]="value"
214
- # instead. Note, by using the alternate form, you must
215
- # #calc before you can trust the int's value. Think of the =
216
- # assignment as "set to equal," while the []= assignment
217
- # as "boxing in" the value. Maybe.
218
- def string=(s)
219
- self[:string] = s
220
- calc
221
- end
222
-
223
- # Shorthand for querying a length. Note that the usual "length"
224
- # and "size" refer to the number of elements of this struct.
225
- def len
226
- self[:int].value
227
- end
228
-
229
- # Override the size, if you must.
230
- def len=(i)
231
- self[:int].value=i
232
- end
233
-
234
- # Read takes a string, assumes an int width as previously
235
- # defined upon initialization, but makes no guarantees
236
- # the int value isn't lying. You're on your own to test
237
- # for that (or use parse() with a :mode set).
238
- def read(s)
239
- unless s[0,int.width].size == int.width
240
- raise StandardError, "String is too short for type #{int.class}"
241
- else
242
- int.read(s[0,int.width])
243
- self[:string] = s[int.width,s.size]
244
- end
245
- self.to_s
246
- end
247
-
248
- # parse() is like read(), except that it interprets the string, either
249
- # based on the declared length, or the actual length. Which strategy
250
- # is used is dependant on which :mode is set (with self.mode).
251
- #
252
- # :parse : Read the length, and then read in that many bytes of the string.
253
- # The string may be truncated or padded out with nulls, as dictated by the value.
254
- #
255
- # :fix : Skip the length, read the rest of the string, then set the length
256
- # to what it ought to be.
257
- #
258
- # else : If neither of these modes are set, just perfom a normal read().
259
- # This is the default.
260
- def parse(s)
261
- unless s[0,int.width].size == int.width
262
- raise StandardError, "String is too short for type #{int.class}"
263
- else
264
- case mode
265
- when :parse
266
- int.read(s[0,int.width])
267
- self[:string] = s[int.width,int.value]
268
- if string.size < int.value
269
- self[:string] += ("\x00" * (int.value - self[:string].size))
270
- end
271
- when :fix
272
- self.string = s[int.width,s.size]
273
- else
274
- return read(s)
275
- end
276
- end
277
- self.to_s
278
- end
279
-
280
- end
7
+ # Normally, self.size and self.length will refer to the Struct
8
+ # size as an array. It's a hassle to redefine, so this introduces some
9
+ # shorthand to get at the size of the resultant string.
10
+ def sz
11
+ self.to_s.size
12
+ end
13
+
14
+ alias len sz
15
+
16
+ # Typecast is used mostly by packet header classes, such as IPHeader,
17
+ # TCPHeader, and the like. It takes an argument, and casts it to the
18
+ # expected type for that element.
19
+ def typecast(i)
20
+ c = caller[0].match(/.*`([^']+)='/)[1]
21
+ self[c.intern].read i
22
+ end
23
+
24
+ # Used like typecast(), but specifically for casting Strings to StructFu::Strings.
25
+ def body=(i)
26
+ if i.kind_of? ::String
27
+ typecast(i)
28
+ elsif i.kind_of? StructFu
29
+ self[:body] = i
30
+ elsif i.nil?
31
+ self[:body] = StructFu::String.new.read("")
32
+ else
33
+ raise ArgumentError, "Can't cram a #{i.class} into a StructFu :body"
34
+ end
35
+ end
36
+
37
+ # Handle deep copies correctly. Marshal in 1.9, re-read myself on 1.8
38
+ def clone
39
+ begin
40
+ Marshal.load(Marshal.dump(self))
41
+ rescue
42
+ self.class.new.read(self.to_s)
43
+ end
44
+ end
45
+
46
+ # Ints all have a value, an endianness, and a default value.
47
+ # Note that the signedness of Int values are implicit as
48
+ # far as the subclasses are concerned; to_i and to_f will
49
+ # return Integer/Float versions of the input value, instead
50
+ # of attempting to unpack the pack value. (This can be a useful
51
+ # hint to other functions).
52
+ #
53
+ # ==== Header Definition
54
+ #
55
+ # Fixnum :value
56
+ # Symbol :endian
57
+ # Fixnum :width
58
+ # Fixnum :default
59
+ class Int < Struct.new(:value, :endian, :width, :default)
60
+ alias :v= :value=
61
+ alias :v :value
62
+ alias :e= :endian=
63
+ alias :e :endian
64
+ alias :w= :width=
65
+ alias :w :width
66
+ alias :d= :default=
67
+ alias :d :default
68
+
69
+ # This is a parent class definition and should not be used directly.
70
+ def to_s
71
+ raise StandardError, "StructFu::Int#to_s accessed, must be redefined."
72
+ end
73
+
74
+ # Returns the Int as an Integer.
75
+ def to_i
76
+ (self.v || self.d).to_i
77
+ end
78
+
79
+ # Returns the Int as a Float.
80
+ def to_f
81
+ (self.v || self.d).to_f
82
+ end
83
+
84
+ def initialize(value=nil, endian=nil, width=nil, default=nil)
85
+ super(value,endian,width,default=0)
86
+ end
87
+
88
+ # Reads either an Integer or a packed string, and populates the value accordingly.
89
+ def read(i)
90
+ self.v = i.kind_of?(Integer) ? i.to_i : i.to_s.unpack(@packstr).first
91
+ self
92
+ end
93
+
94
+ end
95
+
96
+ # Int8 is a one byte value.
97
+ class Int8 < Int
98
+
99
+ def initialize(v=nil)
100
+ super(v,nil,w=1)
101
+ @packstr = "C"
102
+ end
103
+
104
+ # Returns a one byte value as a packed string.
105
+ def to_s
106
+ [(self.v || self.d)].pack("C")
107
+ end
108
+
109
+ end
110
+
111
+ # Int16 is a two byte value.
112
+ class Int16 < Int
113
+ def initialize(v=nil, e=:big)
114
+ super(v,e,w=2)
115
+ @packstr = (self.e == :big) ? "n" : "v"
116
+ end
117
+
118
+ # Returns a two byte value as a packed string.
119
+ def to_s
120
+ @packstr = (self.e == :big) ? "n" : "v"
121
+ [(self.v || self.d)].pack(@packstr)
122
+ end
123
+
124
+ end
125
+
126
+ # Int16be is a two byte value in big-endian format. The endianness cannot be altered.
127
+ class Int16be < Int16
128
+ undef :endian=
129
+ end
130
+
131
+ # Int16le is a two byte value in little-endian format. The endianness cannot be altered.
132
+ class Int16le < Int16
133
+ undef :endian=
134
+ def initialize(v=nil, e=:little)
135
+ super(v,e)
136
+ @packstr = (self.e == :big) ? "n" : "v"
137
+ end
138
+ end
139
+
140
+ # Int32 is a four byte value.
141
+ class Int32 < Int
142
+ def initialize(v=nil, e=:big)
143
+ super(v,e,w=4)
144
+ @packstr = (self.e == :big) ? "N" : "V"
145
+ end
146
+
147
+ # Returns a four byte value as a packed string.
148
+ def to_s
149
+ @packstr = (self.e == :big) ? "N" : "V"
150
+ [(self.v || self.d)].pack(@packstr)
151
+ end
152
+
153
+ end
154
+
155
+ # Int32be is a four byte value in big-endian format. The endianness cannot be altered.
156
+ class Int32be < Int32
157
+ undef :endian=
158
+ end
159
+
160
+ # Int32le is a four byte value in little-endian format. The endianness cannot be altered.
161
+ class Int32le < Int32
162
+ undef :endian=
163
+ def initialize(v=nil, e=:little)
164
+ super(v,e)
165
+ end
166
+ end
167
+
168
+ # Strings are just like regular strings, except it comes with a read() function
169
+ # so that it behaves like other StructFu elements.
170
+ class String < ::String
171
+ def read(str)
172
+ str = str.to_s
173
+ self.replace str
174
+ self
175
+ end
176
+ end
177
+
178
+ # Provides a primitive for creating strings, preceeded by
179
+ # an Int type of length. By default, a string of length zero with
180
+ # a one-byte length is presumed.
181
+ #
182
+ # Note that IntStrings aren't used for much, but it seemed like a good idea at the time.
183
+ class IntString < Struct.new(:int, :string, :mode)
184
+
185
+ def initialize(string='',int=Int8,mode=nil)
186
+ if int < Int
187
+ super(int.new,string,mode)
188
+ calc
189
+ else
190
+ raise "IntStrings need a StructFu::Int for a length."
191
+ end
192
+ end
193
+
194
+ # Calculates the size of a string, and sets it as the value.
195
+ def calc
196
+ int.v = string.to_s.size
197
+ self.to_s
198
+ end
199
+
200
+ # Returns the object as a string, depending on the mode set upon object creation.
201
+ def to_s
202
+ if mode == :parse
203
+ "#{int}" + [string].pack("a#{len}")
204
+ elsif mode == :fix
205
+ self.int.v = string.size
206
+ "#{int}#{string}"
207
+ else
208
+ "#{int}#{string}"
209
+ end
210
+ end
211
+
212
+ # By redefining #string=, we can ensure the correct value
213
+ # is calculated upon assignment. If you'd prefer to have
214
+ # an incorrect value, use the syntax, obj[:string]="value"
215
+ # instead. Note, by using the alternate form, you must
216
+ # #calc before you can trust the int's value. Think of the =
217
+ # assignment as "set to equal," while the []= assignment
218
+ # as "boxing in" the value. Maybe.
219
+ def string=(s)
220
+ self[:string] = s
221
+ calc
222
+ end
223
+
224
+ # Shorthand for querying a length. Note that the usual "length"
225
+ # and "size" refer to the number of elements of this struct.
226
+ def len
227
+ self[:int].value
228
+ end
229
+
230
+ # Override the size, if you must.
231
+ def len=(i)
232
+ self[:int].value=i
233
+ end
234
+
235
+ # Read takes a string, assumes an int width as previously
236
+ # defined upon initialization, but makes no guarantees
237
+ # the int value isn't lying. You're on your own to test
238
+ # for that (or use parse() with a :mode set).
239
+ def read(s)
240
+ unless s[0,int.width].size == int.width
241
+ raise StandardError, "String is too short for type #{int.class}"
242
+ else
243
+ int.read(s[0,int.width])
244
+ self[:string] = s[int.width,s.size]
245
+ end
246
+ self.to_s
247
+ end
248
+
249
+ # parse() is like read(), except that it interprets the string, either
250
+ # based on the declared length, or the actual length. Which strategy
251
+ # is used is dependant on which :mode is set (with self.mode).
252
+ #
253
+ # :parse : Read the length, and then read in that many bytes of the string.
254
+ # The string may be truncated or padded out with nulls, as dictated by the value.
255
+ #
256
+ # :fix : Skip the length, read the rest of the string, then set the length
257
+ # to what it ought to be.
258
+ #
259
+ # else : If neither of these modes are set, just perfom a normal read().
260
+ # This is the default.
261
+ def parse(s)
262
+ unless s[0,int.width].size == int.width
263
+ raise StandardError, "String is too short for type #{int.class}"
264
+ else
265
+ case mode
266
+ when :parse
267
+ int.read(s[0,int.width])
268
+ self[:string] = s[int.width,int.value]
269
+ if string.size < int.value
270
+ self[:string] += ("\x00" * (int.value - self[:string].size))
271
+ end
272
+ when :fix
273
+ self.string = s[int.width,s.size]
274
+ else
275
+ return read(s)
276
+ end
277
+ end
278
+ self.to_s
279
+ end
280
+
281
+ end
281
282
 
282
283
  end
283
284
 
284
285
  class Struct
285
286
 
286
- # Monkeypatch for Struct to include some string safety -- anything that uses
287
- # Struct is going to presume binary strings anyway.
288
- def force_binary(str)
289
- PacketFu.force_binary(str)
290
- end
287
+ # Monkeypatch for Struct to include some string safety -- anything that uses
288
+ # Struct is going to presume binary strings anyway.
289
+ def force_binary(str)
290
+ PacketFu.force_binary(str)
291
+ end
291
292
 
292
293
  end
293
294