1
0
mirror of https://github.com/EV21/dynb.git synced 2025-12-26 16:39:32 +01:00

55 Commits

Author SHA1 Message Date
05120c25e4 feat: add tzdata to Dockerfile for timezone config
You can now set your timezone with the environment variable
`TZ="Europe/Berlin"`
2022-05-26 16:10:47 +02:00
dcb3d7436b fix: 🐛 curl/libcurl doesn't support dns-server option
the latest alpine we are using for the docker image
does also drops the support for that option like debian/ubuntu/etc
2022-05-26 13:30:23 +02:00
fa60312c0d minor: 📝 update CHANGELOG.md 2022-05-26 10:35:03 +02:00
ba6e91bbd6 dev: 🔃 strip more prefix variants from commits for changelog 2022-05-26 10:27:03 +02:00
756c17d117 fix: abort on all dig errors 2022-04-12 20:23:52 +02:00
3d1b27aaed minor: 🎨 change code style 2022-01-31 20:40:29 +01:00
8721af229d dev: add github action with shellcheck for PRs 2021-11-05 20:58:17 +01:00
c712ec5654 minor: 🔃 update .gitchangelog.rc 2021-11-03 18:09:52 +01:00
9cc26d737f feat: validate ip address respons from ip check web service 2021-11-03 18:09:51 +01:00
e6af29dcd4 feat: check internet connection for selected ip versions 2021-11-03 18:09:51 +01:00
b3eb045cc7 doc: 📝 add labels to Dockerfile 2021-11-03 18:09:51 +01:00
4b68ca837d feat: 🎨 colorful info, debug and error messages 2021-11-03 18:09:50 +01:00
eedb0623ac refactor: ♻️ remove unused argbash fragment 2021-11-03 18:09:50 +01:00
b5d300a86e doc: 📝 update CHANGELOG.md 2021-11-03 18:09:50 +01:00
a95effb00d dev: 🔥 remove md-toc from pre-commit 2021-11-03 18:09:49 +01:00
a5dc7ea9b3 doc: 📝 document docker parameters 2021-11-03 18:09:49 +01:00
63cbce3568 feat: enable parameter extensions for docker run --interactive 2021-11-03 18:09:49 +01:00
73d2a7ac44 dev: add pre-commit hooks for markdown 2021-11-03 18:09:49 +01:00
cad6d47cd2 cosmetic: 🎨 lint README.md 2021-11-03 18:09:48 +01:00
06c35d99af doc: 📝 change default dns server setting 2021-11-03 18:09:48 +01:00
9da624b163 dev: 🎨 add shfmt params to pre-commit check 2021-11-03 18:09:47 +01:00
c01bea615e cosmetic: 🎨 fix shell style 2021-11-03 18:09:47 +01:00
046a728f22 change: handle dns server selection 2021-11-03 18:09:47 +01:00
3ded7fc883 refactor: ♻️ seperate parsing logic 2021-11-03 18:09:46 +01:00
f942a4495d dev:️ change git hooks and add shfmt 2021-11-03 18:09:46 +01:00
32404f2957 doc: 📝 update CHANGELOG 2021-11-03 18:09:46 +01:00
3cdcdbc8bf cosmetic: ♻️ normalizing pre-commit configuration to a top-level map 2021-11-03 18:09:45 +01:00
226537b2b0 refactor: ♻️ something 2021-11-03 18:09:45 +01:00
976ea673f8 dev: add pre-commit git hook for shellcheck 2021-11-03 18:09:45 +01:00
9ed30f2bee refactor: ♻️ remove unused help message 2021-11-03 18:09:44 +01:00
3ab88e5993 doc: 📝 update README.md 2021-11-03 18:09:44 +01:00
317abdbcec doc: 📝 document environment variables 2021-11-03 18:09:44 +01:00
4faba4e600 refactor: ♻️ method extractions and other beautifications 2021-11-03 18:09:43 +01:00
84784248c3 fix: 🐛 fix loop and error handling in case of connection issues 2021-11-03 18:09:43 +01:00
b083080c9d doc: 📝 document example of an docker-compose.yml file 2021-11-03 18:09:42 +01:00
48df7c7646 feat: add Dockerfile 2021-11-03 18:09:42 +01:00
b1f6c5a0fd doc: 📝 document loop mode and mention dig as requirement 2021-11-03 18:09:42 +01:00
17e23e7623 feat: add loop mode 2021-11-03 18:09:41 +01:00
bb1e0db9bf doc: 📝 update example of .env in README.md 2021-11-03 18:09:41 +01:00
29c2f62292 change: 🔃 rename environment variables 2021-11-03 18:09:41 +01:00
c85d5eaaee feat: add support for Duck DNS as DynDNS2 provider 2021-11-03 18:09:40 +01:00
3221f29d71 feat: add support for deSEC as DynDNS2 provider 2021-11-03 18:09:40 +01:00
706b6d84c1 feat: add completion
 add man page
2021-11-03 18:09:39 +01:00
6d9bf8a2ae feat: 🔃 replace getopt with argbash 2021-11-03 18:09:39 +01:00
6a14d00256 fix: 🐛 fix error handling 2021-11-03 18:09:39 +01:00
dff0c1c178 refactor: ♻️ main code 2021-11-03 18:09:38 +01:00
9bac611ddf feat: add interpretaton of status codes and act accordingly 2021-11-03 18:09:38 +01:00
1bb0f44d28 feat: make network interface configurable 2021-11-03 18:09:38 +01:00
730858344c fix: 🐛 fix sourcing of config file
♻️ do some shellcheck fixes
2021-11-03 18:09:02 +01:00
e3781a87bb feat: add DynDNS2 support for dynv6.com 2021-11-03 17:00:44 +01:00
fa5086f197 doc: 📝 CHANGELOG.md 2021-11-03 17:00:21 +01:00
3fa15b4d9d minor: 📝 .gitchangelog.rc 2021-11-03 16:59:05 +01:00
2c90e74bf5 doc: 📝 add example.env 2021-11-03 16:57:53 +01:00
cddcd9cfca add: 📝 README.md 2021-11-03 16:56:55 +01:00
616c1d47c1 add: dynb.sh 2021-11-03 16:56:39 +01:00
16 changed files with 2114 additions and 2 deletions

9
.dockerignore Normal file
View File

@@ -0,0 +1,9 @@
**/.env
**/.dockerignore
**/.docker-compose.debug.yml
**/.docker-compose.yml
**/.git
**/.gitignore
**/.gitchangelog.rc
man/man.rst
man/man-defs.rst

305
.gitchangelog.rc Normal file
View File

