libnet4r 0.1 → 0.2

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.
data/CHANGELOG CHANGED
@@ -1,5 +1,12 @@
1
1
  = Libnet Changelog
2
2
 
3
+ == Version 0.2
4
+
5
+ === November 19, 2007
6
+
7
+ * Added support for TCP.
8
+ * Fixed a bug that prevented assigning nil to fields.
9
+
3
10
  == Version 0.1
4
11
 
5
12
  === November 18, 2007
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require 'rake/clean'
6
6
  require 'rake/rdoctask'
7
7
 
8
8
  NAME = 'libnet4r'
9
- VERS = '0.1'
9
+ VERS = '0.2'
10
10
  GEM_NAME = "#{NAME}-#{VERS}.gem"
11
11
 
12
12
  RDOC_MAIN = "README"
@@ -9,6 +9,7 @@ VALUE cLibnet;
9
9
  extern void define_ipv4_methods(void);
10
10
  extern void define_ipv6_methods(void);
11
11
  extern void define_udp_methods(void);
12
+ extern void define_tcp_methods(void);
12
13
  extern void define_ipv4_methods(void);
13
14
  extern void define_ethernet_methods(void);
14
15
  extern void define_vlan_methods(void);
@@ -696,6 +697,7 @@ void Init_libnet4r()
696
697
 
697
698
  /* define the individual protocol methods */
698
699
  define_udp_methods();
700
+ define_tcp_methods();
699
701
  define_ipv4_methods();
700
702
  define_ipv6_methods();
701
703
  define_ethernet_methods();
