bluemonk-net-dns 0.5.0
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/AUTHORS +10 -0
- data/CHANGELOG +7 -0
- data/INSTALL +8 -0
- data/README.rdoc +150 -0
- data/Rakefile +93 -0
- data/THANKS +24 -0
- data/VERSION.yml +4 -0
- data/demo/check_soa.rb +120 -0
- data/demo/threads.rb +22 -0
- data/lib/net/dns/dns.rb +117 -0
- data/lib/net/dns/header.rb +761 -0
- data/lib/net/dns/names/names.rb +109 -0
- data/lib/net/dns/packet.rb +581 -0
- data/lib/net/dns/question.rb +195 -0
- data/lib/net/dns/resolver/socks.rb +154 -0
- data/lib/net/dns/resolver/timeouts.rb +73 -0
- data/lib/net/dns/resolver.rb +1267 -0
- data/lib/net/dns/rr/a.rb +121 -0
- data/lib/net/dns/rr/aaaa.rb +92 -0
- data/lib/net/dns/rr/classes.rb +148 -0
- data/lib/net/dns/rr/cname.rb +69 -0
- data/lib/net/dns/rr/hinfo.rb +74 -0
- data/lib/net/dns/rr/mr.rb +68 -0
- data/lib/net/dns/rr/mx.rb +74 -0
- data/lib/net/dns/rr/ns.rb +70 -0
- data/lib/net/dns/rr/null.rb +61 -0
- data/lib/net/dns/rr/ptr.rb +71 -0
- data/lib/net/dns/rr/soa.rb +85 -0
- data/lib/net/dns/rr/srv.rb +57 -0
- data/lib/net/dns/rr/txt.rb +72 -0
- data/lib/net/dns/rr/types.rb +200 -0
- data/lib/net/dns/rr.rb +406 -0
- data/setup.rb +1360 -0
- data/test/net/dns/resolver/test_timeouts.rb +59 -0
- data/test/net/dns/rr/test_a.rb +72 -0
- data/test/net/dns/rr/test_classes.rb +73 -0
- data/test/net/dns/rr/test_ns.rb +66 -0
- data/test/net/dns/rr/test_types.rb +127 -0
- data/test/net/dns/test_header.rb +170 -0
- data/test/net/dns/test_packet.rb +42 -0
- data/test/net/dns/test_question.rb +54 -0
- data/test/net/dns/test_rr.rb +133 -0
- metadata +105 -0
@@ -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
|