@@ -0,0 +1,305 @@
# -*- coding: utf-8; mode: python -*-
##
## Format
##
## ACTION: [AUDIENCE:] COMMIT_MSG [!TAG ...]
##
## Description
##
## ACTION is one of 'chg', 'fix', 'new'
##
## Is WHAT the change is about.
##
## 'chg' is for refactor, small improvement, cosmetic changes...
## 'fix' is for bug fixes
## 'new' is for new features, big improvement
##
## AUDIENCE is optional and one of 'dev', 'usr', 'pkg', 'test', 'doc'
##
## Is WHO is concerned by the change.
##
## 'dev' is for developpers (API changes, refactors...)
## 'usr' is for final users (UI changes)
## 'pkg' is for packagers (packaging changes)
## 'test' is for testers (test only related changes)
## 'doc' is for doc guys (doc only changes)
##
## COMMIT_MSG is ... well ... the commit message itself.
##
## TAGs are additionnal adjective as 'refactor' 'minor' 'cosmetic'
##
## They are preceded with a '!' or a '@' (prefer the former, as the
## latter is wrongly interpreted in github.) Commonly used tags are:
##
## 'refactor' is obviously for refactoring code only
## 'minor' is for a very meaningless change (a typo, adding a comment)
## 'cosmetic' is for cosmetic driven change (re-indentation, 80-col...)
## 'wip' is for partial functionality but complete subfunctionality.
##
## Example:
##
## new: usr: support of bazaar implemented
## chg: re-indentend some lines !cosmetic
## new: dev: updated code to be compatible with last version of killer lib.
## fix: pkg: updated year of licence coverage.
## new: test: added a bunch of test around user usability of feature X.
## fix: typo in spelling my name in comment. !minor
##
## Please note that multi-line commit message are supported, and only the
## first line will be considered as the "summary" of the commit message. So
## tags, and other rules only applies to the summary. The body of the commit
## message will be displayed in the changelog without reformatting.
##
## ``ignore_regexps`` is a line of regexps
##
## Any commit having its full commit message matching any regexp listed here
## will be ignored and won't be reported in the changelog.
##
ignore_regexps = [
r'@minor', r'!minor',
r'@cosmetic', r'!cosmetic',
r'@refactor', r'!refactor',
r'@wip', r'!wip',
r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*[p|P]kg:',
r'^([cC]hg|[fF]ix|[nN]ew)\s*:\s*[d|D]ev:',
r'^(.{3,3}\s*:)?\s*[fF]irst commit.?\s*$',
r'^$', ## ignore commits with empty messages
]
## ``section_regexps`` is a list of 2-tuples associating a string label and a
## list of regexp
##
## Commit messages will be classified in sections thanks to this. Section
## titles are the label, and a commit is classified under this section if any
## of the regexps associated is matching.
##
## Please note that ``section_regexps`` will only classify commits and won't
## make any changes to the contents. So you'll probably want to go check
## ``subject_process`` (or ``body_process``) to do some changes to the subject,
## whenever you are tweaking this variable.
##
section_regexps = [
('Features', [
r'^[fF]eat\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
]),
('Added', [
r'^[aA]dd\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
]),
('Changed', [
r'^[cC]hange\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
]),
('Deprecated', [
r'^[dD]epricated\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
]),
('Removed', [
r'^[rR]emove\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
]),
('Fix', [
r'^[fF]ix\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
]),
('Security', [
r'^[sS]ec\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
]),
('Documentation', [
r'^[dD]oc\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n]*)$',
]),
#('Other', None ## Match all lines
# ),
]
## ``body_process`` is a callable
##
## This callable will be given the original body and result will
## be used in the changelog.
##
## Available constructs are:
##
## - any python callable that take one txt argument and return txt argument.
##
## - ReSub(pattern, replacement): will apply regexp substitution.
##
## - Indent(chars=" "): will indent the text with the prefix
## Please remember that template engines gets also to modify the text and
## will usually indent themselves the text if needed.
##
## - Wrap(regexp=r"\n\n"): re-wrap text in separate paragraph to fill 80-Columns
##
## - noop: do nothing
##
## - ucfirst: ensure the first letter is uppercase.
## (usually used in the ``subject_process`` pipeline)
##
## - final_dot: ensure text finishes with a dot
## (usually used in the ``subject_process`` pipeline)
##
## - strip: remove any spaces before or after the content of the string
##
## - SetIfEmpty(msg="No commit message."): will set the text to
## whatever given ``msg`` if the current text is empty.
##
## Additionally, you can `pipe` the provided filters, for instance:
#body_process = Wrap(regexp=r'\n(?=\w+\s*:)') | Indent(chars=" ")
#body_process = Wrap(regexp=r'\n(?=\w+\s*:)')
#body_process = noop
body_process = ReSub(r'((^|\n)[A-Z]\w+(-\w+)*: .*(\n\s+.*)*)+$', r'') | strip
## ``subject_process`` is a callable
##
## This callable will be given the original subject and result will
## be used in the changelog.
##
## Available constructs are those listed in ``body_process`` doc.
subject_process = (strip |
ReSub(r'^([cC]hg|[fF]ix|[nN]ew|[dD]oc|[fF]eat|[aA]dd|[cC]hange)\s*:\s*((dev|use?r|pkg|test|doc)\s*:\s*)?([^\n@]*)(@[a-z]+\s+)*$', r'\4') |
SetIfEmpty("No commit message.") | ucfirst | final_dot)
## ``tag_filter_regexp`` is a regexp
##
## Tags that will be used for the changelog must match this regexp.
##
tag_filter_regexp = r'^[0-9]+\.[0-9]+(\.[0-9]+)?$'
## ``unreleased_version_label`` is a string or a callable that outputs a string
##
## This label will be used as the changelog Title of the last set of changes
## between last valid tag and HEAD if any.
unreleased_version_label = "(unreleased)"
## ``output_engine`` is a callable
##
## This will change the output format of the generated changelog file
##
## Available choices are:
##
## - rest_py
##
## Legacy pure python engine, outputs ReSTructured text.
## This is the default.
##
## - mustache(<template_name>)
##
## Template name could be any of the available templates in
## ``templates/mustache/*.tpl``.
## Requires python package ``pystache``.
## Examples:
## - mustache("markdown")
## - mustache("restructuredtext")
##
## - makotemplate(<template_name>)
##
## Template name could be any of the available templates in
## ``templates/mako/*.tpl``.
## Requires python package ``mako``.
## Examples:
## - makotemplate("restructuredtext")
##
#output_engine = rest_py
#output_engine = mustache("restructuredtext")
output_engine = mustache("markdown")
#output_engine = makotemplate("restructuredtext")
## ``include_merge`` is a boolean
##
## This option tells git-log whether to include merge commits in the log.
## The default is to include them.
include_merge = True
## ``log_encoding`` is a string identifier
##
## This option tells gitchangelog what encoding is outputed by ``git log``.
## The default is to be clever about it: it checks ``git config`` for
## ``i18n.logOutputEncoding``, and if not found will default to git's own
## default: ``utf-8``.
#log_encoding = 'utf-8'
## ``publish`` is a callable
##
## Sets what ``gitchangelog`` should do with the output generated by
## the output engine. ``publish`` is a callable taking one argument
## that is an interator on lines from the output engine.
##
## Some helper callable are provided:
##
## Available choices are:
##
## - stdout
##
## Outputs directly to standard output
## (This is the default)
##
## - FileInsertAtFirstRegexMatch(file, pattern, idx=lamda m: m.start())
##
## Creates a callable that will parse given file for the given
## regex pattern and will insert the output in the file.
## ``idx`` is a callable that receive the matching object and
## must return a integer index point where to insert the
## the output in the file. Default is to return the position of
## the start of the matched string.
##
## - FileRegexSubst(file, pattern, replace, flags)
##
## Apply a replace inplace in the given file. Your regex pattern must
## take care of everything and might be more complex. Check the README
## for a complete copy-pastable example.
##
# publish = FileInsertIntoFirstRegexMatch(
# "CHANGELOG.rst",
# r'/(?P<rev>[0-9]+\.[0-9]+(\.[0-9]+)?)\s+\([0-9]+-[0-9]{2}-[0-9]{2}\)\n--+\n/',
# idx=lambda m: m.start(1)
# )
#publish = stdout
## ``revs`` is a list of callable or a list of string
##
## callable will be called to resolve as strings and allow dynamical
## computation of these. The result will be used as revisions for
## gitchangelog (as if directly stated on the command line). This allows
## to filter exaclty which commits will be read by gitchangelog.
##
## To get a full documentation on the format of these strings, please
## refer to the ``git rev-list`` arguments. There are many examples.
##
## Using callables is especially useful, for instance, if you
## are using gitchangelog to generate incrementally your changelog.
##
## Some helpers are provided, you can use them::
##
## - FileFirstRegexMatch(file, pattern): will return a callable that will
## return the first string match for the given pattern in the given file.
## If you use named sub-patterns in your regex pattern, it'll output only
## the string matching the regex pattern named "rev".
##
## - Caret(rev): will return the rev prefixed by a "^", which is a
## way to remove the given revision and all its ancestor.
##
## Please note that if you provide a rev-list on the command line, it'll
## replace this value (which will then be ignored).
##
## If empty, then ``gitchangelog`` will act as it had to generate a full
## changelog.
##
## The default is to use all commits to make the changelog.
#revs = ["^1.0.3", ]
#revs = [
# Caret(
# FileFirstRegexMatch(
# "CHANGELOG.rst",
# r"(?P<rev>[0-9]+\.[0-9]+(\.[0-9]+)?)\s+\([0-9]+-[0-9]{2}-[0-9]{2}\)\n--+\n")),
# "HEAD"
#]
revs = []

16
.github/workflows/shellcheck.yml vendored Normal file
View File

@@ -0,0 +1,16 @@
name: Run shellcheck with reviewdog
on: [pull_request]
jobs:
shellcheck:
name: runner / shellcheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: shellcheck
uses: reviewdog/action-shellcheck@v1.9.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
reporter: github-pr-review
path: "."
pattern: "*.sh"
exclude: "./.git/*"

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.env*
docker-compose*

17
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,17 @@
repos:
- repo: https://github.com/syntaqx/git-hooks
rev: v0.0.17
hooks:
- id: shellcheck
- id: shfmt
args:
- "-l" # list files whose formatting differs from shfmt's
- "-i 2" # indent: 0 for tabs (default), >0 for number of spaces
- "-ci" # switch cases will be indented
- "-w" # write result to file instead of stdout
- repo: https://github.com/executablebooks/mdformat
rev: 0.7.9
hooks:
- id: mdformat
additional_dependencies:
- mdformat-gfm # GitHub Flavored Markdown support

114
CHANGELOG.md Normal file
View File

