ractor_dns 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +4 -8
- data/lib/ractor_dns/active_zone/railtie.rb +7 -0
- data/lib/ractor_dns/active_zone.rb +15 -0
- data/lib/ractor_dns/inflector.rb +1 -1
- data/lib/ractor_dns/server.rb +57 -38
- data/lib/ractor_dns/transaction.rb +9 -11
- data/lib/ractor_dns/version.rb +3 -0
- data/lib/ractor_dns/zone_collection.rb +25 -12
- data/lib/ractor_dns.rb +11 -2
- metadata +33 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 43eb0bf1e0c8c1333e58ab9af172c794203ad40184ef1c501b59d21e654295e9
|
4
|
+
data.tar.gz: 9e3385ab39bed221627a4d34b34ab26060cd19862d9e139e1ee727c83fbe8d1b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c1574d4b92f4334799906f831eeb2c9e96bbb8cf12b96b4f6b3dc2e23c4ae7cb4ac1f94364d6e9ccf0fda855590fb88ded34c6409c662120a3d494157b21a8f1
|
7
|
+
data.tar.gz: a0d547ee9b18754498052a683708024980aed9afc4570c5e3d7573b25abf3abc369ac62525b701efa57af084e16a55150c8aa32b2d4ec55b7ef2ac55c3dc2a12
|
data/README.md
CHANGED
@@ -1,20 +1,16 @@
|
|
1
1
|
# RactorDNS
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/ractor_dns`. To experiment with that code, run `bin/console` for an interactive prompt.
|
3
|
+
RactorDNS is a work-in-progress concurrent DNS server built with Ruby 3.0 Ractors.
|
6
4
|
|
7
5
|
## Installation
|
8
6
|
|
9
|
-
TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
|
10
|
-
|
11
7
|
Install the gem and add to the application's Gemfile by executing:
|
12
8
|
|
13
|
-
|
9
|
+
bundle add ractor_dns
|
14
10
|
|
15
11
|
If bundler is not being used to manage dependencies, install the gem by executing:
|
16
12
|
|
17
|
-
|
13
|
+
gem install ractor_dns
|
18
14
|
|
19
15
|
## Usage
|
20
16
|
|
@@ -28,4 +24,4 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
28
24
|
|
29
25
|
## Contributing
|
30
26
|
|
31
|
-
Bug reports and pull requests are welcome
|
27
|
+
Bug reports and pull requests are welcome at https://codeberg.org/oblong/ractor_dns.
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class RactorDNS::ActiveZone
|
2
|
+
attr_accessor :server
|
3
|
+
def initialize(name: "ns.ractordns.local", ttl: 300, ip: "0.0.0.0", server: nil, port: 53, cpu_count: 8, zones:)
|
4
|
+
@server = server.presence || RactorDNS::Server.new(
|
5
|
+
port:,
|
6
|
+
authority: [name, ttl, RRs::IN::A.new(ip)],
|
7
|
+
cpu_count:,
|
8
|
+
zones:
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
delegate_missing_to :server
|
13
|
+
end
|
14
|
+
|
15
|
+
require "ractor_dns/active_zone/railtie" if defined?(Rails::Railtie)
|
data/lib/ractor_dns/inflector.rb
CHANGED
data/lib/ractor_dns/server.rb
CHANGED
@@ -1,68 +1,87 @@
|
|
1
1
|
module RactorDNS
|
2
2
|
class Server
|
3
|
-
attr_reader :authority, :ractor, :zones
|
3
|
+
attr_reader :authority, :ractor, :zones, :cpu_count, :port
|
4
4
|
|
5
|
-
def initialize(authority:, cpu_count:, zones:)
|
5
|
+
def initialize(authority:, cpu_count:, zones:, port: 53)
|
6
6
|
@authority = authority
|
7
7
|
@cpu_count = cpu_count
|
8
8
|
@zones = zones
|
9
|
+
@port = port
|
9
10
|
end
|
10
11
|
|
11
12
|
def start
|
12
|
-
@
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
@pipe = Ractor.new do
|
14
|
+
loop do
|
15
|
+
recv = Ractor.recv
|
16
|
+
if recv == :stop
|
17
|
+
Ractor.yield([recv, nil, nil])
|
18
|
+
Ractor.current.close_incoming
|
19
|
+
Ractor.current.close_outgoing
|
20
|
+
break
|
16
21
|
end
|
22
|
+
Ractor.yield(recv)
|
17
23
|
end
|
24
|
+
end
|
18
25
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
workers = cpu_count.times.map do
|
24
|
-
Ractor.new(pipe, server_socket, authority) do |pipe, server_socket, authority|
|
25
|
-
loop do
|
26
|
-
message, client, zone_h = pipe.take
|
26
|
+
@resolvers = @cpu_count.times.map do
|
27
|
+
Ractor.new(@pipe, @authority) do |pipe, authority|
|
28
|
+
loop do
|
29
|
+
data, zone_h = pipe.take
|
27
30
|
|
28
|
-
|
31
|
+
break if data == :stop && zone_h.nil?
|
29
32
|
|
30
|
-
|
31
|
-
addr_info = Addrinfo.new(client)
|
33
|
+
decoded = RRs::Message.decode(data)
|
32
34
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
35
|
+
response = RRs::Message.new(decoded.id)
|
36
|
+
response.add_authority(*authority)
|
37
|
+
response.instance_exec(decoded) do |decoded|
|
38
|
+
@question.concat(decoded.question)
|
39
|
+
end
|
40
|
+
decoded.question.each do |q|
|
41
|
+
(zone_h.search(q[0]) || []).filter { |el|
|
42
|
+
el[:rr].class
|
43
|
+
q[1]
|
44
|
+
el[:name] == q[0].to_s
|
45
|
+
}&.each do |ans|
|
46
|
+
response.add_answer(ans[:name], ans[:ttl], ans[:rr])
|
42
47
|
end
|
43
|
-
|
44
|
-
server_socket.send(response.encode, 0, addr_info.ip_address, addr_info.ip_port)
|
45
48
|
end
|
49
|
+
|
50
|
+
Ractor.yield(response.encode)
|
46
51
|
end
|
47
52
|
end
|
53
|
+
end
|
48
54
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
55
|
+
@endpoint = Async::IO::Endpoint.udp('0.0.0.0', @port)
|
56
|
+
|
57
|
+
@zones.start
|
58
|
+
|
59
|
+
@thread = Thread.new do
|
60
|
+
Async do
|
61
|
+
@endpoint.bind do |socket|
|
62
|
+
loop do
|
63
|
+
data, address = socket.recvfrom(1024)
|
64
|
+
@pipe.send([data, @zones.freeze])
|
65
|
+
_, encoded = Ractor.select(*@resolvers)
|
66
|
+
socket.send(encoded, 0, address)
|
67
|
+
end
|
53
68
|
end
|
54
69
|
end
|
70
|
+
end
|
55
71
|
|
56
|
-
|
57
|
-
|
58
|
-
ractor, _result = Ractor.select(listener, *workers, pipe)
|
59
|
-
ractor.take
|
60
|
-
end
|
72
|
+
at_exit do
|
73
|
+
stop
|
61
74
|
end
|
62
75
|
end
|
63
76
|
|
64
77
|
def stop
|
65
|
-
|
78
|
+
begin
|
79
|
+
@thread.kill
|
80
|
+
@pipe.send(:stop)
|
81
|
+
@zones.stop
|
82
|
+
rescue Ractor::ClosedError
|
83
|
+
nil
|
84
|
+
end
|
66
85
|
end
|
67
86
|
end
|
68
87
|
end
|
@@ -8,20 +8,18 @@ module RactorDNS
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def apply(obj)
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
raise e
|
20
|
-
end
|
11
|
+
@original_state = obj.dup
|
12
|
+
@new_obj = obj.deep_transform_values { |value| value.dup }
|
13
|
+
@block.call(@new_obj)
|
14
|
+
obj.replace(@new_obj)
|
15
|
+
rescue => e
|
16
|
+
puts e
|
17
|
+
rollback(obj)
|
18
|
+
raise e
|
21
19
|
end
|
22
20
|
|
23
21
|
def rollback(obj)
|
24
22
|
obj.replace(@original_state) if @original_state
|
25
23
|
end
|
26
24
|
end
|
27
|
-
end
|
25
|
+
end
|
@@ -1,11 +1,16 @@
|
|
1
1
|
module RactorDNS
|
2
2
|
class ZoneCollection
|
3
3
|
def initialize(obj = {})
|
4
|
-
@
|
4
|
+
@obj = obj
|
5
|
+
start
|
6
|
+
end
|
7
|
+
|
8
|
+
def start
|
9
|
+
@queue = Ractor.new(Ractor.make_shareable(@obj.freeze)) do |obj|
|
5
10
|
zones = obj.dup
|
6
11
|
loop do
|
7
12
|
msg, data = Ractor.receive
|
8
|
-
|
13
|
+
|
9
14
|
if msg == :transact
|
10
15
|
transaction = data.dup
|
11
16
|
begin
|
@@ -22,16 +27,20 @@ module RactorDNS
|
|
22
27
|
Ractor.yield ZoneCollection.search(zones, data)
|
23
28
|
elsif msg == :all
|
24
29
|
Ractor.yield zones
|
30
|
+
elsif msg == :stop
|
31
|
+
Ractor.current.close_incoming
|
32
|
+
Ractor.current.close_outgoing
|
33
|
+
break
|
25
34
|
end
|
26
35
|
end
|
27
36
|
end
|
28
37
|
end
|
29
|
-
|
38
|
+
|
30
39
|
def search(name)
|
31
40
|
@queue.send([:search, name])
|
32
41
|
@queue.take
|
33
42
|
end
|
34
|
-
|
43
|
+
|
35
44
|
def self.search(obj, name)
|
36
45
|
if name.nil?
|
37
46
|
return nil
|
@@ -46,31 +55,35 @@ module RactorDNS
|
|
46
55
|
@queue.send([:get, k])
|
47
56
|
@queue.take
|
48
57
|
end
|
49
|
-
|
58
|
+
|
50
59
|
def to_h
|
51
60
|
@queue.send([:all, nil])
|
52
61
|
@queue.take
|
53
62
|
end
|
54
|
-
|
63
|
+
|
55
64
|
def transact(transaction)
|
56
65
|
@queue.send([:transact, Ractor.make_shareable(transaction)])
|
57
66
|
@queue.take
|
58
67
|
end
|
59
|
-
|
68
|
+
|
60
69
|
def freeze
|
61
|
-
FrozenZoneCollection.new(
|
70
|
+
FrozenZoneCollection.new(to_h)
|
71
|
+
end
|
72
|
+
|
73
|
+
def stop
|
74
|
+
@queue.send([:stop, nil])
|
62
75
|
end
|
63
76
|
end
|
64
|
-
|
77
|
+
|
65
78
|
class FrozenZoneCollection
|
66
79
|
def initialize(obj)
|
67
80
|
@obj = Ractor.make_shareable(obj.freeze)
|
68
81
|
end
|
69
|
-
|
82
|
+
|
70
83
|
def [](k)
|
71
84
|
@obj[k]
|
72
85
|
end
|
73
|
-
|
86
|
+
|
74
87
|
def search(name)
|
75
88
|
if name.nil?
|
76
89
|
return nil
|
@@ -81,4 +94,4 @@ module RactorDNS
|
|
81
94
|
@obj.dig(name.join(".")).presence || search(name.drop(1))
|
82
95
|
end
|
83
96
|
end
|
84
|
-
end
|
97
|
+
end
|
data/lib/ractor_dns.rb
CHANGED
@@ -1,19 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "zeitwerk"
|
3
4
|
require_relative "ractor_dns/inflector"
|
4
|
-
require
|
5
|
+
require "socket"
|
5
6
|
require "active_support/all"
|
6
7
|
require "resolv"
|
7
8
|
require "json"
|
8
9
|
require "rrs"
|
10
|
+
require "async"
|
11
|
+
require "async/io"
|
12
|
+
require "async/io/udp_socket"
|
9
13
|
|
10
14
|
loader = Zeitwerk::Loader.for_gem
|
11
15
|
loader.inflector = RactorDNS::Inflector.new
|
16
|
+
loader.ignore("#{__dir__}/ractor_dns/active_zone.rb")
|
17
|
+
loader.ignore("#{__dir__}/ractor_dns/active_zone/**/*")
|
18
|
+
loader.ignore("#{__dir__}/ractor_dns/railtie.rb")
|
19
|
+
loader.ignore("#{__dir__}/ractor_dns/version.rb")
|
12
20
|
loader.setup
|
13
21
|
|
14
22
|
module RactorDNS
|
15
|
-
VERSION = "0.1.0"
|
16
23
|
class Error < StandardError; end
|
17
24
|
end
|
18
25
|
|
19
26
|
loader.eager_load
|
27
|
+
|
28
|
+
require "ractor_dns/active_zone" if defined?(ActiveZone)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ractor_dns
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- reesericci
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-08-
|
11
|
+
date: 2024-08-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -38,6 +38,34 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: async-io
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.43'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.43'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: async
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.15'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.15'
|
41
69
|
description: A concurrent DNS server built on Ractors with a simple and expressive
|
42
70
|
API to integrate into your projects.
|
43
71
|
email:
|
@@ -51,9 +79,12 @@ files:
|
|
51
79
|
- README.md
|
52
80
|
- Rakefile
|
53
81
|
- lib/ractor_dns.rb
|
82
|
+
- lib/ractor_dns/active_zone.rb
|
83
|
+
- lib/ractor_dns/active_zone/railtie.rb
|
54
84
|
- lib/ractor_dns/inflector.rb
|
55
85
|
- lib/ractor_dns/server.rb
|
56
86
|
- lib/ractor_dns/transaction.rb
|
87
|
+
- lib/ractor_dns/version.rb
|
57
88
|
- lib/ractor_dns/zone_collection.rb
|
58
89
|
homepage: https://codeberg.org/oblong/ractor_dns
|
59
90
|
licenses: []
|