tlv 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,31 @@
1
+ module TLV
2
+ class TLV
3
+ class Raw < Field
4
+ def initialize clazz, desc=nil, name=nil, len=0
5
+ desc ||= "Value"
6
+ super
7
+ end
8
+ def define_accessor clazz
9
+ super
10
+ name = @name
11
+ clazz.instance_eval{
12
+ define_method("#{name}="){|val|
13
+ val ||= ""
14
+ raise("must be a String #{val}") unless val.is_a?(String)
15
+ self.instance_variable_set("@#{name}", val)
16
+ }
17
+
18
+ define_method("#{name}") {
19
+ self.instance_variable_get("@#{name}") || ""
20
+ }
21
+ }
22
+ end
23
+ def parse tlv, bytes, length
24
+ val = bytes[0, length]
25
+ rest = bytes[length, bytes.length]
26
+ tlv.send("#{name}=", val)
27
+ rest
28
+ end
29
+ end
30
+ end
31
+ end # module
@@ -0,0 +1,51 @@
1
+ module TLV
2
+ class TLV
3
+
4
+ class << self
5
+ CLASS_MASK = 0xC0
6
+ UNIVERSAL_CLASS = 0x00
7
+ APPLICATION = 0x40
8
+ CTX_SPECIFIC = 0x80
9
+ PRIVATE = 0xC0
10
+
11
+ BYTE6 = 0x20
12
+
13
+ def universal?
14
+ ((@tag & CLASS_MASK) == UNIVERSAL_CLASS) if @tag
15
+ end
16
+ def application?
17
+ ((@tag & CLASS_MASK) == APPLICATION) if @tag
18
+ end
19
+ def context_specific?
20
+ ((@tag & CLASS_MASK) == CTX_SPECIFIC) if @tag
21
+ end
22
+ def private?
23
+ ((@tag & CLASS_MASK) == PRIVATE) if @tag
24
+ end
25
+ def primitive?
26
+ if @tag
27
+ ((@tag & BYTE6) == 0x00)
28
+ else
29
+ true # `tagless` datastructs are by default primitive
30
+ end
31
+ end
32
+ def constructed?
33
+ ((@tag & BYTE6) == BYTE6) if @tag
34
+ end
35
+ # check the tag is approriate length
36
+ def check_tag
37
+ if (tag & 0x1f) == 0x1f # last 5 bits set, 2 byte tag
38
+ raise "Tag too short: #{b2s(tag)} should be 2 bytes" unless tag.length > 1
39
+ if (tag[1]&0x80) == 0x80
40
+ raise "Tag length incorrect: #{b2s(tag)} should be 3 bytes" unless tag.length == 3
41
+ else
42
+ raise "Tag too long: #{b2s(tag)} should be 2 bytes" if tag.length > 2
43
+ end
44
+ else
45
+ raise "Tag too long: #{b2s(tag)} should be 1 bytes" if tag.length > 1
46
+ end
47
+ end
48
+
49
+ end
50
+ end
51
+ end # module
@@ -0,0 +1,144 @@
1
+
2
+ module TLV
3
+
4
+ def self.b2s bytestr
5
+ return "" unless bytestr
6
+ r = bytestr.unpack("H*")[0]
7
+ r.length > 1 ? r : " "
8
+ end
9
+ def self.s2b string
10
+ return "" unless string
11
+ string = string.gsub(/\s+/, "")
12
+ string = "0" + string unless (string.length % 2 == 0)
13
+ [string].pack("H*")
14
+ end
15
+
16
+ class TLV
17
+
18
+ def self.s2b str
19
+ ::TLV.s2b str
20
+ end
21
+ def self.b2s bytes
22
+ ::TLV.b2s bytes
23
+ end
24
+ # def self.register tag, clazz
25
+ # @tlv_classes ||= {}
26
+ # @tlv_classes[tag] = clazz
27
+ # end
28
+
29
+
30
+ DEBUG = ENV["DEBUG"]
31
+
32
+ # Outputs a warning in case the enironment
33
+ # variable `DEBUG` is set.
34
+ def self.warn mes
35
+ STDERR.puts "[warn] #{mes}" if ENV["DEBUG"]
36
+ end
37
+
38
+
39
+ #
40
+ # class A < Field
41
+ # end
42
+ #
43
+ # class AN < Field
44
+ # end
45
+ #
46
+ # class ANS < Field
47
+ # end
48
+ #
49
+ #
50
+ # class CN < Field
51
+ # end
52
+ #
53
+ # class N < Field
54
+ # end
55
+
56
+ class << self
57
+ attr_accessor :tag
58
+ attr_accessor :display_name
59
+ # If this TLV is placed into another as a subfield, this will be
60
+ # the name of the accessor, default is the rubyfied display_name
61
+ attr_accessor :accessor_name
62
+ def tlv tag, display_name, accessor_name=nil
63
+ @tag = case tag
64
+ when String
65
+ TLV.s2b(tag)
66
+ when Fixnum
67
+ TLV::s2b("%x" % tag)
68
+ end
69
+ def @tag.& flag
70
+ self[0] & flag
71
+ end
72
+ check_tag
73
+ @display_name = display_name
74
+ @accessor_name = accessor_name || rubify_a(display_name)
75
+ TLV.register self
76
+ end
77
+
78
+
79
+
80
+ def fields
81
+ @fields ||= (self == TLV ? [] : superclass.fields.dup)
82
+ end
83
+
84
+ def b len, desc, name=nil
85
+ raise "invalid len #{len}" unless (len%8 == 0)
86
+ fields << B.new(self, desc, name, len)
87
+ end
88
+
89
+ def raw desc=nil, name=nil
90
+ @is_raw = true
91
+ fields << Raw.new(self, desc, name)
92
+ end
93
+
94
+ def is_raw?
95
+ @is_raw == true
96
+ end
97
+
98
+ # for constructed tlv's, add subtags that must be present
99
+ end # meta class thingie
100
+
101
+ def display_name
102
+ self.class.display_name
103
+ end
104
+ def to_s
105
+ longest = 0
106
+ fields.each { |field|
107
+ longest = field.display_name.length if field.display_name.length > longest
108
+ }
109
+ fmt = "%#{longest}s : %s\n"
110
+ str = "#{display_name}"
111
+ str << " (0x#{TLV.b2s(tag)})" if tag
112
+ str << "\n"
113
+
114
+ str << "-" * (str.length-1) << "\n"
115
+ fields.each { |field|
116
+ str << (fmt % [field.display_name, TLV.b2s(self.send(field.name))])
117
+ }
118
+ (mandatory+optional).each { |tlv_class|
119
+ temp_tlv = self.send(tlv_class.accessor_name)
120
+ temp = temp_tlv.to_s
121
+ temp.gsub!(/^/, " ")
122
+ str << temp
123
+ }
124
+ str
125
+ end
126
+
127
+
128
+ def fields
129
+ self.class.fields
130
+ end
131
+
132
+ def mandatory
133
+ self.class.mand_tags
134
+ end
135
+ def optional
136
+ self.class.opt_tags
137
+ end
138
+
139
+ def tag
140
+ self.class.tag
141
+ end
142
+ end
143
+
144
+ end # module
@@ -0,0 +1,76 @@
1
+ module TLV
2
+ class TLV
3
+
4
+ def get_bytes
5
+ if self.class.primitive? || (self.is_a?(DGI) && fields.length!=0) || self.class.is_raw?
6
+ bytes = get_bytes_primitive
7
+ else
8
+ bytes = get_bytes_constructed
9
+ end
10
+ end
11
+ def get_bytes_primitive
12
+ bytes = ""
13
+ fields.each { |field|
14
+ bytes << self.send(field.name)
15
+ }
16
+ bytes
17
+ end
18
+
19
+ def get_bytes_constructed
20
+ bytes = ""
21
+ mandatory.each {|t|
22
+ tlv = self.send(t.accessor_name)
23
+ raise "Mandatory subtag #{t} not set!" unless tlv
24
+ bytes << tlv.to_b
25
+ }
26
+ optional.each { |t|
27
+ tlv = self.send(t.accessor_name)
28
+ bytes << tlv.to_b if tlv
29
+ }
30
+ bytes
31
+ end
32
+
33
+ def get_len_bytes len
34
+
35
+ # one byte max = 127
36
+ # two = 255 (2**8)-1
37
+ # three = 65535 (2 ** 16) -1
38
+ # four = 16777215 (2 ** 32) -1
39
+ num_len_bytes = case len
40
+ when 0..127 : 1
41
+ when 128..255 : 2
42
+ when 256..65535 : 3 # short
43
+ when 65536..4294967295 : 5 # long, skip 3 byte len, too difficult :)
44
+ else
45
+ raise "Don't be silly"
46
+ end
47
+ len_bytes = case num_len_bytes
48
+ when 1 : "" << len
49
+ when 2 : "\x81" << len
50
+ when 3 : "\x82" << [len].pack("n")
51
+ when 5 : "\x84" << [len].pack("N")
52
+ else
53
+ raise "Can't happen"
54
+ end
55
+ return len_bytes
56
+ end
57
+
58
+ # this provides an opportunity to manipulate the payload
59
+ # before it is assembled. Expects a proc taking the bytes of the
60
+ # payload and returning the new version of the bytes.
61
+ def payload_hook= hook
62
+ @hook = hook
63
+ end
64
+
65
+ def to_b
66
+ bytes = get_bytes
67
+ bytes = @hook.call(bytes) if @hook
68
+ if tag
69
+ bytes.insert 0, get_len_bytes(bytes.length)
70
+ bytes.insert 0, tag
71
+ end
72
+ bytes
73
+ end
74
+ end
75
+ end # module
76
+
@@ -0,0 +1,106 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/tlv'
3
+
4
+ class TestTLVConstructed < Test::Unit::TestCase
5
+ include TLV
6
+ def setup
7
+ end
8
+
9
+ class TLVTagTest < TLV
10
+ tlv "41", "Test TLV"
11
+ b 8, "first field", :first
12
+ b 8, "second field", :second
13
+ end
14
+ class TLVTestCons < TLV
15
+ tlv "32", "Test Constructed"
16
+ mandatory TLVTagTest, "tlv_tag_test"
17
+ mandatory :tag => "43",
18
+ :display_name => "Another Test"
19
+ end
20
+
21
+ class TLVTestCons2 < TLV
22
+ tlv "32", "Test Constructed 32 2"
23
+ optional TLVTagTest, "tlv_tag_test"
24
+ optional :tag => "43",
25
+ :display_name => "Another Test"
26
+ end
27
+
28
+ class DGITest < DGI
29
+ tlv "9102", "Random Test Data"
30
+ mandatory TLVTagTest, :test
31
+ optional :tag => "43",
32
+ :display_name => "Another Test"
33
+ end
34
+
35
+ def basics t
36
+ assert(t.methods.include?("tlv_tag_test" ))
37
+ assert(t.methods.include?("tlv_tag_test="))
38
+ assert(t.methods.include?("another_test" ))
39
+ assert(t.methods.include?("another_test="))
40
+ end
41
+ def test_basics
42
+ t = TLVTestCons.new
43
+ basics t
44
+ assert_nothing_raised {
45
+ t = TLVTestCons::AnotherTest.new
46
+ }
47
+ t = TLVTestCons2.new
48
+ basics t
49
+ assert_nothing_raised {
50
+ t = TLVTestCons2::AnotherTest.new
51
+ }
52
+ end
53
+
54
+ def test_const_to_b
55
+ t = TLVTagTest.new
56
+ t.first = "\x01"
57
+ t.second = "\x02"
58
+
59
+ t2 = TLVTestCons::AnotherTest.new
60
+ t2.value = "\x34\x56"
61
+
62
+ t3 = TLVTestCons.new
63
+ t3.tlv_tag_test= t
64
+ t3.another_test= t2
65
+
66
+ assert_equal(TLV.s2b("32084102010243023456"), t3.to_b)
67
+
68
+ bytes = t3.to_b
69
+ t, rest = TLVTestCons._parse bytes*2
70
+ assert_equal bytes, rest
71
+ assert_equal "\x01", t.tlv_tag_test.first
72
+ assert_equal "\x02", t.tlv_tag_test.second
73
+ assert_equal "\x34\x56", t.another_test.value
74
+
75
+ t, rest = TLV._parse rest
76
+ assert_equal "\x01", t.tlv_tag_test.first
77
+ assert_equal "\x02", t.tlv_tag_test.second
78
+ assert_equal "\x34\x56", t.another_test.value
79
+ end
80
+
81
+ def test_const_direct_value
82
+ t = TLVTagTest.new
83
+ t.first = "\x01"
84
+ t.second = "\x02"
85
+
86
+ t2 = TLVTestCons.new
87
+ t2.tlv_tag_test=t
88
+ t2.another_test="\x34\x56"
89
+
90
+ assert_equal(TLV.s2b("32084102010243023456"), t2.to_b)
91
+
92
+ end
93
+
94
+ def test_const_dgi
95
+ te = TLVTagTest.new
96
+ te.first= "\x01"
97
+ te.second= "\x02"
98
+ dgi = DGITest.new
99
+ dgi.test= te
100
+ dgi.another_test="\x34\x56"
101
+
102
+ assert_equal(TLV.s2b("9102084102010243023456"), dgi.to_b)
103
+ end
104
+
105
+
106
+ end
@@ -0,0 +1,122 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/tlv'
3
+
4
+ class TestDGI < Test::Unit::TestCase
5
+ include TLV
6
+ def setup
7
+ end
8
+
9
+ class TLVTest < DGI
10
+ tlv "0101", "Test TLV"
11
+ b 8, "first field", :first
12
+ b 8, "second field", :second
13
+ end
14
+
15
+ class TLVTest2 < DGI
16
+ tlv "0102", "Test Rubify"
17
+ b 8, "My Test"
18
+ b 8, "Oh M@i!"
19
+ end
20
+
21
+ class TLVTest3 < DGI
22
+ tlv "9F00", "Test Raw"
23
+ raw
24
+ end
25
+ class TLVTestNoTag < DGI
26
+ b 8, "first field", :first
27
+ b 8, "second field", :second
28
+ end
29
+
30
+ def basics tlv
31
+ tlv.first="\x01"
32
+ tlv.second="\xAA"
33
+ assert_equal "\x01", tlv.first
34
+ assert_equal "\xaa", tlv.second
35
+
36
+ assert_raise(RuntimeError) {
37
+ tlv.first="\x02\x03"
38
+ }
39
+ assert_raise(RuntimeError) {
40
+ tlv.first=Time.new
41
+ }
42
+ assert_raise(RuntimeError) {
43
+ tlv.second=1
44
+ }
45
+ end
46
+
47
+ def test_basics
48
+ t = TLVTest.new
49
+ basics t
50
+ assert_equal "\x01\x01\x02\x01\xaa", t.to_b
51
+
52
+ end
53
+
54
+ def test_parse_tag
55
+
56
+ end
57
+
58
+ def test_length
59
+ t = TLVTest3.new
60
+ t.value = ""
61
+ assert_equal "\x00", t.to_b[2,1]
62
+ t.value = "1"
63
+ assert_equal "\x01\x31", t.to_b[2,2]
64
+ t.value = "1"*127
65
+ assert_equal "\x7F\x31", t.to_b[2,2]
66
+ t.value = "1"*128
67
+ assert_equal "\x80\x31", t.to_b[2,2]
68
+ t.value = "1"*255
69
+ assert_equal "\xFf\x00\xff\x31", t.to_b[2,4]
70
+ t.value = "1"*256
71
+ assert_equal "\xFF\x01\x00\x31", t.to_b[2,4]
72
+
73
+ assert_raises (RuntimeError) {
74
+ t.value = "1"*65535
75
+ assert_equal "\x82\xFF\xFF\x31", t.to_b[2,4]
76
+ }
77
+
78
+ o = Object.new
79
+ def o.length
80
+ return 4294967296
81
+ end
82
+
83
+ assert_raises (RuntimeError) {
84
+ t.value=o
85
+ t.to_b
86
+ }
87
+ end
88
+
89
+ def test_parse
90
+ t = TLVTest.new
91
+ assert_equal "\x00", t.first
92
+ t.first="\x01"
93
+ t.second="\xAA"
94
+
95
+ assert "\x01", t.first
96
+ bytes = t.to_b
97
+ # t, rest = TLVTest.parse bytes
98
+ # assert_equal TLVTest, t.class
99
+ # assert_equal "\x01", t.first
100
+ # assert_equal "\xAA", t.second
101
+ end
102
+ def test_rubify
103
+ t = TLVTest2.new
104
+ t.my_test = "\x01"
105
+ assert_equal "\x01", t.my_test
106
+ t.oh_mi = "\x02"
107
+ assert_equal "\x02", t.oh_mi
108
+ end
109
+ def test_raw
110
+ t = TLVTest3.new
111
+ #puts t.methods.sort
112
+ t.value= "bumsi"
113
+ assert_equal "Test Raw", TLVTest3.display_name
114
+ assert_equal "bumsi", t.value
115
+ bytes = t.to_b
116
+ # t, rest = TLVTest3.parse bytes
117
+ # assert_equal "bumsi", t.value
118
+ # assert_equal TLVTest3, t.class
119
+ end
120
+
121
+
122
+ end