@@ -0,0 +1,114 @@
# Changelog
## 0.3.0 (2022-05-26)
### Features
* ✨ validate ip address respons from ip check web service. [Eduard Veit]
* ✨ check internet connection for selected ip versions. [Eduard Veit]
* 🎨 colorful info, debug and error messages. [Eduard Veit]
### Fix
* Abort on all dig errors. [Eduard Veit]
### Documentation
* 📝 add labels to Dockerfile. [Eduard Veit]
## 0.2.0 (2021-09-24)
### Features
* ✨ enable parameter extensions for `docker run --interactive` [Eduard Veit]
### Changed
* Handle dns server selection. [Eduard Veit]
### Documentation
* 📝 update CHANGELOG.md. [Eduard Veit]
* 📝 document docker parameters. [Eduard Veit]
* 📝 change default dns server setting. [Eduard Veit]
* 📝 update CHANGELOG. [Eduard Veit]
* 📝 update README.md. [Eduard Veit]
## 0.1.2 (2021-04-23)
### Documentation
* 📝 document environment variables. [Eduard Veit]
## 0.1.1 (2021-04-23)
### Fix
* 🐛 fix loop and error handling in case of connection issues. [Eduard Veit]
## 0.1.0 (2021-04-22)
### Features
* ✨ add Dockerfile. [Eduard Veit]
* ✨ add loop mode. [Eduard Veit]
* ✨ add support for Duck DNS as DynDNS2 provider. [Eduard Veit]
* ✨ add support for deSEC as DynDNS2 provider. [Eduard Veit]
* ✨ add completion. [Eduard Veit]
✨ add man page
* 🔃 replace getopt with argbash. [Eduard Veit]
* ✨ add interpretaton of status codes and act accordingly. [Eduard Veit]
* ✨ make network interface configurable. [Eduard Veit]
* ✨ add DynDNS2 support for dynv6.com. [Eduard Veit]
### Added
* 📝 README.md. [Eduard Veit]
* ✨ dynb.sh. [Eduard Veit]
### Changed
* 🔃 rename environment variables. [Eduard Veit]
### Fix
* 🐛 fix error handling. [Eduard Veit]
* 🐛 fix sourcing of config file. [Eduard Veit]
♻️ do some shellcheck fixes
### Documentation
* 📝 document example of an docker-compose.yml file. [Eduard Veit]
* 📝 document `loop mode` and mention `dig` as requirement. [Eduard Veit]
* 📝 update example of .env in README.md. [Eduard Veit]
* 📝 CHANGELOG.md. [Eduard Veit]
* 📝 add example.env. [Eduard Veit]

21
Dockerfile Normal file
View File

@@ -0,0 +1,21 @@
FROM alpine:latest
RUN \
apk update \
&& \
apk add \
bash \
curl \
jq \
bind-tools \
tzdata
WORKDIR /usr/src/app
COPY . .
ENTRYPOINT ["/bin/bash", "/usr/src/app/dynb.sh"]
LABEL org.opencontainers.image.source="https://github.com/EV21/dynb"
LABEL org.opencontainers.image.description="DynB - dynamic DNS update client."
LABEL org.opencontainers.image.licenses="MIT"

186
README.md
View File

