bluemonk-net-dns 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,195 @@
1
+ #---
2
+ # $Id: Question.rb,v 1.8 2006/07/28 19:00:03 bluemonk Exp $
3
+ #+++
4
+
5
+ require 'net/dns/dns'
6
+ require 'net/dns/names/names'
7
+ require 'net/dns/rr/types'
8
+ require 'net/dns/rr/classes'
9
+
10
+ module Net # :nodoc:
11
+ module DNS
12
+
13
+ #
14
+ # =Name
15
+ #
16
+ # Net::DNS::Question - DNS packet question class
17
+ #
18
+ # =Synopsis
19
+ #
20
+ # require 'net/dns/question'
21
+ #
22
+ # =Description
23
+ #
24
+ # This class represent the Question portion of a DNS packet. The number
25
+ # of question entries is stored in the +qdCount+ variable of an Header
26
+ # object.
27
+ #
28
+ # A new object can be created passing the name of the query and the type
29
+ # of answer desired, plus an optional argument containing the class:
30
+ #
31
+ # question = Net::DNS::Question.new("google.com.", Net::DNS::A)
32
+ # #=> "google.com. A IN"
33
+ #
34
+ # Alternatevly, a new object is created when processing a binary
35
+ # packet, as when an answer is received.
36
+ # To obtain the binary data from a question object you can use
37
+ # the method Question#data:
38
+ #
39
+ # question.data
40
+ # #=> "\006google\003com\000\000\001\000\001"
41
+ #
42
+ # A lot of methods were written to keep a compatibility layer with
43
+ # the Perl version of the library, as long as methods name which are
44
+ # more or less the same.
45
+ #
46
+ # =Error classes
47
+ #
48
+ # Some error classes has been defined for the Net::DNS::Header class,
49
+ # which are listed here to keep a light and browsable main documentation.
50
+ # We have:
51
+ #
52
+ # * QuestionArgumentError: generic argument error
53
+ # * QuestionNameError: an error in the +name+ part of a Question entry
54
+ #
55
+ # =Copyright
56
+ #
57
+ # Copyright (c) 2006 Marco Ceresa
58
+ #
59
+ # All rights reserved. This program is free software; you may redistribute
60
+ # it and/or modify it under the same terms as Ruby itself.
61
+ #
62
+ class Question
63
+
64
+ include Net::DNS::Names
65
+
66
+ # +name+ part of a Question entry
67
+ attr_reader :qName
68
+ # +type+ part of a Question entry
69
+ attr_reader :qType
70
+ # +class+ part of a Question entry
71
+ attr_reader :qClass
72
+
73
+ # Creates a new Net::DNS::Question object:
74
+ #
75
+ # question = Net::DNS::Question.new("example.com")
76
+ # #=> "example.com A IN"
77
+ # question = Net::DNS::Question.new("example.com", Net::DNS::MX)
78
+ # #=> "example.com MX IN"
79
+ # question = Net::DNS::Question.new("example.com", Net::DNS::TXT, Net::DNS::HS)
80
+ # #=> "example.com TXT HS"
81
+
82
+ # If not specified, +type+ and +cls+ arguments defaults
83
+ # to Net::DNS::A and Net::DNS::IN respectively.
84
+ #
85
+ def initialize(name,type=Net::DNS::A,cls=Net::DNS::IN)
86
+ @qName = check_name name
87
+ @qType = Net::DNS::RR::Types.new type
88
+ @qClass = Net::DNS::RR::Classes.new cls
89
+ end
90
+
91
+ # Return a new Net::DNS::Question object created by
92
+ # parsing binary data, such as an answer from the
93
+ # nameserver.
94
+ #
95
+ # question = Net::DNS::Question.parse(data)
96
+ # puts "Queried for #{question.qName} type #{question.qType.to_s}"
97
+ # #=> Queried for example.com type A
98
+ #
99
+ def self.parse(arg)
100
+ if arg.kind_of? String
101
+ o = allocate
102
+ o.send(:new_from_binary,arg)
103
+ o
104
+ else
105
+ raise QuestionArgumentError, "Wrong argument format, must be a String"
106
+ end
107
+ end
108
+
109
+ # Known inspect method with nice formatting
110
+ def inspect
111
+ if @qName.size > 29 then
112
+ len = @qName.size + 1
113
+ else
114
+ len = 29
115
+ end
116
+ [@qName,@qClass.to_s,@qType.to_s].pack("A#{len} A8 A8")
117
+ end
118
+
119
+ # Outputs binary data from a Question object
120
+ #
121
+ # question.data
122
+ # #=> "\006google\003com\000\000\001\000\001"
123
+ #
124
+ def data
125
+ [pack_name(@qName),@qType.to_i,@qClass.to_i].pack("a*nn")
126
+ end
127
+
128
+ # Return the binary data of the objects, plus an offset
129
+ # and an Hash with references to compressed names. For use in
130
+ # Net::DNS::Packet compressed packet creation.
131
+ #
132
+ def comp_data
133
+ arr = @qName.split(".")
134
+ str = pack_name(@qName)
135
+ string = ""
136
+ names = {}
137
+ offset = Net::DNS::HFIXEDSZ
138
+ arr.size.times do |i|
139
+ x = i+1
140
+ elem = arr[-x]
141
+ len = elem.size
142
+ string = ((string.reverse)+([len,elem].pack("Ca*")).reverse).reverse
143
+ names[string] = offset
144
+ offset += len
145
+ end
146
+ offset += 2 * Net::DNS::INT16SZ
147
+ str += "\000"
148
+ [[str,@qType.to_i,@qClass.to_i].pack("a*nn"),offset,names]
149
+ end
150
+
151
+ private
152
+
153
+ def build_qName(str)
154
+ result = ""
155
+ offset = 0
156
+ loop do
157
+ len = str.unpack("@#{offset} C")[0]
158
+ break if len == 0
159
+ offset += 1
160
+ result += str[offset..offset+len-1]
161
+ result += "."
162
+ offset += len
163
+ end
164
+ result
165
+ end
166
+
167
+ def check_name(name)
168
+ name.strip!
169
+ if name =~ /[^\w\.\-_]/
170
+ raise QuestionNameError, "Question name #{name.inspect} not valid"
171
+ else
172
+ name
173
+ end
174
+ rescue
175
+ raise QuestionNameError, "Question name #{name.inspect} not valid"
176
+ end
177
+
178
+ def new_from_binary(data)
179
+ str,type,cls = data.unpack("a#{data.size-4}nn")
180
+ @qName = build_qName(str)
181
+ @qType = Net::DNS::RR::Types.new type
182
+ @qClass = Net::DNS::RR::Classes.new cls
183
+ rescue StandardError => e
184
+ raise QuestionArgumentError, "Invalid data: #{data.inspect}\n{e.backtrace}"
185
+ end
186
+
187
+ end # class Question
188
+
189
+ end # class DNS
190
+ end # module Net
191
+
192
+ class QuestionArgumentError < ArgumentError # :nodoc:
193
+ end
194
+ class QuestionNameError < StandardError # :nodoc:
195
+ end
@@ -0,0 +1,154 @@
1
+ require 'socket'
2
+ require 'ipaddr'
3
+
4
+ class RawSocket # :nodoc:
5
+
6
+ @@id_arr = []
7
+
8
+ def initialize(src_addr,dest_addr)
9
+
10
+ # Define socket
11
+ begin
12
+ @socket = Socket.new PF_INET, SOCK_RAW, IPPROTO_RAW
13
+ rescue SystemCallError => e
14
+ raise SystemCallError, "You must be root to use raw sockets! #{e}"
15
+ end
16
+
17
+ @socket.setsockopt IPPROTO_IP, IP_HDRINCL, 1
18
+
19
+ # Checks addresses
20
+ @src_addr = check_addr src_addr
21
+ @dest_addr = check_addr dest_addr
22
+
23
+ # Source and destination port are zero
24
+ @src_port = 0
25
+ @dest_port = 0
26
+
27
+ # Set correct protocol version in the header
28
+ @version = @dest_addr.ipv4? ? "0100" : "0110"
29
+
30
+ # Total lenght: must be overridden by subclasses
31
+ @tot_lenght = 20
32
+
33
+ # Protocol: must be overridden by subclasses
34
+ @protocol = 1 # ICMP by default
35
+
36
+ # Generate a new id
37
+ # @id = genID
38
+ @id = 1234
39
+
40
+ # Generate peer sockaddr
41
+ @to = Socket.pack_sockaddr_in @dest_port, @dest_addr.to_s
42
+ end
43
+
44
+ def send(payload = '')
45
+ packet = make_ip_header([[ @version+'0101', 'B8' ], # version, hlen
46
+ [ 0, 'C' ], # tos
47
+ [ @tot_lenght + payload.size, 'n' ], # total len
48
+ [ @id, 'n' ], # id
49
+ [ 0, 'n' ], # flags, offset
50
+ [ 64, 'C' ], # ttl
51
+ [ @protocol, 'C' ], # protocol
52
+ [ 0, 'n' ], # checksum
53
+ [ @src_addr.to_i, 'N' ], # source
54
+ [ @dest_addr.to_i, 'N' ], # destination
55
+ ])
56
+ packet << make_transport_header(payload.size)
57
+ packet << [payload].pack("a*")
58
+ @socket.send(packet,0,@to)
59
+ end
60
+
61
+ private
62
+
63
+ def check_addr addr
64
+ case addr
65
+ when String
66
+ IPAddr.new addr
67
+ when IPAddr
68
+ addr
69
+ else
70
+ raise ArgumentError, "Wrong address format: #{addr}"
71
+ end
72
+ end
73
+
74
+ def check_port port
75
+ if (1..65535).include? port and port.kind_of? Integer
76
+ port
77
+ else
78
+ raise ArgumentError, "Port #{port} not valid"
79
+ end
80
+ end
81
+
82
+ def genID
83
+ while (@@id_arr.include?(q = rand(65535)))
84
+ end
85
+ @@id_arr.push(q)
86
+ q
87
+ end
88
+
89
+ def ipchecksum(data)
90
+ checksum = data.unpack("n*").inject(0) { |s, x| s + x }
91
+ ((checksum >> 16) + (checksum & 0xffff)) ^ 0xffff
92
+ end
93
+
94
+ def make_ip_header(parts)
95
+ template = ''
96
+ data = []
97
+ parts.each do |part|
98
+ data += part[0..-2]
99
+ template << part[-1]
100
+ end
101
+ data_str = data.pack(template)
102
+ checksum = ipchecksum(data_str)
103
+ data[-3] = checksum
104
+ data.pack(template)
105
+ end
106
+
107
+ def make_transport_header
108
+ ""
109
+ end
110
+
111
+ end
112
+
113
+ class UdpRawSocket < RawSocket # :nodoc:
114
+
115
+ def initialize(src_addr,src_port,dest_addr,dest_port)
116
+
117
+ super(src_addr,dest_addr)
118
+
119
+ # Check ports
120
+ @src_port = check_port src_port
121
+ @dest_port = check_port dest_port
122
+
123
+ # Total lenght: must be overridden by subclasses
124
+ @tot_lenght = 20 + 8 # 8 bytes => UDP Header
125
+
126
+ # Protocol: must be overridden by subclasses
127
+ @protocol = 17 # UDP protocol
128
+
129
+ @to = Socket.pack_sockaddr_in @dest_port, @dest_addr.to_s
130
+ end
131
+
132
+ private
133
+
134
+ def make_udp_header(parts)
135
+ template = ''
136
+ data = []
137
+ parts.each do |part|
138
+ data += part[0..-2]
139
+ template << part[-1]
140
+ end
141
+ data.pack(template)
142
+ end
143
+
144
+ def make_transport_header(pay_size)
145
+ make_udp_header([
146
+ [ @src_port, 'n'], # source port
147
+ [ @dest_port, 'n' ], # destination port
148
+ [ 8 + pay_size, 'n' ], # len
149
+ [ 0, 'n' ] # checksum (mandatory)
150
+ ])
151
+ end
152
+
153
+ end
154
+
@@ -0,0 +1,73 @@
1
+ require 'timeout'
2
+
3
+ module SecondsHandle #:nodoc: all
4
+ def transform(secs)
5
+ case secs
6
+ when 0
7
+ to_s
8
+ when 1..59
9
+ "#{secs} seconds"
10
+ when 60..3559
11
+ "#{secs/60} minutes and #{secs%60} seconds"
12
+ else
13
+ hours = secs/3600
14
+ secs -= (hours*3600)
15
+ "#{hours} hours, #{secs/60} minutes and #{secs%60} seconds"
16
+ end
17
+ end
18
+ end
19
+
20
+ class DnsTimeout # :nodoc: all
21
+
22
+ include SecondsHandle
23
+
24
+ def initialize(seconds)
25
+ if seconds.is_a? Numeric and seconds >= 0
26
+ @timeout = seconds
27
+ else
28
+ raise DnsTimeoutArgumentError, "Invalid value for tcp timeout"
29
+ end
30
+ end
31
+
32
+ def to_s
33
+ if @timeout == 0
34
+ @output
35
+ else
36
+ @timeout.to_s
37
+ end
38
+ end
39
+
40
+ def pretty_to_s
41
+ transform(@timeout)
42
+ end
43
+
44
+ def timeout
45
+ unless block_given?
46
+ raise DnsTimeoutArgumentError, "Block required but missing"
47
+ end
48
+ if @timeout == 0
49
+ yield
50
+ else
51
+ return Timeout.timeout(@timeout) do
52
+ yield
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ class TcpTimeout < DnsTimeout # :nodoc: all
59
+ def initialize(seconds)
60
+ @output = "infinite"
61
+ super(seconds)
62
+ end
63
+ end
64
+
65
+ class UdpTimeout < DnsTimeout # :nodoc: all
66
+ def initialize(seconds)
67
+ @output = "not defined"
68
+ super(seconds)
69
+ end
70
+ end
71
+
72
+ class DnsTimeoutArgumentError < ArgumentError # :nodoc: all
73
+ end