@@ -0,0 +1,138 @@
1
+ #include <ruby.h>
2
+ #include <libnet.h>
3
+
4
+ extern VALUE cLibnet;
5
+
6
+ static VALUE net_s_decode_tcp(VALUE self, VALUE bytes)
7
+ {
8
+ VALUE tcp_obj;
9
+ VALUE str;
10
+ VALUE payload;
11
+ struct libnet_tcp_hdr *tcp;
12
+ u_int16_t th_sport, th_dport;
13
+ u_int32_t th_seq, th_ack;
14
+ u_int8_t th_off, th_x2, th_flags;
15
+ u_int16_t th_win, th_sum, th_urp;
16
+
17
+ tcp_obj = rb_funcall(self, rb_intern("new"), 0);
18
+
19
+ str = StringValue(bytes);
20
+
21
+ if (RSTRING(str)->len < LIBNET_TCP_H) {
22
+ rb_raise(rb_eArgError, "string is too small to contain a TCP header");
23
+ }
24
+
25
+ tcp = (struct libnet_tcp_hdr *)RSTRING(str)->ptr;
26
+
27
+ th_sport = ntohs(tcp->th_sport);
28
+ th_dport = ntohs(tcp->th_dport);
29
+ th_seq = ntohl(tcp->th_seq);
30
+ th_ack = ntohl(tcp->th_ack);
31
+ th_off = tcp->th_off;
32
+ th_x2 = tcp->th_x2 << 3;
33
+ th_x2 |= (tcp->th_flags & 0xe0) >> 5;
34
+ th_flags = tcp->th_flags & 0x1f;
35
+ th_win = ntohs(tcp->th_win);
36
+ th_sum = ntohs(tcp->th_sum);
37
+ th_urp = ntohs(tcp->th_urp);
38
+
39
+ rb_iv_set(tcp_obj, "@src_port", UINT2NUM(th_sport));
40
+ rb_iv_set(tcp_obj, "@dst_port", UINT2NUM(th_dport));
41
+ rb_iv_set(tcp_obj, "@seq", ULONG2NUM(th_seq));
42
+ rb_iv_set(tcp_obj, "@ack", ULONG2NUM(th_ack));
43
+ rb_iv_set(tcp_obj, "@data_offset", UINT2NUM(th_off));
44
+ rb_iv_set(tcp_obj, "@reserved", UINT2NUM(th_x2));
45
+ rb_iv_set(tcp_obj, "@control", UINT2NUM(th_flags));
46
+ rb_iv_set(tcp_obj, "@window", UINT2NUM(th_win));
47
+ rb_iv_set(tcp_obj, "@checksum", UINT2NUM(th_sum));
48
+ rb_iv_set(tcp_obj, "@urgent", UINT2NUM(th_urp));
49
+
50
+ if (RSTRING(str)->len > LIBNET_TCP_H) {
51
+ payload = rb_str_new(RSTRING(str)->ptr + LIBNET_TCP_H,
52
+ RSTRING(str)->len - LIBNET_TCP_H);
53
+ }
54
+ else {
55
+ payload = Qnil;
56
+ }
57
+
58
+ rb_iv_set(tcp_obj, "@payload", payload);
59
+
60
+ return tcp_obj;
61
+ }
62
+
63
+ static VALUE net_build_tcp(int argc, VALUE *argv, VALUE self)
64
+ {
65
+ libnet_t *l;
66
+ VALUE tcp, v;
67
+ u_int16_t sp, dp;
68
+ u_int32_t seq, ack;
69
+ u_int8_t control;
70
+ u_int16_t win, sum, urg;
71
+ char *payload = NULL;
72
+ u_int32_t payload_s = 0;
73
+ VALUE ptag_obj;
74
+ libnet_ptag_t ptag = 0;
75
+
76
+ rb_scan_args(argc, argv, "01", &tcp);
77
+
78
+ Data_Get_Struct(self, libnet_t, l);
79
+
80
+ if (NIL_P(tcp)) {
81
+ tcp = rb_class_new_instance(0, NULL, rb_const_get(cLibnet, rb_intern("TCP")));
82
+ rb_yield(tcp);
83
+ }
84
+
85
+ /* check that all required fields are set */
86
+ rb_funcall(tcp, rb_intern("check_packable"), 0);
87
+
88
+ v = rb_funcall(tcp, rb_intern("src_port"), 0);
89
+ sp = NUM2UINT(v);
90
+ v = rb_funcall(tcp, rb_intern("dst_port"), 0);
91
+ dp = NUM2UINT(v);
92
+ v = rb_funcall(tcp, rb_intern("seq"), 0);
93
+ seq = NUM2ULONG(v);
94
+ v = rb_funcall(tcp, rb_intern("ack"), 0);
95
+ ack = NUM2ULONG(v);
96
+ v = rb_funcall(tcp, rb_intern("control"), 0);
97
+ control = NUM2UINT(v);
98
+ v = rb_funcall(tcp, rb_intern("window"), 0);
99
+ win = NUM2UINT(v);
100
+ v = rb_funcall(tcp, rb_intern("checksum"), 0);
101
+ sum = NUM2UINT(v);
102
+ v = rb_funcall(tcp, rb_intern("urgent"), 0);
103
+ urg = NUM2UINT(v);
104
+
105
+ /* get the payload */
106
+ if ((v = rb_funcall(tcp, rb_intern("payload"), 0)) != Qnil) {
107
+ v = StringValue(v);
108
+ payload = RSTRING(v)->ptr;
109
+ payload_s = RSTRING(v)->len;
110
+ }
111
+
112
+ ptag_obj = rb_iv_get(tcp, "@ptag");
113
+
114
+ if (!NIL_P(ptag_obj)) {
115
+ ptag = NUM2LONG(ptag_obj);
116
+ }
117
+
118
+ ptag = libnet_build_tcp(sp, dp, seq, ack, control, win, sum, urg,
119
+ LIBNET_TCP_H + payload_s, (u_int8_t *)payload, payload_s, l, ptag);
120
+
121
+ if (ptag == -1) {
122
+ rb_raise(rb_eRuntimeError, libnet_geterror(l));
123
+ }
124
+
125
+ rb_iv_set(tcp, "@ptag", LONG2NUM(ptag));
126
+
127
+ return tcp;
128
+ }
129
+
130
+ void define_tcp_methods()
131
+ {
132
+ VALUE cTCP = rb_const_get(cLibnet, rb_intern("TCP"));
133
+
134
+ rb_define_singleton_method(cTCP, "decode", net_s_decode_tcp, 1);
135
+
136
+ rb_define_method(cLibnet, "build_tcp", net_build_tcp, -1);
137
+ }
138
+
@@ -40,14 +40,17 @@ class Libnet
40
40
  unless opts[:read_only]