@@ -1,2 +1,184 @@
# dynb # 🔃 DynB
dynb - dynamic DNS update script for bash
DynB - dynamic DNS update script, written in bash
IPv4 (A) and IPv6 (AAAA) record updates are supported.
<!-- TOC -->
- [✨ Update Methods](#-update-methods)
- [APIs](#apis)
- [DynDNS2](#dyndns2)
- [📦 Requirements](#-requirements)
- [🚀 Installation](#-installation)
- [⚙ Configuration](#-configuration)
- [🏃 Run](#-run)
- [⏰ Cron](#-cron)
- [loop mode](#loop-mode)
- [crontab](#crontab)
- [🐟 docker](#-docker)
- [environment variables](#environment-variables)
<!-- /TOC -->
## ✨ Update Methods
The following update methods are currently implemented:
### APIs
- INWX.com Domrobot JSON-RPC-API\
Limitations:
- minimum TTL is 300 (5 minutes)
### DynDNS2
- INWX.com
- deSEC.io (dedyn.io)
- DuckDNS.org
- dynv6.com
## 📦 Requirements
- `curl` - The minimum requirement for all API requests.
- `dig` - You can get it by installing `dnsutils` (debian/ubuntu/ArchLinux), `bind-utils` (CentOS/RHEL), `bind-tools` (Alpine)
also essential if you are using other APIs:
- `jq` - Command-line JSON processor
## 🚀 Installation
Download the latest release
or simply clone this repo
```shell
git clone https://github.com/EV21/dynb.git
```
If you want to add the script to you PATH, run :point_down:
```shell
bash dynb.sh --link
```
## ⚙ Configuration
You can use a config in form of an `.env` file.
Or you can just use CLI parameters.
Create `.env` in the app root directory or at `~/.local/share/dynb/.env`.
```bash
DYNB_DYN_DOMAIN=dyndns.example.com
## service provider could be deSEC, duckdns, dynv6, inwx
DYNB_SERVICE_PROVIDER=inwx
## update method options: domrobot, dyndns
DYNB_UPDATE_METHOD=domrobot
## ip mode could be either: 4, 6 or dual for dualstack
DYNB_IP_MODE=64
## If you are using the DomRobot RPC-API enter your credentials for the web interface login here
## If you are using the DynDNS2 protocol enter your credentials here
DYNB_USERNAME=User42
DYNB_PASSWORD=SuperSecretPassword
## or use a token
DYNB_TOKEN=
```
## 🏃 Run
If you have a config file just run :point_down:
```bash
dynb
```
Alternatively you can use parameters if your system meets the relevant requirements. This example shows the long form parameter, there are also short ones.\
Call the help function :point_down:
```bash
dynb --help
```
```bash
dynb --ip-mode dualstack --update-method domrobot --domain dyndns.example.com --username user42 --password SuperSecretPassword
```
```bash
dynb --ip-mode dualstack --update-method dyndns --provider inwx --domain dyndns.example.com --username user42 --password SuperSecretPassword
```
## ⏰ Cron
To automatically call the script you can use either crontab or the script can also run in a loop mode.
### loop mode
Just use the parameter `--interval 60` or the environment variable `DYNB_INTERVAL=60` so the script will check every 60 seconds if it needs to do an update.
### crontab
execute :point_down:
```bash
crontab -e
```
then enter :point_down: to run dynb every five minutes.
```bash
*/5 * * * * $HOME/.local/bin/dynb >> $HOME/.local/share/dynb/dynb-cron.log
```
alternative with docker and parameters::
```bash
*/5 * * * * docker run --interactive --tty --rm --network host ev21/dynb:latest --ip-mode 64 --update-method domrobot --domain dyndns.example.com --username user42 --password SuperSecretPassword
```
Note, cron typically does not use the users PATH variable.
## 🐟 docker
This is an example of a `docker-compose.yml` file. If you are using IPv6 make sure the routing works properly with your docker container. Note: [IPv6 networking](https://docs.docker.com/config/daemon/ipv6/) is only supported on Docker daemons running on Linux hosts.
```yaml
version: '3.4'
services:
dynb:
image: ev21/dynb
container_name: dynb
network_mode: host
environment:
- DYNB_DYN_DOMAIN=dyndns.example.com
- DYNB_SERVICE_PROVIDER=desec
- DYNB_UPDATE_METHOD=dyndns
- DYNB_IP_MODE=64
- DYNB_USERNAME=User42
- DYNB_PASSWORD=SuperSecretPassword
- DYNB_INTERVAL=60
- TZ=Europe/Berlin
```
## environment variables
| variable | default value | description |
| --------------------- | --------------- | -------------------------------------------------------------------------------------------------------------- |
| DYNB_DYN_DOMAIN | undefined | required; `dyndns.example.com` |
| DYNB_SERVICE_PROVIDER | undefined | required; `deSEC`, `duckdns`, `dynv6`, `inwx` |
| DYNB_UPDATE_METHOD | undefined | required; `dyndns` or `domrobot` (with inwx) |
| DYNB_IP_MODE | undefined | required; `4`, `6` or `64` for both |
| DYNB_USERNAME | undefined | the requirement depends on your provider and the update method |
| DYNB_PASSWORD | undefined | the requirement depends on your provider and the update method |
| DYNB_TOKEN | undefined | the requirement depends on your provider and the update method |
| DYNB_INTERVAL | undefined | without this setting the script/docker container will run one time and exits |
| DYNB_DEBUG | undefined | more console outputs |
| DYNB_IPv4_CHECK_SITE | api64.ipify.org | You need a website or Web-API that outputs your remote IP |
| DYNB_IPv6_CHECK_SITE | api64.ipify.org | You need a website or Web-API that outputs your remote IP |
| DYNB_DNS_CHECK_SERVER | undefined | If you are using a local DNS Resolver/Server make sure it answers with the public answer or set another server |

41
_dynb.sh Executable file
View File

@@ -0,0 +1,41 @@
#!/bin/bash
# Put this file to /etc/bash_completion.d/dynb.sh
# needed because of Argbash --> m4_ignore([
### START OF CODE GENERATED BY Argbash v2.10.0 one line above ###
# Argbash is a bash code generator used to get arguments parsing right.
# Argbash is FREE SOFTWARE, see https://argbash.io for more info
_dynb_sh ()
{
local cur prev opts base
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
all_long_opts="--version --link --reset --debug --update-method --ip-mode --domain --service-provider --username --password --token --interval --help "
all_short_opts="-v -l -r -m -i -d -s -u -p -t -h "
case "$prev" in
--update-method|-m|--ip-mode|-i|--domain|-d|--service-provider|-s|--username|-u|--password|-p|--token|-t|--interval)
COMPREPLY=( $(compgen -o bashdefault -o default -- "${cur}") )
return 0
;;
*)
case "$cur" in
--*)
COMPREPLY=( $(compgen -W "${all_long_opts}" -- "${cur}") )
return 0
;;
-*)
COMPREPLY=( $(compgen -W "${all_short_opts}" -- "${cur}") )
return 0
;;
*)
COMPREPLY=( $(compgen -o bashdefault -o default -- "${cur}") )
return 0
;;
esac
esac
}
complete -F _dynb_sh dynb.sh
### END OF CODE GENERATED BY Argbash (sortof) ### ])

24
dynb-parsing-template.m4 Normal file
View File

@@ -0,0 +1,24 @@
#!/bin/bash
# m4_ignore(
echo "This is just a script template for argbash, not the script." >&2
exit 11 #)Created by argbash-init v2.10.0
# Rearrange the order of options below according to what you would like to see in the help message.
# ARG_OPTIONAL_BOOLEAN([version],[v],[outputs the client version],[off])
# ARG_OPTIONAL_BOOLEAN([link],[l],[links to your script at ~/.local/bin/dynb],[off])
# ARG_OPTIONAL_BOOLEAN([reset],[r],[deletes the client blocking status file],[off])
# ARG_OPTIONAL_BOOLEAN([debug],[],[enables debug mode],[off])
# ARG_OPTIONAL_SINGLE([update-method],[m],[choose if you want to use DynDNS2 or the DomRobot RPC-API],[])
# ARG_OPTIONAL_SINGLE([ip-mode],[i],[updates type A (IPv4) and AAAA (IPv6) records],[])
# ARG_OPTIONAL_SINGLE([domain],[d],[set the domain you want to update],[])
# ARG_OPTIONAL_SINGLE([service-provider],[s],[set your provider in case you are using DynDNS2],[])
# ARG_OPTIONAL_SINGLE([username],[u],[depends on your selected update method and your provider],[])
# ARG_OPTIONAL_SINGLE([password],[p],[depends on your selected update method and your provider],[])
# ARG_OPTIONAL_SINGLE([token],[t],[depends on your selected update method and your provider],[])
# ARG_OPTIONAL_SINGLE([interval],[],[choose the seconds interval to run the script in a loop, minimum is 60],[])
# ARG_HELP([DynB - dynamic DNS update script for bash])
# ARGBASH_GO
# [ <-- needed because of Argbash
# ] <-- needed because of Argbash

297
dynb-parsing.sh Normal file
View File

@@ -0,0 +1,297 @@
#!/bin/bash
# Created by argbash-init v2.10.0
# Rearrange the order of options below according to what you would like to see in the help message.
# ARG_OPTIONAL_BOOLEAN([version],[v],[outputs the client version],[off])
# ARG_OPTIONAL_BOOLEAN([link],[l],[links to your script at ~/.local/bin/dynb],[off])
# ARG_OPTIONAL_BOOLEAN([reset],[r],[deletes the client blocking status file],[off])
# ARG_OPTIONAL_BOOLEAN([debug],[],[enables debug mode],[off])
# ARG_OPTIONAL_SINGLE([update-method],[m],[choose if you want to use DynDNS2 or the DomRobot RPC-API],[])
# ARG_OPTIONAL_SINGLE([ip-mode],[i],[updates type A (IPv4) and AAAA (IPv6) records],[])
# ARG_OPTIONAL_SINGLE([domain],[d],[set the domain you want to update],[])
# ARG_OPTIONAL_SINGLE([service-provider],[s],[set your provider in case you are using DynDNS2],[])
# ARG_OPTIONAL_SINGLE([username],[u],[depends on your selected update method and your provider],[])
# ARG_OPTIONAL_SINGLE([password],[p],[depends on your selected update method and your provider],[])
# ARG_OPTIONAL_SINGLE([token],[t],[depends on your selected update method and your provider],[])
# ARG_OPTIONAL_SINGLE([interval],[],[choose the seconds interval to run the script in a loop, minimum is 60],[])
# ARG_HELP([DynB - dynamic DNS update script for bash])
# ARGBASH_GO()
# needed because of Argbash --> m4_ignore([
### START OF CODE GENERATED BY Argbash v2.10.0 one line above ###
# Argbash is a bash code generator used to get arguments parsing right.
# Argbash is FREE SOFTWARE, see https://argbash.io for more info
# # When called, the process ends.
# Args:
# $1: The exit message (print to stderr)
# $2: The exit code (default is 1)
# if env var _PRINT_HELP is set to 'yes', the usage is print to stderr (prior to $1)
# Example:
# test -f "$_arg_infile" || _PRINT_HELP=yes die "Can't continue, have to supply file as an argument, got '$_arg_infile'" 4
die()
{
local _ret="${2:-1}"
test "${_PRINT_HELP:-no}" = yes && print_help >&2
echo "$1" >&2
exit "${_ret}"
}
# Function that evaluates whether a value passed to it begins by a character
# that is a short option of an argument the script knows about.
# This is required in order to support getopts-like short options grouping.
begins_with_short_option()
{
local first_option all_short_options='vlrmidsupth'
first_option="${1:0:1}"
test "$all_short_options" = "${all_short_options/$first_option/}" && return 1 || return 0
}
# THE DEFAULTS INITIALIZATION - OPTIONALS
_arg_version="off"
_arg_link="off"
_arg_reset="off"
_arg_debug="off"
_arg_update_method=
_arg_ip_mode=
_arg_domain=
_arg_service_provider=
_arg_username=
_arg_password=
_arg_token=
_arg_interval=
# Function that prints general usage of the script.
# This is useful if users asks for it, or if there is an argument parsing error (unexpected / spurious arguments)
# and it makes sense to remind the user how the script is supposed to be called.
print_help()
{
printf '%s\n' "DynB - dynamic DNS update script for bash"
printf 'Usage: %s [-v|--(no-)version] [-l|--(no-)link] [-r|--(no-)reset] [--(no-)debug] [-m|--update-method <arg>] [-i|--ip-mode <arg>] [-d|--domain <arg>] [-s|--service-provider <arg>] [-u|--username <arg>] [-p|--password <arg>] [-t|--token <arg>] [--interval <arg>] [-h|--help]\n' "$0"
printf '\t%s\n' "-v, --version, --no-version: outputs the client version (off by default)"
printf '\t%s\n' "-l, --link, --no-link: links to your script at ~/.local/bin/dynb (off by default)"
printf '\t%s\n' "-r, --reset, --no-reset: deletes the client blocking status file (off by default)"
printf '\t%s\n' "--debug, --no-debug: enables debug mode (off by default)"
printf '\t%s\n' "-m, --update-method: choose if you want to use DynDNS2 or the DomRobot RPC-API (no default)"
printf '\t%s\n' "-i, --ip-mode: updates type A (IPv4) and AAAA (IPv6) records (no default)"
printf '\t%s\n' "-d, --domain: set the domain you want to update (no default)"
printf '\t%s\n' "-s, --service-provider: set your provider in case you are using DynDNS2 (no default)"
printf '\t%s\n' "-u, --username: depends on your selected update method and your provider (no default)"
printf '\t%s\n' "-p, --password: depends on your selected update method and your provider (no default)"
printf '\t%s\n' "-t, --token: depends on your selected update method and your provider (no default)"
printf '\t%s\n' "--interval: choose the seconds interval to run the script in a loop, minimum is 60 (no default)"
printf '\t%s\n' "-h, --help: Prints help"
}
# The parsing of the command-line
parse_commandline()
{
while test $# -gt 0
do
_key="$1"
case "$_key" in
# The version argurment doesn't accept a value,
# we expect the --version or -v, so we watch for them.
-v|--no-version|--version)
_arg_version="on"
test "${1:0:5}" = "--no-" && _arg_version="off"
;;
# We support getopts-style short arguments clustering,
# so as -v doesn't accept value, other short options may be appended to it, so we watch for -v*.
# After stripping the leading -v from the argument, we have to make sure
# that the first character that follows coresponds to a short option.
-v*)
_arg_version="on"
_next="${_key##-v}"
if test -n "$_next" -a "$_next" != "$_key"
then
{ begins_with_short_option "$_next" && shift && set -- "-v" "-${_next}" "$@"; } || die "The short option '$_key' can't be decomposed to ${_key:0:2} and -${_key:2}, because ${_key:0:2} doesn't accept value and '-${_key:2:1}' doesn't correspond to a short option."
fi
;;
# See the comment of option '--version' to see what's going on here - principle is the same.
-l|--no-link|--link)
_arg_link="on"
test "${1:0:5}" = "--no-" && _arg_link="off"
;;
# See the comment of option '-v' to see what's going on here - principle is the same.
-l*)
_arg_link="on"
_next="${_key##-l}"
if test -n "$_next" -a "$_next" != "$_key"
then
{ begins_with_short_option "$_next" && shift && set -- "-l" "-${_next}" "$@"; } || die "The short option '$_key' can't be decomposed to ${_key:0:2} and -${_key:2}, because ${_key:0:2} doesn't accept value and '-${_key:2:1}' doesn't correspond to a short option."
fi
;;
# See the comment of option '--version' to see what's going on here - principle is the same.
-r|--no-reset|--reset)
_arg_reset="on"
test "${1:0:5}" = "--no-" && _arg_reset="off"
;;
# See the comment of option '-v' to see what's going on here - principle is the same.
-r*)
_arg_reset="on"
_next="${_key##-r}"
if test -n "$_next" -a "$_next" != "$_key"
then
{ begins_with_short_option "$_next" && shift && set -- "-r" "-${_next}" "$@"; } || die "The short option '$_key' can't be decomposed to ${_key:0:2} and -${_key:2}, because ${_key:0:2} doesn't accept value and '-${_key:2:1}' doesn't correspond to a short option."
fi
;;
# See the comment of option '--version' to see what's going on here - principle is the same.
--no-debug|--debug)
_arg_debug="on"
test "${1:0:5}" = "--no-" && _arg_debug="off"
;;
# We support whitespace as a delimiter between option argument and its value.
# Therefore, we expect the --update-method or -m value.
# so we watch for --update-method and -m.
# Since we know that we got the long or short option,
# we just reach out for the next argument to get the value.
-m|--update-method)
test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
_arg_update_method="$2"
shift
;;
# We support the = as a delimiter between option argument and its value.
# Therefore, we expect --update-method=value, so we watch for --update-method=*
# For whatever we get, we strip '--update-method=' using the ${var##--update-method=} notation
# to get the argument value
--update-method=*)
_arg_update_method="${_key##--update-method=}"
;;
# We support getopts-style short arguments grouping,
# so as -m accepts value, we allow it to be appended to it, so we watch for -m*
# and we strip the leading -m from the argument string using the ${var##-m} notation.
-m*)
_arg_update_method="${_key##-m}"
;;
# See the comment of option '--update-method' to see what's going on here - principle is the same.
-i|--ip-mode)
test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
_arg_ip_mode="$2"
shift
;;
# See the comment of option '--update-method=' to see what's going on here - principle is the same.
--ip-mode=*)
_arg_ip_mode="${_key##--ip-mode=}"
;;
# See the comment of option '-m' to see what's going on here - principle is the same.
-i*)
_arg_ip_mode="${_key##-i}"
;;
# See the comment of option '--update-method' to see what's going on here - principle is the same.
-d|--domain)
test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
_arg_domain="$2"
shift
;;
# See the comment of option '--update-method=' to see what's going on here - principle is the same.
--domain=*)
_arg_domain="${_key##--domain=}"
;;
# See the comment of option '-m' to see what's going on here - principle is the same.
-d*)
_arg_domain="${_key##-d}"
;;
# See the comment of option '--update-method' to see what's going on here - principle is the same.
-s|--service-provider)
test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
_arg_service_provider="$2"
shift
;;
# See the comment of option '--update-method=' to see what's going on here - principle is the same.
--service-provider=*)
_arg_service_provider="${_key##--service-provider=}"
;;
# See the comment of option '-m' to see what's going on here - principle is the same.
-s*)
_arg_service_provider="${_key##-s}"
;;
# See the comment of option '--update-method' to see what's going on here - principle is the same.
-u|--username)
test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
_arg_username="$2"
shift
;;
# See the comment of option '--update-method=' to see what's going on here - principle is the same.
--username=*)
_arg_username="${_key##--username=}"
;;
# See the comment of option '-m' to see what's going on here - principle is the same.
-u*)
_arg_username="${_key##-u}"
;;
# See the comment of option '--update-method' to see what's going on here - principle is the same.
-p|--password)
test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
_arg_password="$2"
shift
;;
# See the comment of option '--update-method=' to see what's going on here - principle is the same.
--password=*)
_arg_password="${_key##--password=}"
;;
# See the comment of option '-m' to see what's going on here - principle is the same.
-p*)
_arg_password="${_key##-p}"
;;
# See the comment of option '--update-method' to see what's going on here - principle is the same.
-t|--token)
test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
_arg_token="$2"
shift
;;
# See the comment of option '--update-method=' to see what's going on here - principle is the same.
--token=*)
_arg_token="${_key##--token=}"
;;
# See the comment of option '-m' to see what's going on here - principle is the same.
-t*)
_arg_token="${_key##-t}"
;;
# See the comment of option '--update-method' to see what's going on here - principle is the same.
--interval)
test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
_arg_interval="$2"
shift
;;
# See the comment of option '--update-method=' to see what's going on here - principle is the same.
--interval=*)
_arg_interval="${_key##--interval=}"
;;
# See the comment of option '--version' to see what's going on here - principle is the same.
-h|--help)
print_help
exit 0
;;
# See the comment of option '-v' to see what's going on here - principle is the same.
-h*)
print_help
exit 0
;;
*)
_PRINT_HELP=yes die "FATAL ERROR: Got an unexpected argument '$1'" 1
;;
esac
shift
done
}
# Now call all the functions defined above that are needed to get the job done
parse_commandline "$@"
# OTHER STUFF GENERATED BY Argbash
### END OF CODE GENERATED BY Argbash (sortof) ### ])
# [ <-- needed because of Argbash
# vvv PLACE YOUR CODE HERE vvv
# PUT YOUR CODE HERE
# ^^^ TERMINATE YOUR CODE BEFORE THE BOTTOM ARGBASH MARKER ^^^
# ] <-- needed because of Argbash

