async-dns 1.3.0 → 1.4.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.
- 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
|