packetfu 1.1.9 → 1.1.10

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 (77) hide show
  1. data/bench/octets.rb +9 -9
  2. data/examples/100kpackets.rb +12 -12
  3. data/examples/ackscan.rb +16 -16
  4. data/examples/arp.rb +35 -35
  5. data/examples/arphood.rb +36 -36
  6. data/examples/dissect_thinger.rb +6 -6
  7. data/examples/new-simple-stats.rb +23 -23
  8. data/examples/packetfu-shell.rb +25 -25
  9. data/examples/simple-sniffer.rb +9 -9
  10. data/examples/simple-stats.rb +23 -23
  11. data/examples/slammer.rb +3 -3
  12. data/lib/packetfu.rb +127 -127
  13. data/lib/packetfu/capture.rb +169 -169
  14. data/lib/packetfu/config.rb +52 -52
  15. data/lib/packetfu/inject.rb +56 -56
  16. data/lib/packetfu/packet.rb +528 -528
  17. data/lib/packetfu/pcap.rb +579 -579
  18. data/lib/packetfu/protos/arp.rb +90 -90
  19. data/lib/packetfu/protos/arp/header.rb +158 -158
  20. data/lib/packetfu/protos/arp/mixin.rb +36 -36
  21. data/lib/packetfu/protos/eth.rb +44 -44
  22. data/lib/packetfu/protos/eth/header.rb +243 -243
  23. data/lib/packetfu/protos/eth/mixin.rb +3 -3
  24. data/lib/packetfu/protos/hsrp.rb +69 -69
  25. data/lib/packetfu/protos/hsrp/header.rb +107 -107
  26. data/lib/packetfu/protos/hsrp/mixin.rb +29 -29
  27. data/lib/packetfu/protos/icmp.rb +71 -71
  28. data/lib/packetfu/protos/icmp/header.rb +82 -82
  29. data/lib/packetfu/protos/icmp/mixin.rb +14 -14
  30. data/lib/packetfu/protos/invalid.rb +49 -49
  31. data/lib/packetfu/protos/ip.rb +69 -69
  32. data/lib/packetfu/protos/ip/header.rb +291 -291
  33. data/lib/packetfu/protos/ip/mixin.rb +40 -40
  34. data/lib/packetfu/protos/ipv6.rb +50 -50
  35. data/lib/packetfu/protos/ipv6/header.rb +188 -188
  36. data/lib/packetfu/protos/ipv6/mixin.rb +29 -29
  37. data/lib/packetfu/protos/tcp.rb +176 -176
  38. data/lib/packetfu/protos/tcp/ecn.rb +35 -35
  39. data/lib/packetfu/protos/tcp/flags.rb +74 -74
  40. data/lib/packetfu/protos/tcp/header.rb +268 -268
  41. data/lib/packetfu/protos/tcp/hlen.rb +32 -32
  42. data/lib/packetfu/protos/tcp/mixin.rb +46 -46
  43. data/lib/packetfu/protos/tcp/option.rb +321 -321
  44. data/lib/packetfu/protos/tcp/options.rb +95 -95
  45. data/lib/packetfu/protos/tcp/reserved.rb +35 -35
  46. data/lib/packetfu/protos/udp.rb +116 -116
  47. data/lib/packetfu/protos/udp/header.rb +91 -91
  48. data/lib/packetfu/protos/udp/mixin.rb +3 -3
  49. data/lib/packetfu/structfu.rb +280 -280
  50. data/lib/packetfu/utils.rb +226 -217
  51. data/lib/packetfu/version.rb +41 -41
  52. data/packetfu.gemspec +2 -1
  53. data/spec/ethpacket_spec.rb +48 -48
  54. data/spec/packet_spec.rb +57 -57
  55. data/spec/packet_subclasses_spec.rb +8 -8
  56. data/spec/packetfu_spec.rb +59 -59
  57. data/spec/structfu_spec.rb +268 -268
  58. data/spec/tcp_spec.rb +75 -75
  59. data/test/all_tests.rb +13 -13
  60. data/test/func_lldp.rb +3 -3
  61. data/test/ptest.rb +2 -2
  62. data/test/test_arp.rb +116 -116
  63. data/test/test_capture.rb +45 -45
  64. data/test/test_eth.rb +68 -68
  65. data/test/test_hsrp.rb +9 -9
  66. data/test/test_icmp.rb +52 -52
  67. data/test/test_inject.rb +18 -18
  68. data/test/test_invalid.rb +16 -16
  69. data/test/test_ip.rb +36 -36
  70. data/test/test_ip6.rb +48 -48
  71. data/test/test_octets.rb +21 -21
  72. data/test/test_packet.rb +154 -154
  73. data/test/test_pcap.rb +170 -170
  74. data/test/test_structfu.rb +97 -97
  75. data/test/test_tcp.rb +320 -320
  76. data/test/test_udp.rb +76 -76
  77. metadata +4 -3
@@ -1,108 +1,108 @@
1
1
  # -*- coding: binary -*-
2
2
  module PacketFu
3
3
 
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)
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)
16
16
 
17
- include StructFu
17
+ include StructFu
18
18
 
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
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
28
28
 
29
- # Returns the object in string form.
30
- def to_s
31
- self.to_a.map {|x| x.to_s}.join
32
- end
29
+ # Returns the object in string form.
30
+ def to_s
31
+ self.to_a.map {|x| x.to_s}.join
32
+ end
33
33
 
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
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
45
45
 
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
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
62
62
 
63
- # Returns the true length of the UDP packet.
64
- def udp_calc_len
65
- body.to_s.size + 8
66
- end
63
+ # Returns the true length of the UDP packet.
64
+ def udp_calc_len
65
+ body.to_s.size + 8
66
+ end
67
67
 
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
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
80
80
 
81
- # Equivalent to udp_src.to_i
82
- def udp_sport
83
- self.udp_src
84
- end
81
+ # Equivalent to udp_src.to_i
82
+ def udp_sport
83
+ self.udp_src
84
+ end
85
85
 
86
- # Equivalent to udp_src=
87
- def udp_sport=(arg)
88
- self.udp_src=(arg)
89
- end
86
+ # Equivalent to udp_src=
87
+ def udp_sport=(arg)
88
+ self.udp_src=(arg)
89
+ end
90
90
 
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
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
100
100
 
101
- # Readability aliases
101
+ # Readability aliases
102
102
 
103
- def udp_sum_readable
104
- "0x%04x" % udp_sum
105
- end
103
+ def udp_sum_readable
104
+ "0x%04x" % udp_sum
105
+ end
106
106
 
107
- end
107
+ end
108
108
  end
@@ -1,8 +1,8 @@
1
1
  # -*- coding: binary -*-
2
2
  module PacketFu
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)
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)
6
6
  module UDPHeaderMixin
7
7
  def udp_src=(v); self.udp_header.udp_src= v; end
8
8
  def udp_src; self.udp_header.udp_src; end
@@ -3,292 +3,292 @@
3
3
  # to create meaningful binary data.
4
4
 
5
5
  module StructFu
6
+
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
6
125
 
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
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
282
282
 
283
283
  end
284
284
 
285
285
  class Struct
286
286
 
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
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
292
292
 
293
293
  end
294
294