826
dynb.sh Executable file
View File

@@ -0,0 +1,826 @@
#!/usr/bin/env bash
## Copyright (c) 2021 Eduard Veit
## All rights reserved. This program and the accompanying materials
## are made available under the terms of the MIT license
## which accompanies this distribution, and is available at
## https://opensource.org/licenses/MIT
###################
## Configuration ##
###################
#DYNB_DYN_DOMAIN=
## service provider could be deSEC, duckdns, dynv6, inwx
#DYNB_SERVICE_PROVIDER=
## update method options: domrobot, dyndns
#DYNB_UPDATE_METHOD=
## ip mode could be either: 4, 6 or dual for dualstack
#DYNB_IP_MODE=
## If you are using the DomRobot RPC-API enter your credentials for the web interface login here
## If you are using the DynDNS2 protocol enter your credentials here
#DYNB_USERNAME=
#DYNB_PASSWORD=
## or use a token
#DYNB_TOKEN=
## TTL (time to live) for the DNS record
## This setting is only relevant for API based record updates (not DnyDNS2!)
## minimum allowed TTL value by inwx is 300 (5 minutes)
TTL=300
## The IP-Check sites (some sites have different urls for v4 and v6)
## Pro tip: use your own ip check server for privacy
## it could be as simple as that...
## create an index.php with <?php echo $_SERVER'REMOTE_ADDR'; ?>
_ipv4_checker=api64.ipify.org
_ipv6_checker=api64.ipify.org
## An exernal DNS check server prevents wrong info from local DNS servers/resolvers
_DNS_checkServer=
## if you are actively using multiple network interfaces you might want to specify this
## normally the default value is okay
#_network_interface=eth0
_network_interface=
######################################################
## You don't need to change the following variables ##
_INWX_JSON_API_URL=https://api.domrobot.com/jsonrpc/
_internet_connectivity_test_server=https://www.google.de
_new_IPv4=
_new_IPv6=
_dns_records=
_main_domain=
_is_IPv4_enabled=false
_is_IPv6_enabled=false
_interface_str=
_status=
_eventTime=0
_errorCounter=0
_response=
_statusHostname=
_statusUsername=
_statusPassword=
_version=0.3.2
_userAgent="DynB/$_version github.com/EV21/dynb"
_configFile=$HOME/.local/share/dynb/.env
_statusFile=/tmp/dynb.status
_debug=0
_minimum_looptime=60
_loopMode=0
# Ansi color code variables
yellow_color="\e[0;33m"
green_color="\e[0;92m"
expand_bg="\e[K"
red_color_bg="\e[0;101m${expand_bg}"
bold="\e[1m"
reset_color_modification="\e[0m"
REGEX_IPv4="^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$"
REGEX_IPv6="^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$"
function is_IPv4_address
{
local ip=$1
if [[ $ip =~ $REGEX_IPv4 ]]
then return 0
else return 1
fi
}
function is_IPv6_address
{
local ip=$1
if [[ $ip =~ $REGEX_IPv6 ]]
then return 0
else return 1
fi
}
function loopMode
{
if [[ $_loopMode -eq 1 ]]
then return 0
else return 1
fi
}
function debugMode
{
if [[ $_debug -eq 1 ]]
then return 0
else return 1
fi
}
function infoMessage
{
echo -e "${green_color}$(logtime) INFO: $*${reset_color_modification}"
}
function debugMessage
{
if debugMode
then echo -e "${yellow_color}$(logtime) DEBUG: ${*}${reset_color_modification}"
fi
}
function errorMessage
{
echo -e "${red_color_bg}${bold}$(logtime) ERROR: $*${reset_color_modification}" >&2
}
function logtime
{
LOGTIME=$(date "+%Y-%m-%d %H:%M:%S")
echo "[$LOGTIME]"
}
# The main domain as an identifier for the dns zone is required for the updateRecord call
function getMainDomain
{
request=$(
echo "{}" |
jq '(.method="nameserver.list")' |
jq "(.params.user=\"$DYNB_USERNAME\")" |
jq "(.params.pass=\"$DYNB_PASSWORD\")"
)
_response=$(
curl --silent \
"$_interface_str" \
--user-agent "$_userAgent" \
--header "Content-Type: application/json" \
--request POST $_INWX_JSON_API_URL \
--data "$request" | jq ".resData.domains[] | select(inside(.domain=\"$DYNB_DYN_DOMAIN\"))"
)
_main_domain=$(echo "$_response" | jq --raw-output '.domain')
}
function fetchDNSRecords
{
request=$(
echo "{}" |
jq '(.method="'nameserver.info'")' |
jq "(.params.user=\"$DYNB_USERNAME\")" |
jq "(.params.pass=\"$DYNB_PASSWORD\")" |
jq "(.params.domain=\"$_main_domain\")" |
jq "(.params.name=\"$DYNB_DYN_DOMAIN\")"
)
_response=$(
curl --silent \
"$_interface_str" \
--user-agent "$_userAgent" \
--header "Content-Type: application/json" \
--request POST $_INWX_JSON_API_URL \
--data "$request"
)
_dns_records=$(echo "$_response" | jq '.resData.record[]')
}
# requires parameter A or AAAA
# result to stdout
function getRecordID
{
echo "$_dns_records" |
jq "select(.type == \"${1}\") | .id"
}
# requires parameter A or AAAA
# result to stdout
function getDNSIP() {
echo "$_dns_records" |
jq --raw-output "select(.type == \"${1}\") | .content"
}
# requires parameter
# 1. param: 4 or 6 for ip version
# 2. param: IP check server address
# result to stdout
function getRemoteIP
{
local ip_version=$1
local ip_check_server=$2
curl --silent "$_interface_str" --user-agent "$_userAgent" \
--ipv"${ip_version}" --location "${ip_check_server}"
local curls_status_code=$?
# shellcheck disable=2181
if [[ $curls_status_code -gt 0 ]]; then
errorMessage "IPCheck (getRemoteIP $ip_version) request failed"
exit 1
fi
}
# requires parameter
# 1. param: 4 or 6 as ip version
function updateRecord
{
local ip_version=$1
if [[ ${ip_version} == 4 ]]
then
ID=$(getRecordID A)
IP=$_new_IPv4
fi
if [[ ${ip_version} == 6 ]]
then
ID=$(getRecordID AAAA)
IP=$_new_IPv6
fi
if [[ $IP != "" ]]
then
request=$(
echo "{}" |
jq '(.method="nameserver.updateRecord")' |
jq "(.params.user=\"$DYNB_USERNAME\")" |
jq "(.params.pass=\"$DYNB_PASSWORD\")" |
jq "(.params.id=\"$ID\")" |
jq "(.params.content=\"$IP\")" |
jq "(.params.ttl=\"$TTL\")"
)
_response=$(
curl --silent \
"$_interface_str" \
--user-agent "$_userAgent" \
--header "Content-Type: application/json" \
--request POST $_INWX_JSON_API_URL \
--data "$request"
)
infoMessage "$(echo "$_response" | jq --raw-output '.msg')\n Domain: $DYNB_DYN_DOMAIN\n New IPv${1}: $IP"
fi
}
# using DynDNS2 protocol
function dynupdate
{
# default parameter values
myip_str=myip
myipv6_str=myipv6
INWX_DYNDNS_UPDATE_URL="https://dyndns.inwx.com/nic/update?"
DESEC_DYNDNS_UPDATE_URL="https://update.dedyn.io/?"
DUCKDNS_DYNDNS_UPDATE_URL="https://www.duckdns.org/update?domains=$DYNB_DYN_DOMAIN&token=$DYNB_TOKEN&"
DYNV6_DYNDNS_UPDATE_URL="https://dynv6.com/api/update?zone=$DYNB_DYN_DOMAIN&token=$DYNB_TOKEN&"
case $DYNB_SERVICE_PROVIDER in
inwx* | INWX*)
dyndns_update_url=$INWX_DYNDNS_UPDATE_URL
;;
deSEC* | desec* | dedyn*)
dyndns_update_url="${DESEC_DYNDNS_UPDATE_URL}"
;;
dynv6*)
dyndns_update_url="${DYNV6_DYNDNS_UPDATE_URL}"
myip_str=ipv4
myipv6_str=ipv6
;;
DuckDNS* | duckdns*)
dyndns_update_url="${DUCKDNS_DYNDNS_UPDATE_URL}"
myip_str=ipv4
myipv6_str=ipv6
;;
*)
errorMessage "$DYNB_SERVICE_PROVIDER is not supported"
exit 1
;;
esac
# pre encode ip parameters
if [[ $_is_IPv4_enabled == true ]] && [[ $_is_IPv6_enabled == true ]]; then
dyndns_update_url="${dyndns_update_url}${myip_str}=${_new_IPv4}&${myipv6_str}=${_new_IPv6}"
fi
if [[ $_is_IPv4_enabled == true ]] && [[ $_is_IPv6_enabled == false ]]; then
dyndns_update_url="${dyndns_update_url}${myip_str}=${_new_IPv4}"
fi
if [[ $_is_IPv4_enabled == false ]] && [[ $_is_IPv6_enabled == true ]]; then
dyndns_update_url="${dyndns_update_url}${myipv6_str}=${_new_IPv6}"
fi
debugMessage "Update URL was: $dyndns_update_url"
## request ##
case $DYNB_SERVICE_PROVIDER in
inwx* | INWX*)
_response=$(curl --silent "$_interface_str" \
--user-agent "$_userAgent" \
--user "$DYNB_USERNAME":"$DYNB_PASSWORD" \
"${dyndns_update_url}")
;;
deSEC* | desec* | dedyn*)
_response=$(curl --silent "$_interface_str" \
--user-agent "$_userAgent" \
--header "Authorization: Token $DYNB_TOKEN" \
--get --data-urlencode "hostname=$DYNB_DYN_DOMAIN" \
"${dyndns_update_url}")
;;
dynv6* | duckDNS* | duckdns*)
_response=$(
curl --silent "$_interface_str" \
--user-agent "$_userAgent" \
"${dyndns_update_url}"
)
;;
esac
case $_response in
good* | OK* | "addresses updated")
if [[ $_response == "good 127.0.0.1" ]]; then
errorMessage "$_response: Request ignored."
return 1
else
infoMessage "$_response: The DynDNS update has been executed."
_errorCounter=0
return 0
fi
;;
nochg*)
infoMessage "$_response: Nothing has changed, IP addresses are still up to date."
return 1
;;
abuse)
errorMessage "$_response: Username is blocked due to abuse."
return 1
;;
badauth | 401)
errorMessage "$_response: Invalid username password combination."
return 1
;;
badagent)
errorMessage "$_response: Client disabled. Something is very wrong!"
return 1
;;
!donator)
errorMessage "$_response: An update request was sent, including a feature that is not available to that particular user such as offline options."
return 1
;;
!yours)
errorMessage "$_response: The domain does not belong to your user account"
return 1
;;
notfqdn)
errorMessage "$_response: Hostname $DYNB_DYN_DOMAIN is invalid"
return 1
;;
nohost)
errorMessage "$_response: Hostname supplied does not exist under specified account, enter new login credentials before performing an additional request."
return 1
;;
numhost)
errorMessage "$_response: Too many hostnames have been specified for this update"
return 1
;;
dnserr)
errorMessage "$_response: There is an internal error in the dyndns update system. Retry update no sooner than 30 minutes."
return 1
;;
911 | 5*)
errorMessage "$_response: A fatal error on provider side such as a database outage. Retry update no sooner than 30 minutes."
return 1
;;
*)
if [[ "$_response" == "$_status" ]]; then
errorMessage "An unknown response code has been received. $_response"
return 1
else
errorMessage "unknown respnse code: $_response"
return 0
fi
;;
esac
}
function setStatus
{
echo "_status=$1; _eventTime=$2; _errorCounter=$3; _statusHostname=$4; _statusUsername=$5; _statusPassword=$6" >/tmp/dynb.status
}
# handle errors from past update requests
function checkStatus
{
case $_status in
nochg*)
if [[ _errorCounter -gt 1 ]]; then
errorMessage "The update client was spamming unnecessary update requests, something might be wrong with your IP-Check site."
errorMessage "Fix your config an then delete $_statusFile or restart your docker container"
return 1
fi
;;
nohost | !yours)
if [[ "$_statusHostname" == "$DYNB_DYN_DOMAIN" && ("$_statusUsername" == "$DYNB_USERNAME" || $_statusUsername == "$DYNB_TOKEN") ]]; then
errorMessage "Hostname supplied does not exist under specified account, enter new login credentials before performing an additional request."
return 1
else rm "$_statusFile"
fi
return 0
;;
badauth | 401)
if [[ "$_statusUsername" == "$DYNB_USERNAME" && "$_statusPassword" == "$DYNB_PASSWORD" ]]; then
errorMessage "Invalid username password combination."
return 1
else rm "$_statusFile"
fi
return 0
;;
badagent)
errorMessage "Client is deactivated by provider."
echo "Fix your config and then manually remove $_statusFile to reset the client blockade."
echo "If it still fails file an issue at github or try another client :)"
return 1
;;
!donator)
errorMessage "An update request was sent, including a feature that is not available to that particular user such as offline options."
echo "Fix your config and then manually remove $_statusFile to reset the client blockade"
echo "If it still fails file an issue at github or try another client :)"
return 1
;;
abuse)
errorMessage "Username is blocked due to abuse."
echo "Fix your config and then manually remove $_statusFile to reset the client blockade"
echo "If it still fails file an issue at github or try another client :)"
return 1
;;
911 | 5*)
delta=$(($(date +%s) - _eventTime))
if [[ $delta -lt 1800 ]]
then
errorMessage "$_status: The provider currently has an fatal error. DynB will wait for next update until 30 minutes have passed since last request, $(date --date=@$delta -u +%M) minutes already passed."
return 1
else rm "$_statusFile"
fi
return 0
;;
*)
if [[ _errorCounter -gt 1 ]]
then
errorMessage "An unknown response code has repeatedly been received. $_response"
return 1
else return 0
fi
;;
esac
}
# requires parameter
# 1. param: 4 or 6 for IP version
function ipHasChanged
{
local ip_version=$1
case ${ip_version} in
4)
remote_ip=$(getRemoteIP 4 $_ipv4_checker)
if ! is_IPv4_address "$remote_ip"
then
errorMessage "The response from the IP check server is not an IPv4 address: $remote_ip"
return 1
fi
if [[ $DYNB_UPDATE_METHOD == domrobot ]]
then dns_ip=$(getDNSIP A)
else
if [[ -n $_DNS_checkServer ]]
then dig_response=$(dig @"${_DNS_checkServer}" in a +short "$DYNB_DYN_DOMAIN")
else dig_response=$(dig in a +short "$DYNB_DYN_DOMAIN")
fi
if [[ $dig_response == ";; connection timed out; no servers could be reached" ]]
then
errorMessage "DNS request failed $dig_response"
return 1
fi
# If the dns resolver lists multiple records in the answer section we filter the first line
# using short option "-n" and not "--lines" because of alpines limited BusyBox head command
dns_ip=$(echo "$dig_response" | head -n 1)
fi
_new_IPv4=$remote_ip
debugMessage "IPv4 from remote IP check server: $_new_IPv4, IPv4 from DNS: $dns_ip"
;;
6)
remote_ip=$(getRemoteIP 6 $_ipv6_checker)
if ! is_IPv6_address "$remote_ip"
then
errorMessage "The response from the IP check server is not an IPv6 address: $remote_ip"
return 1
fi
if [[ $DYNB_UPDATE_METHOD == domrobot ]]
then dns_ip=$(getDNSIP AAAA)
else
if [[ -n $_DNS_checkServer ]]
then dig_response=$(dig @"${_DNS_checkServer}" in aaaa +short "$DYNB_DYN_DOMAIN")
else dig_response=$(dig in aaaa +short "$DYNB_DYN_DOMAIN")
fi
exitcode=$?
if [[ $exitcode -gt 0 ]]
then
errorMessage "DNS request failed with exit code: $exitcode $dig_response"
return 1
fi
# If the dns server lists multiple records in the answer section we filter the first line
dns_ip=$(echo "$dig_response" | head -n 1)
fi
_new_IPv6=$remote_ip
debugMessage "IPv6 from remote IP check server: $_new_IPv6, IPv4 from DNS: $dns_ip"
;;
*) ;;
esac
if [[ "$remote_ip" == "$dns_ip" ]]
then return 1
else
case ${ip_version} in
4) infoMessage "New IPv4: $_new_IPv4 old was: $dns_ip";;
6) infoMessage "New IPv6: $_new_IPv6 old was: $dns_ip";;
esac
return 0
fi
}
function handleParameters
{
# shellcheck disable=SC2154
if [[ $_arg_version == "on" ]]
then echo $_version; exit 0
fi
# shellcheck disable=SC2154
if [[ $_arg_link == "on" ]]
then ln --verbose --symbolic "$(realpath "$0")" "$HOME/.local/bin/dynb"; exit 0
fi
# shellcheck disable=SC2154
if [[ $_arg_reset == "on" ]] && test -f "$_statusFile"
then rm --verbose "$_statusFile"; exit 0
fi
# shellcheck disable=SC2154
if [[ $_arg_debug == "on" ]]
then _debug=1
fi
# shellcheck disable=SC2154
if [[ $_arg_update_method != "" ]]
then DYNB_UPDATE_METHOD=$_arg_update_method
fi
# shellcheck disable=SC2154
if [[ $_arg_ip_mode != "" ]]
then DYNB_IP_MODE=$_arg_ip_mode
fi
# shellcheck disable=SC2154
if [[ $_arg_domain != "" ]]
then DYNB_DYN_DOMAIN=$_arg_domain
fi
# shellcheck disable=SC2154
if [[ $_arg_service_provider != "" ]]
then DYNB_SERVICE_PROVIDER=$_arg_service_provider
fi
# shellcheck disable=SC2154
if [[ $_arg_username != "" ]]
then DYNB_USERNAME=$_arg_username
fi
# shellcheck disable=SC2154
if [[ $_arg_password != "" ]]
then DYNB_PASSWORD=$_arg_password
fi
# shellcheck disable=SC2154
if [[ $_arg_token != "" ]]
then DYNB_TOKEN=$_arg_token
fi
# shellcheck disable=SC2154
if [[ $_arg_interval != "" ]]
then DYNB_INTERVAL=$_arg_interval
fi
if [[ -z $DYNB_INTERVAL ]]
then _loopMode=0
elif [[ $DYNB_INTERVAL -lt _minimum_looptime ]]
then
DYNB_INTERVAL=$_minimum_looptime
_loopMode=1
else _loopMode=1
fi
if [[ $_network_interface != "" ]]
then _interface_str="--interface $_network_interface"
fi
if [[ $DYNB_IP_MODE == d* ]]
then
_is_IPv4_enabled=true
_is_IPv6_enabled=true
fi
if [[ $DYNB_IP_MODE == *4* ]]
then _is_IPv4_enabled=true
fi
if [[ $DYNB_IP_MODE == *6* ]]
then _is_IPv6_enabled=true
fi
if [[ $DYNB_DEBUG == true ]]
then _debug=1
fi
# shellcheck disable=SC2154
if [[ -n $DYNB_IPv4_CHECK_SITE ]]
then _ipv4_checker=$DYNB_IPv4_CHECK_SITE
fi
# shellcheck disable=SC2154
if [[ -n $DYNB_IPv6_CHECK_SITE ]]
then _ipv6_checker=$DYNB_IPv6_CHECK_SITE
fi
if [[ -n $DYNB_DNS_CHECK_SERVER ]]
then _DNS_checkServer=$DYNB_DNS_CHECK_SERVER
fi
return 0
}
function checkDependencies
{
failCounter=0
for i in curl dig; do
if ! command -v $i >/dev/null 2>&1
then
errorMessage "could not find \"$i\", DynB depends on it. "
((failCounter++))
fi
done
[[ -x $(command -v jq 2>/dev/null) ]] || {
if [[ $DYNB_UPDATE_METHOD != dyndns* ]]
then
echo "This script depends on jq and it is not available." >&2
((failCounter++))
fi
}
if [[ failCounter -gt 0 ]]
then exit 1
fi
}
function doUnsets
{
unset _network_interface
unset _DNS_checkServer
unset _dns_records
unset _has_getopt
unset _help_message
unset _INWX_JSON_API_URL
unset _ipv4_checker
unset _ipv6_checker
unset _is_IPv4_enabled
unset _is_IPv6_enabled
unset _main_domain
unset _new_IPv4
unset _new_IPv6
unset _version
unset DYNB_DYN_DOMAIN
unset DYNB_USERNAME
unset DYNB_PASSWORD
unset DYNB_TOKEN
unset DYNB_SERVICE_PROVIDER
unset DYNB_IP_MODE
unset DYNB_INTERVAL
unset DYNB_IPv4_CHECK_SITE
unset DYNB_IPv6_CHECK_SITE
unset DYNB_DNS_CHECK_SERVER
unset DYNB_DEBUG
}
function doDomrobotUpdates
{
getMainDomain
fetchDNSRecords
if [[ $_is_IPv4_enabled == true ]]
then
if ipHasChanged 4
then updateRecord 4
else debugMessage "Skip IPv4 record update, it is already up to date"
fi
fi
if [[ $_is_IPv6_enabled == true ]]
then
if ipHasChanged 6
then updateRecord 6
else debugMessage "Skip IPv6 record update, it is already up to date"
fi
fi
}
function doDynDNS2Updates
{
changed=0
if [[ $_is_IPv4_enabled == true ]] && ipHasChanged 4
then ((changed += 1))
fi
if [[ $_is_IPv6_enabled == true ]] && ipHasChanged 6
then ((changed += 1))
fi
if [[ $changed -gt 0 ]]
then
if checkStatus
then
debugMessage "checkStatus has no errors, try update"
if dynupdate
then debugMessage "DynDNS2 update success"
else
debugMessage "Save new status after dynupdate has failed"
setStatus "$_response" "$(date +%s)" $((_errorCounter += 1)) "$DYNB_DYN_DOMAIN" "${DYNB_USERNAME}" "${DYNB_PASSWORD}${DYNB_TOKEN}"
fi
else debugMessage "Skip DynDNS2 update, checkStatus fetched previous error."
fi
else debugMessage "Skip DynDNS2 update, IPs are up to date or there is a connection problem"
fi
}
function doUpdates
{
if [[ $DYNB_UPDATE_METHOD == "domrobot" ]]
then doDomrobotUpdates
elif [[ $DYNB_UPDATE_METHOD == "dyndns" ]]
then doDynDNS2Updates
fi
}
function ipv6_is_not_working
{
curl --ipv6 --head --silent --max-time 5 $_internet_connectivity_test_server > /dev/null
status_code=$?
if test $status_code -gt 0
then return 0
else return 1
fi
}
function ipv4_is_not_working
{
curl --ipv4 --head --silent --max-time 5 $_internet_connectivity_test_server > /dev/null
status_code=$?
if test $status_code -gt 0
then return 0
else return 1
fi
}
function check_internet_connection
{
if [[ $_is_IPv4_enabled == true ]]
then
if ipv4_is_not_working
then
_is_IPv4_enabled="false"
errorMessage "Your IPv4 internet connection does not work."
fi
fi
if [[ $_is_IPv6_enabled == true ]]
then
if ipv6_is_not_working
then
_is_IPv6_enabled="false"
errorMessage "Your IPv6 internet connection does not work."
fi
fi
}
function main
{
# shellcheck disable=SC1091,SC1090
source "$(dirname "$(realpath "$0")")/dynb-parsing.sh"
# shellcheck source=.env
if test -f "$_configFile"
then
# shellcheck disable=SC1091
source "$_configFile"
else
alternativeConfig="$(dirname "$(realpath "$0")")/.env"
if test -f "$alternativeConfig"
then
# shellcheck disable=SC1091
source "$alternativeConfig"
fi
fi
if test -f "$_statusFile"
then
debugMessage "read previous status file"
# shellcheck disable=SC1090
source "$_statusFile"
fi
## parameters and checks
handleParameters
checkDependencies
check_internet_connection
if loopMode
then
while :
do
doUpdates
debugMessage "wait $DYNB_INTERVAL seconds until next check"
sleep $DYNB_INTERVAL
done
else doUpdates
fi
doUnsets
return 0
}
main "${@}"
exit $?

