1
0
mirror of https://github.com/cmur2/dyndnsd.git synced 2025-08-08 08:33:56 +02:00

Compare commits

...

47 Commits

Author SHA1 Message Date
cn
9dfafd7e8d release: 2.1.1 2020-03-01 21:55:35 +01:00
cn
af5e4ca3e0 dyndnsd: handle potential nil cases detected by sorbet
- including review suggestions from @jgraichen
2020-03-01 21:51:28 +01:00
cn
0317189057 release: 2.1.0 2020-03-01 11:43:39 +01:00
cn
73dbf2a5fa gem: add solargraph support 2020-02-29 20:21:50 +01:00
cn
6d0457d70c gems: specify bundler-audit version 2020-02-29 00:32:21 +01:00
cn
9ab080072f updater: rename parameter to match expected type 2020-02-28 16:01:44 +01:00
cn
fdcd6e8da4 gem: add Ruby 2.7 support 2020-02-28 13:30:11 +01:00
cn
31be09c1c2 travis: fix build config validation problems
- https://docs.travis-ci.com/user/reference/overview/#deprecated-virtualization-environments
2020-02-28 13:13:02 +01:00
cn
3406e22588 gem: enable new rubocop rules, cleanup 2020-02-26 11:33:44 +01:00
depfu[bot]
a42a864f56 gems: update rubocop to version 0.80.0
Update rubocop to version 0.80.0 (#44)
2020-02-19 15:09:25 +01:00
depfu[bot]
07fd8681e4 gems: update rubocop to version 0.79.0
Update rubocop to version 0.79.0 (#43)
2020-01-07 19:32:10 +01:00
depfu[bot]
6831744e7b gems: update rubocop to version 0.78.0
Update rubocop to version 0.78.0 (#42)
2019-12-19 23:43:30 +01:00
depfu[bot]
7d49b861fc gems: update rubocop to version 0.77.0
Update rubocop to version 0.77.0 (#41)
2019-11-29 09:21:46 +01:00
depfu[bot]
2ad816b866 gems: update rubocop to version 0.76.0
Update rubocop to version 0.76.0 (#40)
2019-10-29 20:28:30 +01:00
depfu[bot]
f4f10c94c4 gems: update rubocop to version 0.75.0
Update rubocop to version 0.75.0 (#39)
2019-10-02 08:58:33 +02:00
depfu[bot]
bb7302407a gems: update rubocop to version 0.74.0
Update rubocop to version 0.74.0 (#37)
2019-08-02 09:14:36 +02:00
depfu[bot]
9f132b4a7d gems: update rubocop to version 0.73.0
Update rubocop to version 0.73.0 (#36)
2019-07-17 12:53:49 +02:00
depfu[bot]
32d6d01d9d gems: update rubocop to version 0.72.0
Update rubocop to version 0.72.0 (#35)
2019-06-26 18:10:54 +02:00
depfu[bot]
9d80220d24 gems: update rubocop to version 0.71.0
Update rubocop to version 0.71.0 (#34)
2019-06-01 10:17:52 +02:00
depfu[bot]
b00aa0e464 gems: update rubocop to version 0.70.0
Update rubocop to version 0.70.0 (#33)
2019-05-22 14:28:33 +02:00
depfu[bot]
a55d882d4d gems: update rubocop to version 0.69.0
Update rubocop to version 0.69.0 (#32)
2019-05-14 12:42:55 +02:00
depfu[bot]
aa5de52f4d gems: update rubocop to version 0.68.0
Update rubocop to version 0.68.0 (#31)
2019-04-30 20:01:57 +02:00
depfu[bot]
ee56fe8d07 gems: update rubocop to version 0.66.0
Update rubocop to version 0.66.0 (#29)
2019-03-19 13:30:15 +01:00
depfu[bot]
051d561700 gems: update rubocop to version 0.65.0
Update rubocop to version 0.65.0 (#28)
2019-02-20 11:33:15 +01:00
depfu[bot]
ccaa39e871 gems: update rubocop to version 0.64.0
Update rubocop to version 0.64.0 (#27)
2019-02-11 16:45:44 +01:00
depfu[bot]
b69aa6dc5b gems: update rack-tracer to version 0.9.0
Update rack-tracer to version 0.9.0 (#26)
2019-02-08 18:24:56 +01:00
cn
7a03415dd9 release: 2.0.0 2019-01-25 13:56:43 +01:00
cn
77914c42e1 gem: do not restrict Bundler version anymore since bundle-audit 0.6.1 is Bundler 2 compatible now 2019-01-25 13:22:27 +01:00
depfu[bot]
a9d1cc644a gems: update rubocop to version 0.63.0
Update rubocop to version 0.63.0 (#25)
2019-01-17 19:55:04 +01:00
depfu[bot]
902e5868b3 gems: update opentracing to version 0.5.0
Update opentracing to version 0.5.0 (#24)
2019-01-17 08:57:10 +01:00
cn
ffe01467c5 gem: add Ruby 2.6 support 2019-01-04 15:39:51 +01:00
cn
79db1b1911 gem: stick with Bundler 1.x until bundle-audit has a new Bundler 2 compatible release 2019-01-04 15:33:20 +01:00
depfu[bot]
f37a77094f gems: update jaeger-client to version 0.10.0
Update jaeger-client to version 0.10.0 (#22)
2019-01-03 13:40:42 +01:00
depfu[bot]
2e233422d3 gems: update rubocop to version 0.62.0
Update rubocop to version 0.62.0 (#23)
2019-01-02 11:35:48 +01:00
depfu[bot]
6f044ec5f2 gems: update jaeger-client to version 0.8.0
Update jaeger-client to version 0.8.0 (#21)
2018-12-22 22:45:20 +01:00
depfu[bot]
988e30dac9 gems: update jaeger-client to version 0.7.0
Update jaeger-client to version 0.7.0 (#20)
2018-12-14 15:33:04 +01:00
depfu[bot]
31233df5ee gems: update rubocop to version 0.61.1
Update rubocop to version 0.61.1 (#19)
2018-12-07 11:01:09 +01:00
cn
1f35f60c55 gem: add bundler-audit for vulnerability scan 2018-10-28 13:02:32 +01:00
depfu[bot]
6310343e45 deps: update rubocop to version 0.60.0
Update rubocop to version 0.60.0 (#18)
2018-10-27 17:13:12 +02:00
depfu[bot]
1dd0eb7d1a gems: update rubocop to version 0.59.0
Update rubocop to version 0.59.0 (#17)
2018-09-09 21:39:24 +02:00
cn
280bfbeb82 tracing: mark failed spans by catching and re-throwing StandardErrors 2018-07-13 15:08:21 +02:00
depfu[bot]
bd7c786c9c gems: update rack-tracer to version 0.8.0
Since rack-tracer now supports OpenTracing.active_span we should start
using it as well and therefore do not need spanmanager gem anymore.

See: Update rack-tracer to version 0.8.0 (#16)
2018-07-13 11:38:15 +02:00
depfu[bot]
4f3e591f13 gems: upgrade rubocop to version 0.58.0
Update rubocop to version 0.58.0 (#14)
2018-07-07 20:03:48 +02:00
depfu[bot]
c1b5e14862 gems: upgrade jaeger-client to version 0.6.0
Upgrade jaeger-client to version 0.6.0 (#13)
2018-07-06 13:20:53 +02:00
depfu[bot]
d1dc91ed8d gems: upgrade rack-tracer to version 0.6.0
Upgrade rack-tracer to version 0.6.0 (#11)
2018-06-27 11:55:59 +02:00
depfu[bot]
14fe8ebedf gems: upgrade rubocop to version 0.57.0 2018-06-06 09:00:50 +02:00
depfu[bot]
5264b3e6f1 gems: upgrade rubocop to version 0.56.0 2018-05-14 19:51:56 +02:00
17 changed files with 222 additions and 40 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
.DS_Store .DS_Store
*.lock *.lock
pkg/* pkg/*
.yardoc

View File

@@ -1,15 +1,25 @@
inherit_from: .rubocop_todo.yml
AllCops: AllCops:
TargetRubyVersion: '2.3' TargetRubyVersion: '2.3'
Gemspec/OrderedDependencies: Gemspec/OrderedDependencies:
Enabled: false Enabled: false
Layout/EmptyLineAfterGuardClause:
Enabled: false
# allows nicer usage of private_class_method # allows nicer usage of private_class_method
Layout/EmptyLinesAroundArguments: Layout/EmptyLinesAroundArguments:
Enabled: false Enabled: false
Layout/HashAlignment:
Enabled: false
Layout/LeadingEmptyLines:
Enabled: false
Layout/LineLength:
Max: 200
Layout/SpaceInsideHashLiteralBraces: Layout/SpaceInsideHashLiteralBraces:
Enabled: false Enabled: false
@@ -25,16 +35,13 @@ Metrics/ClassLength:
Metrics/CyclomaticComplexity: Metrics/CyclomaticComplexity:
Enabled: false Enabled: false
Metrics/LineLength:
Max: 200
Metrics/MethodLength: Metrics/MethodLength:
Enabled: false Enabled: false
Metrics/PerceivedComplexity: Metrics/PerceivedComplexity:
Enabled: false Enabled: false
Naming/UncommunicativeMethodParamName: Naming/MethodParameterName:
Enabled: false Enabled: false
Naming/MemoizedInstanceVariableName: Naming/MemoizedInstanceVariableName:
@@ -55,6 +62,15 @@ Style/FrozenStringLiteralComment:
Style/GuardClause: Style/GuardClause:
Enabled: false Enabled: false
Style/HashEachMethods:
Enabled: true
Style/HashTransformKeys:
Enabled: true
Style/HashTransformValues:
Enabled: true
Style/IdenticalConditionalBranches: Style/IdenticalConditionalBranches:
Enabled: false Enabled: false

View File

@@ -1,7 +0,0 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2018-02-23 12:54:10 +0100 using RuboCop version 0.52.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.

16
.solargraph.yml Normal file
View File

@@ -0,0 +1,16 @@
---
include:
- "**/*.rb"
- "bin/dyndnsd"
exclude:
- spec/**/*
- test/**/*
- vendor/**/*
- ".bundle/**/*"
require: []
domains: []
reporters:
- rubocop
- require_not_found
require_paths: []
max_files: 5000

View File

@@ -1,7 +1,12 @@
--- ---
sudo: false os: linux
language: ruby language: ruby
rvm: rvm:
- 2.5 - 2.7
- 2.4 - 2.6
- 2.3 - 2.5
- 2.4
- 2.3
script:
- bundle exec rake travis

View File

@@ -1,6 +1,20 @@
# Changelog # Changelog
## 2.0.0 ## 2.1.1 (March 1, 2020)
IMPROVEMENTS:
- Fix potential `nil` cases detected by [Sorbet](https://sorbet.org) including refactorings
## 2.1.0 (March 1, 2020)
IMPROVEMENTS:
- Add Ruby 2.7 support
- Add [solargraph](https://github.com/castwide/solargraph) to dev tooling as Ruby Language Server usable e.g. for IDEs (used solargraph version not compatible with Ruby 2.7 as bundler-audit 0.6.x requires old `thor` gem)
- Document code using YARD tags, e.g. for type information and better code completion
## 2.0.0 (January 25, 2019)
IMPROVEMENTS: IMPROVEMENTS:
@@ -8,8 +22,8 @@ IMPROVEMENTS:
- Better protocol compliance by returning `badauth` in response body on HTTP 401 errors - Better protocol compliance by returning `badauth` in response body on HTTP 401 errors
- Better code maintainability by refactorings - Better code maintainability by refactorings
- Update dependencies, mainly `rack` to new major version 2 - Update dependencies, mainly `rack` to new major version 2
- Add Ruby 2.5 support - Add Ruby 2.5 and Ruby 2.6 support
- Add experimental [OpenTracing](http://opentracing.io/) support with [CNCF Jaeger](https://github.com/jaegertracing/jaeger) - Add experimental [OpenTracing](https://opentracing.io/) support with [CNCF Jaeger](https://github.com/jaegertracing/jaeger)
- Support host offlining by deleting the associated DNS records - Support host offlining by deleting the associated DNS records
- Add textfile reporter to write Graphite-style metrics (also compatible with [Prometheus](https://prometheus.io/)) into a file - Add textfile reporter to write Graphite-style metrics (also compatible with [Prometheus](https://prometheus.io/)) into a file

View File

@@ -1,8 +1,22 @@
require 'bundler/gem_tasks' require 'bundler/gem_tasks'
require 'rspec/core/rake_task' require 'rspec/core/rake_task'
require 'rubocop/rake_task' require 'rubocop/rake_task'
require 'bundler/audit/task'
RSpec::Core::RakeTask.new(:spec) RSpec::Core::RakeTask.new(:spec)
RuboCop::RakeTask.new RuboCop::RakeTask.new
Bundler::Audit::Task.new
task default: [:rubocop, :spec] desc 'Should be run by developer once to prepare initial solargraph usage (fill caches etc.)'
task :'solargraph:init' do
sh 'solargraph download-core'
end
desc 'Run experimental solargraph type checker'
task :'solargraph:tc' do
sh 'solargraph typecheck'
end
task default: [:rubocop, :spec, 'bundle:audit']
task travis: [:default, :'solargraph:tc']

View File

@@ -23,14 +23,15 @@ Gem::Specification.new do |s|
s.add_runtime_dependency 'rack', '~> 2.0' s.add_runtime_dependency 'rack', '~> 2.0'
s.add_runtime_dependency 'json' s.add_runtime_dependency 'json'
s.add_runtime_dependency 'metriks' s.add_runtime_dependency 'metriks'
s.add_runtime_dependency 'opentracing', '~> 0.4.0' s.add_runtime_dependency 'opentracing', '~> 0.5.0'
s.add_runtime_dependency 'rack-tracer', '~> 0.5.0' s.add_runtime_dependency 'rack-tracer', '~> 0.9.0'
s.add_runtime_dependency 'spanmanager', '~> 0.3.0' s.add_runtime_dependency 'jaeger-client', '~> 0.10.0'
s.add_runtime_dependency 'jaeger-client', '~> 0.4.0'
s.add_development_dependency 'bundler' s.add_development_dependency 'bundler'
s.add_development_dependency 'rake' s.add_development_dependency 'rake'
s.add_development_dependency 'rspec' s.add_development_dependency 'rspec'
s.add_development_dependency 'rack-test' s.add_development_dependency 'rack-test'
s.add_development_dependency 'rubocop', '~> 0.55.0' s.add_development_dependency 'rubocop', '~> 0.80.0'
s.add_development_dependency 'bundler-audit', '~> 0.6.0'
s.add_development_dependency 'solargraph'
end end

View File

@@ -10,7 +10,6 @@ require 'metriks'
require 'metriks/reporter/graphite' require 'metriks/reporter/graphite'
require 'opentracing' require 'opentracing'
require 'rack/tracer' require 'rack/tracer'
require 'spanmanager'
require 'dyndnsd/generator/bind' require 'dyndnsd/generator/bind'
require 'dyndnsd/updater/command_with_bind_zone' require 'dyndnsd/updater/command_with_bind_zone'
@@ -22,21 +21,32 @@ require 'dyndnsd/textfile_reporter'
require 'dyndnsd/version' require 'dyndnsd/version'
module Dyndnsd module Dyndnsd
# @return [Logger]
def self.logger def self.logger
@logger @logger
end end
# @param logger [Logger]
# @return [Logger]
def self.logger=(logger) def self.logger=(logger)
@logger = logger @logger = logger
end end
class LogFormatter class LogFormatter
# @param lvl [Object]
# @param _time [DateTime]
# @param _progname [String]
# @param msg [Object]
# @return [String]
def call(lvl, _time, _progname, msg) def call(lvl, _time, _progname, msg)
format("[%s] %-5s %s\n", Time.now.strftime('%Y-%m-%d %H:%M:%S'), lvl, msg.to_s) format("[%s] %-5s %s\n", Time.now.strftime('%Y-%m-%d %H:%M:%S'), lvl, msg.to_s)
end end
end end
class Daemon class Daemon
# @param config [Hash{String => Object}]
# @param db [Dyndnsd::Database]
# @param updater [#update]
def initialize(config, db, updater) def initialize(config, db, updater)
@users = config['users'] @users = config['users']
@domain = config['domain'] @domain = config['domain']
@@ -52,6 +62,9 @@ module Dyndnsd
end end
end end
# @param username [String]
# @param password [String]
# @return [Boolean]
def authorized?(username, password) def authorized?(username, password)
Helper.span('check_authorized') do |span| Helper.span('check_authorized') do |span|
span.set_tag('dyndnsd.user', username) span.set_tag('dyndnsd.user', username)
@@ -65,6 +78,8 @@ module Dyndnsd
end end
end end
# @param env [Hash{String => String}]
# @return [Array{Integer,Hash{String => String},Array{String}}]
def call(env) def call(env)
return [422, {'X-DynDNS-Response' => 'method_forbidden'}, []] if env['REQUEST_METHOD'] != 'GET' return [422, {'X-DynDNS-Response' => 'method_forbidden'}, []] if env['REQUEST_METHOD'] != 'GET'
return [422, {'X-DynDNS-Response' => 'not_found'}, []] if env['PATH_INFO'] != '/nic/update' return [422, {'X-DynDNS-Response' => 'not_found'}, []] if env['PATH_INFO'] != '/nic/update'
@@ -72,6 +87,7 @@ module Dyndnsd
handle_dyndns_request(env) handle_dyndns_request(env)
end end
# @return [void]
def self.run! def self.run!
if ARGV.length != 1 if ARGV.length != 1
puts 'Usage: dyndnsd config_file' puts 'Usage: dyndnsd config_file'
@@ -96,8 +112,14 @@ module Dyndnsd
# drop priviliges as soon as possible # drop priviliges as soon as possible
# NOTE: first change group than user # NOTE: first change group than user
Process::Sys.setgid(Etc.getgrnam(config['group']).gid) if config['group'] if config['group']
Process::Sys.setuid(Etc.getpwnam(config['user']).uid) if config['user'] group = Etc.getgrnam(config['group'])
Process::Sys.setgid(group.gid) if group
end
if config['user']
user = Etc.getpwnam(config['user'])
Process::Sys.setuid(user.uid) if user
end
setup_traps setup_traps
@@ -110,6 +132,8 @@ module Dyndnsd
private private
# @param params [Hash{String => String}]
# @return [Array{String}]
def extract_v4_and_v6_address(params) def extract_v4_and_v6_address(params)
return [] if !(params['myip']) return [] if !(params['myip'])
begin begin
@@ -121,6 +145,9 @@ module Dyndnsd
end end
end end
# @param env [Hash{String => String}]
# @param params [Hash{String => String}]
# @return [Array{String}]
def extract_myips(env, params) def extract_myips(env, params)
# require presence of myip parameter as valid IPAddr (v4) and valid myip6 # require presence of myip parameter as valid IPAddr (v4) and valid myip6
return extract_v4_and_v6_address(params) if params.key?('myip6') return extract_v4_and_v6_address(params) if params.key?('myip6')
@@ -135,6 +162,9 @@ module Dyndnsd
[env['REMOTE_ADDR']] [env['REMOTE_ADDR']]
end end
# @param hostnames [String]
# @param myips [Array{String}]
# @return [Array{Symbol}]
def process_changes(hostnames, myips) def process_changes(hostnames, myips)
changes = [] changes = []
Helper.span('process_changes') do |span| Helper.span('process_changes') do |span|
@@ -159,6 +189,7 @@ module Dyndnsd
changes changes
end end
# @return [void]
def update_db def update_db
@db['serial'] += 1 @db['serial'] += 1
Dyndnsd.logger.info "Committing update ##{@db['serial']}" Dyndnsd.logger.info "Committing update ##{@db['serial']}"
@@ -167,6 +198,8 @@ module Dyndnsd
Metriks.meter('updates.committed').mark Metriks.meter('updates.committed').mark
end end
# @param env [Hash{String => String}]
# @return [Array{Integer,Hash{String => String},Array{String}}]
def handle_dyndns_request(env) def handle_dyndns_request(env)
params = Rack::Utils.parse_query(env['QUERY_STRING']) params = Rack::Utils.parse_query(env['QUERY_STRING'])
@@ -205,6 +238,8 @@ module Dyndnsd
# SETUP # SETUP
# @param config [Hash{String => Object}]
# @return [void]
private_class_method def self.setup_logger(config) private_class_method def self.setup_logger(config)
if config['logfile'] if config['logfile']
Dyndnsd.logger = Logger.new(config['logfile']) Dyndnsd.logger = Logger.new(config['logfile'])
@@ -216,6 +251,7 @@ module Dyndnsd
Dyndnsd.logger.formatter = LogFormatter.new Dyndnsd.logger.formatter = LogFormatter.new
end end
# @return [void]
private_class_method def self.setup_traps private_class_method def self.setup_traps
Signal.trap('INT') do Signal.trap('INT') do
Dyndnsd.logger.info 'Quitting...' Dyndnsd.logger.info 'Quitting...'
@@ -227,6 +263,8 @@ module Dyndnsd
end end
end end
# @param config [Hash{String => Object}]
# @return [void]
private_class_method def self.setup_monitoring(config) private_class_method def self.setup_monitoring(config)
# configure metriks # configure metriks
if config['graphite'] if config['graphite']
@@ -254,6 +292,8 @@ module Dyndnsd
end end
end end
# @param config [Hash{String => Object}]
# @return [void]
private_class_method def self.setup_tracing(config) private_class_method def self.setup_tracing(config)
# configure OpenTracing # configure OpenTracing
if config.dig('tracing', 'jaeger') if config.dig('tracing', 'jaeger')
@@ -266,14 +306,14 @@ module Dyndnsd
host: host, port: port, service_name: service_name, flush_interval: 1 host: host, port: port, service_name: service_name, flush_interval: 1
) )
end end
# always use SpanManager
OpenTracing.global_tracer = SpanManager::Tracer.new(OpenTracing.global_tracer)
end end
# @param config [Hash{String => Object}]
# @return [void]
private_class_method def self.setup_rack(config) private_class_method def self.setup_rack(config)
# configure daemon # configure daemon
db = Database.new(config['db']) db = Database.new(config['db'])
updater = Updater::CommandWithBindZone.new(config['domain'], config['updater']['params']) if config['updater']['name'] == 'command_with_bind_zone' updater = Updater::CommandWithBindZone.new(config['domain'], config.dig('updater', 'params')) if config.dig('updater', 'name') == 'command_with_bind_zone'
daemon = Daemon.new(config, db, updater) daemon = Daemon.new(config, db, updater)
# configure rack # configure rack

View File

@@ -7,19 +7,22 @@ module Dyndnsd
def_delegators :@db, :[], :[]=, :each, :has_key? def_delegators :@db, :[], :[]=, :each, :has_key?
# @param db_file [String]
def initialize(db_file) def initialize(db_file)
@db_file = db_file @db_file = db_file
end end
# @return [void]
def load def load
if File.file?(@db_file) if File.file?(@db_file)
@db = JSON.parse(File.open(@db_file, 'r', &:read)) @db = JSON.parse(File.read(@db_file, mode: 'r'))
else else
@db = {} @db = {}
end end
@db_hash = @db.hash @db_hash = @db.hash
end end
# @return [void]
def save def save
Helper.span('database_save') do |_span| Helper.span('database_save') do |_span|
File.open(@db_file, 'w') { |f| JSON.dump(@db, f) } File.open(@db_file, 'w') { |f| JSON.dump(@db, f) }
@@ -27,6 +30,7 @@ module Dyndnsd
end end
end end
# @return [Boolean]
def changed? def changed?
@db_hash != @db.hash @db_hash != @db.hash
end end

View File

@@ -2,6 +2,8 @@
module Dyndnsd module Dyndnsd
module Generator module Generator
class Bind class Bind
# @param domain [String]
# @param config [Hash{String => Object}]
def initialize(domain, config) def initialize(domain, config)
@domain = domain @domain = domain
@ttl = config['ttl'] @ttl = config['ttl']
@@ -10,6 +12,8 @@ module Dyndnsd
@additional_zone_content = config['additional_zone_content'] @additional_zone_content = config['additional_zone_content']
end end
# @param db [Dyndnsd::Database]
# @return [String]
def generate(db) def generate(db)
out = [] out = []
out << "$TTL #{@ttl}" out << "$TTL #{@ttl}"

View File

@@ -3,6 +3,9 @@ require 'ipaddr'
module Dyndnsd module Dyndnsd
class Helper class Helper
# @param hostname [String]
# @param domain [String]
# @return [Boolean]
def self.fqdn_valid?(hostname, domain) def self.fqdn_valid?(hostname, domain)
return false if hostname.length < domain.length + 2 return false if hostname.length < domain.length + 2
return false if !hostname.end_with?(domain) return false if !hostname.end_with?(domain)
@@ -11,6 +14,8 @@ module Dyndnsd
true true
end end
# @param ip [String]
# @return [Boolean]
def self.ip_valid?(ip) def self.ip_valid?(ip)
IPAddr.new(ip) IPAddr.new(ip)
true true
@@ -18,23 +23,45 @@ module Dyndnsd
false false
end end
# @param username [String]
# @param password [String]
# @param users [Hash]
# @return [Boolean]
def self.user_allowed?(username, password, users) def self.user_allowed?(username, password, users)
(users.key? username) && (users[username]['password'] == password) (users.key? username) && (users[username]['password'] == password)
end end
# @param hostname [String]
# @param myips [Array]
# @param hosts [Hash]
# @return [Boolean]
def self.changed?(hostname, myips, hosts) def self.changed?(hostname, myips, hosts)
# myips order is always deterministic # myips order is always deterministic
((!hosts.include? hostname) || (hosts[hostname] != myips)) && !myips.empty? ((!hosts.include? hostname) || (hosts[hostname] != myips)) && !myips.empty?
end end
# @param operation [String]
# @param block [Proc]
# @return [void]
def self.span(operation, &block) def self.span(operation, &block)
span = OpenTracing.start_span(operation) scope = OpenTracing.start_active_span(operation)
span = scope.span
span.set_tag('component', 'dyndnsd') span.set_tag('component', 'dyndnsd')
span.set_tag('span.kind', 'server') span.set_tag('span.kind', 'server')
begin begin
block.call(span) block.call(span)
rescue StandardError => e
span.set_tag('error', true)
span.log_kv(
event: 'error',
'error.kind': e.class.to_s,
'error.object': e,
message: e.message,
stack: e.backtrace&.join("\n") || ''
)
raise
ensure ensure
span.finish scope.close
end end
end end
end end

View File

@@ -2,10 +2,13 @@
module Dyndnsd module Dyndnsd
module Responder module Responder
class DynDNSStyle class DynDNSStyle
# @param app [#call]
def initialize(app) def initialize(app)
@app = app @app = app
end end
# @param env [Hash{String => String}]
# @return [Array{Integer,Hash{String => String},Array{String}}]
def call(env) def call(env)
@app.call(env).tap do |status_code, headers, body| @app.call(env).tap do |status_code, headers, body|
if headers.key?('X-DynDNS-Response') if headers.key?('X-DynDNS-Response')
@@ -18,6 +21,10 @@ module Dyndnsd
private private
# @param status_code [Integer]
# @param headers [Hash{String => String}]
# @param body [Array{String}]
# @return [Array{Integer,Hash{String => String},Array{String}}]
def decorate_dyndnsd_response(status_code, headers, body) def decorate_dyndnsd_response(status_code, headers, body)
if status_code == 200 if status_code == 200
[200, {'Content-Type' => 'text/plain'}, [get_success_body(body[0], body[1])]] [200, {'Content-Type' => 'text/plain'}, [get_success_body(body[0], body[1])]]
@@ -26,6 +33,10 @@ module Dyndnsd
end end
end end
# @param status_code [Integer]
# @param headers [Hash{String => String}]
# @param _body [Array{String}]
# @return [Array{Integer,Hash{String => String},Array{String}}]
def decorate_other_response(status_code, headers, _body) def decorate_other_response(status_code, headers, _body)
if status_code == 400 if status_code == 400
[status_code, headers, ['Bad Request']] [status_code, headers, ['Bad Request']]
@@ -34,10 +45,14 @@ module Dyndnsd
end end
end end
# @param changes [Array{Symbol}]
# @param myips [Array{String}]
# @return [String]
def get_success_body(changes, myips) def get_success_body(changes, myips)
changes.map { |change| "#{change} #{myips.join(' ')}" }.join("\n") changes.map { |change| "#{change} #{myips.join(' ')}" }.join("\n")
end end
# @return [Hash{String => Object}]
def error_response_map def error_response_map
{ {
# general http errors # general http errors

View File

@@ -2,10 +2,13 @@
module Dyndnsd module Dyndnsd
module Responder module Responder
class RestStyle class RestStyle
# @param app [#call]
def initialize(app) def initialize(app)
@app = app @app = app
end end
# @param env [Hash{String => String}]
# @return [Array{Integer,Hash{String => String},Array{String}}]
def call(env) def call(env)
@app.call(env).tap do |status_code, headers, body| @app.call(env).tap do |status_code, headers, body|
if headers.key?('X-DynDNS-Response') if headers.key?('X-DynDNS-Response')
@@ -18,6 +21,10 @@ module Dyndnsd
private private
# @param status_code [Integer]
# @param headers [Hash{String => String}]
# @param body [Array{String}]
# @return [Array{Integer,Hash{String => String},Array{String}}]
def decorate_dyndnsd_response(status_code, headers, body) def decorate_dyndnsd_response(status_code, headers, body)
if status_code == 200 if status_code == 200
[200, {'Content-Type' => 'text/plain'}, [get_success_body(body[0], body[1])]] [200, {'Content-Type' => 'text/plain'}, [get_success_body(body[0], body[1])]]
@@ -26,6 +33,10 @@ module Dyndnsd
end end
end end
# @param status_code [Integer]
# @param headers [Hash{String => String}]
# @param _body [Array{String}]
# @return [Array{Integer,Hash{String => String},Array{String}}]
def decorate_other_response(status_code, headers, _body) def decorate_other_response(status_code, headers, _body)
if status_code == 400 if status_code == 400
[status_code, headers, ['Bad Request']] [status_code, headers, ['Bad Request']]
@@ -34,10 +45,14 @@ module Dyndnsd
end end
end end
# @param changes [Array{Symbol}]
# @param myips [Array{String}]
# @return [String]
def get_success_body(changes, myips) def get_success_body(changes, myips)
changes.map { |change| change == :good ? "Changed to #{myips.join(' ')}" : "No change needed for #{myips.join(' ')}" }.join("\n") changes.map { |change| change == :good ? "Changed to #{myips.join(' ')}" : "No change needed for #{myips.join(' ')}" }.join("\n")
end end
# @return [Hash{String => Object}]
def error_response_map def error_response_map
{ {
# general http errors # general http errors

View File

@@ -5,8 +5,11 @@ require 'metriks'
module Dyndnsd module Dyndnsd
class TextfileReporter class TextfileReporter
# @return [String]
attr_reader :file attr_reader :file
# @param file [String]
# @param options [Hash{Symbol => Object}]
def initialize(file, options = {}) def initialize(file, options = {})
@file = file @file = file
@@ -17,6 +20,7 @@ module Dyndnsd
@on_error = options[:on_error] || proc { |ex| } @on_error = options[:on_error] || proc { |ex| }
end end
# @return [void]
def start def start
@thread ||= Thread.new do @thread ||= Thread.new do
loop do loop do
@@ -33,16 +37,19 @@ module Dyndnsd
end end
end end
# @return [void]
def stop def stop
@thread&.kill @thread&.kill
@thread = nil @thread = nil
end end
# @return [void]
def restart def restart
stop stop
start start
end end
# @return [void]
def write def write
File.open(@file, 'w') do |f| File.open(@file, 'w') do |f|
@registry.each do |name, metric| @registry.each do |name, metric|
@@ -85,6 +92,12 @@ module Dyndnsd
end end
end end
# @param file [String]
# @param base_name [String]
# @param metric [Object]
# @param keys [Array{Symbol}]
# @param snapshot_keys [Array{Symbol}]
# @return [void]
def write_metric(file, base_name, metric, keys, snapshot_keys = []) def write_metric(file, base_name, metric, keys, snapshot_keys = [])
time = Time.now.to_i time = Time.now.to_i

View File

@@ -2,25 +2,29 @@
module Dyndnsd module Dyndnsd
module Updater module Updater
class CommandWithBindZone class CommandWithBindZone
# @param domain [String]
# @param config [Hash{String => Object}]
def initialize(domain, config) def initialize(domain, config)
@zone_file = config['zone_file'] @zone_file = config['zone_file']
@command = config['command'] @command = config['command']
@generator = Generator::Bind.new(domain, config) @generator = Generator::Bind.new(domain, config)
end end
def update(zone) # @param db [Dyndnsd::Database]
# @return [void]
def update(db)
Helper.span('updater_update') do |span| Helper.span('updater_update') do |span|
span.set_tag('dyndnsd.updater.name', self.class.name.split('::').last) span.set_tag('dyndnsd.updater.name', self.class.name&.split('::')&.last || 'None')
# write zone file in bind syntax # write zone file in bind syntax
File.open(@zone_file, 'w') { |f| f.write(@generator.generate(zone)) } File.open(@zone_file, 'w') { |f| f.write(@generator.generate(db)) }
# call user-defined command # call user-defined command
pid = fork do pid = fork do
exec @command exec @command
end end
# detach so children don't become zombies # detach so children don't become zombies
Process.detach(pid) Process.detach(pid) if pid
end end
end end
end end

View File

@@ -1,4 +1,4 @@
module Dyndnsd module Dyndnsd
VERSION = '2.0.0.rc2'.freeze VERSION = '2.1.1'.freeze
end end