41
41
  self.class_eval do
42
42
  define_method("#{name}=") do |v|
43
- if self.assignment_filters.has_key? name
44
- v = self.assignment_filters[name].call(v)
45
- end
43
+ # allow for setting to nil
44
+ if v
45
+ if self.assignment_filters.has_key? name
46
+ v = self.assignment_filters[name].call(v)
47
+ end
46
48
 
47
- check_integer(v)
49
+ check_integer(v)
48
50
 
49
- unless range.include? v
50
- raise ArgumentError, "value #{v} is out of range for field '#{name}'"
51
+ unless range.include? v
52
+ raise ArgumentError, "value #{v} is out of range for field '#{name}'"
53
+ end
51
54
  end
52
55
 
53
56
  instance_variable_set(ivar, v)
@@ -67,15 +70,20 @@ class Libnet
67
70
  unless opts[:read_only]
68
71
  self.class_eval do
69
72
  define_method("#{name}=") do |v|
70
- value = v.dup
71
- if self.assignment_filters.has_key? name
72
- value = self.assignment_filters[name].call(v)
73
- end
74
-
75
- check_string(value)
76
-
77
- if size && value.length != size
78
- raise ArgumentError, "value must be #{size} bytes long for field '#{name}'"
73
+ if v
74
+ value = v.dup
75
+ if self.assignment_filters.has_key? name
76
+ value = self.assignment_filters[name].call(v)
77
+ end
78
+
79
+ check_string(value)
80
+
81
+ if size && value.length != size
82
+ raise ArgumentError, "value must be #{size} bytes long for field '#{name}'"
83
+ end
84
+ else
85
+ # allow for setting to nil
86
+ value = v
79
87
  end
80
88
 
81
89
  instance_variable_set(ivar, value)
@@ -515,6 +523,82 @@ class Libnet
515
523
  octets_field :payload, nil, :optional => true
516
524
  end
517
525
 
526
+ # = TCP
527
+ #
528
+ # Class to represent an RFC 793 Transmission Control Protocol (TCP) header.
529
+ #
530
+ # === Example
531
+ #
532
+ # t = Libnet::TCP.new
533
+ #
534
+ # t.src_port = 0x4321
535
+ # t.dst_port = 0xabcd
536
+ # t.seq = 32
537
+ # t.ack = 0
538
+ # t.control = 0x0b
539
+ # t.window = 0x1122
540
+ # t.checksum = 0x1111
541
+ # t.urgent = 0
542
+ # t.payload = payload
543
+ #
544
+ # == Public Class Methods
545
+ #
546
+ # === decode(string) -> new TCP object
547
+ #
548
+ # Decode a packed TCP packet.
549
+ #
550
+ # == Public Instance Methods
551
+ #
552
+ # === src_port, src_port=, src_port?
553
+ # Get/set/query the source port.
554
+ #
555
+ # === dst_port, dst_port=, dst_port?
556
+ # Get/set/query the destination port.
557
+ #
558
+ # === seq, seq=, seq?
559
+ # Get/set/query the sequence number.
560
+ #
561
+ # === ack, ack=, ack?
562
+ # Get/set/query the acknowledgement number.
563
+ #
564
+ # === data_offset, data_offset?
565
+ # Get/query the data offset.
566
+ #
567
+ # === reserved, reserved?
568
+ # Get/query the reserved field.
569
+ #
570
+ # === control, control=, control?
571
+ # Get/set/query the control flags.
572
+ #
573
+ # === window, window=, window?
574
+ # Get/set/query the window size.
575
+ #
576
+ # === checksum, checksum=, checksum?
577
+ # Get/set/query the checksum.
578
+ #
579
+ # === urgent, urgent=, urgent?
580
+ # Get/set/query the urgent pointer.
581
+ #
582
+ # === payload, payload=, payload?
583
+ # Get/set/query the payload.
584
+ #
585
+ # === ptag
586
+ # Get the ptag value for this header object. Set by the Libnet builder method.
587
+ #
588
+ class TCP < Header
589
+ unsigned_field :src_port, 2
590
+ unsigned_field :dst_port, 2
591
+ unsigned_field :seq, 4
592
+ unsigned_field :ack, 4
593
+ unsigned_field :data_offset, 4, :units => :bits, :read_only => true
594
+ unsigned_field :reserved, 6, :units => :bits, :read_only => true
595
+ unsigned_field :control, 6, :units => :bits
596
+ unsigned_field :window, 2
597
+ unsigned_field :checksum, 2
598
+ unsigned_field :urgent, 2
599
+ octets_field :payload, nil, :optional => true
600
+ end
601
+
518
602
  # = ARP