21
example.env Normal file
View File

@@ -0,0 +1,21 @@
###################
## Configuration ##
###################
DYNB_DYN_DOMAIN=dyndns.example.com
## service provider could be deSEC, duckdns, dynv6, inwx
DYNB_SERVICE_PROVIDER=inwx
## update method options: domrobot, dyndns
DYNB_UPDATE_METHOD=domrobot
## ip mode could be either: 4, 6 or dual for dualstack
DYNB_IP_MODE=64
## If you are using the DomRobot RPC-API enter your credentials for the web interface login here
## If you are using the DynDNS2 protocol enter your credentials here
DYNB_USERNAME=User42
DYNB_PASSWORD=SuperSecretPassword
## or use a token
DYNB_TOKEN=

114
man/dynb.1 Normal file
View File

@@ -0,0 +1,114 @@
.\" Man page generated from reStructuredText.
.
.TH MAN 1 "2021-04-21" "" ""
.SH NAME
man \- DynB - dynamic DNS update script for bash
.
.nr rst2man-indent-level 0
.
.de1 rstReportMargin
\\$1 \\n[an-margin]
level \\n[rst2man-indent-level]
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
-
\\n[rst2man-indent0]
\\n[rst2man-indent1]
\\n[rst2man-indent2]
..
.de1 INDENT
.\" .rstReportMargin pre:
. RS \\$1
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
. nr rst2man-indent-level +1
.\" .rstReportMargin post:
..
.de UNINDENT
. RE
.\" indent \\n[an-margin]
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
.nr rst2man-indent-level -1
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.SH SYNOPSIS
.sp
\fBman [\-\-version] [\-\-link] [\-\-reset] [\-\-debug] [\-\-update\-method UPDATE\-METHOD] [\-\-ip\-mode IP\-MODE] [\-\-domain DOMAIN] [\-\-service\-provider SERVICE\-PROVIDER] [\-\-username USERNAME] [\-\-password PASSWORD] [\-\-token TOKEN] [\-\-interval INTERVAL] [\-\-help]\fP
.SH DESCRIPTION
.sp
IPv4 (A) and IPv6 (AAAA) record updates are supported.
.SH ARGUMENTS
.INDENT 0.0
.TP
.B \-v\fP,\fB \-\-version
outputs the client version.
[Default: off]
.sp
.TP
.B \-l\fP,\fB \-\-link
links to your script at ~/.local/bin/dynb.
[Default: off]
.sp
.TP
.B \-r\fP,\fB \-\-reset
deletes the client blocking status file.
[Default: off]
.sp
.TP
.B \-\-debug
enables debug mode.
[Default: off]
.sp
.TP
.BI \-m \ UPDATE\-METHOD\fR,\fB \ \-\-update\-method \ UPDATE\-METHOD
choose if you want to use DynDNS2 or the DomRobot RPC\-API.
.sp
dyndns | domrobot
.TP
.BI \-i \ IP\-MODE\fR,\fB \ \-\-ip\-mode \ IP\-MODE
updates type A (IPv4) and AAAA (IPv6) records.
.sp
4 | 6 | dual
.TP
.BI \-d \ DOMAIN\fR,\fB \ \-\-domain \ DOMAIN
set the domain you want to update.
.sp
dyndns.example.com
.TP
.BI \-s \ SERVICE\-PROVIDER\fR,\fB \ \-\-service\-provider \ SERVICE\-PROVIDER
set your provider in case you are using DynDNS2.
.sp
deSEC | duckdns | dynv6 | inwx
.TP
.BI \-u \ USERNAME\fR,\fB \ \-\-username \ USERNAME
depends on your selected update method and your provider.
.sp
e.g. user42
.TP
.BI \-p \ PASSWORD\fR,\fB \ \-\-password \ PASSWORD
depends on your selected update method and your provider.
.sp
e.g. SuperSecretPassword
.TP
.BI \-t \ TOKEN\fR,\fB \ \-\-token \ TOKEN
depends on your selected update method and your provider.
.sp
YourProviderGivenToken
.TP
.BI \-\-interval \ INTERVAL
choose the seconds interval to run the script in a loop, minimum is 60.
.sp
.TP
.B \-h\fP,\fB \-\-help
Prints help.
.sp
.UNINDENT
.SH AUTHOR
Eduard Veit
.\" Generated by docutils manpage writer.
.

