packetfu 1.1.8 → 1.1.9

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 (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