packetfu 1.1.9 → 1.1.10

Sign up to get free protection for your applications and to get access to all the features.
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