async-dns 1.3.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/async/dns/cache.rb +69 -0
- data/lib/async/dns/chunked.rb +4 -19
- data/lib/async/dns/endpoint.rb +23 -0
- data/lib/async/dns/extensions/resolv.rb +22 -82
- data/lib/async/dns/extensions/string.rb +6 -20
- data/lib/async/dns/handler.rb +72 -54
- data/lib/async/dns/resolver.rb +151 -137
- data/lib/async/dns/server.rb +62 -69
- data/lib/async/dns/system.rb +147 -80
- data/lib/async/dns/transaction.rb +30 -37
- data/lib/async/dns/transport.rb +27 -39
- data/lib/async/dns/version.rb +6 -20
- data/lib/async/dns.rb +11 -27
- data/license.md +29 -0
- data/readme.md +53 -0
- data/releases.md +21 -0
- data.tar.gz.sig +0 -0
- metadata +49 -137
- metadata.gz.sig +0 -0
- data/lib/async/dns/message.rb +0 -53
- data/lib/async/dns/replace.rb +0 -54
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87d280ed7be9505a629d09e7ffad5135723a80cee1ac849fa8d19597a833eb09
|
4
|
+
data.tar.gz: d2a037310e59fbefb715cbf6c369be55f2eb567636066605b82ede388886ec04
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 685fdcd3685e465742bd2c47d53937a332739c9bc337ac3542d30d2d5fd6e4f213482498b0780be7a666b7c5956596dfdb51a6b213687772c4d3e52c3222d298
|
7
|
+
data.tar.gz: 68af6b3a768695e02a5be1a71a162ba528ff68b01988aa67c3c41552b67af5c6d06054515ec20920ac959fe2cf551919535fff9fbcde30c08aa5406b53b8f285
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2017-2025, by Samuel Williams.
|
5
|
+
|
6
|
+
module Async
|
7
|
+
module DNS
|
8
|
+
# Provides a local in-memory cache for DNS resources.
|
9
|
+
class Cache
|
10
|
+
Entry = Struct.new(:timestamp, :name, :resource_class, :resource) do
|
11
|
+
def age(now)
|
12
|
+
now - timestamp
|
13
|
+
end
|
14
|
+
|
15
|
+
def fresh?(now = Async::Clock.now)
|
16
|
+
if ttl = resource.ttl
|
17
|
+
self.age(now) <= ttl
|
18
|
+
else
|
19
|
+
true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Create a new cache.
|
25
|
+
def initialize
|
26
|
+
@store = {}
|
27
|
+
end
|
28
|
+
|
29
|
+
# Fetch a resource from the cache, or if it is not present, yield to the block to fetch it.
|
30
|
+
#
|
31
|
+
# @parameter name [String] The name of the resource.
|
32
|
+
# @parameter resource_classes [Array(Class(Resolv::DNS::Resource))] The classes of the resources to fetch.
|
33
|
+
# @yields {|name, resource_class| ...} The block to fetch the resource, it should call {#store} to store the resource in the cache.
|
34
|
+
def fetch(name, resource_classes)
|
35
|
+
now = Async::Clock.now
|
36
|
+
|
37
|
+
resource_classes.map do |resource_class|
|
38
|
+
key = [name, resource_class]
|
39
|
+
|
40
|
+
if entries = @store[key]
|
41
|
+
entries.delete_if do |entry|
|
42
|
+
!entry.fresh?(now)
|
43
|
+
end
|
44
|
+
else
|
45
|
+
entries = (@store[key] = [])
|
46
|
+
end
|
47
|
+
|
48
|
+
if entries.empty?
|
49
|
+
yield(name, resource_class)
|
50
|
+
end
|
51
|
+
|
52
|
+
entries
|
53
|
+
end.flatten.map(&:resource)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Store a resource in the cache.
|
57
|
+
#
|
58
|
+
# @parameter name [String] The name of the resource.
|
59
|
+
# @parameter resource_class [Class(Resolv::DNS::Resource)] The class of the resource.
|
60
|
+
# @parameter resource [Resolv::DNS::Resource] The resource to store.
|
61
|
+
def store(name, resource_class, resource)
|
62
|
+
key = [name, resource_class]
|
63
|
+
entries = (@store[key] ||= [])
|
64
|
+
|
65
|
+
entries << Entry.new(Async::Clock.now, name, resource_class, resource)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/async/dns/chunked.rb
CHANGED
@@ -1,22 +1,7 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
#
|
4
|
-
#
|
5
|
-
# in the Software without restriction, including without limitation the rights
|
6
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
-
# copies of the Software, and to permit persons to whom the Software is
|
8
|
-
# furnished to do so, subject to the following conditions:
|
9
|
-
#
|
10
|
-
# The above copyright notice and this permission notice shall be included in
|
11
|
-
# all copies or substantial portions of the Software.
|
12
|
-
#
|
13
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
-
# THE SOFTWARE.
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2015-2024, by Samuel Williams.
|
20
5
|
|
21
6
|
module Async::DNS
|
22
7
|
# Produces an array of arrays of binary data with each sub-array a maximum of chunk_size bytes.
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2024-2025, by Samuel Williams.
|
5
|
+
|
6
|
+
module Async
|
7
|
+
module DNS
|
8
|
+
# DNS endpoint helpers.
|
9
|
+
module Endpoint
|
10
|
+
# Get a list of standard nameserver connections which can be used for querying any standard servers that the system has been configured with.
|
11
|
+
def self.for(nameservers, port: 53, **options)
|
12
|
+
connections = []
|
13
|
+
|
14
|
+
Array(nameservers).each do |host|
|
15
|
+
connections << IO::Endpoint.udp(host, port, **options)
|
16
|
+
connections << IO::Endpoint.tcp(host, port, **options)
|
17
|
+
end
|
18
|
+
|
19
|
+
return IO::Endpoint.composite(*connections)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,27 +1,15 @@
|
|
1
|
-
#
|
2
|
-
#
|
3
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
-
# of this software and associated documentation files (the "Software"), to deal
|
5
|
-
# in the Software without restriction, including without limitation the rights
|
6
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
-
# copies of the Software, and to permit persons to whom the Software is
|
8
|
-
# furnished to do so, subject to the following conditions:
|
9
|
-
#
|
10
|
-
# The above copyright notice and this permission notice shall be included in
|
11
|
-
# all copies or substantial portions of the Software.
|
12
|
-
#
|
13
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
-
# THE SOFTWARE.
|
1
|
+
# frozen_string_literal: true
|
20
2
|
|
21
|
-
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2015-2024, by Samuel Williams.
|
22
5
|
|
6
|
+
require "resolv"
|
7
|
+
|
8
|
+
# @namespace
|
23
9
|
class Resolv
|
10
|
+
# @namespace
|
24
11
|
class DNS
|
12
|
+
# Extensions to the `Resolv::DNS::Message` class.
|
25
13
|
class Message
|
26
14
|
# Merge the given message with this message. A number of heuristics are applied in order to ensure that the result makes sense. For example, If the current message is not recursive but is being merged with a message that was recursive, this bit is maintained. If either message is authoritive, then the result is also authoritive.
|
27
15
|
#
|
@@ -46,19 +34,17 @@ class Resolv
|
|
46
34
|
end
|
47
35
|
end
|
48
36
|
|
37
|
+
# Represents a failure to construct a fullly qualified name due to a mismatched origin.
|
49
38
|
class OriginError < ArgumentError
|
50
39
|
end
|
51
40
|
|
41
|
+
# Extensions to the `Resolv::DNS::Name` class.
|
52
42
|
class Name
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
"#<#{self.class}: #{self.to_s}>"
|
59
|
-
end
|
60
|
-
|
61
|
-
# Return the name, typically absolute, with the specified origin as a suffix. If the origin is nil, don't change the name, but change it to absolute (as specified).
|
43
|
+
# Computes the name, typically absolute, with the specified origin as a suffix. If the origin is nil, don't change the name, but change it to absolute (as specified).
|
44
|
+
#
|
45
|
+
# @parameter origin [Array | String] The origin to append to the name.
|
46
|
+
# @parameter absolute [Boolean] If true, the name will be made absolute.
|
47
|
+
# @returns The name, with the origin suffix.
|
62
48
|
def with_origin(origin, absolute = true)
|
63
49
|
return self.class.new(@labels, absolute) if origin == nil
|
64
50
|
|
@@ -67,7 +53,12 @@ class Resolv
|
|
67
53
|
return self.class.new(@labels + origin, absolute)
|
68
54
|
end
|
69
55
|
|
70
|
-
#
|
56
|
+
# Compute the name, typically relative, without the specified origin suffix. If the origin is nil, don't change the name, but change it to absolute (as specified).
|
57
|
+
#
|
58
|
+
# @parameter origin [Array | String] The origin to remove from the name.
|
59
|
+
# @parameter absolute [Boolean] If true, the name will be made absolute.
|
60
|
+
# @returns The name, without the origin suffix.
|
61
|
+
# @raises [OriginError] If the name does not end with the specified origin.
|
71
62
|
def without_origin(origin, absolute = false)
|
72
63
|
return self.class.new(@labels, absolute) if origin == nil
|
73
64
|
|
@@ -81,55 +72,4 @@ class Resolv
|
|
81
72
|
end
|
82
73
|
end
|
83
74
|
end
|
84
|
-
|
85
|
-
if RUBY_VERSION == "2.3.0"
|
86
|
-
# Clearly, the Ruby 2.3.0 release was throughly tested.
|
87
|
-
class IPv6
|
88
|
-
def self.create(arg)
|
89
|
-
case arg
|
90
|
-
when IPv6
|
91
|
-
return arg
|
92
|
-
when String
|
93
|
-
address = ''.b
|
94
|
-
if Regex_8Hex =~ arg
|
95
|
-
arg.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')}
|
96
|
-
elsif Regex_CompressedHex =~ arg
|
97
|
-
prefix = $1
|
98
|
-
suffix = $2
|
99
|
-
a1 = ''.b
|
100
|
-
a2 = ''.b
|
101
|
-
prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')}
|
102
|
-
suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')}
|
103
|
-
omitlen = 16 - a1.length - a2.length
|
104
|
-
address << a1 << "\0" * omitlen << a2
|
105
|
-
elsif Regex_6Hex4Dec =~ arg
|
106
|
-
prefix, a, b, c, d = $1, $2.to_i, $3.to_i, $4.to_i, $5.to_i
|
107
|
-
if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d
|
108
|
-
prefix.scan(/[0-9A-Fa-f]+/) {|hex| address << [hex.hex].pack('n')}
|
109
|
-
address << [a, b, c, d].pack('CCCC')
|
110
|
-
else
|
111
|
-
raise ArgumentError.new("not numeric IPv6 address: " + arg)
|
112
|
-
end
|
113
|
-
elsif Regex_CompressedHex4Dec =~ arg
|
114
|
-
prefix, suffix, a, b, c, d = $1, $2, $3.to_i, $4.to_i, $5.to_i, $6.to_i
|
115
|
-
if (0..255) === a && (0..255) === b && (0..255) === c && (0..255) === d
|
116
|
-
a1 = ''.b
|
117
|
-
a2 = ''.b
|
118
|
-
prefix.scan(/[0-9A-Fa-f]+/) {|hex| a1 << [hex.hex].pack('n')}
|
119
|
-
suffix.scan(/[0-9A-Fa-f]+/) {|hex| a2 << [hex.hex].pack('n')}
|
120
|
-
omitlen = 12 - a1.length - a2.length
|
121
|
-
address << a1 << "\0" * omitlen << a2 << [a, b, c, d].pack('CCCC')
|
122
|
-
else
|
123
|
-
raise ArgumentError.new("not numeric IPv6 address: " + arg)
|
124
|
-
end
|
125
|
-
else
|
126
|
-
raise ArgumentError.new("not numeric IPv6 address: " + arg)
|
127
|
-
end
|
128
|
-
return IPv6.new(address)
|
129
|
-
else
|
130
|
-
raise ArgumentError.new("cannot interpret as IPv6 address: #{arg.inspect}")
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
75
|
+
end
|
@@ -1,25 +1,11 @@
|
|
1
|
-
#
|
2
|
-
#
|
3
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
-
# of this software and associated documentation files (the "Software"), to deal
|
5
|
-
# in the Software without restriction, including without limitation the rights
|
6
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
-
# copies of the Software, and to permit persons to whom the Software is
|
8
|
-
# furnished to do so, subject to the following conditions:
|
9
|
-
#
|
10
|
-
# The above copyright notice and this permission notice shall be included in
|
11
|
-
# all copies or substantial portions of the Software.
|
12
|
-
#
|
13
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
-
# THE SOFTWARE.
|
1
|
+
# frozen_string_literal: true
|
20
2
|
|
21
|
-
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2015-2024, by Samuel Williams.
|
22
5
|
|
6
|
+
require_relative "../chunked"
|
7
|
+
|
8
|
+
# Extensions for the String class.
|
23
9
|
class String
|
24
10
|
# Chunk a string which is required for the TEXT `resource_class`.
|
25
11
|
def chunked(chunk_size = 255)
|
data/lib/async/dns/handler.rb
CHANGED
@@ -1,39 +1,43 @@
|
|
1
|
-
#
|
2
|
-
#
|
3
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
-
# of this software and associated documentation files (the "Software"), to deal
|
5
|
-
# in the Software without restriction, including without limitation the rights
|
6
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
-
# copies of the Software, and to permit persons to whom the Software is
|
8
|
-
# furnished to do so, subject to the following conditions:
|
9
|
-
#
|
10
|
-
# The above copyright notice and this permission notice shall be included in
|
11
|
-
# all copies or substantial portions of the Software.
|
12
|
-
#
|
13
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
-
# THE SOFTWARE.
|
1
|
+
# frozen_string_literal: true
|
20
2
|
|
21
|
-
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2015-2025, by Samuel Williams.
|
5
|
+
# Copyright, 2021, by Mike Perham.
|
22
6
|
|
23
|
-
|
7
|
+
require "resolv"
|
8
|
+
require_relative "extensions/resolv"
|
9
|
+
|
10
|
+
require_relative "transport"
|
24
11
|
|
25
12
|
module Async::DNS
|
13
|
+
# The maximum size of a normal DNS packet (excluding EDNS).
|
14
|
+
UDP_REASONABLE_SIZE = 512
|
15
|
+
|
16
|
+
# The maximum size of a UDP packet.
|
17
|
+
UDP_MAXIMUM_SIZE = 2**16
|
18
|
+
|
19
|
+
# A generic handler for DNS queries.
|
26
20
|
class GenericHandler
|
21
|
+
# Create a new handler.
|
22
|
+
#
|
23
|
+
# @parameter server [Server] The server to handle queries for.
|
24
|
+
# @parameter socket [Socket] The socket to read/write data from/to.
|
27
25
|
def initialize(server, socket)
|
28
26
|
@server = server
|
29
27
|
@socket = socket
|
30
|
-
|
31
|
-
@logger = @server.logger || Console.logger
|
32
28
|
end
|
33
29
|
|
30
|
+
# @attribute [Server] The server that will process incoming queries.
|
34
31
|
attr :server
|
32
|
+
|
33
|
+
# @attribute [Socket] The socket to read/write data from/to.
|
35
34
|
attr :socket
|
36
35
|
|
36
|
+
# Create a new error response.
|
37
|
+
#
|
38
|
+
# @parameter query [Resolv::DNS::Message] The query that caused the error.
|
39
|
+
# @parameter code [Integer] The error code to return.
|
40
|
+
# @returns [Resolv::DNS::Message] The error response.
|
37
41
|
def error_response(query = nil, code = Resolv::DNS::RCode::ServFail)
|
38
42
|
# Encoding may fail, so we need to handle this particular case:
|
39
43
|
server_failure = Resolv::DNS::Message::new(query ? query.id : 0)
|
@@ -50,43 +54,50 @@ module Async::DNS
|
|
50
54
|
return server_failure
|
51
55
|
end
|
52
56
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
+
# Process an incoming query.
|
58
|
+
#
|
59
|
+
# @parameter data [String] The incoming query data.
|
60
|
+
# @parameter options [Hash] Additional options to pass to the server.
|
61
|
+
def process_query(data, **options)
|
62
|
+
Console.debug "Receiving incoming query (#{data.bytesize} bytes) to #{self.class.name}..."
|
63
|
+
|
57
64
|
begin
|
58
|
-
query =
|
65
|
+
query = Resolv::DNS::Message.decode(data)
|
59
66
|
|
60
|
-
return @server.process_query(query, options)
|
61
|
-
rescue
|
62
|
-
|
67
|
+
return @server.process_query(query, **options)
|
68
|
+
rescue => error
|
69
|
+
Console.error(self, "Failed to process query!", error: error)
|
63
70
|
|
64
71
|
return error_response(query)
|
65
72
|
end
|
66
73
|
end
|
67
74
|
end
|
68
75
|
|
69
|
-
#
|
76
|
+
# Handle incoming UDP requests, which are single data packets, and pass them on to the given server.
|
70
77
|
class DatagramHandler < GenericHandler
|
71
|
-
|
78
|
+
# Run the handler, processing incoming UDP requests.
|
79
|
+
#
|
80
|
+
# @parameter wrapper [Interface(:async)] The parent task to run the handler under.
|
81
|
+
def run(wrapper = ::IO::Endpoint::Wrapper.default)
|
72
82
|
while true
|
73
|
-
input_data, remote_address = @socket.recvmsg(
|
83
|
+
input_data, remote_address = @socket.recvmsg(UDP_MAXIMUM_SIZE)
|
74
84
|
|
75
|
-
|
85
|
+
wrapper.async do
|
76
86
|
respond(@socket, input_data, remote_address)
|
77
87
|
end
|
78
88
|
end
|
79
89
|
end
|
80
90
|
|
91
|
+
# Respond to an incoming query.
|
81
92
|
def respond(socket, input_data, remote_address)
|
82
93
|
response = process_query(input_data, remote_address: remote_address)
|
83
94
|
|
84
95
|
output_data = response.encode
|
85
96
|
|
86
|
-
|
97
|
+
Console.debug "Writing #{output_data.bytesize} bytes response to client via UDP...", response_id: response.id
|
87
98
|
|
88
|
-
if output_data.bytesize >
|
89
|
-
|
99
|
+
if output_data.bytesize > UDP_REASONABLE_SIZE
|
100
|
+
Console.warn "Response via UDP was larger than #{UDP_REASONABLE_SIZE}!", response_id: response.id
|
90
101
|
|
91
102
|
# Reencode data with truncation flag marked as true:
|
92
103
|
truncation_error = Resolv::DNS::Message.new(response.id)
|
@@ -97,23 +108,30 @@ module Async::DNS
|
|
97
108
|
|
98
109
|
socket.sendmsg(output_data, 0, remote_address)
|
99
110
|
rescue IOError => error
|
100
|
-
|
111
|
+
Console.error(self, "UDP response failed!", error: error)
|
101
112
|
rescue EOFError => error
|
102
|
-
|
103
|
-
rescue DecodeError
|
104
|
-
|
113
|
+
Console.error(self, "UDP session ended prematurely!", error: error)
|
114
|
+
rescue Resolv::DNS::DecodeError => error
|
115
|
+
Console.error(self, "Could not decode incoming UDP data!", error: error)
|
105
116
|
end
|
106
117
|
end
|
107
118
|
|
119
|
+
# Handle incoming TCP requests, which are stream requests, and pass them on to the given server.
|
108
120
|
class StreamHandler < GenericHandler
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
121
|
+
# Run the handler, processing incoming TCP requests.
|
122
|
+
#
|
123
|
+
# @parameter wrapper [Interface(:async)] The parent task to run the handler under.
|
124
|
+
def run(wrapper = ::IO::Endpoint::Wrapper.default, **options)
|
125
|
+
wrapper.accept(@socket, **options) do |peer|
|
126
|
+
handle_connection(peer)
|
114
127
|
end
|
115
128
|
end
|
116
129
|
|
130
|
+
# Handle an incoming TCP connection.
|
131
|
+
#
|
132
|
+
# Reads zero or more queries from the given socket and processes them.
|
133
|
+
#
|
134
|
+
# @parameter socket [Socket] The incoming TCP connection.
|
117
135
|
def handle_connection(socket)
|
118
136
|
transport = Transport.new(socket)
|
119
137
|
|
@@ -121,16 +139,16 @@ module Async::DNS
|
|
121
139
|
response = process_query(input_data, remote_address: socket.remote_address)
|
122
140
|
length = transport.write_message(response)
|
123
141
|
|
124
|
-
|
142
|
+
Console.debug "Wrote #{length} bytes via TCP...", response_id: response.id
|
125
143
|
end
|
126
144
|
rescue EOFError => error
|
127
|
-
|
145
|
+
Console.error(self, "TCP session ended prematurely!", error: error)
|
128
146
|
rescue Errno::ECONNRESET => error
|
129
|
-
|
130
|
-
rescue Errno::EPIPE
|
131
|
-
|
132
|
-
rescue DecodeError
|
133
|
-
|
147
|
+
Console.error(self, "TCP connection reset by peer!", error: error)
|
148
|
+
rescue Errno::EPIPE => error
|
149
|
+
Console.error(self, "TCP session failed due to broken pipe!", error: error)
|
150
|
+
rescue Resolv::DNS::DecodeError => error
|
151
|
+
Console.error(self, "Could not decode incoming TCP data!", error: error)
|
134
152
|
end
|
135
153
|
end
|
136
154
|
end
|