35
man/man-defs.rst Normal file
View File

@@ -0,0 +1,35 @@
.. |AUTHOR| replace:: Eduard Veit
.. |VERSION| replace:: \
.. |MAN_SECTION| replace:: 1
.. |DESCRIPTION| replace::
IPv4 (A) and IPv6 (AAAA) record updates are supported.
.. |OPTION_VERSION| replace:: \
.. |OPTION_LINK| replace:: \
.. |OPTION_RESET| replace:: \
.. |OPTION_DEBUG| replace:: \
.. |OPTION_UPDATE_METHOD| replace:: dyndns | domrobot
.. |OPTION_IP_MODE| replace:: 4 | 6 | dual
.. |OPTION_DOMAIN| replace:: dyndns.example.com
.. |OPTION_SERVICE_PROVIDER| replace:: deSEC | duckdns | dynv6 | inwx
.. |OPTION_USERNAME| replace:: e.g. user42
.. |OPTION_PASSWORD| replace:: e.g. SuperSecretPassword
.. |OPTION_TOKEN| replace:: YourProviderGivenToken
.. |OPTION_INTERVAL| replace:: \
.. |OPTION_HELP| replace:: \

88
man/man.rst Normal file
View File

@@ -0,0 +1,88 @@
.. include:: man-defs.rst
===
man
===
-----------------------------------------
DynB - dynamic DNS update script for bash
-----------------------------------------
:Author: |AUTHOR|
:Date: 2021-04-21
:Version: |VERSION|
:Manual section: |MAN_SECTION|
SYNOPSIS
========
``man [--version] [--link] [--reset] [--debug] [--update-method UPDATE-METHOD] [--ip-mode IP-MODE] [--domain DOMAIN] [--service-provider SERVICE-PROVIDER] [--username USERNAME] [--password PASSWORD] [--token TOKEN] [--interval INTERVAL] [--help]``
DESCRIPTION
===========
|DESCRIPTION|
ARGUMENTS
=========
-v, --version outputs the client version.
[Default: off]
|OPTION_VERSION|
-l, --link links to your script at ~/.local/bin/dynb.
[Default: off]
|OPTION_LINK|
-r, --reset deletes the client blocking status file.
[Default: off]
|OPTION_RESET|
--debug enables debug mode.
[Default: off]
|OPTION_DEBUG|
-m UPDATE-METHOD, --update-method UPDATE-METHOD choose if you want to use DynDNS2 or the DomRobot RPC-API.
|OPTION_UPDATE_METHOD|
-i IP-MODE, --ip-mode IP-MODE updates type A (IPv4) and AAAA (IPv6) records.
|OPTION_IP_MODE|
-d DOMAIN, --domain DOMAIN set the domain you want to update.
|OPTION_DOMAIN|
-s SERVICE-PROVIDER, --service-provider SERVICE-PROVIDER set your provider in case you are using DynDNS2.
|OPTION_SERVICE_PROVIDER|
-u USERNAME, --username USERNAME depends on your selected update method and your provider.
|OPTION_USERNAME|
-p PASSWORD, --password PASSWORD depends on your selected update method and your provider.
|OPTION_PASSWORD|
-t TOKEN, --token TOKEN depends on your selected update method and your provider.
|OPTION_TOKEN|
--interval INTERVAL choose the seconds interval to run the script in a loop, minimum is 60.
|OPTION_INTERVAL|
-h, --help Prints help.
|OPTION_HELP|