519
603
  #
520
604
  # Class to represent an Address Resolution Protocol (ARP) header. Depending
@@ -52,6 +52,12 @@ class TC_Libnet_Header < Test::Unit::TestCase
52
52
  assert_raise NoMethodError do
53
53
  p.baz = 1
54
54
  end
55
+
56
+ # allow for setting fields to nil
57
+ assert_nothing_raised do
58
+ p.foo = nil
59
+ p.qux = nil
60
+ end
55
61
  end
56
62
 
57
63
  def test_read_query
@@ -0,0 +1,198 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
2
+
3
+ require 'test/unit'
4
+ require 'libnet4r'
5
+
6
+ class TC_Libnet_TCP < Test::Unit::TestCase
7
+ def test_unpack
8
+ bytes = [
9
+ 0x43, 0x21, 0xab, 0xcd,
10
+ 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x50, 0x0b, 0x11, 0x22,
11
+ 0x57, 0x9c, 0x00, 0x00, 0x74, 0x68, 0x65, 0x20, 0x71, 0x75, 0x69, 0x63,
12
+ 0x6b, 0x20, 0x62, 0x72, 0x6f, 0x77, 0x6e, 0x20, 0x66, 0x6f, 0x78, 0x20,
13
+ 0x6a, 0x75, 0x6d, 0x70, 0x65, 0x64, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x20,
14
+ 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x7a, 0x79, 0x20, 0x64, 0x6f, 0x67
15
+ ].pack("C*")
16
+ payload = "the quick brown fox jumped over the lazy dog"
17
+
18
+ t = Libnet::TCP.decode(bytes)
19
+
20
+ assert_equal(0x4321, t.src_port)
21
+ assert_equal(0xabcd, t.dst_port)
22
+ assert_equal(32, t.seq)
23
+ assert_equal(0, t.ack)
24
+ assert_equal(5, t.data_offset)
25
+ assert_equal(0, t.reserved)
26
+ assert_equal(0x0b, t.control)
27
+ assert_equal(0x1122, t.window)
28
+ assert_equal(0x579c, t.checksum)
29
+ assert_equal(0, t.urgent)
30
+ assert_equal(payload, t.payload)
31
+
32
+ assert_raise ArgumentError do
33
+ Libnet::TCP.decode("foobar")
34
+ end
35
+ end
36
+
37
+ def test_pack
38
+ bytes = [
39
+ 0x43, 0x21, 0xab, 0xcd,
40
+ 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x50, 0x0b, 0x11, 0x22,
41
+ 0x11, 0x11, 0x00, 0x00, 0x74, 0x68, 0x65, 0x20, 0x71, 0x75, 0x69, 0x63,
42
+ 0x6b, 0x20, 0x62, 0x72, 0x6f, 0x77, 0x6e, 0x20, 0x66, 0x6f, 0x78, 0x20,
43
+ 0x6a, 0x75, 0x6d, 0x70, 0x65, 0x64, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x20,
44
+ 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x7a, 0x79, 0x20, 0x64, 0x6f, 0x67
45
+ ].pack("C*")
46
+ payload = "the quick brown fox jumped over the lazy dog"
47
+
48
+ t = Libnet::TCP.new
49
+
50
+ t.src_port = 0x4321
51
+ t.dst_port = 0xabcd
52
+ t.seq = 32
53
+ t.ack = 0
54
+ t.control = 0x0b
55
+ t.window = 0x1122
56
+ t.checksum = 0x1111
57
+ t.urgent = 0
58
+ t.payload = payload
59
+
60
+ assert_nil(t.ptag)
61
+
62
+ l = Libnet.new
63
+
64
+ l.build_tcp(t)
65
+
66
+ assert(t.ptag > 0)
67
+
68
+ assert_equal(bytes, l.pack)
69
+
70
+ l = Libnet.new
71
+
72
+ t = nil
73
+ l.build_tcp do |t|
74
+ t.src_port = 0x4321
75
+ t.dst_port = 0xabcd
76
+ t.seq = 32
77
+ t.ack = 0
78
+ t.control = 0x0b
79
+ t.window = 0x1122
80
+ t.checksum = 0x1111
81
+ t.urgent = 0
82
+ t.payload = payload
83
+ end
84
+
85
+ assert(t.ptag > 0)
86
+ assert_equal(bytes, l.pack)
87
+ end
88
+
89
+ def test_modify_ptag
90
+ bytes1 = [
91
+ 0x43, 0x21, 0xab, 0xcd,
92
+ 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x50, 0x0b, 0x11, 0x22,
93
+ 0x11, 0x11, 0x00, 0x00, 0x74, 0x68, 0x65, 0x20, 0x71, 0x75, 0x69, 0x63,
94
+ 0x6b, 0x20, 0x62, 0x72, 0x6f, 0x77, 0x6e, 0x20, 0x66, 0x6f, 0x78, 0x20,
95
+ 0x6a, 0x75, 0x6d, 0x70, 0x65, 0x64, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x20,
96
+ 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x7a, 0x79, 0x20, 0x64, 0x6f, 0x67
97
+ ].pack("C*")
98
+ bytes2 = [
99
+ 0xfe, 0xed, 0xfa, 0xce,
100
+ 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x50, 0x0b, 0x11, 0x22,
101
+ 0x11, 0x11, 0x00, 0x00, 0x74, 0x68, 0x65, 0x20, 0x71, 0x75, 0x69, 0x63,
102
+ 0x6b, 0x20, 0x62, 0x72, 0x6f, 0x77, 0x6e, 0x20, 0x66, 0x6f, 0x78, 0x20,
103
+ 0x6a, 0x75, 0x6d, 0x70, 0x65, 0x64, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x20,
104
+ 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x7a, 0x79, 0x20, 0x64, 0x6f, 0x67
105
+ ].pack("C*")
106
+ payload = "the quick brown fox jumped over the lazy dog"
107
+
108
+ l = Libnet.new
109
+
110
+ t = l.build_tcp do |t|
111
+ t.src_port = 0x4321
112
+ t.dst_port = 0xabcd
113
+ t.seq = 32
114
+ t.ack = 0
115
+ t.control = 0x0b
116
+ t.window = 0x1122
117
+ t.checksum = 0x1111
118
+ t.urgent = 0
119
+ t.payload = payload
120
+ end
121
+
122
+ assert(t.ptag > 0)
123
+ ptag = t.ptag
124
+
125
+ assert_equal(bytes1, l.pack)
126
+
127
+ t.src_port = 0xfeed
128
+ t.dst_port = 0xface
129
+
130
+ l.build_tcp(t)
131
+
132
+ assert_equal(ptag, t.ptag)
133
+ assert_equal(bytes2, l.pack)
134
+ end
135
+
136
+ def test_ether_ipv4_tcp
137
+ bytes = [
138
+ # ethernet
139
+ 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
140
+ 0x08, 0x00,
141
+ # ipv4
142
+ 0x45, 0x12, 0x00, 0x54, 0x43, 0x21, 0x00, 0x00, 0x48, 0x06, 0xac, 0x1b,
143
+ 0xc0, 0xa8, 0x01, 0x02, 0xc0, 0xa8, 0x01, 0x03,
144
+ # tcp
145
+ 0x43, 0x21, 0xab, 0xcd,
146
+ 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x50, 0x0b, 0x11, 0x22,
147
+ 0x57, 0x9c, 0x00, 0x00, 0x74, 0x68, 0x65, 0x20, 0x71, 0x75, 0x69, 0x63,
148
+ 0x6b, 0x20, 0x62, 0x72, 0x6f, 0x77, 0x6e, 0x20, 0x66, 0x6f, 0x78, 0x20,
149
+ 0x6a, 0x75, 0x6d, 0x70, 0x65, 0x64, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x20,
150
+ 0x74, 0x68, 0x65, 0x20, 0x6c, 0x61, 0x7a, 0x79, 0x20, 0x64, 0x6f, 0x67
151
+ ].pack("C*")
152
+ payload = "the quick brown fox jumped over the lazy dog"
153
+
154
+ l = Libnet.new(:link)
155
+
156
+ t = l.build_tcp do |t|
157
+ t.src_port = 0x4321
158
+ t.dst_port = 0xabcd
159
+ t.seq = 32
160
+ t.ack = 0
161
+ t.control = 0x0b
162
+ t.window = 0x1122
163
+ t.checksum = 0
164
+ t.urgent = 0
165
+ t.payload = payload
166
+ end
167
+
168
+ l.build_ipv4 do |i|
169
+ i.tos = 0x12
170
+ i.length = Libnet::HL_IPV4 + Libnet::HL_TCP + payload.length
171
+ i.id = 0x4321
172
+ i.frag_off = 0
173
+ i.ttl = 72
174
+ i.protocol = Libnet::IPPROTO_TCP
175
+ i.checksum = 0
176
+ i.src_ip = '192.168.1.2'
177
+ i.dst_ip = 0xc0a80103
178
+ end
179
+
180
+ l.build_ethernet do |e|
181
+ e.dst = 'aa:bb:cc:dd:ee:ff'
182
+ e.src = '11:22:33:44:55:66'
183
+ e.type = 0x0800
184
+ end
185
+
186
+ assert_equal(bytes, l.pack)
187
+
188
+ if Process.uid > 0 && Process.euid > 0
189
+ # user is not root, so we should get an exception here
190
+ assert_raise RuntimeError do
191
+ l.write
192
+ end
193
+ else
194
+ # user is root, so this should work
195
+ assert_equal(bytes.length, l.write)
196
+ end
197
+ end
198
+ end
@@ -13,3 +13,4 @@ require 'tc_arp'
13
13
  require 'tc_ipv4'
14
14
  require 'tc_ipv6'
15
15
  require 'tc_udp'
16
+ require 'tc_tcp'
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: libnet4r
5
5
  version: !ruby/object:Gem::Version
6
- version: "0.1"
7
- date: 2007-11-18 00:00:00 -06:00
6
+ version: "0.2"
7
+ date: 2007-11-19 00:00:00 -06:00
8
8
  summary: Packet builder and injection library.
9
9
  require_paths:
10
10
  - lib
@@ -38,6 +38,7 @@ files:
38
38
  - ext/libnet_ethernet.c
39
39
  - ext/libnet_ipv4.c
40
40
  - ext/libnet_ipv6.c
41
+ - ext/libnet_tcp.c
41
42
  - ext/libnet_udp.c
42
43
  - ext/libnet_vlan.c
43
44
  - lib/libnet4r/header.rb
@@ -50,6 +51,7 @@ files:
50
51
  - test/tc_init.rb
51
52
  - test/tc_ipv4.rb
52
53
  - test/tc_ipv6.rb
54
+ - test/tc_tcp.rb
53
55
  - test/tc_udp.rb
54
56
  - test/tc_vlan.rb
55
57
  - test/ts_all.rb