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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3ce870d0f62325886ce1055117b49e081cbd7b5f74ba1d4aa9894e5b16c7f8ad
4
- data.tar.gz: e8d04590744cf6458bd5d395457930e38916590568a2f57e261ad20fe47f829a
3
+ metadata.gz: 43eb0bf1e0c8c1333e58ab9af172c794203ad40184ef1c501b59d21e654295e9
4
+ data.tar.gz: 9e3385ab39bed221627a4d34b34ab26060cd19862d9e139e1ee727c83fbe8d1b
5
5
  SHA512:
6
- metadata.gz: 4a5ce6ba4208da2b999f8087f4748eb9090451618f192c726c77f90287c83ac387db2618146e01626040306084f76b1492a2ec5271b79fc46e31f4390e5cf808
7
- data.tar.gz: a2caa666683138cb78fb239e856e12383a5ee651fb7fe0e5d86ddb7d042b4f9cdee96117974971c1b1c4f12c828d2ba969e5388fe1a124f968bdcb5ec6eeef1a
6
+ metadata.gz: c1574d4b92f4334799906f831eeb2c9e96bbb8cf12b96b4f6b3dc2e23c4ae7cb4ac1f94364d6e9ccf0fda855590fb88ded34c6409c662120a3d494157b21a8f1
7
+ data.tar.gz: a0d547ee9b18754498052a683708024980aed9afc4570c5e3d7573b25abf3abc369ac62525b701efa57af084e16a55150c8aa32b2d4ec55b7ef2ac55c3dc2a12
data/README.md CHANGED
@@ -1,20 +1,16 @@
1
1
  # RactorDNS
2
2
 
3
- TODO: Delete this and the text below, and describe your gem
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
- $ bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
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
- $ gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
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 on GitHub at https://github.com/[USERNAME]/ractor_dns.
27
+ Bug reports and pull requests are welcome at https://codeberg.org/oblong/ractor_dns.
@@ -0,0 +1,7 @@
1
+ class RactorDNS::ActiveZone::Railtie < Rails::Railtie
2
+ server do
3
+ if ActiveZone.provider.is_a? RactorDNS::ActiveZone
4
+ ActiveZone.provider.start
5
+ end
6
+ end
7
+ end
@@ -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)
@@ -8,4 +8,4 @@ module RactorDNS
8
8
  end
9
9
  end
10
10
  end
11
- end
11
+ end
@@ -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
- @ractor = Ractor.new(@authority, @cpu_count, @zones) do |authority, cpu_count, zones|
13
- pipe = Ractor.new do
14
- loop do
15
- Ractor.yield(Ractor.recv)
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
- # Create socket outside of Ractor context
20
- server_socket = UDPSocket.new :INET6
21
- server_socket.bind("::", 8080)
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
- decoded = RRs::Message.decode(message)
31
+ break if data == :stop && zone_h.nil?
29
32
 
30
- # Extract client information
31
- addr_info = Addrinfo.new(client)
33
+ decoded = RRs::Message.decode(data)
32
34
 
33
- response = RRs::Message.new(decoded.id)
34
- response.add_authority(*authority)
35
- response.instance_exec(decoded) do |decoded|
36
- @question.concat(decoded.question)
37
- end
38
- decoded.question.each do |q|
39
- (zone_h.search(q[0]) || []).filter { |el| el[:rr].class == q[1]; el[:name] == q[0].to_s }&.each do |ans|
40
- response.add_answer(ans[:name], ans[:ttl], ans[:rr])
41
- end
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
- listener = Ractor.new(pipe, server_socket, zones) do |pipe, server_socket, zones|
50
- loop do
51
- msg, client = server_socket.recvfrom(1024)
52
- pipe.send([msg, client, zones.freeze])
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
- loop do
57
- Signal.trap('INT') { exit! }
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
- @ractor.take
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
- begin
12
- @original_state = obj.dup
13
- @new_obj = obj.deep_transform_values { |value| value = value.dup }
14
- @block.call(@new_obj)
15
- obj.replace(@new_obj)
16
- rescue => e
17
- puts e
18
- rollback(obj)
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
@@ -0,0 +1,3 @@
1
+ module RactorDNS
2
+ VERSION = "0.2.0"
3
+ end
@@ -1,11 +1,16 @@
1
1
  module RactorDNS
2
2
  class ZoneCollection
3
3
  def initialize(obj = {})
4
- @queue = Ractor.new(Ractor.make_shareable(obj.freeze)) do |obj|
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(self.to_h)
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 'socket'
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.1.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-18 00:00:00.000000000 Z
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: []