dyndnsd 1.6.1 → 2.0.0.rc1

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
- SHA1:
3
- metadata.gz: 8f5c478f470f3567d427cd64d85bfb275cb9b4bf
4
- data.tar.gz: 0fe278562fbfe094d78b7afd04250a288732b429
2
+ SHA256:
3
+ metadata.gz: aac098254fd64311eda2e12bba4f438f7b7fc6e91a25c24cf3f709b76c1f925e
4
+ data.tar.gz: ebee973229eb3c4d4f3a001ed21cffd3a6be400f31bfedb9dbc6529281988657
5
5
  SHA512:
6
- metadata.gz: 6fcf2c23eef6cd1c2c9dd2b09fee33c9b760975c1c63631b48c34f86c25b94ef5fe7ea869cc9e6b48231c5ad60c43b8a172f598eb20aef0790e1a23e9f3d945b
7
- data.tar.gz: 134d8ce48ea9df369a08516e355f06ea7db441e116d7c7a7c985aa75158eaeecafe0fc9febe7823da62a7c9f36ed488189291d3b9470adcb5fd90787c391057a
6
+ metadata.gz: 4497985876b27f5a8afffa1a9fba699a7837a32488035ba725edf5afd13c307f1c741561e75d2c54a5621936e59555da98f903abd43247aa4769d55b4485e340
7
+ data.tar.gz: 9f5cf58eb67acee24c0174ec7d65503deef1ccfc44568f118bccb7a0215c84a185e3ba023086ff6ae53a1feb2593793e822c6c16c258e3be588e1164363551d9
data/.rubocop.yml ADDED
@@ -0,0 +1,62 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ AllCops:
4
+ TargetRubyVersion: '2.3'
5
+
6
+ Gemspec/OrderedDependencies:
7
+ Enabled: false
8
+
9
+ # allows nicer usage of private_class_method
10
+ Layout/EmptyLinesAroundArguments:
11
+ Enabled: false
12
+
13
+ Layout/SpaceInsideHashLiteralBraces:
14
+ Enabled: false
15
+
16
+ Metrics/AbcSize:
17
+ Enabled: false
18
+
19
+ Metrics/BlockLength:
20
+ Enabled: false
21
+
22
+ Metrics/ClassLength:
23
+ Enabled: false
24
+
25
+ Metrics/CyclomaticComplexity:
26
+ Enabled: false
27
+
28
+ Metrics/LineLength:
29
+ Max: 200
30
+
31
+ Metrics/MethodLength:
32
+ Enabled: false
33
+
34
+ Metrics/PerceivedComplexity:
35
+ Enabled: false
36
+
37
+ Style/ConditionalAssignment:
38
+ Enabled: false
39
+
40
+ Style/Documentation:
41
+ Enabled: false
42
+
43
+ Style/FormatStringToken:
44
+ Enabled: false
45
+
46
+ Style/FrozenStringLiteralComment:
47
+ Enabled: false
48
+
49
+ Style/GuardClause:
50
+ Enabled: false
51
+
52
+ Style/IdenticalConditionalBranches:
53
+ Enabled: false
54
+
55
+ Style/InverseMethods:
56
+ Enabled: false
57
+
58
+ Style/NegatedIf:
59
+ Enabled: false
60
+
61
+ Style/SymbolArray:
62
+ Enabled: false
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,7 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2018-02-23 12:54:10 +0100 using RuboCop version 0.52.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
data/.travis.yml CHANGED
@@ -1,11 +1,7 @@
1
+ ---
2
+ sudo: false
1
3
  language: ruby
2
-
3
4
  rvm:
4
- - 2.0.0
5
- - 1.9.3
6
-
7
- gemfile:
8
- - Gemfile
9
-
10
- before_install:
11
- - gem install bundler
5
+ - 2.5
6
+ - 2.4
7
+ - 2.3
data/CHANGELOG.md ADDED
@@ -0,0 +1,78 @@
1
+ # Changelog
2
+
3
+ ## 2.0.0
4
+
5
+ IMPROVEMENTS:
6
+
7
+ - Drop Ruby 2.2 and lower support
8
+ - Better protocol compliance by returning `badauth` in response body on HTTP 401 errors
9
+ - Better code maintainability by refactorings
10
+ - Update dependencies, mainly `rack` to new major version 2
11
+ - Add Ruby 2.5 support
12
+ - Add experimental [OpenTracing](http://opentracing.io/) support with [CNCF Jaeger](https://github.com/jaegertracing/jaeger)
13
+ - Support host offlining by deleting the associated DNS records
14
+
15
+ ## 1.6.1 (October 31, 2017)
16
+
17
+ IMPROVEMENTS:
18
+
19
+ - Fix broken password check affecting all previous releases
20
+
21
+ ## 1.6.0 (December 7, 2016)
22
+
23
+ IMPROVEMENTS:
24
+
25
+ - Support providing an IPv6 address in addition to a IPv4 for the same hostname
26
+
27
+ ## 1.5.0 (November 30, 2016)
28
+
29
+ IMPROVEMENTS:
30
+
31
+ - Drop Ruby 1.8.7 support
32
+ - Pin `json` gem to allow supporting Ruby 1.9.3
33
+ - Support determining effective client IP address also from `X-Real-IP` header
34
+
35
+ ## 1.4.0 (November 27, 2016)
36
+
37
+ IMPROVEMENTS:
38
+
39
+ - Pin `rack` gem to allow supporting Ruby versions < 2.2.2
40
+ - Support IPv6 addresses
41
+
42
+ ## 1.3.0 (October 8, 2013)
43
+
44
+ IMPROVEMENTS:
45
+
46
+ - Handle `SIGTERM` \*nix signal properly and shutdown the daemon
47
+
48
+ ## 1.2.2 (June 8, 2013)
49
+
50
+ IMPROVEMENTS:
51
+
52
+ - Add proper logging to the provided init script for dyndnsd.rb
53
+
54
+ ## 1.2.1 (June 5, 2013)
55
+
56
+ IMPROVEMENTS:
57
+
58
+ - Fix bug in previous release related to metrics preventing startup
59
+
60
+ ## 1.2.0 (May 29, 2013)
61
+
62
+ IMPROVEMENTS:
63
+
64
+ - Support sending metrics to graphite via undocumented `graphite:` section in configuration file
65
+
66
+ ## 1.1.0 (April 30, 2013)
67
+
68
+ IMPROVEMENTS:
69
+
70
+ - Support dropping priviliges on startup, also affects external commands run
71
+ - Add [metriks](https://github.com/eric/metriks) support for basic metrics in the process title
72
+ - Detach from child processes running external commands to avoid zombie processes
73
+
74
+ ## 1.0.0 (April 28, 2013)
75
+
76
+ NEW FEATURES:
77
+
78
+ - Initial 1.0 release
data/README.md CHANGED
@@ -1,16 +1,18 @@
1
1
  # dyndnsd.rb
2
2
 
3
- [![Build Status](https://travis-ci.org/cmur2/dyndnsd.png)](https://travis-ci.org/cmur2/dyndnsd)
3
+ [![Build Status](https://travis-ci.org/cmur2/dyndnsd.svg?branch=master)](https://travis-ci.org/cmur2/dyndnsd) [![Dependencies](https://badges.depfu.com/badges/4f25da8493f7a29f652ac892fbf9227b/overview.svg)](https://depfu.com/github/cmur2/dyndnsd)
4
4
 
5
5
  A small, lightweight and extensible DynDNS server written with Ruby and Rack.
6
6
 
7
7
  ## Description
8
8
 
9
- dyndnsd.rb aims to implement a small [DynDNS-compliant](http://dyn.com/support/developers/api/) server in Ruby supporting IPv4 and IPv6 addresses. It has an integrated user and hostname database in it's configuration file that is used for authentication and authorization. Besides talking the DynDNS protocol it is able to invoke an so-called *updater*, a small Ruby module that takes care of supplying the current host => ip mapping to a DNS server.
9
+ dyndnsd.rb aims to implement a small [DynDNS-compliant](https://help.dyn.com/remote-access-api/) server in Ruby supporting IPv4 and IPv6 addresses. It has an integrated user and hostname database in it's configuration file that is used for authentication and authorization. Besides talking the DynDNS protocol it is able to invoke a so-called *updater*, a small Ruby module that takes care of supplying the current hostname => ip mapping to a DNS server.
10
10
 
11
- There is currently one updater shipped with dyndnsd.rb `command_with_bind_zone` that writes out a zone file in BIND syntax onto the current system and invokes a user-supplied command afterwards that is assumed to trigger the DNS server (not necessarily BIND since it's zone files are read by other DNS servers too) to reload it's zone configuration.
11
+ There is currently one updater shipped with dyndnsd.rb `command_with_bind_zone` that writes out a zone file in BIND syntax onto the current system and invokes a user-supplied command afterwards that is assumed to trigger the DNS server (not necessarily BIND since it's zone files are read by other DNS servers, too) to reload it's zone configuration.
12
12
 
13
- Because of the mechanisms used dyndnsd.rb is known to work only on \*nix systems.
13
+ Because of the mechanisms used, dyndnsd.rb is known to work only on \*nix systems.
14
+
15
+ See the [changelog](CHANGELOG.md) before upgrading. The older version 1.x of dyndnsd.rb is still available on [branch dyndnsd-1.x](https://github.com/cmur2/dyndnsd/tree/dyndnsd-1.x).
14
16
 
15
17
  ## General Usage
16
18
 
@@ -101,7 +103,7 @@ Please provide ideas if you are using dyndnsd.rb with other DNS servers :)
101
103
 
102
104
  The update URL you want to tell your clients (humans or scripts ^^) consists of the following
103
105
 
104
- http[s]://[USER]:[PASSWORD]@[DOMAIN]:[PORT]/nic/update?hostname=[HOSTNAMES]&myip=[MYIP]
106
+ http[s]://[USER]:[PASSWORD]@[DOMAIN]:[PORT]/nic/update?hostname=[HOSTNAMES]&myip=[MYIP]&myip6=[MYIP6]
105
107
 
106
108
  where:
107
109
 
@@ -110,7 +112,8 @@ where:
110
112
  * DOMAIN should match what you defined in your config.yaml as domain but may be anything else when using a webserver as proxy
111
113
  * PORT depends on your (webserver/proxy) settings
112
114
  * HOSTNAMES is a required list of comma separated FQDNs (they all have to end with your config.yaml domain) the user wants to update
113
- * MYIP is optional and the HTTP client's address will be used if missing
115
+ * MYIP is optional and the HTTP client's IP address will be used if missing
116
+ * MYIP6 is optional but if present also requires presence of MYIP
114
117
 
115
118
  ### IP address determination
116
119
 
@@ -130,6 +133,79 @@ Use a webserver as a proxy to handle SSL and/or multiple listen addresses and po
130
133
 
131
134
  The [Debian 6 init.d script](init.d/debian-6-dyndnsd) assumes that dyndnsd.rb is installed into the system ruby (no RVM support) and the config.yaml is at /opt/dyndnsd/config.yaml. Modify to your needs.
132
135
 
136
+ ### Monitoring
137
+
138
+ For monitoring dyndnsd.rb uses the [metriks](https://github.com/eric/metriks) framework and exposes several metrics like the number of unauthenticated requests, requests that did (not) update a hostname, etc. By default the most important metrics are shown in the [proctitle](https://github.com/eric/metriks#proc-title-reporter) but you can also configure a [Graphite](https://graphiteapp.org/) backend for central monitoring.
139
+
140
+ ```yaml
141
+ host: "0.0.0.0"
142
+ port: "8245" # the DynDNS.com alternative HTTP port
143
+ db: "/opt/dyndnsd/db.json"
144
+ domain: "dyn.example.org"
145
+ # configure the Graphite backend to be used instead of proctitle
146
+ graphite:
147
+ host: localhost # defaults for host and port of a carbon server
148
+ port: 2003
149
+ prefix: "my.graphite.metrics.naming.structure.dyndnsd"
150
+ # configure the updater, here we use command_with_bind_zone, params are updater-specific
151
+ updater:
152
+ name: "command_with_bind_zone"
153
+ params:
154
+ zone_file: "dyn.zone"
155
+ command: "echo 'Hello'"
156
+ ttl: "5m"
157
+ dns: "dns.example.org."
158
+ email_addr: "admin.example.org."
159
+ # user database with hostnames a user is allowed to update
160
+ users:
161
+ # 'foo' is username, 'secret' the password
162
+ foo:
163
+ password: "secret"
164
+ hosts:
165
+ - foo.example.org
166
+ - bar.example.org
167
+ test:
168
+ password: "ihavenohosts"
169
+ ```
170
+
171
+ ### Tracing (experimental)
172
+
173
+ For tracing dyndnsd.rb is instrumented using the [OpenTracing](http://opentracing.io/) framework and will emit span tracing data for the most important operations happening during the request/response cycle. Using a middleware for Rack allows handling incoming OpenTracing span information properly.
174
+ Currently only one OpenTracing-compatible tracer implementation named [CNCF Jaeger](https://github.com/jaegertracing/jaeger) can be configured to use with dyndnsd.rb.
175
+
176
+ ```yaml
177
+ host: "0.0.0.0"
178
+ port: "8245" # the DynDNS.com alternative HTTP port
179
+ db: "/opt/dyndnsd/db.json"
180
+ domain: "dyn.example.org"
181
+ # enable and configure tracing using the (currently only) tracer jaeger
182
+ tracing:
183
+ trust_incoming_span: false # default value, change to accept incoming OpenTracing spans as parents
184
+ jaeger:
185
+ host: 127.0.0.1 # defaults for host and port of local jaeger-agent
186
+ port: 6831
187
+ service_name: "my.dyndnsd.identifier"
188
+ # configure the updater, here we use command_with_bind_zone, params are updater-specific
189
+ updater:
190
+ name: "command_with_bind_zone"
191
+ params:
192
+ zone_file: "dyn.zone"
193
+ command: "echo 'Hello'"
194
+ ttl: "5m"
195
+ dns: "dns.example.org."
196
+ email_addr: "admin.example.org."
197
+ # user database with hostnames a user is allowed to update
198
+ users:
199
+ # 'foo' is username, 'secret' the password
200
+ foo:
201
+ password: "secret"
202
+ hosts:
203
+ - foo.example.org
204
+ - bar.example.org
205
+ test:
206
+ password: "ihavenohosts"
207
+ ```
208
+
133
209
  ## License
134
210
 
135
211
  dyndnsd.rb is licensed under the Apache License, Version 2.0. See LICENSE for more information.
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rspec/core/rake_task'
3
+ require 'rubocop/rake_task'
3
4
 
4
5
  RSpec::Core::RakeTask.new(:spec)
6
+ RuboCop::RakeTask.new
5
7
 
6
- task :default => :spec
8
+ task default: [:rubocop, :spec]
data/dyndnsd.gemspec CHANGED
@@ -1,31 +1,36 @@
1
1
 
2
- $:.push File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
3
3
 
4
4
  require 'dyndnsd/version'
5
5
 
6
6
  Gem::Specification.new do |s|
7
- s.name = 'dyndnsd'
7
+ s.name = 'dyndnsd'
8
8
  s.version = Dyndnsd::VERSION
9
9
  s.summary = 'dyndnsd.rb'
10
10
  s.description = 'A small, lightweight and extensible DynDNS server written with Ruby and Rack.'
11
- s.author = 'Christian Nicolai'
11
+ s.author = 'Christian Nicolai'
12
12
  s.email = 'chrnicolai@gmail.com'
13
- s.license = 'Apache License Version 2.0'
14
- s.homepage = 'https://github.com/cmur2/dyndnsd'
13
+ s.homepage = 'https://github.com/cmur2/dyndnsd'
14
+ s.license = 'Apache-2.0'
15
15
 
16
- s.files = `git ls-files`.split($/)
16
+ s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
17
17
  s.test_files = s.files.grep(%r{^(test|spec|features)/})
18
-
19
18
  s.require_paths = ['lib']
20
-
21
19
  s.executables = ['dyndnsd']
22
20
 
23
- s.add_runtime_dependency 'rack', '~> 1.6'
24
- s.add_runtime_dependency 'json', '~> 1.8'
21
+ s.required_ruby_version = '>= 2.3'
22
+
23
+ s.add_runtime_dependency 'rack', '~> 2.0'
24
+ s.add_runtime_dependency 'json'
25
25
  s.add_runtime_dependency 'metriks'
26
+ s.add_runtime_dependency 'opentracing', '~> 0.3'
27
+ s.add_runtime_dependency 'rack-tracer', '~> 0.4'
28
+ s.add_runtime_dependency 'spanmanager', '~> 0.3'
29
+ s.add_runtime_dependency 'jaeger-client', '~> 0.4'
26
30
 
27
- s.add_development_dependency 'bundler', '~> 1.3'
31
+ s.add_development_dependency 'bundler'
28
32
  s.add_development_dependency 'rake'
29
33
  s.add_development_dependency 'rspec'
30
34
  s.add_development_dependency 'rack-test'
35
+ s.add_development_dependency 'rubocop', '~> 0.52.1'
31
36
  end
@@ -4,27 +4,29 @@ require 'forwardable'
4
4
  module Dyndnsd
5
5
  class Database
6
6
  extend Forwardable
7
-
7
+
8
8
  def_delegators :@db, :[], :[]=, :each, :has_key?
9
-
9
+
10
10
  def initialize(db_file)
11
11
  @db_file = db_file
12
12
  end
13
-
13
+
14
14
  def load
15
15
  if File.file?(@db_file)
16
- @db = JSON.load(File.open(@db_file, 'r') { |f| f.read })
16
+ @db = JSON.parse(File.open(@db_file, 'r', &:read))
17
17
  else
18
18
  @db = {}
19
19
  end
20
20
  @db_hash = @db.hash
21
21
  end
22
-
22
+
23
23
  def save
24
- File.open(@db_file, 'w') { |f| JSON.dump(@db, f) }
25
- @db_hash = @db.hash
24
+ Helper.span('database_save') do |_span|
25
+ File.open(@db_file, 'w') { |f| JSON.dump(@db, f) }
26
+ @db_hash = @db.hash
27
+ end
26
28
  end
27
-
29
+
28
30
  def changed?
29
31
  @db_hash != @db.hash
30
32
  end
@@ -10,25 +10,25 @@ module Dyndnsd
10
10
  @additional_zone_content = config['additional_zone_content']
11
11
  end
12
12
 
13
- def generate(zone)
13
+ def generate(db)
14
14
  out = []
15
15
  out << "$TTL #{@ttl}"
16
16
  out << "$ORIGIN #{@domain}."
17
- out << ""
18
- out << "@ IN SOA #{@dns} #{@email_addr} ( #{zone['serial']} 3h 5m 1w 1h )"
17
+ out << ''
18
+ out << "@ IN SOA #{@dns} #{@email_addr} ( #{db['serial']} 3h 5m 1w 1h )"
19
19
  out << "@ IN NS #{@dns}"
20
- out << ""
21
- zone['hosts'].each do |hostname,ips|
22
- (ips.is_a?(Array) ? ips : [ips]).each do |ip|
20
+ out << ''
21
+ db['hosts'].each do |hostname, ips|
22
+ ips.each do |ip|
23
23
  ip = IPAddr.new(ip).native
24
- type = ip.ipv6? ? "AAAA" : "A"
24
+ type = ip.ipv6? ? 'AAAA' : 'A'
25
25
  name = hostname.chomp('.' + @domain)
26
26
  out << "#{name} IN #{type} #{ip}"
27
27
  end
28
28
  end
29
- out << ""
29
+ out << ''
30
30
  out << @additional_zone_content
31
- out << ""
31
+ out << ''
32
32
  out.join("\n")
33
33
  end
34
34
  end
@@ -0,0 +1,41 @@
1
+
2
+ require 'ipaddr'
3
+
4
+ module Dyndnsd
5
+ class Helper
6
+ def self.fqdn_valid?(hostname, domain)
7
+ return false if hostname.length < domain.length + 2
8
+ return false if !hostname.end_with?(domain)
9
+ name = hostname.chomp(domain)
10
+ return false if !name.match(/^[a-zA-Z0-9_-]+\.$/)
11
+ true
12
+ end
13
+
14
+ def self.ip_valid?(ip)
15
+ IPAddr.new(ip)
16
+ return true
17
+ rescue ArgumentError
18
+ return false
19
+ end
20
+
21
+ def self.user_allowed?(username, password, users)
22
+ (users.key? username) && (users[username]['password'] == password)
23
+ end
24
+
25
+ def self.changed?(hostname, myips, hosts)
26
+ # myips order is always deterministic
27
+ ((!hosts.include? hostname) || (hosts[hostname] != myips)) && !myips.empty?
28
+ end
29
+
30
+ def self.span(operation, &block)
31
+ span = OpenTracing.start_span(operation)
32
+ span.set_tag('component', 'dyndnsd')
33
+ span.set_tag('span.kind', 'server')
34
+ begin
35
+ block.call(span)
36
+ ensure
37
+ span.finish
38
+ end
39
+ end
40
+ end
41
+ end
@@ -2,19 +2,52 @@
2
2
  module Dyndnsd
3
3
  module Responder
4
4
  class DynDNSStyle
5
- def response_for_error(state)
6
- # general http errors
7
- return [405, {"Content-Type" => "text/plain"}, ["Method Not Allowed"]] if state == :method_forbidden
8
- return [404, {"Content-Type" => "text/plain"}, ["Not Found"]] if state == :not_found
9
- # specific errors
10
- return [200, {"Content-Type" => "text/plain"}, ["notfqdn"]] if state == :hostname_missing
11
- return [200, {"Content-Type" => "text/plain"}, ["nohost"]] if state == :host_forbidden
12
- return [200, {"Content-Type" => "text/plain"}, ["notfqdn"]] if state == :hostname_malformed
5
+ def initialize(app)
6
+ @app = app
13
7
  end
14
8
 
15
- def response_for_changes(states, ip)
16
- body = states.map { |state| "#{state} #{ip.is_a?(Array) ? ip.join(' ') : ip}" }.join("\n")
17
- return [200, {"Content-Type" => "text/plain"}, [body]]
9
+ def call(env)
10
+ @app.call(env).tap do |status_code, headers, body|
11
+ if headers.key?('X-DynDNS-Response')
12
+ return decorate_dyndnsd_response(status_code, headers, body)
13
+ else
14
+ return decorate_other_response(status_code, headers, body)
15
+ end
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def decorate_dyndnsd_response(status_code, headers, body)
22
+ if status_code == 200
23
+ [200, {'Content-Type' => 'text/plain'}, [get_success_body(body[0], body[1])]]
24
+ elsif status_code == 422
25
+ error_response_map[headers['X-DynDNS-Response']]
26
+ end
27
+ end
28
+
29
+ def decorate_other_response(status_code, headers, _body)
30
+ if status_code == 400
31
+ [status_code, headers, ['Bad Request']]
32
+ elsif status_code == 401
33
+ [status_code, headers, ['badauth']]
34
+ end
35
+ end
36
+
37
+ def get_success_body(changes, myips)
38
+ changes.map { |change| "#{change} #{myips.join(' ')}" }.join("\n")
39
+ end
40
+
41
+ def error_response_map
42
+ {
43
+ # general http errors
44
+ 'method_forbidden' => [405, {'Content-Type' => 'text/plain'}, ['Method Not Allowed']],
45
+ 'not_found' => [404, {'Content-Type' => 'text/plain'}, ['Not Found']],
46
+ # specific errors
47
+ 'hostname_missing' => [200, {'Content-Type' => 'text/plain'}, ['notfqdn']],
48
+ 'hostname_malformed' => [200, {'Content-Type' => 'text/plain'}, ['notfqdn']],
49
+ 'host_forbidden' => [200, {'Content-Type' => 'text/plain'}, ['nohost']]
50
+ }
18
51
  end
19
52
  end
20
53
  end
@@ -2,19 +2,52 @@
2
2
  module Dyndnsd
3
3
  module Responder
4
4
  class RestStyle
5
- def response_for_error(state)
6
- # general http errors
7
- return [405, {"Content-Type" => "text/plain"}, ["Method Not Allowed"]] if state == :method_forbidden
8
- return [404, {"Content-Type" => "text/plain"}, ["Not Found"]] if state == :not_found
9
- # specific errors
10
- return [422, {"Content-Type" => "text/plain"}, ["Hostname missing"]] if state == :hostname_missing
11
- return [403, {"Content-Type" => "text/plain"}, ["Forbidden"]] if state == :host_forbidden
12
- return [422, {"Content-Type" => "text/plain"}, ["Hostname malformed"]] if state == :hostname_malformed
5
+ def initialize(app)
6
+ @app = app
13
7
  end
14
8
 
15
- def response_for_changes(states, ip)
16
- body = states.map { |state| state == :good ? "Changed to #{ip.is_a?(Array) ? ip.join(' ') : ip}" : "No change needed for #{ip.is_a?(Array) ? ip.join(' ') : ip}" }.join("\n")
17
- return [200, {"Content-Type" => "text/plain"}, [body]]
9
+ def call(env)
10
+ @app.call(env).tap do |status_code, headers, body|
11
+ if headers.key?('X-DynDNS-Response')
12
+ return decorate_dyndnsd_response(status_code, headers, body)
13
+ else
14
+ return decorate_other_response(status_code, headers, body)
15
+ end
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def decorate_dyndnsd_response(status_code, headers, body)
22
+ if status_code == 200
23
+ [200, {'Content-Type' => 'text/plain'}, [get_success_body(body[0], body[1])]]
24
+ elsif status_code == 422
25
+ error_response_map[headers['X-DynDNS-Response']]
26
+ end
27
+ end
28
+
29
+ def decorate_other_response(status_code, headers, _body)
30
+ if status_code == 400
31
+ [status_code, headers, ['Bad Request']]
32
+ elsif status_code == 401
33
+ [status_code, headers, ['Unauthorized']]
34
+ end
35
+ end
36
+
37
+ def get_success_body(changes, myips)
38
+ changes.map { |change| change == :good ? "Changed to #{myips.join(' ')}" : "No change needed for #{myips.join(' ')}" }.join("\n")
39
+ end
40
+
41
+ def error_response_map
42
+ {
43
+ # general http errors
44
+ 'method_forbidden' => [405, {'Content-Type' => 'text/plain'}, ['Method Not Allowed']],
45
+ 'not_found' => [404, {'Content-Type' => 'text/plain'}, ['Not Found']],
46
+ # specific errors
47
+ 'hostname_missing' => [422, {'Content-Type' => 'text/plain'}, ['Hostname missing']],
48
+ 'hostname_malformed' => [422, {'Content-Type' => 'text/plain'}, ['Hostname malformed']],
49
+ 'host_forbidden' => [403, {'Content-Type' => 'text/plain'}, ['Forbidden']]
50
+ }
18
51
  end
19
52
  end
20
53
  end
@@ -7,16 +7,21 @@ module Dyndnsd
7
7
  @command = config['command']
8
8
  @generator = Generator::Bind.new(domain, config)
9
9
  end
10
-
10
+
11
11
  def update(zone)
12
- # write zone file in bind syntax
13
- File.open(@zone_file, 'w') { |f| f.write(@generator.generate(zone)) }
14
- # call user-defined command
15
- pid = fork do
16
- exec @command
12
+ Helper.span('updater_update') do |span|
13
+ span.set_tag('dyndnsd.updater.name', self.class.name.split('::').last)
14
+
15
+ # write zone file in bind syntax
16
+ File.open(@zone_file, 'w') { |f| f.write(@generator.generate(zone)) }
17
+ # call user-defined command
18
+ pid = fork do
19
+ exec @command
20
+ end
21
+
22
+ # detach so children don't become zombies
23
+ Process.detach(pid)
17
24
  end
18
- # detach so children don't become zombies
19
- Process.detach(pid)
20
25
  end
21
26
  end
22
27
  end
@@ -1,4 +1,4 @@
1
1
 
2
2
  module Dyndnsd
3
- VERSION = "1.6.1"
3
+ VERSION = '2.0.0.rc1'.freeze
4
4
  end
data/lib/dyndnsd.rb CHANGED
@@ -8,12 +8,16 @@ require 'yaml'
8
8
  require 'rack'
9
9
  require 'metriks'
10
10
  require 'metriks/reporter/graphite'
11
+ require 'opentracing'
12
+ require 'rack/tracer'
13
+ require 'spanmanager'
11
14
 
12
15
  require 'dyndnsd/generator/bind'
13
16
  require 'dyndnsd/updater/command_with_bind_zone'
14
17
  require 'dyndnsd/responder/dyndns_style'
15
18
  require 'dyndnsd/responder/rest_style'
16
19
  require 'dyndnsd/database'
20
+ require 'dyndnsd/helper'
17
21
  require 'dyndnsd/version'
18
22
 
19
23
  module Dyndnsd
@@ -26,154 +30,203 @@ module Dyndnsd
26
30
  end
27
31
 
28
32
  class LogFormatter
29
- def call(lvl, time, progname, msg)
30
- "[%s] %-5s %s\n" % [Time.now.strftime('%Y-%m-%d %H:%M:%S'), lvl, msg.to_s]
33
+ def call(lvl, _time, _progname, msg)
34
+ format("[%s] %-5s %s\n", Time.now.strftime('%Y-%m-%d %H:%M:%S'), lvl, msg.to_s)
31
35
  end
32
36
  end
33
37
 
34
38
  class Daemon
35
- def initialize(config, db, updater, responder)
39
+ def initialize(config, db, updater)
36
40
  @users = config['users']
37
41
  @domain = config['domain']
38
42
  @db = db
39
43
  @updater = updater
40
- @responder = responder
41
44
 
42
45
  @db.load
43
46
  @db['serial'] ||= 1
44
47
  @db['hosts'] ||= {}
45
- (@db.save; update) if @db.changed?
48
+ if @db.changed?
49
+ @db.save
50
+ @updater.update(@db)
51
+ end
46
52
  end
47
53
 
48
- def update
49
- @updater.update(@db)
50
- end
54
+ def authorized?(username, password)
55
+ Helper.span('check_authorized') do |span|
56
+ span.set_tag('dyndnsd.user', username)
51
57
 
52
- def is_fqdn_valid?(hostname)
53
- return false if hostname.length < @domain.length + 2
54
- return false if not hostname.end_with?(@domain)
55
- name = hostname.chomp(@domain)
56
- return false if not name.match(/^[a-zA-Z0-9_-]+\.$/)
57
- true
58
+ allow = Helper.user_allowed?(username, password, @users)
59
+ if !allow
60
+ Dyndnsd.logger.warn "Login failed for #{username}"
61
+ Metriks.meter('requests.auth_failed').mark
62
+ end
63
+ allow
64
+ end
58
65
  end
59
66
 
60
67
  def call(env)
61
- return @responder.response_for_error(:method_forbidden) if env["REQUEST_METHOD"] != "GET"
62
- return @responder.response_for_error(:not_found) if env["PATH_INFO"] != "/nic/update"
68
+ return [422, {'X-DynDNS-Response' => 'method_forbidden'}, []] if env['REQUEST_METHOD'] != 'GET'
69
+ return [422, {'X-DynDNS-Response' => 'not_found'}, []] if env['PATH_INFO'] != '/nic/update'
63
70
 
64
- params = Rack::Utils.parse_query(env["QUERY_STRING"])
71
+ handle_dyndns_request(env)
72
+ end
65
73
 
66
- return @responder.response_for_error(:hostname_missing) if not params["hostname"]
74
+ def self.run!
75
+ if ARGV.length != 1
76
+ puts 'Usage: dyndnsd config_file'
77
+ exit 1
78
+ end
67
79
 
68
- hostnames = params["hostname"].split(',')
80
+ config_file = ARGV[0]
69
81
 
70
- # Check if hostname match rules
71
- hostnames.each do |hostname|
72
- return @responder.response_for_error(:hostname_malformed) if not is_fqdn_valid?(hostname)
82
+ if !File.file?(config_file)
83
+ puts 'Config file not found!'
84
+ exit 1
73
85
  end
74
86
 
75
- user = env["REMOTE_USER"]
87
+ puts "DynDNSd version #{Dyndnsd::VERSION}"
88
+ puts "Using config file #{config_file}"
76
89
 
77
- hostnames.each do |hostname|
78
- return @responder.response_for_error(:host_forbidden) if not @users[user]['hosts'].include? hostname
79
- end
90
+ config = YAML.safe_load(File.open(config_file, 'r', &:read))
80
91
 
81
- myip = nil
82
-
83
- if params.has_key?("myip6")
84
- # require presence of myip parameter as valid IPAddr (v4) and valid myip6
85
- return @responder.response_for_error(:host_forbidden) if not params["myip"]
86
- begin
87
- IPAddr.new(params["myip"], Socket::AF_INET)
88
- IPAddr.new(params["myip6"], Socket::AF_INET6)
89
-
90
- # myip will be an array
91
- myip = [params["myip"], params["myip6"]]
92
- rescue ArgumentError
93
- return @responder.response_for_error(:host_forbidden)
94
- end
95
- else
96
- # fallback value, always present
97
- myip = env["REMOTE_ADDR"]
98
-
99
- # check whether X-Real-IP header has valid IPAddr
100
- if env.has_key?("HTTP_X_REAL_IP")
101
- begin
102
- IPAddr.new(env["HTTP_X_REAL_IP"])
103
- myip = env["HTTP_X_REAL_IP"]
104
- rescue ArgumentError
105
- end
106
- end
92
+ setup_logger(config)
107
93
 
108
- # check whether myip parameter has valid IPAddr
109
- if params.has_key?("myip")
110
- begin
111
- IPAddr.new(params["myip"])
112
- myip = params["myip"]
113
- rescue ArgumentError
114
- end
115
- end
94
+ Dyndnsd.logger.info 'Starting...'
95
+
96
+ # drop priviliges as soon as possible
97
+ # NOTE: first change group than user
98
+ Process::Sys.setgid(Etc.getgrnam(config['group']).gid) if config['group']
99
+ Process::Sys.setuid(Etc.getpwnam(config['user']).uid) if config['user']
100
+
101
+ setup_traps
102
+
103
+ setup_monitoring(config)
104
+
105
+ setup_tracing(config)
106
+
107
+ setup_rack(config)
108
+ end
109
+
110
+ private
111
+
112
+ def extract_v4_and_v6_address(params)
113
+ return [] if !(params['myip'])
114
+ begin
115
+ IPAddr.new(params['myip'], Socket::AF_INET)
116
+ IPAddr.new(params['myip6'], Socket::AF_INET6)
117
+ [params['myip'], params['myip6']]
118
+ rescue ArgumentError
119
+ []
116
120
  end
121
+ end
117
122
 
118
- Metriks.meter('requests.valid').mark
119
- Dyndnsd.logger.info "Request to update #{hostnames} to #{myip} for user #{user}"
123
+ def extract_myips(env, params)
124
+ # require presence of myip parameter as valid IPAddr (v4) and valid myip6
125
+ return extract_v4_and_v6_address(params) if params.key?('myip6')
126
+
127
+ # check whether myip parameter has valid IPAddr
128
+ return [params['myip']] if params.key?('myip') && Helper.ip_valid?(params['myip'])
129
+
130
+ # check whether X-Real-IP header has valid IPAddr
131
+ return [env['HTTP_X_REAL_IP']] if env.key?('HTTP_X_REAL_IP') && Helper.ip_valid?(env['HTTP_X_REAL_IP'])
132
+
133
+ # fallback value, always present
134
+ [env['REMOTE_ADDR']]
135
+ end
120
136
 
137
+ def process_changes(hostnames, myips)
121
138
  changes = []
122
- hostnames.each do |hostname|
123
- if (not @db['hosts'].include? hostname) or (@db['hosts'][hostname] != myip)
124
- changes << :good
125
- @db['hosts'][hostname] = myip
126
- Metriks.meter('requests.good').mark
127
- else
128
- changes << :nochg
129
- Metriks.meter('requests.nochg').mark
139
+ Helper.span('process_changes') do |span|
140
+ span.set_tag('dyndnsd.hostnames', hostnames.join(','))
141
+
142
+ hostnames.each do |hostname|
143
+ # myips order is always deterministic
144
+ if myips.empty? && @db['hosts'].include?(hostname)
145
+ @db['hosts'].delete(hostname)
146
+ changes << :good
147
+ Metriks.meter('requests.good').mark
148
+ elsif Helper.changed?(hostname, myips, @db['hosts'])
149
+ @db['hosts'][hostname] = myips
150
+ changes << :good
151
+ Metriks.meter('requests.good').mark
152
+ else
153
+ changes << :nochg
154
+ Metriks.meter('requests.nochg').mark
155
+ end
130
156
  end
131
157
  end
158
+ changes
159
+ end
132
160
 
133
- if @db.changed?
134
- @db['serial'] += 1
135
- Dyndnsd.logger.info "Committing update ##{@db['serial']}"
136
- @db.save
137
- update
138
- Metriks.meter('updates.committed').mark
139
- end
140
-
141
- @responder.response_for_changes(changes, myip)
161
+ def update_db
162
+ @db['serial'] += 1
163
+ Dyndnsd.logger.info "Committing update ##{@db['serial']}"
164
+ @db.save
165
+ @updater.update(@db)
166
+ Metriks.meter('updates.committed').mark
142
167
  end
143
168
 
144
- def self.run!
145
- if ARGV.length != 1
146
- puts "Usage: dyndnsd config_file"
147
- exit 1
148
- end
169
+ def handle_dyndns_request(env)
170
+ params = Rack::Utils.parse_query(env['QUERY_STRING'])
149
171
 
150
- config_file = ARGV[0]
172
+ # require hostname parameter
173
+ return [422, {'X-DynDNS-Response' => 'hostname_missing'}, []] if !(params['hostname'])
151
174
 
152
- if not File.file?(config_file)
153
- puts "Config file not found!"
154
- exit 1
175
+ hostnames = params['hostname'].split(',')
176
+
177
+ # check for invalid hostnames
178
+ invalid_hostnames = hostnames.select { |h| !Helper.fqdn_valid?(h, @domain) }
179
+ return [422, {'X-DynDNS-Response' => 'hostname_malformed'}, []] if invalid_hostnames.any?
180
+
181
+ user = env['REMOTE_USER']
182
+
183
+ # check for hostnames that the user does not own
184
+ forbidden_hostnames = hostnames - @users[user]['hosts']
185
+ return [422, {'X-DynDNS-Response' => 'host_forbidden'}, []] if forbidden_hostnames.any?
186
+
187
+ if params['offline'] == 'YES'
188
+ myips = []
189
+ else
190
+ myips = extract_myips(env, params)
191
+ # require at least one IP to update
192
+ return [422, {'X-DynDNS-Response' => 'host_forbidden'}, []] if myips.empty?
155
193
  end
156
194
 
157
- puts "DynDNSd version #{Dyndnsd::VERSION}"
158
- puts "Using config file #{config_file}"
195
+ Metriks.meter('requests.valid').mark
196
+ Dyndnsd.logger.info "Request to update #{hostnames} to #{myips} for user #{user}"
197
+
198
+ changes = process_changes(hostnames, myips)
159
199
 
160
- config = YAML::load(File.open(config_file, 'r') { |f| f.read })
200
+ update_db if @db.changed?
161
201
 
202
+ [200, {'X-DynDNS-Response' => 'success'}, [changes, myips]]
203
+ end
204
+
205
+ # SETUP
206
+
207
+ private_class_method def self.setup_logger(config)
162
208
  if config['logfile']
163
209
  Dyndnsd.logger = Logger.new(config['logfile'])
164
210
  else
165
211
  Dyndnsd.logger = Logger.new(STDOUT)
166
212
  end
167
213
 
168
- Dyndnsd.logger.progname = "dyndnsd"
214
+ Dyndnsd.logger.progname = 'dyndnsd'
169
215
  Dyndnsd.logger.formatter = LogFormatter.new
216
+ end
170
217
 
171
- Dyndnsd.logger.info "Starting..."
172
-
173
- # drop privs (first change group than user)
174
- Process::Sys.setgid(Etc.getgrnam(config['group']).gid) if config['group']
175
- Process::Sys.setuid(Etc.getpwnam(config['user']).uid) if config['user']
218
+ private_class_method def self.setup_traps
219
+ Signal.trap('INT') do
220
+ Dyndnsd.logger.info 'Quitting...'
221
+ Rack::Handler::WEBrick.shutdown
222
+ end
223
+ Signal.trap('TERM') do
224
+ Dyndnsd.logger.info 'Quitting...'
225
+ Rack::Handler::WEBrick.shutdown
226
+ end
227
+ end
176
228
 
229
+ private_class_method def self.setup_monitoring(config)
177
230
  # configure metriks
178
231
  if config['graphite']
179
232
  host = config['graphite']['host'] || 'localhost'
@@ -192,33 +245,43 @@ module Dyndnsd
192
245
  end
193
246
  reporter.start
194
247
  end
248
+ end
249
+
250
+ private_class_method def self.setup_tracing(config)
251
+ # configure OpenTracing
252
+ if config.dig('tracing', 'jaeger')
253
+ require 'jaeger/client'
254
+
255
+ host = config['tracing']['jaeger']['host'] || '127.0.0.1'
256
+ port = config['tracing']['jaeger']['port'] || 6831
257
+ service_name = config['tracing']['jaeger']['service_name'] || 'dyndnsd'
258
+ OpenTracing.global_tracer = Jaeger::Client.build(
259
+ host: host, port: port, service_name: service_name, flush_interval: 1
260
+ )
261
+ end
262
+ # always use SpanManager
263
+ OpenTracing.global_tracer = SpanManager::Tracer.new(OpenTracing.global_tracer)
264
+ end
195
265
 
266
+ private_class_method def self.setup_rack(config)
196
267
  # configure daemon
197
268
  db = Database.new(config['db'])
198
269
  updater = Updater::CommandWithBindZone.new(config['domain'], config['updater']['params']) if config['updater']['name'] == 'command_with_bind_zone'
199
- responder = Responder::DynDNSStyle.new
270
+ daemon = Daemon.new(config, db, updater)
200
271
 
201
272
  # configure rack
202
- app = Daemon.new(config, db, updater, responder)
203
- app = Rack::Auth::Basic.new(app, "DynDNS") do |user,pass|
204
- allow = ((config['users'].has_key? user) and (config['users'][user]['password'] == pass))
205
- if not allow
206
- Dyndnsd.logger.warn "Login failed for #{user}"
207
- Metriks.meter('requests.auth_failed').mark
208
- end
209
- allow
210
- end
273
+ app = Rack::Auth::Basic.new(daemon, 'DynDNS', &daemon.method(:authorized?))
211
274
 
212
- Signal.trap('INT') do
213
- Dyndnsd.logger.info "Quitting..."
214
- Rack::Handler::WEBrick.shutdown
215
- end
216
- Signal.trap('TERM') do
217
- Dyndnsd.logger.info "Quitting..."
218
- Rack::Handler::WEBrick.shutdown
275
+ if config['responder'] == 'RestStyle'
276
+ app = Responder::RestStyle.new(app)
277
+ else
278
+ app = Responder::DynDNSStyle.new(app)
219
279
  end
220
280
 
221
- Rack::Handler::WEBrick.run app, :Host => config['host'], :Port => config['port']
281
+ trust_incoming_span = config.dig('tracing', 'trust_incoming_span') || false
282
+ app = Rack::Tracer.new(app, trust_incoming_span: trust_incoming_span)
283
+
284
+ Rack::Handler::WEBrick.run app, Host: config['host'], Port: config['port']
222
285
  end
223
286
  end
224
287
  end
data/spec/daemon_spec.rb CHANGED
@@ -18,20 +18,25 @@ describe Dyndnsd::Daemon do
18
18
  }
19
19
  db = Dyndnsd::DummyDatabase.new({})
20
20
  updater = Dyndnsd::Updater::Dummy.new
21
- responder = Dyndnsd::Responder::DynDNSStyle.new
22
- app = Dyndnsd::Daemon.new(config, db, updater, responder)
21
+ daemon = Dyndnsd::Daemon.new(config, db, updater)
23
22
 
24
- Rack::Auth::Basic.new(app, "DynDNS") do |user,pass|
25
- (config['users'].has_key? user) and (config['users'][user]['password'] == pass)
26
- end
23
+ app = Rack::Auth::Basic.new(daemon, 'DynDNS', &daemon.method(:authorized?))
24
+
25
+ app = Dyndnsd::Responder::DynDNSStyle.new(app)
26
+
27
+ Rack::Tracer.new(app, trust_incoming_span: false)
27
28
  end
28
29
 
29
30
  it 'requires authentication' do
30
31
  get '/'
31
32
  expect(last_response.status).to eq(401)
33
+ expect(last_response.body).to eq('badauth')
34
+ end
32
35
 
33
- pending 'Need to find a way to add custom body on 401 responses'
34
- expect(last_response).not_to be_ok
36
+ it 'requires configured correct credentials' do
37
+ authorize 'test', 'wrongsecret'
38
+ get '/'
39
+ expect(last_response.status).to eq(401)
35
40
  expect(last_response.body).to eq('badauth')
36
41
  end
37
42
 
@@ -96,6 +101,7 @@ describe Dyndnsd::Daemon do
96
101
 
97
102
  it 'rejects request if user does not own one hostname' do
98
103
  authorize 'test', 'secret'
104
+
99
105
  get '/nic/update?hostname=notmyhost.example.org'
100
106
  expect(last_response).to be_ok
101
107
  expect(last_response.body).to eq('nohost')
@@ -161,6 +167,34 @@ describe Dyndnsd::Daemon do
161
167
  expect(last_response.body).to eq("nochg 2001:db8::1\ngood 2001:db8::1")
162
168
  end
163
169
 
170
+ it 'offlines a host' do
171
+ authorize 'test', 'secret'
172
+
173
+ get '/nic/update?hostname=foo.example.org&myip=1.2.3.4'
174
+ expect(last_response).to be_ok
175
+ expect(last_response.body).to eq('good 1.2.3.4')
176
+
177
+ get '/nic/update?hostname=foo.example.org&offline=YES'
178
+ expect(last_response).to be_ok
179
+ expect(last_response.body).to eq('good ')
180
+
181
+ get '/nic/update?hostname=foo.example.org&offline=YES'
182
+ expect(last_response).to be_ok
183
+ expect(last_response.body).to eq('nochg ')
184
+
185
+ get '/nic/update?hostname=foo.example.org&myip=1.2.3.4'
186
+ expect(last_response).to be_ok
187
+ expect(last_response.body).to eq('good 1.2.3.4')
188
+
189
+ get '/nic/update?hostname=foo.example.org&myip=1.2.3.4&offline=YES'
190
+ expect(last_response).to be_ok
191
+ expect(last_response.body).to eq('good ')
192
+
193
+ get '/nic/update?hostname=foo.example.org&myip=1.2.3.4&offline=YES'
194
+ expect(last_response).to be_ok
195
+ expect(last_response.body).to eq('nochg ')
196
+ end
197
+
164
198
  it 'uses clients remote IP address if myip not specified' do
165
199
  authorize 'test', 'secret'
166
200
  get '/nic/update?hostname=foo.example.org'
@@ -185,7 +219,7 @@ describe Dyndnsd::Daemon do
185
219
 
186
220
  get '/nic/update?hostname=foo.example.org&myip=1.2.3.4&myip6=2001:db8::1'
187
221
  expect(last_response).to be_ok
188
- expect(last_response.body).to eq("good 1.2.3.4 2001:db8::1")
222
+ expect(last_response.body).to eq('good 1.2.3.4 2001:db8::1')
189
223
 
190
224
  get '/nic/update?hostname=foo.example.org&myip=BROKENIP&myip6=2001:db8::1'
191
225
  expect(last_response).to be_ok
@@ -4,7 +4,7 @@ require 'forwardable'
4
4
  module Dyndnsd
5
5
  class DummyDatabase
6
6
  extend Forwardable
7
-
7
+
8
8
  def_delegators :@db, :[], :[]=, :each, :has_key?
9
9
 
10
10
  def initialize(db_init)
@@ -25,5 +25,3 @@ module Dyndnsd
25
25
  end
26
26
  end
27
27
  end
28
-
29
-
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dyndnsd
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.1
4
+ version: 2.0.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christian Nicolai
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-31 00:00:00.000000000 Z
11
+ date: 2018-03-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.6'
19
+ version: '2.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.6'
26
+ version: '2.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: json
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '1.8'
33
+ version: '0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '1.8'
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: metriks
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -53,19 +53,75 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: bundler
56
+ name: opentracing
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '1.3'
62
- type: :development
61
+ version: '0.3'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rack-tracer
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.4'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.4'
83
+ - !ruby/object:Gem::Dependency
84
+ name: spanmanager
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.3'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.3'
97
+ - !ruby/object:Gem::Dependency
98
+ name: jaeger-client
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.4'
104
+ type: :runtime
63
105
  prerelease: false
64
106
  version_requirements: !ruby/object:Gem::Requirement
65
107
  requirements:
66
108
  - - "~>"
67
109
  - !ruby/object:Gem::Version
68
- version: '1.3'
110
+ version: '0.4'
111
+ - !ruby/object:Gem::Dependency
112
+ name: bundler
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
69
125
  - !ruby/object:Gem::Dependency
70
126
  name: rake
71
127
  requirement: !ruby/object:Gem::Requirement
@@ -108,6 +164,20 @@ dependencies:
108
164
  - - ">="
109
165
  - !ruby/object:Gem::Version
110
166
  version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: rubocop
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: 0.52.1
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: 0.52.1
111
181
  description: A small, lightweight and extensible DynDNS server written with Ruby and
112
182
  Rack.
113
183
  email: chrnicolai@gmail.com
@@ -117,7 +187,10 @@ extensions: []
117
187
  extra_rdoc_files: []
118
188
  files:
119
189
  - ".gitignore"
190
+ - ".rubocop.yml"
191
+ - ".rubocop_todo.yml"
120
192
  - ".travis.yml"
193
+ - CHANGELOG.md
121
194
  - Gemfile
122
195
  - LICENSE
123
196
  - README.md
@@ -128,6 +201,7 @@ files:
128
201
  - lib/dyndnsd.rb
129
202
  - lib/dyndnsd/database.rb
130
203
  - lib/dyndnsd/generator/bind.rb
204
+ - lib/dyndnsd/helper.rb
131
205
  - lib/dyndnsd/responder/dyndns_style.rb
132
206
  - lib/dyndnsd/responder/rest_style.rb
133
207
  - lib/dyndnsd/updater/command_with_bind_zone.rb
@@ -138,7 +212,7 @@ files:
138
212
  - spec/support/dummy_updater.rb
139
213
  homepage: https://github.com/cmur2/dyndnsd
140
214
  licenses:
141
- - Apache License Version 2.0
215
+ - Apache-2.0
142
216
  metadata: {}
143
217
  post_install_message:
144
218
  rdoc_options: []
@@ -148,15 +222,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
148
222
  requirements:
149
223
  - - ">="
150
224
  - !ruby/object:Gem::Version
151
- version: '0'
225
+ version: '2.3'
152
226
  required_rubygems_version: !ruby/object:Gem::Requirement
153
227
  requirements:
154
- - - ">="
228
+ - - ">"
155
229
  - !ruby/object:Gem::Version
156
- version: '0'
230
+ version: 1.3.1
157
231
  requirements: []
158
232
  rubyforge_project:
159
- rubygems_version: 2.4.8
233
+ rubygems_version: 2.7.5
160
234
  signing_key:
161
235
  specification_version: 4
162
236
  summary: dyndnsd.rb