libnet4r 0.1 → 0.2

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