knot-ruby 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 04ae67be521fbc10dc42972c8b3c7bfc9c0edc2822617a0ef565564e7e93bd6f
4
+ data.tar.gz: 89396bbc12e9aa181dcbf4af502262d4e8a4fb3ea982c28034c54d374078bd99
5
+ SHA512:
6
+ metadata.gz: 8cf55c28b347d9f5103451ad60d0da577c1febf8909b10bd30449985057c7d8640777953de99ff5509736ead73b8a8f5a9329150948f1a2dea9c30e55728c738
7
+ data.tar.gz: 826ffdff42cbf5a3dd7fc0a54707796c283d13eb0e99478c293ac620cf4b179c24393f0c2db2638717904d7aec0ef097dda2c1cb3d566ad81ebfe43273e8b53c
@@ -0,0 +1,154 @@
1
+ # ruby
2
+ /.bundle/
3
+ /.yardoc
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ /Gemfile.lock
12
+
13
+ # ---> Vim
14
+ # Swap
15
+ [._]*.s[a-v][a-z]
16
+ !*.svg # comment out if you don't need vector files
17
+ [._]*.sw[a-p]
18
+ [._]s[a-rt-v][a-z]
19
+ [._]ss[a-gi-z]
20
+ [._]sw[a-p]
21
+
22
+ # Session
23
+ Session.vim
24
+ Sessionx.vim
25
+
26
+ # Temporary
27
+ .netrwhist
28
+ *~
29
+ # Auto-generated tag files
30
+ tags
31
+ # Persistent undo
32
+ [._]*.un~
33
+
34
+ # ---> Kate
35
+ # Swap Files #
36
+ .*.kate-swp
37
+ .swp.*
38
+
39
+ # ---> KDevelop4
40
+ *.kdev4
41
+ .kdev4/
42
+
43
+ # ---> Emacs
44
+ # -*- mode: gitignore; -*-
45
+ *~
46
+ \#*\#
47
+ /.emacs.desktop
48
+ /.emacs.desktop.lock
49
+ *.elc
50
+ auto-save-list
51
+ tramp
52
+ .\#*
53
+
54
+ # Org-mode
55
+ .org-id-locations
56
+ *_archive
57
+
58
+ # flymake-mode
59
+ *_flymake.*
60
+
61
+ # eshell files
62
+ /eshell/history
63
+ /eshell/lastdir
64
+
65
+ # elpa packages
66
+ /elpa/
67
+
68
+ # reftex files
69
+ *.rel
70
+
71
+ # AUCTeX auto folder
72
+ /auto/
73
+
74
+ # cask packages
75
+ .cask/
76
+ dist/
77
+
78
+ # Flycheck
79
+ flycheck_*.el
80
+
81
+ # server auth directory
82
+ /server/
83
+
84
+ # projectiles files
85
+ .projectile
86
+
87
+ # directory configuration
88
+ .dir-locals.el
89
+
90
+ # network security
91
+ /network-security.data
92
+
93
+
94
+ # ---> Eclipse
95
+ .metadata
96
+ bin/
97
+ tmp/
98
+ *.tmp
99
+ *.bak
100
+ *.swp
101
+ *~.nib
102
+ local.properties
103
+ .settings/
104
+ .loadpath
105
+ .recommenders
106
+
107
+ # External tool builders
108
+ .externalToolBuilders/
109
+
110
+ # Locally stored "Eclipse launch configurations"
111
+ *.launch
112
+
113
+ # PyDev specific (Python IDE for Eclipse)
114
+ *.pydevproject
115
+
116
+ # CDT-specific (C/C++ Development Tooling)
117
+ .cproject
118
+
119
+ # CDT- autotools
120
+ .autotools
121
+
122
+ # Java annotation processor (APT)
123
+ .factorypath
124
+
125
+ # PDT-specific (PHP Development Tools)
126
+ .buildpath
127
+
128
+ # sbteclipse plugin
129
+ .target
130
+
131
+ # Tern plugin
132
+ .tern-project
133
+
134
+ # TeXlipse plugin
135
+ .texlipse
136
+
137
+ # STS (Spring Tool Suite)
138
+ .springBeans
139
+
140
+ # Code Recommenders
141
+ .recommenders/
142
+
143
+ # Annotation Processing
144
+ .apt_generated/
145
+ .apt_generated_test/
146
+
147
+ # Scala IDE specific (Scala & Java development for Eclipse)
148
+ .cache-main
149
+ .scala_dependencies
150
+ .worksheet
151
+
152
+ # ---> NotepadPP
153
+ # Notepad++ backups #
154
+ *.bak
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in knot.gemspec
4
+ gemspec
5
+
6
+ gem "rake", "~> 12.0"
data/LICENSE ADDED
@@ -0,0 +1,163 @@
1
+ GNU LESSER GENERAL PUBLIC LICENSE
2
+
3
+ Version 3, 29 June 2007
4
+
5
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
6
+
7
+ Everyone is permitted to copy and distribute verbatim copies of this license
8
+ document, but changing it is not allowed.
9
+
10
+ This version of the GNU Lesser General Public License incorporates the terms
11
+ and conditions of version 3 of the GNU General Public License, supplemented
12
+ by the additional permissions listed below.
13
+
14
+ 0. Additional Definitions.
15
+
16
+
17
+
18
+ As used herein, "this License" refers to version 3 of the GNU Lesser General
19
+ Public License, and the "GNU GPL" refers to version 3 of the GNU General Public
20
+ License.
21
+
22
+
23
+
24
+ "The Library" refers to a covered work governed by this License, other than
25
+ an Application or a Combined Work as defined below.
26
+
27
+
28
+
29
+ An "Application" is any work that makes use of an interface provided by the
30
+ Library, but which is not otherwise based on the Library. Defining a subclass
31
+ of a class defined by the Library is deemed a mode of using an interface provided
32
+ by the Library.
33
+
34
+
35
+
36
+ A "Combined Work" is a work produced by combining or linking an Application
37
+ with the Library. The particular version of the Library with which the Combined
38
+ Work was made is also called the "Linked Version".
39
+
40
+
41
+
42
+ The "Minimal Corresponding Source" for a Combined Work means the Corresponding
43
+ Source for the Combined Work, excluding any source code for portions of the
44
+ Combined Work that, considered in isolation, are based on the Application,
45
+ and not on the Linked Version.
46
+
47
+
48
+
49
+ The "Corresponding Application Code" for a Combined Work means the object
50
+ code and/or source code for the Application, including any data and utility
51
+ programs needed for reproducing the Combined Work from the Application, but
52
+ excluding the System Libraries of the Combined Work.
53
+
54
+ 1. Exception to Section 3 of the GNU GPL.
55
+
56
+ You may convey a covered work under sections 3 and 4 of this License without
57
+ being bound by section 3 of the GNU GPL.
58
+
59
+ 2. Conveying Modified Versions.
60
+
61
+ If you modify a copy of the Library, and, in your modifications, a facility
62
+ refers to a function or data to be supplied by an Application that uses the
63
+ facility (other than as an argument passed when the facility is invoked),
64
+ then you may convey a copy of the modified version:
65
+
66
+ a) under this License, provided that you make a good faith effort to ensure
67
+ that, in the event an Application does not supply the function or data, the
68
+ facility still operates, and performs whatever part of its purpose remains
69
+ meaningful, or
70
+
71
+ b) under the GNU GPL, with none of the additional permissions of this License
72
+ applicable to that copy.
73
+
74
+ 3. Object Code Incorporating Material from Library Header Files.
75
+
76
+ The object code form of an Application may incorporate material from a header
77
+ file that is part of the Library. You may convey such object code under terms
78
+ of your choice, provided that, if the incorporated material is not limited
79
+ to numerical parameters, data structure layouts and accessors, or small macros,
80
+ inline functions and templates (ten or fewer lines in length), you do both
81
+ of the following:
82
+
83
+ a) Give prominent notice with each copy of the object code that the Library
84
+ is used in it and that the Library and its use are covered by this License.
85
+
86
+ b) Accompany the object code with a copy of the GNU GPL and this license document.
87
+
88
+ 4. Combined Works.
89
+
90
+ You may convey a Combined Work under terms of your choice that, taken together,
91
+ effectively do not restrict modification of the portions of the Library contained
92
+ in the Combined Work and reverse engineering for debugging such modifications,
93
+ if you also do each of the following:
94
+
95
+ a) Give prominent notice with each copy of the Combined Work that the Library
96
+ is used in it and that the Library and its use are covered by this License.
97
+
98
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
99
+ document.
100
+
101
+ c) For a Combined Work that displays copyright notices during execution, include
102
+ the copyright notice for the Library among these notices, as well as a reference
103
+ directing the user to the copies of the GNU GPL and this license document.
104
+
105
+ d) Do one of the following:
106
+
107
+ 0) Convey the Minimal Corresponding Source under the terms of this License,
108
+ and the Corresponding Application Code in a form suitable for, and under terms
109
+ that permit, the user to recombine or relink the Application with a modified
110
+ version of the Linked Version to produce a modified Combined Work, in the
111
+ manner specified by section 6 of the GNU GPL for conveying Corresponding Source.
112
+
113
+ 1) Use a suitable shared library mechanism for linking with the Library. A
114
+ suitable mechanism is one that (a) uses at run time a copy of the Library
115
+ already present on the user's computer system, and (b) will operate properly
116
+ with a modified version of the Library that is interface-compatible with the
117
+ Linked Version.
118
+
119
+ e) Provide Installation Information, but only if you would otherwise be required
120
+ to provide such information under section 6 of the GNU GPL, and only to the
121
+ extent that such information is necessary to install and execute a modified
122
+ version of the Combined Work produced by recombining or relinking the Application
123
+ with a modified version of the Linked Version. (If you use option 4d0, the
124
+ Installation Information must accompany the Minimal Corresponding Source and
125
+ Corresponding Application Code. If you use option 4d1, you must provide the
126
+ Installation Information in the manner specified by section 6 of the GNU GPL
127
+ for conveying Corresponding Source.)
128
+
129
+ 5. Combined Libraries.
130
+
131
+ You may place library facilities that are a work based on the Library side
132
+ by side in a single library together with other library facilities that are
133
+ not Applications and are not covered by this License, and convey such a combined
134
+ library under terms of your choice, if you do both of the following:
135
+
136
+ a) Accompany the combined library with a copy of the same work based on the
137
+ Library, uncombined with any other library facilities, conveyed under the
138
+ terms of this License.
139
+
140
+ b) Give prominent notice with the combined library that part of it is a work
141
+ based on the Library, and explaining where to find the accompanying uncombined
142
+ form of the same work.
143
+
144
+ 6. Revised Versions of the GNU Lesser General Public License.
145
+
146
+ The Free Software Foundation may publish revised and/or new versions of the
147
+ GNU Lesser General Public License from time to time. Such new versions will
148
+ be similar in spirit to the present version, but may differ in detail to address
149
+ new problems or concerns.
150
+
151
+ Each version is given a distinguishing version number. If the Library as you
152
+ received it specifies that a certain numbered version of the GNU Lesser General
153
+ Public License "or any later version" applies to it, you have the option of
154
+ following the terms and conditions either of that published version or of
155
+ any later version published by the Free Software Foundation. If the Library
156
+ as you received it does not specify a version number of the GNU Lesser General
157
+ Public License, you may choose any version of the GNU Lesser General Public
158
+ License ever published by the Free Software Foundation.
159
+
160
+ If the Library as you received it specifies that a proxy can decide whether
161
+ future versions of the GNU Lesser General Public License shall apply, that
162
+ proxy's public statement of acceptance of any version is permanent authorization
163
+ for you to choose that version for the Library.
@@ -0,0 +1,46 @@
1
+ Knot
2
+ ====
3
+
4
+ Implements the knot-protocol.
5
+ It is possible to write a knot-cli like knotc in ruby.
6
+ Or your own tool, for provisioning.
7
+
8
+ Installation
9
+ ============
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ [source,ruby]
14
+ ----
15
+ gem 'knot'
16
+ ----
17
+
18
+ And then execute:
19
+
20
+ [source,sh]
21
+ -----
22
+ $ bundle install
23
+ -----
24
+
25
+ Or install it yourself as:
26
+
27
+ [source,sh]
28
+ -----
29
+ $ gem install knot-ruby
30
+ -----
31
+
32
+ Usage
33
+ -----
34
+
35
+ TODO: Write usage instructions here
36
+
37
+ Development
38
+ -----------
39
+
40
+ To install this gem onto your local machine, run `bundle exec rake install`.
41
+ To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
42
+
43
+ Contributing
44
+ ------------
45
+
46
+ Bug reports and pull requests are welcome at https://git.denkn.at/deac/knot-ruby.
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
@@ -0,0 +1,31 @@
1
+ require_relative 'lib/knot/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "knot-ruby"
5
+ spec.version = Knot::VERSION
6
+ spec.authors = ['Denis Knauf']
7
+ spec.email = ['gems+knot@denkn.at']
8
+ spec.licenses = ["LGPL-3.0"]
9
+
10
+ spec.summary = %q{Provides interface to knot-server.}
11
+ spec.description = %q{Implements knot-protocol to provide an interface to knot-DNS-server}
12
+ spec.homepage = 'https://git.denkn.at/deac/knot'
13
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
14
+
15
+ #spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = spec.homepage
19
+ spec.metadata["changelog_uri"] = spec.homepage
20
+
21
+ spec.add_dependency 'iounpack', '~> 0'
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
26
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
+ end
28
+ spec.bindir = "bin"
29
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+ end
@@ -0,0 +1,4 @@
1
+ require 'knot/version'
2
+ require 'knot/errors'
3
+ require 'knot/protocol'
4
+ require 'knot/interface'
@@ -0,0 +1,166 @@
1
+ module Knot
2
+ class Error < ::Exception
3
+ attr_reader :Errno
4
+ attr_reader :Key
5
+ attr_reader :Errstr
6
+ end
7
+ end
8
+
9
+ module Knot::Errors
10
+ @num2exc = {}
11
+ @key2exc = {}
12
+ @err2exc = {}
13
+ class << self
14
+ attr_reader :num2exc, :key2exc, :err2exc
15
+
16
+ def array_arguments_with_typecheck *args
17
+ l = args.length
18
+ lambda do |a|
19
+ l == a.length and
20
+ args.zip(a).all? {|t,v| t === v }
21
+ end
22
+ end
23
+ end
24
+
25
+ def method_missing key
26
+ self[key]
27
+ end
28
+
29
+ def [] key_or_errno
30
+ case key_or_errno
31
+ when Integer
32
+ @num2exc[key_or_errno]
33
+ when Exception
34
+ key_or_errno
35
+ when Symbol
36
+ @key2exc[key_or_errno]
37
+ else
38
+ raise ArgumentError, "Invalid type. Expect Integer/Knot::Error/Symbol"
39
+ end
40
+ end
41
+
42
+ i = 0
43
+ [
44
+ [:EOK, 0, "OK"],
45
+ # Directly mapped error codes.
46
+ [:ENOMEM, -Errno::ENOMEM::Errno, "not enough memory" ],
47
+ [:EINVAL, -Errno::EINVAL::Errno, "invalid parameter" ],
48
+ [:ENOTSUP, -Errno::ENOTSUP::Errno, "operation not supported" ],
49
+ [:EBUSY, -Errno::EBUSY::Errno, "requested resource is busy" ],
50
+ [:EAGAIN, -Errno::EAGAIN::Errno, "OS lacked necessary resources" ],
51
+ [:EACCES, -Errno::EACCES::Errno, "operation not permitted" ],
52
+ [:ECONNREFUSED, -Errno::ECONNREFUSED::Errno, "connection refused" ],
53
+ [:EISCONN, -Errno::EISCONN::Errno, "already connected" ],
54
+ [:EADDRINUSE, -Errno::EADDRINUSE::Errno, "address already in use" ],
55
+ [:ENOENT, -Errno::ENOENT::Errno, "not exists" ],
56
+ [:EEXIST, -Errno::EEXIST::Errno, "already exists" ],
57
+ [:ERANGE, -Errno::ERANGE::Errno, "value is out of range" ],
58
+ [:EADDRNOTAVAIL, -Errno::EADDRNOTAVAIL::Errno, "address is not available" ],
59
+
60
+ # General errors.
61
+ [:ERROR, -1000, "failed"],
62
+ [:EPARSEFAIL, "parser failed"],
63
+ [:ESEMCHECK, "semantic check"],
64
+ [:EUPTODATE, "zone is up-to-date"],
65
+ [:EFEWDATA, "not enough data to parse"],
66
+ [:ESPACE, "not enough space provided"],
67
+ [:EMALF, "malformed data"],
68
+ [:ENSEC3PAR, "missing or wrong NSEC3PARAM record"],
69
+ [:ENSEC3CHAIN, "missing or wrong NSEC3 chain in the zone"],
70
+ [:EOUTOFZONE, "name does not belong to the zone"],
71
+ [:EZONEINVAL, "invalid zone file"],
72
+ [:ENOZONE, "no such zone found"],
73
+ [:ENONODE, "no such node in zone found"],
74
+ [:ENORECORD, "no such record in zone found"],
75
+ [:EISRECORD, "such record already exists in zone"],
76
+ [:ENOMASTER, "no usable master"],
77
+ [:EPREREQ, "UPDATE prerequisity not met"],
78
+ [:ETTL, "TTL mismatch"],
79
+ [:ENOXFR, "transfer was not sent"],
80
+ [:EDENIED, "not allowed"],
81
+ [:ECONN, "connection reset"],
82
+ [:ETIMEOUT, "connection timeout"],
83
+ [:ENODIFF, "cannot create zone diff"],
84
+ [:ENOTSIG, "expected a TSIG or SIG(0)"],
85
+ [:ELIMIT, "exceeded response rate limit"],
86
+ [:EZONESIZE, "zone size exceeded"],
87
+ [:EOF, "end of file"],
88
+ [:ESYSTEM, "system error"],
89
+ [:EFILE, "file error"],
90
+ [:ESOAINVAL, "SOA mismatch"],
91
+ [:ETRAIL, "trailing data"],
92
+ [:EPROCESSING, "processing error"],
93
+
94
+ # Control states.
95
+ [:CTL_ESTOP, "stopping server"],
96
+ # Network errors.
97
+ [:NET_EADDR, "bad address or host name"],
98
+ [:NET_ESOCKET, "can't create socket"],
99
+ [:NET_ECONNECT, "can't connect"],
100
+ [:NET_ESEND, "can't send data"],
101
+ [:NET_ERECV, "can't receive data"],
102
+ [:NET_ETIMEOUT, "network timeout"],
103
+ # Encoding errors.
104
+ [:BASE64_ESIZE, "invalid base64 string length"],
105
+ [:BASE64_ECHAR, "invalid base64 character"],
106
+ [:BASE32HEX_ESIZE, "invalid base32hex string length"],
107
+ [:BASE32HEX_ECHAR, "invalid base32hex character"],
108
+ # TSIG errors.
109
+ [:KNOT_TSIG_EBADSIG, "failed to verify TSIG"],
110
+ [:KNOT_TSIG_EBADKEY, "TSIG key not recognized or invalid"],
111
+ [:KNOT_TSIG_EBADTIME, "TSIG out of time window"],
112
+ [:KNOT_TSIG_EBADTRUNC, "TSIG bad truncation"],
113
+ # DNSSEC errors.
114
+ [:DNSSEC_ENOKEY, "no keys for signing"],
115
+ [:DNSSEC_EMISSINGKEYTYPE, "missing active KSK or ZSK"],
116
+ # Yparser errors.
117
+ [:YP_ECHAR_TAB, "tabulator character is not allowed"],
118
+ [:YP_EINVAL_ITEM, "invalid item"],
119
+ [:YP_EINVAL_ID, "invalid identifier"],
120
+ [:YP_EINVAL_DATA, "invalid value"],
121
+ [:YP_EINVAL_INDENT, "invalid indentation"],
122
+ [:YP_ENOTSUP_DATA, "value not supported"],
123
+ [:YP_ENOTSUP_ID, "identifier not supported"],
124
+ [:YP_ENODATA, "missing value"],
125
+ [:YP_ENOID, "missing identifier"],
126
+ # Configuration errors.
127
+ [:CONF_ENOTINIT, "config DB not initialized"],
128
+ [:CONF_EVERSION, "invalid config DB version"],
129
+ [:CONF_EREDEFINE, "duplicate identifier"],
130
+ # Transaction errors.
131
+ [:TXN_EEXISTS, "too many transactions"],
132
+ [:TXN_ENOTEXISTS, "no active transaction"],
133
+ # DNSSEC errors.
134
+ [:INVALID_PUBLIC_KEY, "invalid public key"],
135
+ [:INVALID_PRIVATE_KEY, "invalid private key"],
136
+ [:INVALID_KEY_ALGORITHM, "invalid key algorithm"],
137
+ [:INVALID_KEY_SIZE, "invalid key size"],
138
+ [:INVALID_KEY_ID, "invalid key ID"],
139
+ [:INVALID_KEY_NAME, "invalid key name"],
140
+ [:NO_PUBLIC_KEY, "no public key"],
141
+ [:NO_PRIVATE_KEY, "no private key"],
142
+ ].each do |v|
143
+ e = nil
144
+ case v
145
+ when array_arguments_with_typecheck( Symbol, String)
146
+ v, e = v
147
+ when array_arguments_with_typecheck( String, String)
148
+ v, e = v
149
+ v = v.to_sym
150
+ when array_arguments_with_typecheck( Symbol, Integer, String)
151
+ v, i, e = v
152
+ when array_arguments_with_typecheck( String, Integer, String)
153
+ v, i, e = v
154
+ v = v.to_sym
155
+ else
156
+ raise ArgumentError, "[Symbol, String] | [Symbol, Int, String] expected, not #{v}"
157
+ end
158
+ cl = Class.new Exception
159
+ cl.const_set :Key, v
160
+ cl.const_set :Errno, i
161
+ cl.const_set :Errstr, e
162
+ const_set v, cl
163
+ @num2exc[i] = @key2exc[v] = @err2exc[e] = cl
164
+ i += 1
165
+ end
166
+ end
@@ -0,0 +1,159 @@
1
+ require_relative 'protocol'
2
+ require_relative 'errors'
3
+
4
+ module Knot
5
+ end
6
+
7
+ class Knot::Zone
8
+ attr_reader :protocol, :zone
9
+ def initialize zone, protocol = nil
10
+ @protocol = protocol || Protocol.new
11
+ @zone, @transaction_opened = zone, 0
12
+ end
13
+
14
+ def begin
15
+ @transaction_opened += 1
16
+ @protocol.call command: 'zone-begin', zone: @zone if 1 == @transaction_opened
17
+ end
18
+
19
+ def commit
20
+ @protocol.call command: 'zone-commit', zone: @zone if 1 == @transaction_opened
21
+ @transaction_opened -= 1 if 0 < @transaction_opened
22
+ end
23
+
24
+ def abort
25
+ @protocol.call command: 'zone-abort', zone: @zone
26
+ @transaction_opened = 0
27
+ end
28
+
29
+ def transaction
30
+ self.begin
31
+ yield self
32
+ rescue Object
33
+ self.abort
34
+ raise
35
+ ensure
36
+ self.commit unless $!
37
+ end
38
+
39
+ # zone operation
40
+
41
+ def check() @protocol.call command: 'zone-check', zone: @zone end
42
+ def reload() @protocol.call command: 'zone-reload', zone: @zone end
43
+ def refresh() @protocol.call command: 'zone-refresh', zone: @zone end
44
+ def notify() @protocol.call command: 'zone-notify', zone: @zone end
45
+ def retransfer() @protocol.call command: 'zone-retransfer', zone: @zone end
46
+ def sign() @protocol.call command: 'zone-sign', zone: @zone end
47
+ def freeze() @protocol.call command: 'zone-freeze', zone: @zone end
48
+ def thaw() @protocol.call command: 'zone-thaw', zone: @zone end
49
+ def status( filter = nil) @protocol.call command: 'zone-status', zone: @zone, filter: filter end
50
+
51
+ def stats( modul = nil, counter = nil)
52
+ @protocol.call command: 'zone-stats', zone: @zone, module: modul, counter: counter
53
+ end
54
+
55
+ # zone manipulation
56
+
57
+ def read() @protocol.call command: 'zone-read', zone: @zone end
58
+ def diff() @protocol.call command: 'zone-diff', zone: @zone end
59
+
60
+ # setting record
61
+ # if data is nil, it will be unset.
62
+ def set owner, ttl = nil, type, data
63
+ @protocol.call command: data.nil? ? 'zone-unset' : 'zone-set',
64
+ zone: @zone, owner: owner, ttl: ttl, type: type, data: data
65
+ rescue Knot::Errors::EISRECORD, Knot::Errors::ENONODE, Knot::Errors::ENOENT
66
+ end
67
+ alias []= set
68
+
69
+ def unset owner, type = nil, data = nil
70
+ @protocol.call command: 'zone-unset',
71
+ zone: @zone, owner: owner, type: type, data: data
72
+ rescue Knot::Errors::ENONODE, Knot::Errors::ENOENT
73
+ end
74
+ alias delete unset
75
+
76
+ def get owner = nil, type = nil
77
+ @protocol.call command: 'zone-get',
78
+ zone: @zone, owner: owner, type: type
79
+ rescue Knot::Errors::ENONODE, Knot::Errors::ENOENT
80
+ nil
81
+ end
82
+ alias [] get
83
+ end
84
+
85
+ class Knot::Conf
86
+ def initialize protocol = nil
87
+ @protocol = protocol || Protocol.new
88
+ @transaction_opened = 0
89
+ end
90
+
91
+ def begin
92
+ @transaction_opened += 1
93
+ @protocol.call command: 'conf-begin' if 1 == @transaction_opened
94
+ end
95
+
96
+ def commit
97
+ @protocol.call command: 'conf-commit' if 1 == @transaction_opened
98
+ @transaction_opened -= 1 if 0 < @transaction_opened
99
+ end
100
+
101
+ def abort
102
+ @protocol.call command: 'conf-abort'
103
+ @transaction_opened = 0
104
+ end
105
+
106
+ def transaction
107
+ self.begin
108
+ yield self
109
+ rescue Object
110
+ self.abort
111
+ raise
112
+ ensure
113
+ self.commit
114
+ end
115
+
116
+ def parse_item k
117
+ case k
118
+ when k
119
+ case k.keys.sort
120
+ when %w[section], %w[id section], %w[item section], %w[id item section] then k
121
+ else raise ArgumentError, "Invalid Item-format"
122
+ end
123
+
124
+ when Array
125
+ case k.length
126
+ when 1 then {section: k[0]}
127
+ when 2 then {section: k[0], item: k[1]}
128
+ when 3 then {section: k[0], id: k[1], item: k[2]}
129
+ else raise ArgumentError, "Invalid Item-format"
130
+ end
131
+
132
+ when /\A
133
+ (?<section> [a-z0-9_-]+ )
134
+ (?: \[ (?<id> [a-z0-9_.-]+) \] )?
135
+ (?: \. (?<item>[a-z0-9_-]+) )?
136
+ \z/xi
137
+ $~.named_captures.delete_if {|_,v| v.nil? }
138
+ else raise ArgumentError, "Invalid Item-format"
139
+ end
140
+ end
141
+
142
+ def set item, value
143
+ @protocol.call parse_item( item).update( command: 'conf-set', data: value)
144
+ end
145
+ alias [] set
146
+
147
+ def unset item, value = nil
148
+ @protocol.call parse_item( item).update( command: 'conf-unset', data: value)
149
+ end
150
+ alias delete unset
151
+
152
+ def list item = nil
153
+ @protocol.call (item ? parse_item( item) : {}).update( command: 'conf-list')
154
+ end
155
+
156
+ def read item = nil
157
+ @protocol.call (item ? parse_item( item) : {}).update( command: 'conf-read')
158
+ end
159
+ end
@@ -0,0 +1,188 @@
1
+ require 'iounpack'
2
+ require_relative 'errors'
3
+
4
+ module Knot
5
+ class Protocol
6
+ end
7
+ end
8
+
9
+ module Knot::Protocol::Type
10
+ def [] code
11
+ case code
12
+ when 0, End then return End
13
+ when 1, Data then return Data
14
+ when 2, Extra then return Extra
15
+ when 3, Block then return Block
16
+ end
17
+ end
18
+
19
+ class Base
20
+ def self.expect_data?
21
+ false
22
+ end
23
+ def expect_data?() self.class.expect_data? end
24
+ def code() self.class.code end
25
+ end
26
+ class End < Base
27
+ def self.code() 0 end
28
+ end
29
+ class Data < Base
30
+ def code() 1 end
31
+ def expect_data?
32
+ true
33
+ end
34
+ attr_reader :data
35
+ def initialize data
36
+ @data = data
37
+ end
38
+ end
39
+ class Extra < Base
40
+ def code() 2 end
41
+ def expect_data?
42
+ true
43
+ end
44
+ attr_reader :data
45
+ def initialize data
46
+ @data = data
47
+ end
48
+ end
49
+ class Block < Base
50
+ def code() 3 end
51
+ end
52
+ end
53
+
54
+ module Knot::Protocol::Idx
55
+ Idx = [
56
+ :command, # 10, :CMD, # Control command name.
57
+ :flags, # 11, :FLAGS, # Control command flags.
58
+ :error, # 12, :ERROR, # Error message.
59
+ :section, # 13, :SECTION, # Configuration section name.
60
+ :item, # 14, :ITEM, # Configuration item name.
61
+ :id, # 15, :ID, # Congiguration item identifier.
62
+ :zone, # 16, :ZONE, # Zone name.
63
+ :owner, # 17, :OWNER, # Zone record owner
64
+ :ttl, # 18, :TTL, # Zone record TTL.
65
+ :type, # 19, :TYPE, # Zone record type name.
66
+ :data, # 1a, :DATA, # Configuration item/zone record data.
67
+ :filter, # 1b, :FILTER, # An option or a filter for output data processing.
68
+ ]
69
+ Name = {}
70
+ Code = {}
71
+ Idx.each_with_index do |v, i|
72
+ Code[0x10+i] = v
73
+ Name[v] = i
74
+ end
75
+ def self.[] k
76
+ case k
77
+ when Symbol
78
+ Name[k] or raise Knot::Errors::EINVAL, "Unknown Idx: #{k}"
79
+ when Integer
80
+ Idx[k] or raise Knot::Errors::EINVAL, "Unknown Idx: #{k}"
81
+ else
82
+ raise ArgumentError, "Unknown Idx-Type: #{k}"
83
+ end
84
+ end
85
+ end
86
+
87
+ class Knot::Protocol
88
+ attr_reader :sock, :conf, :zones
89
+ attr_accessor :debug
90
+
91
+ def initialize path_or_sock = nil
92
+ case path_or_sock
93
+ when String, Pathname
94
+ @sock = UNIXSocket.new path_or_sock.to_s
95
+ when Socket
96
+ @sock = path_or_sock
97
+ when nil
98
+ @sock = UNIXSocket.new '/run/knot/knot.sock'
99
+ end
100
+ @debug = false
101
+ @conf = Knot::Conf.new self
102
+ @zones = Hash.new {|h, zone| h[zone] = Knot::Zone.new zone, self }
103
+ end
104
+
105
+ def snd sock: nil, **data
106
+ rsock = sock || @sock
107
+ s = ''
108
+ sock = StringIO.new s
109
+ sock.write [1].pack( 'c')
110
+ data[:flags] ||= ''
111
+ Idx::Idx.each_with_index do |n, i|
112
+ v = data[n]&.to_s
113
+ sock.write [0x10+i, v.size, v].pack( 'c na*') if v
114
+ end
115
+ sock.write [3].pack( 'c')
116
+ sock.flush
117
+ STDERR.puts( {data: data, _: s}.inspect) if @debug
118
+ rsock.write s
119
+ rsock.flush
120
+ end
121
+
122
+ class RecordIO
123
+ attr_reader :str
124
+
125
+ def initialize sock, str = nil
126
+ @str, @sock = str || '', sock
127
+ end
128
+
129
+ def unpack pattern
130
+ IOUnpack.new(pattern).unpack self
131
+ end
132
+
133
+ def unpack1 pattern
134
+ IOUnpack.new(pattern).unpack1 self
135
+ end
136
+
137
+ def read n
138
+ s = @sock.read n
139
+ @str.insert -1, s
140
+ s
141
+ end
142
+ end
143
+
144
+ def rcv sock: nil
145
+ ret, r = [], nil
146
+ sock = sock || @sock
147
+ sock = RecordIO.new sock if @debug
148
+ loop do
149
+ t = sock.unpack1 'c'
150
+ case t
151
+ when 0, 3
152
+ return ret
153
+ when 1, 2
154
+ type = t
155
+ ret.push( r = {})
156
+ else
157
+ raise Knot::Errors::EINVAL, "Missing Type before: #{t}" if ret.empty?
158
+ i = Idx::Idx[t - 0x10] or raise Knot::Errors::EINVAL, "Unknown index: #{t-0x10}"
159
+ l = sock.unpack1 'n'
160
+ r[i] = sock.read( l)
161
+ end
162
+ end
163
+ ensure
164
+ STDERR.puts( {rcvd: ret, read: sock.str}.inspect) if @debug
165
+ ret
166
+ end
167
+
168
+ def call sock: nil, **data
169
+ snd sock: sock, **data
170
+ rcv( sock: sock).each do |r|
171
+ if r[:error]
172
+ if e = Knot::Errors.err2exc[r[:error]]
173
+ raise e, r[:error]
174
+ end
175
+ raise Knot::Error, r[:error]
176
+ end
177
+ end
178
+ end
179
+
180
+ def zone( zone) @zones[zone.to_s.to_sym] end
181
+
182
+ def conf_set( **opts) call opts.update( command: 'conf-set') end
183
+ def conf_unset( **opts) call opts.update( command: 'conf-set') end
184
+
185
+ def zone_set( **opts) call opts.update( command: 'zone-set') end
186
+ def zone_unset( **opts) call opts.update( command: 'zone-unset') end
187
+ def zone_get( **opts) call opts.update( command: 'zone-get') end
188
+ end
@@ -0,0 +1,3 @@
1
+ module Knot
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: knot-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Denis Knauf
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-06-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: iounpack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: Implements knot-protocol to provide an interface to knot-DNS-server
28
+ email:
29
+ - gems+knot@denkn.at
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".gitignore"
35
+ - Gemfile
36
+ - LICENSE
37
+ - README.adoc
38
+ - Rakefile
39
+ - knot-ruby.gemspec
40
+ - lib/knot.rb
41
+ - lib/knot/errors.rb
42
+ - lib/knot/interface.rb
43
+ - lib/knot/protocol.rb
44
+ - lib/knot/version.rb
45
+ homepage: https://git.denkn.at/deac/knot
46
+ licenses:
47
+ - LGPL-3.0
48
+ metadata:
49
+ homepage_uri: https://git.denkn.at/deac/knot
50
+ source_code_uri: https://git.denkn.at/deac/knot
51
+ changelog_uri: https://git.denkn.at/deac/knot
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 2.3.0
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubyforge_project:
68
+ rubygems_version: 2.7.6.2
69
+ signing_key:
70
+ specification_version: 4
71
+ summary: Provides interface to knot-server.
72
+ test_files: []